mirror of
				https://github.com/fairwaves/UHD-Fairwaves.git
				synced 2025-11-04 05:53:17 +00:00 
			
		
		
		
	Compare commits
	
		
			92 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2a89674c56 | ||
| 
						 | 
					77e9066bf8 | ||
| 
						 | 
					1e43f04790 | ||
| 
						 | 
					cbff81745b | ||
| 
						 | 
					57d5ca4b51 | ||
| 
						 | 
					b78ceeb9b1 | ||
| 
						 | 
					fcd92d8f50 | ||
| 
						 | 
					2727f62ab8 | ||
| 
						 | 
					75c3380ccf | ||
| 
						 | 
					80e65f35cf | ||
| 
						 | 
					f16599cfa9 | ||
| 
						 | 
					e6b82c85e6 | ||
| 
						 | 
					8e5b9e8cba | ||
| 
						 | 
					fc9ecf7709 | ||
| 
						 | 
					913f19357b | ||
| 
						 | 
					d96cea8000 | ||
| 
						 | 
					c401071ea6 | ||
| 
						 | 
					b5b351e535 | ||
| 
						 | 
					1cf16e57f7 | ||
| 
						 | 
					b78a96661a | ||
| 
						 | 
					5452567fd9 | ||
| 
						 | 
					47b3e1aaea | ||
| 
						 | 
					1e042157be | ||
| 
						 | 
					4cb89755bb | ||
| 
						 | 
					efba897843 | ||
| 
						 | 
					35ccac9f54 | ||
| 
						 | 
					963f3e90dc | ||
| 
						 | 
					0e8abb2068 | ||
| 
						 | 
					e069b6d39c | ||
| 
						 | 
					28f423567b | ||
| 
						 | 
					67303bcbcc | ||
| 
						 | 
					0fc60d2275 | ||
| 
						 | 
					ce5f31f713 | ||
| 
						 | 
					802b24c565 | ||
| 
						 | 
					56c4174ad4 | ||
| 
						 | 
					0073096b27 | ||
| 
						 | 
					7727cd283c | ||
| 
						 | 
					ff13c7d73a | ||
| 
						 | 
					0bee662b81 | ||
| 
						 | 
					7b955574a5 | ||
| 
						 | 
					4b102f0c4c | ||
| 
						 | 
					0d7b517923 | ||
| 
						 | 
					aec754eb07 | ||
| 
						 | 
					756cb9a74c | ||
| 
						 | 
					0b105893a4 | ||
| 
						 | 
					41b93944a0 | ||
| 
						 | 
					a872102cd9 | ||
| 
						 | 
					89277c6e56 | ||
| 
						 | 
					1773223b81 | ||
| 
						 | 
					24527ece28 | ||
| 
						 | 
					7834aa4709 | ||
| 
						 | 
					f61d102791 | ||
| 
						 | 
					d8b282404b | ||
| 
						 | 
					4cd94528aa | ||
| 
						 | 
					fb59ec9bd2 | ||
| 
						 | 
					9577f31bfb | ||
| 
						 | 
					df513b81e5 | ||
| 
						 | 
					471eedbb6a | ||
| 
						 | 
					10e758be8c | ||
| 
						 | 
					9c20b295d5 | ||
| 
						 | 
					14e58af929 | ||
| 
						 | 
					9a752069e7 | ||
| 
						 | 
					283dae8214 | ||
| 
						 | 
					df90e2b342 | ||
| 
						 | 
					0b1c002f32 | ||
| 
						 | 
					313488b2d3 | ||
| 
						 | 
					189828f29c | ||
| 
						 | 
					f913c39c0c | ||
| 
						 | 
					84c5426fd8 | ||
| 
						 | 
					ac22360125 | ||
| 
						 | 
					6bf16b4e66 | ||
| 
						 | 
					451cc221be | ||
| 
						 | 
					73ab35d46c | ||
| 
						 | 
					a189138078 | ||
| 
						 | 
					2d83e6caed | ||
| 
						 | 
					9becc6bdf8 | ||
| 
						 | 
					a5cee8c757 | ||
| 
						 | 
					5001cb8f95 | ||
| 
						 | 
					1337b13d56 | ||
| 
						 | 
					93aaef643d | ||
| 
						 | 
					a33b3e92e4 | ||
| 
						 | 
					08c05c5f1b | ||
| 
						 | 
					000a874189 | ||
| 
						 | 
					7b15bc857b | ||
| 
						 | 
					e14c900821 | ||
| 
						 | 
					271b8a045e | ||
| 
						 | 
					30a4557825 | ||
| 
						 | 
					32f82d646b | ||
| 
						 | 
					c3754d5048 | ||
| 
						 | 
					052a817f30 | ||
| 
						 | 
					8e92e8ca43 | ||
| 
						 | 
					e64d3d5940 | 
							
								
								
									
										30
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
umtrx (1.0.5) trusty; urgency=low
 | 
			
		||||
 | 
			
		||||
  * host: disable umtrx_fifo_ctrl cache of spi config
 | 
			
		||||
  * host: Fix getters in umtrx_property_tree.py.
 | 
			
		||||
  * debian: added firmware to package and umtrx_firmware script to handle it
 | 
			
		||||
  * fpga: created axi stream controled spi core
 | 
			
		||||
  * fpga: use axi stream spi core (still single dest)
 | 
			
		||||
  * fpga: connect both spi settings drivers
 | 
			
		||||
  * fpga: simplify spi setting regs with generate loop
 | 
			
		||||
  * fpga: updated 4x ddc image for spi work
 | 
			
		||||
 | 
			
		||||
 -- Kirill Zakharenko <earwin@gmail.com>  Mon, 23 Nov 2015 15:51:56 +0300
 | 
			
		||||
 | 
			
		||||
umtrx (1.0.4) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Do not add 'g' to a git id when creating a version string.
 | 
			
		||||
  * Proper debianization.
 | 
			
		||||
  * Add ability to set diversity switches position from UHD args.
 | 
			
		||||
  * By default route each Rx channel to it's own antenna.
 | 
			
		||||
  * JSON API to question/control property tree of a running UHD app. Useful for querying sensors and for debugging purposes.
 | 
			
		||||
  * Python utility to query VSWR from a running UHD app in real time using JSON API.
 | 
			
		||||
  * Fix DC and IQ calibration utilities.
 | 
			
		||||
 | 
			
		||||
 -- Alexander Chemeris <Alexander.Chemeris@fairwaves.co>  Tue, 21 Jul 2015 18:51:56 -0400
 | 
			
		||||
 | 
			
		||||
umtrx (1.0.3) unstable; urgency=low
 | 
			
		||||
 | 
			
		||||
  * Created debian control files for 1.0.3 release of umtrx
 | 
			
		||||
 | 
			
		||||
 -- Josh Blum <josh@pothosware.com>  Sat, 20 Jun 2015 16:31:24 -0700
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/compat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
9
 | 
			
		||||
							
								
								
									
										33
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								debian/control
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
Source: umtrx
 | 
			
		||||
Section: libs
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Josh Blum <josh@pothosware.com>
 | 
			
		||||
Build-Depends:
 | 
			
		||||
    debhelper (>= 9.0.0),
 | 
			
		||||
    cmake (>= 2.8),
 | 
			
		||||
    libboost-all-dev,
 | 
			
		||||
    libuhd-dev (>= 3.7)
 | 
			
		||||
Standards-Version: 3.9.5
 | 
			
		||||
Homepage: http://umtrx.org/
 | 
			
		||||
Vcs-Git: https://github.com/fairwaves/UHD-Fairwaves.git
 | 
			
		||||
Vcs-Browser: https://github.com/fairwaves/UHD-Fairwaves
 | 
			
		||||
 | 
			
		||||
Package: umtrx
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Pre-Depends: multiarch-support, ${misc:Pre-Depends}
 | 
			
		||||
Depends: ${shlibs:Depends}, ${misc:Depends}, uhd-umtrx
 | 
			
		||||
Conflicts: umtrx-uhd
 | 
			
		||||
Replaces: umtrx-uhd
 | 
			
		||||
Recommends: python
 | 
			
		||||
Description: Fairwaves UmTRX driver - runtime utilities
 | 
			
		||||
 The industrial grade dual-channel wide-band SDR transceiver.
 | 
			
		||||
 | 
			
		||||
Package: uhd-umtrx
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends}, ${misc:Depends}
 | 
			
		||||
Conflicts: umtrx-uhd
 | 
			
		||||
Replaces: umtrx-uhd
 | 
			
		||||
Description: Fairwaves UmTRX driver - UHD plugin module
 | 
			
		||||
 The industrial grade dual-channel wide-band SDR transceiver.
 | 
			
		||||
							
								
								
									
										12
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
			
		||||
Upstream-Name: umtrx
 | 
			
		||||
Source: https://github.com/fairwaves/UHD-Fairwaves
 | 
			
		||||
 | 
			
		||||
Files: *
 | 
			
		||||
Copyright:
 | 
			
		||||
    Copyright 2012-2015 Fairwaves LLC
 | 
			
		||||
    Copyright 2010-2012 Ettus Research LLC
 | 
			
		||||
License: GPL-3
 | 
			
		||||
 On Debian systems, the full text of the GNU General Public
 | 
			
		||||
 License version 3 can be found in the file
 | 
			
		||||
 `/usr/share/common-licenses/GPL-3'.
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/docs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
README
 | 
			
		||||
							
								
								
									
										18
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								debian/rules
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#!/usr/bin/make -f
 | 
			
		||||
# -*- makefile -*-
 | 
			
		||||
 | 
			
		||||
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
 | 
			
		||||
export DEB_HOST_MULTIARCH
 | 
			
		||||
 | 
			
		||||
# Uncomment this to turn on verbose mode.
 | 
			
		||||
#export DH_VERBOSE=1
 | 
			
		||||
 | 
			
		||||
# This has to be exported to make some magic below work.
 | 
			
		||||
export DH_OPTIONS
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
%:
 | 
			
		||||
	dh $@ --buildsystem=cmake --parallel --sourcedirectory=host
 | 
			
		||||
 | 
			
		||||
override_dh_auto_configure:
 | 
			
		||||
	dh_auto_configure -- -DLIB_SUFFIX="/$(DEB_HOST_MULTIARCH)"
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/source/format
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
3.0 (native)
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/uhd-umtrx.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/uhd-umtrx.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
usr/lib/*/uhd/modules/
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/umtrx.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								debian/umtrx.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
usr/bin
 | 
			
		||||
images/u2plus_umtrx_v2.bin images/umtrx_txrx_uhd.bin usr/share/umtrx/firmware
 | 
			
		||||
@@ -58,5 +58,6 @@ gpio_atr.v \
 | 
			
		||||
user_settings.v \
 | 
			
		||||
settings_fifo_ctrl.v \
 | 
			
		||||
simple_spi_core.v \
 | 
			
		||||
axis_spi_core.v \
 | 
			
		||||
simple_i2c_core.v \
 | 
			
		||||
))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										243
									
								
								fpga/control_lib/axis_spi_core.v
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								fpga/control_lib/axis_spi_core.v
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,243 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright 2012 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/>.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// Simple SPI core, the simplest, yet complete spi core I can think of
 | 
			
		||||
 | 
			
		||||
// Settings register controlled.
 | 
			
		||||
// 2 settings regs, control and data
 | 
			
		||||
// 1 32-bit readback and status signal
 | 
			
		||||
 | 
			
		||||
// Settings reg map:
 | 
			
		||||
//
 | 
			
		||||
// BASE+0 divider setting
 | 
			
		||||
// bits [15:0] spi clock divider
 | 
			
		||||
//
 | 
			
		||||
// BASE+1 configuration input
 | 
			
		||||
// bits [23:0] slave select, bit0 = slave0 enabled
 | 
			
		||||
// bits [29:24] num bits (1 through 32)
 | 
			
		||||
// bit [30] data input edge = in data bit latched on rising edge of clock
 | 
			
		||||
// bit [31] data output edge = out data bit latched on rising edge of clock
 | 
			
		||||
//
 | 
			
		||||
// BASE+2 input data
 | 
			
		||||
// Writing this register begins a spi transaction.
 | 
			
		||||
// Bits are latched out from bit 0.
 | 
			
		||||
// Therefore, load this register in reverse.
 | 
			
		||||
//
 | 
			
		||||
// Readback
 | 
			
		||||
// Bits are latched into bit 0.
 | 
			
		||||
// Therefore, data will be in-order.
 | 
			
		||||
 | 
			
		||||
module axis_spi_core
 | 
			
		||||
    #(
 | 
			
		||||
        //set to 1 for ILA
 | 
			
		||||
        parameter DEBUG = 0,
 | 
			
		||||
 | 
			
		||||
        //tdest width for number of core users
 | 
			
		||||
        parameter DESTW = 1,
 | 
			
		||||
 | 
			
		||||
        //width of serial enables (up to 24 is possible)
 | 
			
		||||
        parameter WIDTH = 8,
 | 
			
		||||
 | 
			
		||||
        //idle state of the spi clock
 | 
			
		||||
        parameter CLK_IDLE = 0,
 | 
			
		||||
 | 
			
		||||
        //idle state of the serial enables
 | 
			
		||||
        parameter SEN_IDLE = 24'hffffff
 | 
			
		||||
    )
 | 
			
		||||
    (
 | 
			
		||||
        //clock and synchronous reset
 | 
			
		||||
        input clock, input reset,
 | 
			
		||||
 | 
			
		||||
        //configuration settings bus
 | 
			
		||||
        input [DESTW-1:0] CONFIG_tdest,
 | 
			
		||||
        input [79:0] CONFIG_tdata,
 | 
			
		||||
        input CONFIG_tvalid,
 | 
			
		||||
        output CONFIG_tready,
 | 
			
		||||
 | 
			
		||||
        //32-bit data readback
 | 
			
		||||
        output reg [DESTW-1:0] READBACK_tdest,
 | 
			
		||||
        output [31:0] READBACK_tdata,
 | 
			
		||||
        output READBACK_tvalid,
 | 
			
		||||
        input READBACK_tready,
 | 
			
		||||
 | 
			
		||||
        //spi interface, slave selects, clock, data in, data out
 | 
			
		||||
        output [WIDTH-1:0] sen,
 | 
			
		||||
        output sclk,
 | 
			
		||||
        output mosi,
 | 
			
		||||
        input miso
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    //state
 | 
			
		||||
    localparam WAIT_CONFIG = 0;
 | 
			
		||||
    localparam PRE_IDLE = 1;
 | 
			
		||||
    localparam CLK_REG = 2;
 | 
			
		||||
    localparam CLK_INV = 3;
 | 
			
		||||
    localparam POST_IDLE = 4;
 | 
			
		||||
    localparam IDLE_SEN = 5;
 | 
			
		||||
    localparam WAIT_READBACK = 6;
 | 
			
		||||
    reg [2:0] state;
 | 
			
		||||
 | 
			
		||||
    //configuration settings
 | 
			
		||||
    reg [15:0] sclk_divider;
 | 
			
		||||
    reg [23:0] slave_select;
 | 
			
		||||
    reg [5:0] num_bits;
 | 
			
		||||
    reg datain_edge, dataout_edge;
 | 
			
		||||
 | 
			
		||||
    //output ready/valid signals
 | 
			
		||||
    assign CONFIG_tready = (state == WAIT_CONFIG);
 | 
			
		||||
    assign READBACK_tvalid = (state == WAIT_READBACK);
 | 
			
		||||
 | 
			
		||||
    //serial clock either idles or is in one of two clock states
 | 
			
		||||
    reg sclk_reg;
 | 
			
		||||
    assign sclk = sclk_reg;
 | 
			
		||||
 | 
			
		||||
    //serial enables either idle or enabled based on state
 | 
			
		||||
    wire sen_is_idle = (state == WAIT_CONFIG) || (state == IDLE_SEN);
 | 
			
		||||
    wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
 | 
			
		||||
    reg [WIDTH-1:0] sen_reg;
 | 
			
		||||
    always @(posedge clock) sen_reg <= sen24[WIDTH-1:0];
 | 
			
		||||
    assign sen = sen_reg;
 | 
			
		||||
 | 
			
		||||
    //data output shift register
 | 
			
		||||
    reg [31:0] dataout_reg;
 | 
			
		||||
    wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
 | 
			
		||||
    assign mosi = dataout_reg[31];
 | 
			
		||||
 | 
			
		||||
    //data input shift register
 | 
			
		||||
    reg [31:0] datain_reg;
 | 
			
		||||
    wire [31:0] datain_next = {datain_reg[30:0], miso};
 | 
			
		||||
    assign READBACK_tdata = datain_reg;
 | 
			
		||||
 | 
			
		||||
    //counter for spi clock
 | 
			
		||||
    reg [15:0] sclk_counter;
 | 
			
		||||
    wire sclk_counter_done = (sclk_counter == sclk_divider);
 | 
			
		||||
    wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
 | 
			
		||||
 | 
			
		||||
    //counter for latching bits miso/mosi
 | 
			
		||||
    reg [6:0] bit_counter;
 | 
			
		||||
    wire [6:0] bit_counter_next = bit_counter + 1;
 | 
			
		||||
    wire bit_counter_done = (bit_counter_next == num_bits);
 | 
			
		||||
 | 
			
		||||
    always @(posedge clock) begin
 | 
			
		||||
        if (reset) begin
 | 
			
		||||
            state <= WAIT_CONFIG;
 | 
			
		||||
            sclk_reg <= CLK_IDLE;
 | 
			
		||||
        end
 | 
			
		||||
        else begin
 | 
			
		||||
            case (state)
 | 
			
		||||
 | 
			
		||||
            WAIT_CONFIG: begin
 | 
			
		||||
                if (CONFIG_tvalid && CONFIG_tready) begin
 | 
			
		||||
                    state <= PRE_IDLE;
 | 
			
		||||
                end
 | 
			
		||||
                {sclk_divider, dataout_edge, datain_edge, num_bits, slave_select, dataout_reg} <= CONFIG_tdata;
 | 
			
		||||
                READBACK_tdest <= CONFIG_tdest;
 | 
			
		||||
                sclk_counter <= 0;
 | 
			
		||||
                bit_counter <= 0;
 | 
			
		||||
                sclk_reg <= CLK_IDLE;
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            PRE_IDLE: begin
 | 
			
		||||
                if (sclk_counter_done) state <= CLK_REG;
 | 
			
		||||
                sclk_counter <= sclk_counter_next;
 | 
			
		||||
                sclk_reg <= CLK_IDLE;
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            CLK_REG: begin
 | 
			
		||||
                if (sclk_counter_done) begin
 | 
			
		||||
                    state <= CLK_INV;
 | 
			
		||||
                    if (datain_edge  != CLK_IDLE)                     datain_reg  <= datain_next;
 | 
			
		||||
                    if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
 | 
			
		||||
                    sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
 | 
			
		||||
                end
 | 
			
		||||
                sclk_counter <= sclk_counter_next;
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            CLK_INV: begin
 | 
			
		||||
                if (sclk_counter_done) begin
 | 
			
		||||
                    state <= (bit_counter_done)? POST_IDLE : CLK_REG;
 | 
			
		||||
                    bit_counter <= bit_counter_next;
 | 
			
		||||
                    if (datain_edge  == CLK_IDLE)                      datain_reg  <= datain_next;
 | 
			
		||||
                    if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
 | 
			
		||||
                    sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
 | 
			
		||||
                end
 | 
			
		||||
                sclk_counter <= sclk_counter_next;
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            POST_IDLE: begin
 | 
			
		||||
                if (sclk_counter_done) state <= IDLE_SEN;
 | 
			
		||||
                sclk_counter <= sclk_counter_next;
 | 
			
		||||
                sclk_reg <= CLK_IDLE;
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            IDLE_SEN: begin
 | 
			
		||||
                if (sclk_counter_done) state <= WAIT_READBACK;
 | 
			
		||||
                sclk_counter <= sclk_counter_next;
 | 
			
		||||
                sclk_reg <= CLK_IDLE;
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            WAIT_READBACK: begin
 | 
			
		||||
                if (READBACK_tready && READBACK_tvalid) begin
 | 
			
		||||
                    state <= WAIT_CONFIG;
 | 
			
		||||
                end
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            default: state <= WAIT_CONFIG;
 | 
			
		||||
 | 
			
		||||
            endcase //state
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    /*******************************************************************
 | 
			
		||||
     * Debug
 | 
			
		||||
     ******************************************************************/
 | 
			
		||||
    generate
 | 
			
		||||
    if (DEBUG == 1) begin
 | 
			
		||||
        wire [35:0] CONTROL0;
 | 
			
		||||
        chipscope_icon chipscope_icon
 | 
			
		||||
        (
 | 
			
		||||
            .CONTROL0(CONTROL0)
 | 
			
		||||
        );
 | 
			
		||||
        wire [255:0] DATA;
 | 
			
		||||
        wire [7:0] TRIG0;
 | 
			
		||||
        chipscope_ila chipscope_ila
 | 
			
		||||
        (
 | 
			
		||||
            .CONTROL(CONTROL0),
 | 
			
		||||
            .CLK(clock),
 | 
			
		||||
            .DATA(DATA),
 | 
			
		||||
            .TRIG0(TRIG0)
 | 
			
		||||
        );
 | 
			
		||||
        assign TRIG0 =
 | 
			
		||||
        {
 | 
			
		||||
            4'b0,
 | 
			
		||||
            CONFIG_tvalid, CONFIG_tready,
 | 
			
		||||
            READBACK_tvalid, READBACK_tready
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        assign DATA[79:0] = CONFIG_tdata;
 | 
			
		||||
        assign DATA[111:80] = READBACK_tdata;
 | 
			
		||||
 | 
			
		||||
        assign DATA[112] = CONFIG_tvalid;
 | 
			
		||||
        assign DATA[113] = CONFIG_tready;
 | 
			
		||||
        assign DATA[114] = READBACK_tvalid;
 | 
			
		||||
        assign DATA[115] = READBACK_tready;
 | 
			
		||||
 | 
			
		||||
        assign DATA[127:120] = state;
 | 
			
		||||
    end
 | 
			
		||||
    endgenerate
 | 
			
		||||
 | 
			
		||||
endmodule //axis_spi_core
 | 
			
		||||
@@ -175,6 +175,10 @@ module umtrx_core
 | 
			
		||||
   wire [31:0] 	set_data, set_data_dsp, set_data_sys, set_data_fe, set_data_udp_wb, set_data_udp_sys;
 | 
			
		||||
   wire 	set_stb, set_stb_dsp, set_stb_sys, set_stb_fe, set_stb_udp_wb, set_stb_udp_sys;
 | 
			
		||||
 | 
			
		||||
   wire set_stb_dsp0, set_stb_dsp1;
 | 
			
		||||
   wire [31:0] set_data_dsp0, set_data_dsp1;
 | 
			
		||||
   wire [7:0] set_addr_dsp0, set_addr_dsp1;
 | 
			
		||||
   
 | 
			
		||||
   reg 		wb_rst;
 | 
			
		||||
   wire 	dsp_rst, sys_rst, fe_rst;
 | 
			
		||||
   wire     net_clr;
 | 
			
		||||
@@ -412,17 +416,80 @@ module umtrx_core
 | 
			
		||||
 | 
			
		||||
   // /////////////////////////////////////////////////////////////////////////
 | 
			
		||||
   // SPI -- Slave #2
 | 
			
		||||
    wire [31:0] spi_debug;
 | 
			
		||||
    wire [31:0] spi_readback;
 | 
			
		||||
    reg [31:0] spi_readback0;
 | 
			
		||||
    reg [31:0] spi_readback1;
 | 
			
		||||
    wire spi_ready;
 | 
			
		||||
    simple_spi_core #(.BASE(SR_SPI_CORE), .WIDTH(5)) shared_spi(
 | 
			
		||||
 | 
			
		||||
    wire [0:0] AXIS_SPI_CONFIG_tdest;
 | 
			
		||||
    wire [79:0] AXIS_SPI_CONFIG_tdata;
 | 
			
		||||
    wire AXIS_SPI_CONFIG_tvalid;
 | 
			
		||||
    wire AXIS_SPI_CONFIG_tready;
 | 
			
		||||
 | 
			
		||||
    wire [0:0] AXIS_SPI_READBACK_tdest;
 | 
			
		||||
    wire [31:0] AXIS_SPI_READBACK_tdata;
 | 
			
		||||
    wire AXIS_SPI_READBACK_tvalid;
 | 
			
		||||
    wire AXIS_SPI_READBACK_tready;
 | 
			
		||||
 | 
			
		||||
    axis_spi_core #(.DESTW(1), .WIDTH(5), .DEBUG(0)) axis_shared_spi(
 | 
			
		||||
        .clock(dsp_clk), .reset(dsp_rst),
 | 
			
		||||
        .set_stb(set_stb_dsp), .set_addr(set_addr_dsp), .set_data(set_data_dsp),
 | 
			
		||||
        .readback(spi_readback), .ready(spi_ready),
 | 
			
		||||
 | 
			
		||||
        .CONFIG_tdest(AXIS_SPI_CONFIG_tdest),
 | 
			
		||||
        .CONFIG_tdata(AXIS_SPI_CONFIG_tdata),
 | 
			
		||||
        .CONFIG_tvalid(AXIS_SPI_CONFIG_tvalid),
 | 
			
		||||
        .CONFIG_tready(AXIS_SPI_CONFIG_tready),
 | 
			
		||||
 | 
			
		||||
        .READBACK_tdest(AXIS_SPI_READBACK_tdest),
 | 
			
		||||
        .READBACK_tdata(AXIS_SPI_READBACK_tdata),
 | 
			
		||||
        .READBACK_tvalid(AXIS_SPI_READBACK_tvalid),
 | 
			
		||||
        .READBACK_tready(AXIS_SPI_READBACK_tready),
 | 
			
		||||
 | 
			
		||||
        .sen({aux_sen2,aux_sen1,sen_dac,sen_lms2,sen_lms1}),
 | 
			
		||||
        .sclk(sclk), .mosi(mosi), .miso(miso), .debug(spi_debug)
 | 
			
		||||
        .sclk(sclk), .mosi(mosi), .miso(miso)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    //setting register block for spi dest 0 (wishbone) and spi dest 1 (ctrl fifo)
 | 
			
		||||
    //Note: the strobes are exclusive (settings fifo cross clock)
 | 
			
		||||
    wire [79:0] spi_config [0:1];
 | 
			
		||||
    wire [0:1] spi_trigger;
 | 
			
		||||
    wire [0:1] set_stb_dsp_n = {set_stb_dsp0, set_stb_dsp1};
 | 
			
		||||
    genvar i;
 | 
			
		||||
    generate for (i=0; i <= 1; i=i+1) begin
 | 
			
		||||
        setting_reg #(.my_addr(SR_SPI_CORE+2),.width(32)) axis_shared_spi_sr0(
 | 
			
		||||
            .clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp_n[i]),.addr(set_addr_dsp),.in(set_data_dsp),
 | 
			
		||||
            .out(spi_config[i][31:0]),.changed(spi_trigger[i]));
 | 
			
		||||
 | 
			
		||||
        setting_reg #(.my_addr(SR_SPI_CORE+1),.width(32)) axis_shared_spi_sr1(
 | 
			
		||||
            .clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp_n[i]),.addr(set_addr_dsp),.in(set_data_dsp),
 | 
			
		||||
            .out(spi_config[i][63:32]),.changed());
 | 
			
		||||
 | 
			
		||||
        setting_reg #(.my_addr(SR_SPI_CORE+0),.width(16)) axis_shared_spi_sr2(
 | 
			
		||||
            .clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp_n[i]),.addr(set_addr_dsp),.in(set_data_dsp),
 | 
			
		||||
            .out(spi_config[i][79:64]),.changed());
 | 
			
		||||
    end endgenerate
 | 
			
		||||
 | 
			
		||||
    //assign config bus from setting register sources
 | 
			
		||||
    //Note: the triggers are exclusive (settings fifo cross clock)
 | 
			
		||||
    assign AXIS_SPI_CONFIG_tdest = (spi_trigger[0])?1'b0:1'b1;
 | 
			
		||||
    assign AXIS_SPI_CONFIG_tdata = (spi_trigger[0])?spi_config[0]:spi_config[1];
 | 
			
		||||
    assign AXIS_SPI_CONFIG_tvalid = (spi_trigger != 0);
 | 
			
		||||
 | 
			
		||||
    //create spi ready to block the ctrl fifo ASAP
 | 
			
		||||
    wire spi_ready_now = AXIS_SPI_CONFIG_tready && !AXIS_SPI_CONFIG_tvalid;
 | 
			
		||||
    assign spi_ready = spi_ready_now && spi_ready_prev;
 | 
			
		||||
    reg spi_ready_prev;
 | 
			
		||||
    always @(posedge dsp_clk) begin
 | 
			
		||||
        spi_ready_prev <= spi_ready_now;
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    //readback output bus latches values into readback register
 | 
			
		||||
    assign AXIS_SPI_READBACK_tready = 1'b1;
 | 
			
		||||
    always @(posedge dsp_clk) begin
 | 
			
		||||
        if (AXIS_SPI_READBACK_tvalid && AXIS_SPI_READBACK_tready) begin
 | 
			
		||||
            if (AXIS_SPI_READBACK_tdest == 1'b0) spi_readback0 <= AXIS_SPI_READBACK_tdata;
 | 
			
		||||
            if (AXIS_SPI_READBACK_tdest == 1'b1) spi_readback1 <= AXIS_SPI_READBACK_tdata;
 | 
			
		||||
        end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
   // /////////////////////////////////////////////////////////////////////////
 | 
			
		||||
   // I2C -- Slave #3
 | 
			
		||||
   i2c_master_top #(.ARST_LVL(1)) 
 | 
			
		||||
@@ -448,7 +515,7 @@ module umtrx_core
 | 
			
		||||
   // Buffer Pool Status -- Slave #5   
 | 
			
		||||
   
 | 
			
		||||
   //compatibility number -> increment when the fpga has been sufficiently altered
 | 
			
		||||
   localparam compat_num = {16'd9, 16'd0}; //major, minor
 | 
			
		||||
   localparam compat_num = {16'd9, 16'd2}; //major, minor
 | 
			
		||||
 | 
			
		||||
   wire [31:0] irq_readback = {16'b0, aux_ld2, aux_ld1, button, spi_ready, 12'b0};
 | 
			
		||||
 | 
			
		||||
@@ -456,7 +523,7 @@ module umtrx_core
 | 
			
		||||
     (.wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_stb_i(s5_stb),
 | 
			
		||||
      .wb_adr_i(s5_adr), .wb_dat_o(s5_dat_i), .wb_ack_o(s5_ack),
 | 
			
		||||
 | 
			
		||||
      .word00(spi_readback),.word01(`NUMDDC),.word02(`NUMDUC),.word03(32'b0),
 | 
			
		||||
      .word00(spi_readback0),.word01(`NUMDDC),.word02(`NUMDUC),.word03(32'b0),
 | 
			
		||||
      .word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0),
 | 
			
		||||
      .word08(status),.word09(32'b0),.word10(vita_time[63:32]),
 | 
			
		||||
      .word11(vita_time[31:0]),.word12(compat_num),.word13(irq_readback),
 | 
			
		||||
@@ -498,10 +565,6 @@ module umtrx_core
 | 
			
		||||
     (.clk_i(dsp_clk), .rst_i(dsp_rst), .set_stb_i(set_stb_dsp), .set_addr_i(set_addr_dsp), .set_data_i(set_data_dsp),
 | 
			
		||||
      .clk_o(fe_clk), .rst_o(fe_rst), .set_stb_o(set_stb_fe), .set_addr_o(set_addr_fe), .set_data_o(set_data_fe));
 | 
			
		||||
 | 
			
		||||
   wire set_stb_dsp0, set_stb_dsp1;
 | 
			
		||||
   wire [31:0] set_data_dsp0, set_data_dsp1;
 | 
			
		||||
   wire [7:0] set_addr_dsp0, set_addr_dsp1;
 | 
			
		||||
 | 
			
		||||
   //mux settings_bus_crossclock and settings_readback_bus_fifo_ctrl with prio
 | 
			
		||||
   assign set_stb_dsp = set_stb_dsp0 | set_stb_dsp1;
 | 
			
		||||
   assign set_addr_dsp = set_stb_dsp1? set_addr_dsp1 : set_addr_dsp0;
 | 
			
		||||
@@ -542,7 +605,7 @@ module umtrx_core
 | 
			
		||||
        .in_data(ctrl_data_dsp), .in_valid(ctrl_valid_dsp), .in_ready(ctrl_ready_dsp),
 | 
			
		||||
        .out_data(resp_data_dsp), .out_valid(resp_valid_dsp), .out_ready(resp_ready_dsp),
 | 
			
		||||
        .strobe(set_stb_dsp1), .addr(set_addr_dsp1), .data(set_data_dsp1),
 | 
			
		||||
        .word00(spi_readback),.word01(32'b0),.word02(32'b0),.word03(32'b0),
 | 
			
		||||
        .word00(spi_readback1),.word01(32'b0),.word02(32'b0),.word03(32'b0),
 | 
			
		||||
        .word04(32'b0),.word05(32'b0),.word06(32'b0),.word07(32'b0),
 | 
			
		||||
        .word08(32'b0),.word09(32'b0),.word10(vita_time[63:32]),
 | 
			
		||||
        .word11(vita_time[31:0]),.word12(32'b0),.word13(irq_readback),
 | 
			
		||||
@@ -672,20 +735,20 @@ module umtrx_core
 | 
			
		||||
 | 
			
		||||
   // /////////////////////////////////////////////////////////////////////////
 | 
			
		||||
   // RX Frontend
 | 
			
		||||
    wire [23:0] front0_i, front0_q;
 | 
			
		||||
    wire [23:0] rx_front0_i, rx_front0_q;
 | 
			
		||||
    rx_frontend #(.BASE(SR_RX_FRONT0)) rx_frontend0
 | 
			
		||||
    (
 | 
			
		||||
        .clk(fe_clk), .rst(fe_rst),
 | 
			
		||||
        .set_stb(set_stb_fe),.set_addr(set_addr_fe),.set_data(set_data_fe),
 | 
			
		||||
        .i_out(front0_i), .q_out(front0_q), .run(1'b1),
 | 
			
		||||
        .i_out(rx_front0_i), .q_out(rx_front0_q), .run(1'b1),
 | 
			
		||||
        .adc_a({adc0_a, 4'b0}), .adc_b({adc0_b, 4'b0})
 | 
			
		||||
    );
 | 
			
		||||
    wire [23:0] front1_i, front1_q;
 | 
			
		||||
    wire [23:0] rx_front1_i, rx_front1_q;
 | 
			
		||||
    rx_frontend #(.BASE(SR_RX_FRONT1)) rx_frontend1
 | 
			
		||||
    (
 | 
			
		||||
        .clk(fe_clk), .rst(fe_rst),
 | 
			
		||||
        .set_stb(set_stb_fe),.set_addr(set_addr_fe),.set_data(set_data_fe),
 | 
			
		||||
        .i_out(front1_i), .q_out(front1_q), .run(1'b1),
 | 
			
		||||
        .i_out(rx_front1_i), .q_out(rx_front1_q), .run(1'b1),
 | 
			
		||||
        .adc_a({adc1_a, 4'b0}), .adc_b({adc1_b, 4'b0})
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@@ -716,8 +779,8 @@ module umtrx_core
 | 
			
		||||
        .fe_clk(fe_clk), .fe_rst(fe_rst),
 | 
			
		||||
        .set_stb_dsp(set_stb_dsp), .set_addr_dsp(set_addr_dsp), .set_data_dsp(set_data_dsp),
 | 
			
		||||
        .set_stb_fe(set_stb_fe), .set_addr_fe(set_addr_fe), .set_data_fe(set_data_fe),
 | 
			
		||||
        .front_i(rx_fe_sw[0]?front1_i:front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[0]?front1_q:front0_q),
 | 
			
		||||
        .front_i(rx_fe_sw[0]?rx_front1_i:rx_front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[0]?rx_front1_q:rx_front0_q),
 | 
			
		||||
        .adc_stb(rx_fe_sw[0]?adc1_strobe:adc0_strobe),
 | 
			
		||||
        .run(run_rx_dsp[0]),
 | 
			
		||||
        .vita_data_sys(dsp_rx0_data), .vita_valid_sys(dsp_rx0_valid), .vita_ready_sys(dsp_rx0_ready),
 | 
			
		||||
@@ -743,8 +806,8 @@ module umtrx_core
 | 
			
		||||
        .fe_clk(fe_clk), .fe_rst(fe_rst),
 | 
			
		||||
        .set_stb_dsp(set_stb_dsp), .set_addr_dsp(set_addr_dsp), .set_data_dsp(set_data_dsp),
 | 
			
		||||
        .set_stb_fe(set_stb_fe), .set_addr_fe(set_addr_fe), .set_data_fe(set_data_fe),
 | 
			
		||||
        .front_i(rx_fe_sw[1]?front1_i:front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[1]?front1_q:front0_q),
 | 
			
		||||
        .front_i(rx_fe_sw[1]?rx_front1_i:rx_front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[1]?rx_front1_q:rx_front0_q),
 | 
			
		||||
        .adc_stb(rx_fe_sw[1]?adc1_strobe:adc0_strobe),
 | 
			
		||||
        .run(run_rx_dsp[1]),
 | 
			
		||||
        .vita_data_sys(dsp_rx1_data), .vita_valid_sys(dsp_rx1_valid), .vita_ready_sys(dsp_rx1_ready),
 | 
			
		||||
@@ -770,8 +833,8 @@ module umtrx_core
 | 
			
		||||
        .fe_clk(fe_clk), .fe_rst(fe_rst),
 | 
			
		||||
        .set_stb_dsp(set_stb_dsp), .set_addr_dsp(set_addr_dsp), .set_data_dsp(set_data_dsp),
 | 
			
		||||
        .set_stb_fe(set_stb_fe), .set_addr_fe(set_addr_fe), .set_data_fe(set_data_fe),
 | 
			
		||||
        .front_i(rx_fe_sw[2]?front1_i:front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[2]?front1_q:front0_q),
 | 
			
		||||
        .front_i(rx_fe_sw[2]?rx_front1_i:rx_front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[2]?rx_front1_q:rx_front0_q),
 | 
			
		||||
        .adc_stb(rx_fe_sw[2]?adc1_strobe:adc0_strobe),
 | 
			
		||||
        .run(run_rx_dsp[2]),
 | 
			
		||||
        .vita_data_sys(dsp_rx2_data), .vita_valid_sys(dsp_rx2_valid), .vita_ready_sys(dsp_rx2_ready),
 | 
			
		||||
@@ -797,8 +860,8 @@ module umtrx_core
 | 
			
		||||
        .fe_clk(fe_clk), .fe_rst(fe_rst),
 | 
			
		||||
        .set_stb_dsp(set_stb_dsp), .set_addr_dsp(set_addr_dsp), .set_data_dsp(set_data_dsp),
 | 
			
		||||
        .set_stb_fe(set_stb_fe), .set_addr_fe(set_addr_fe), .set_data_fe(set_data_fe),
 | 
			
		||||
        .front_i(rx_fe_sw[3]?front1_i:front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[3]?front1_q:front0_q),
 | 
			
		||||
        .front_i(rx_fe_sw[3]?rx_front1_i:rx_front0_i),
 | 
			
		||||
        .front_q(rx_fe_sw[3]?rx_front1_q:rx_front0_q),
 | 
			
		||||
        .adc_stb(rx_fe_sw[3]?adc1_strobe:adc0_strobe),
 | 
			
		||||
        .run(run_rx_dsp[3]),
 | 
			
		||||
        .vita_data_sys(dsp_rx3_data), .vita_valid_sys(dsp_rx3_valid), .vita_ready_sys(dsp_rx3_ready),
 | 
			
		||||
@@ -810,6 +873,28 @@ module umtrx_core
 | 
			
		||||
    end
 | 
			
		||||
    endgenerate
 | 
			
		||||
 | 
			
		||||
   // /////////////////////////////////////////////////////////////////////////
 | 
			
		||||
   // TX Frontend
 | 
			
		||||
    wire [23:0] tx_front0_i, tx_front0_q;
 | 
			
		||||
    wire [3:0] dac0_a_pad, dac0_b_pad;
 | 
			
		||||
    tx_frontend #(.BASE(SR_TX_FRONT0)) tx_frontend0
 | 
			
		||||
    (
 | 
			
		||||
        .clk(fe_clk), .rst(fe_rst),
 | 
			
		||||
        .set_stb(set_stb_fe),.set_addr(set_addr_fe),.set_data(set_data_fe),
 | 
			
		||||
        .tx_i(tx_front0_i), .tx_q(tx_front0_q), .run(1'b1),
 | 
			
		||||
        .dac_a({dac0_a, dac0_a_pad}), .dac_b({dac0_b, dac0_b_pad})
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    wire [23:0] tx_front1_i, tx_front1_q;
 | 
			
		||||
    wire [3:0] dac1_a_pad, dac1_b_pad;
 | 
			
		||||
    tx_frontend #(.BASE(SR_TX_FRONT1)) tx_frontend1
 | 
			
		||||
    (
 | 
			
		||||
        .clk(fe_clk), .rst(fe_rst),
 | 
			
		||||
        .set_stb(set_stb_fe),.set_addr(set_addr_fe),.set_data(set_data_fe),
 | 
			
		||||
        .tx_i(tx_front1_i), .tx_q(tx_front1_q), .run(1'b1),
 | 
			
		||||
        .dac_a({dac1_a, dac1_a_pad}), .dac_b({dac1_b, dac1_b_pad})
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
   // /////////////////////////////////////////////////////////////////////////
 | 
			
		||||
   // TX chains
 | 
			
		||||
    wire [35:0] sram0_data, sram1_data;
 | 
			
		||||
@@ -823,10 +908,10 @@ module umtrx_core
 | 
			
		||||
     (.clk(dsp_clk),.rst(dsp_rst),.strobe(set_stb_dsp),.addr(set_addr_dsp),.in(set_data_dsp),.out(tx_fe_sw),.changed());
 | 
			
		||||
 | 
			
		||||
    //assign dac switch
 | 
			
		||||
    wire [11:0] dac0_a_int, dac0_b_int;
 | 
			
		||||
    wire [11:0] dac1_a_int, dac1_b_int;
 | 
			
		||||
    assign {dac0_a, dac0_b} = (tx_fe_sw == 0)? {dac0_a_int, dac0_b_int} : {dac1_a_int, dac1_b_int};
 | 
			
		||||
    assign {dac1_a, dac1_b} = (tx_fe_sw == 1)? {dac0_a_int, dac0_b_int} : {dac1_a_int, dac1_b_int};
 | 
			
		||||
    wire [23:0] dac0_a_int, dac0_b_int;
 | 
			
		||||
    wire [23:0] dac1_a_int, dac1_b_int;
 | 
			
		||||
    assign {tx_front0_i, tx_front0_q} = (tx_fe_sw == 0)? {dac0_a_int, dac0_b_int} : {dac1_a_int, dac1_b_int};
 | 
			
		||||
    assign {tx_front1_i, tx_front1_q} = (tx_fe_sw == 1)? {dac0_a_int, dac0_b_int} : {dac1_a_int, dac1_b_int};
 | 
			
		||||
 | 
			
		||||
    generate
 | 
			
		||||
    if (`NUMDUC > 0) begin
 | 
			
		||||
@@ -834,7 +919,6 @@ module umtrx_core
 | 
			
		||||
    #(
 | 
			
		||||
        .PROT_DEST(0),
 | 
			
		||||
        .DSPNO(0),
 | 
			
		||||
        .FRONT_BASE(SR_TX_FRONT0),
 | 
			
		||||
        .DSP_BASE(SR_TX_DSP0),
 | 
			
		||||
        .CTRL_BASE(SR_TX_CTRL0),
 | 
			
		||||
        .FIFOSIZE(DSP_TX_FIFOSIZE)
 | 
			
		||||
@@ -846,7 +930,7 @@ module umtrx_core
 | 
			
		||||
        .fe_clk(fe_clk), .fe_rst(fe_rst),
 | 
			
		||||
        .set_stb_dsp(set_stb_dsp), .set_addr_dsp(set_addr_dsp), .set_data_dsp(set_data_dsp),
 | 
			
		||||
        .set_stb_fe(set_stb_fe), .set_addr_fe(set_addr_fe), .set_data_fe(set_data_fe),
 | 
			
		||||
        .dac_a(dac0_a_int), .dac_b(dac0_b_int), .dac_stb(dac0_strobe), .run(run_tx_dsp0),
 | 
			
		||||
        .front_i(dac0_a_int), .front_q(dac0_b_int), .dac_stb(dac0_strobe), .run(run_tx_dsp0),
 | 
			
		||||
        .vita_data_sys(sram0_data), .vita_valid_sys(sram0_valid), .vita_ready_sys(sram0_ready),
 | 
			
		||||
        .err_data_sys(err_tx0_data), .err_valid_sys(err_tx0_valid), .err_ready_sys(err_tx0_ready),
 | 
			
		||||
        .vita_time(vita_time)
 | 
			
		||||
@@ -861,7 +945,6 @@ module umtrx_core
 | 
			
		||||
    #(
 | 
			
		||||
        .PROT_DEST(1),
 | 
			
		||||
        .DSPNO(1),
 | 
			
		||||
        .FRONT_BASE(SR_TX_FRONT1),
 | 
			
		||||
        .DSP_BASE(SR_TX_DSP1),
 | 
			
		||||
        .CTRL_BASE(SR_TX_CTRL1),
 | 
			
		||||
        .FIFOSIZE(DSP_TX_FIFOSIZE)
 | 
			
		||||
@@ -873,7 +956,7 @@ module umtrx_core
 | 
			
		||||
        .fe_clk(fe_clk), .fe_rst(fe_rst),
 | 
			
		||||
        .set_stb_dsp(set_stb_dsp), .set_addr_dsp(set_addr_dsp), .set_data_dsp(set_data_dsp),
 | 
			
		||||
        .set_stb_fe(set_stb_fe), .set_addr_fe(set_addr_fe), .set_data_fe(set_data_fe),
 | 
			
		||||
        .dac_a(dac1_a_int), .dac_b(dac1_b_int), .dac_stb(dac1_strobe), .run(run_tx_dsp1),
 | 
			
		||||
        .front_i(dac1_a_int), .front_q(dac1_b_int), .dac_stb(dac1_strobe), .run(run_tx_dsp1),
 | 
			
		||||
        .vita_data_sys(sram1_data), .vita_valid_sys(sram1_valid), .vita_ready_sys(sram1_ready),
 | 
			
		||||
        .err_data_sys(err_tx1_data), .err_valid_sys(err_tx1_valid), .err_ready_sys(err_tx1_ready),
 | 
			
		||||
        .vita_time(vita_time)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,6 @@ module umtrx_tx_chain
 | 
			
		||||
#(
 | 
			
		||||
    parameter PROT_DEST = 0, //framer index
 | 
			
		||||
    parameter DSPNO = 0, //the dsp unit number: 0, 1, 2...
 | 
			
		||||
    parameter FRONT_BASE = 0,
 | 
			
		||||
    parameter DSP_BASE = 0,
 | 
			
		||||
    parameter CTRL_BASE = 0,
 | 
			
		||||
    parameter FIFOSIZE = 10,
 | 
			
		||||
@@ -32,9 +31,9 @@ module umtrx_tx_chain
 | 
			
		||||
    input [7:0] set_addr_fe,
 | 
			
		||||
    input [31:0] set_data_fe,
 | 
			
		||||
 | 
			
		||||
    //dsp clock domain
 | 
			
		||||
    output reg [11:0] dac_a,
 | 
			
		||||
    output reg [11:0] dac_b,
 | 
			
		||||
    //fe clock domain
 | 
			
		||||
    output [23:0] front_i,
 | 
			
		||||
    output [23:0] front_q,
 | 
			
		||||
    input dac_stb,
 | 
			
		||||
    output run,
 | 
			
		||||
 | 
			
		||||
@@ -52,28 +51,6 @@ module umtrx_tx_chain
 | 
			
		||||
    wire [63:0] vita_time
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
    /*******************************************************************
 | 
			
		||||
     * Cross DAC signals from fe to dsp clock domain
 | 
			
		||||
     * dac_a/b come from a register on the fe clock domain
 | 
			
		||||
     ******************************************************************/
 | 
			
		||||
    wire [15:0] dac_a_16, dac_b_16;
 | 
			
		||||
    always @(posedge fe_clk) begin
 | 
			
		||||
        dac_a <= dac_a_16[15:4];
 | 
			
		||||
        dac_b <= dac_b_16[15:4];
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    /*******************************************************************
 | 
			
		||||
     * TX frontend on fe clock domain
 | 
			
		||||
     ******************************************************************/
 | 
			
		||||
    wire [23:0] front_i, front_q;
 | 
			
		||||
    tx_frontend #(.BASE(FRONT_BASE)) tx_frontend
 | 
			
		||||
    (
 | 
			
		||||
        .clk(fe_clk), .rst(fe_rst),
 | 
			
		||||
        .set_stb(set_stb_fe),.set_addr(set_addr_fe),.set_data(set_data_fe),
 | 
			
		||||
        .tx_i(front_i), .tx_q(front_q), .run(1'b1),
 | 
			
		||||
        .dac_a(dac_a_16), .dac_b(dac_b_16)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    /*******************************************************************
 | 
			
		||||
     * DUC chain on fe clock domain
 | 
			
		||||
     ******************************************************************/
 | 
			
		||||
@@ -180,8 +157,6 @@ module umtrx_tx_chain
 | 
			
		||||
        assign DATA[63:32] = vita_sample;
 | 
			
		||||
        assign DATA[95:64] = duc_sample;
 | 
			
		||||
        assign DATA[127:96] = {front_i[23:8], front_q[23:8]};
 | 
			
		||||
        assign DATA[159:128] = {dac_a_16, dac_b_16};
 | 
			
		||||
        assign DATA[191:160] = {dac_a, 4'b0, dac_b, 4'b0};
 | 
			
		||||
    end
 | 
			
		||||
    endgenerate
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,8 @@ project(UmTRX-UHD)
 | 
			
		||||
# extract version info from git
 | 
			
		||||
########################################################################
 | 
			
		||||
include(${PROJECT_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake)
 | 
			
		||||
git_describe(UMTRX_VERSION)
 | 
			
		||||
git_describe(UMTRX_VERSION --dirty)
 | 
			
		||||
string(REPLACE "g" "" UMTRX_VERSION ${UMTRX_VERSION}) #remove hash prefix g
 | 
			
		||||
message(STATUS "UMTRX_VERSION: ${UMTRX_VERSION}")
 | 
			
		||||
configure_file(
 | 
			
		||||
    "${CMAKE_CURRENT_SOURCE_DIR}/umtrx_version.in.hpp"
 | 
			
		||||
@@ -38,6 +39,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
 | 
			
		||||
########################################################################
 | 
			
		||||
list(APPEND UMTRX_SOURCES
 | 
			
		||||
    umtrx_impl.cpp
 | 
			
		||||
    umtrx_monitor.cpp
 | 
			
		||||
    umtrx_io_impl.cpp
 | 
			
		||||
    umtrx_find.cpp
 | 
			
		||||
    umtrx_iface.cpp
 | 
			
		||||
@@ -46,6 +48,7 @@ list(APPEND UMTRX_SOURCES
 | 
			
		||||
    lms6002d_ctrl.cpp
 | 
			
		||||
    tmp102_ctrl.cpp
 | 
			
		||||
    ads1015_ctrl.cpp
 | 
			
		||||
    power_amp.cpp
 | 
			
		||||
    umtrx_fifo_ctrl.cpp
 | 
			
		||||
    missing/platform.cpp #not properly exported from uhd, so we had to copy it
 | 
			
		||||
    cores/rx_frontend_core_200.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -104,6 +104,12 @@ function(git_describe _var)
 | 
			
		||||
 | 
			
		||||
	#message(STATUS "Arguments to execute_process: ${ARGN}")
 | 
			
		||||
 | 
			
		||||
	#support dirty option (hash cant be specified)
 | 
			
		||||
	list(FIND ARGN "--dirty" _index)
 | 
			
		||||
	if (${_index} GREATER -1)
 | 
			
		||||
		unset(hash)
 | 
			
		||||
	endif()
 | 
			
		||||
 | 
			
		||||
	execute_process(COMMAND
 | 
			
		||||
		"${GIT_EXECUTABLE}"
 | 
			
		||||
		describe
 | 
			
		||||
 
 | 
			
		||||
@@ -105,7 +105,13 @@ static void apply_fe_corrections(
 | 
			
		||||
 | 
			
		||||
    //make the calibration file path
 | 
			
		||||
    const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal" / (file_prefix + db_eeprom.serial + ".csv");
 | 
			
		||||
    if (not fs::exists(cal_data_path)) return;
 | 
			
		||||
    UHD_MSG(status) << "Looking for FE correction at: " << cal_data_path.c_str() << "...  ";
 | 
			
		||||
    if (not fs::exists(cal_data_path)) {
 | 
			
		||||
        UHD_MSG(status) << "Not found" << std::endl;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    UHD_MSG(status) << "Found, loading...  ";
 | 
			
		||||
 | 
			
		||||
    //parse csv file or get from cache
 | 
			
		||||
    if (not fe_cal_cache.has_key(cal_data_path.string())){
 | 
			
		||||
@@ -133,8 +139,9 @@ static void apply_fe_corrections(
 | 
			
		||||
        }
 | 
			
		||||
        std::sort(datas.begin(), datas.end(), fe_cal_comp);
 | 
			
		||||
        fe_cal_cache[cal_data_path.string()] = datas;
 | 
			
		||||
        UHD_MSG(status) << "Loaded " << cal_data_path.string() << std::endl;
 | 
			
		||||
 | 
			
		||||
        UHD_MSG(status) << "Loaded" << std::endl;
 | 
			
		||||
    } else {
 | 
			
		||||
        UHD_MSG(status) << "Loaded from cache" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sub_tree->access<std::complex<double> >(fe_path)
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
 * LMS6002D control class
 | 
			
		||||
@@ -142,23 +143,49 @@ public:
 | 
			
		||||
        return lms_read_shift(0x41, 0x1f, 0) - 35;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	/** Set Rx VGA1 gain.
 | 
			
		||||
	gain is raw values [0 .. 127]
 | 
			
		||||
	/** Set Rx VGA1 gain raw value
 | 
			
		||||
	gain is raw values [0 .. 120]
 | 
			
		||||
	Returns the old gain value */
 | 
			
		||||
	int8_t set_rx_vga1gain(int8_t gain){
 | 
			
		||||
		if (gain < 0) // according to standard max value of int8_t is always 127
 | 
			
		||||
	int8_t set_rx_vga1gain_int(int8_t gain){
 | 
			
		||||
		if (gain < 0 || gain >120)
 | 
			
		||||
			gain = 0;
 | 
			
		||||
		int8_t old_bits = lms_write_bits(0x76, 0x7f, gain);
 | 
			
		||||
		return old_bits & 0x7f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Get Rx VGA1 gain in dB.
 | 
			
		||||
	gain is in [0 .. 127] range of abstract values
 | 
			
		||||
	/** Get Rx VGA1 gain raw value
 | 
			
		||||
	gain is in [0 .. 120] range of abstract values
 | 
			
		||||
	Returns the gain value */
 | 
			
		||||
	int8_t get_rx_vga1gain(){
 | 
			
		||||
	int8_t get_rx_vga1gain_int(){
 | 
			
		||||
		return lms_read_shift(0x76, 0x7f, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    /** Converts Rx VGA1 raw values to absolute dBs
 | 
			
		||||
    code must be in [0 .. 120] range */
 | 
			
		||||
    double rxvga1_int_to_db(int8_t code){
 | 
			
		||||
        return 5.0 + 20*log10(127.0/(127.0-code));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Converts Rx VGA1 gain into raw integer values
 | 
			
		||||
    db must be in [5 .. 30.17] dB range */
 | 
			
		||||
    int8_t rxvga1_db_to_int(double db){
 | 
			
		||||
        return (int8_t)(127.5 - 127 / pow(10, (db-5.0)/20));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Set Rx VGA1 gain in dB
 | 
			
		||||
	gain is in [5 .. 30.17] dB range
 | 
			
		||||
	Returns the old gain value */
 | 
			
		||||
	double set_rx_vga1gain(double gain){
 | 
			
		||||
		int8_t code = rxvga1_db_to_int(gain);
 | 
			
		||||
		return rxvga1_int_to_db(set_rx_vga1gain_int(code));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Get Rx VGA1 gain in dB
 | 
			
		||||
    Returns the gain value in [5 .. 30.17] dB range */
 | 
			
		||||
	double get_rx_vga1gain(){
 | 
			
		||||
		return rxvga1_int_to_db(get_rx_vga1gain_int());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    /**  Set VGA2 gain.
 | 
			
		||||
    gain is in dB [0 .. 25]
 | 
			
		||||
    Returns the old gain value */
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
#include <boost/thread.hpp>
 | 
			
		||||
#include <boost/array.hpp>
 | 
			
		||||
#include <boost/math/special_functions/round.hpp>
 | 
			
		||||
#include <boost/thread/recursive_mutex.hpp>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <cfloat>
 | 
			
		||||
@@ -69,7 +70,34 @@ static const uhd::dict<std::string, gain_range_t> lms_tx_gain_ranges = map_list_
 | 
			
		||||
;
 | 
			
		||||
 | 
			
		||||
static const uhd::dict<std::string, gain_range_t> lms_rx_gain_ranges = map_list_of
 | 
			
		||||
    ("VGA1", gain_range_t(0, 126, double(1.0)))
 | 
			
		||||
    // VGA1 follows the approximation of [dB] = 5 + 20*log10(127/(127-Code)) where 0 <= Code <= 120
 | 
			
		||||
    ("VGA1", gain_range_t( list_of
 | 
			
		||||
         (range_t(5.00))(range_t(5.07))(range_t(5.14))(range_t(5.21))(range_t(5.28))
 | 
			
		||||
         (range_t(5.35))(range_t(5.42))(range_t(5.49))(range_t(5.57))(range_t(5.64))
 | 
			
		||||
         (range_t(5.71))(range_t(5.79))(range_t(5.86))(range_t(5.94))(range_t(6.01))
 | 
			
		||||
         (range_t(6.09))(range_t(6.17))(range_t(6.25))(range_t(6.33))(range_t(6.41))
 | 
			
		||||
         (range_t(6.49))(range_t(6.57))(range_t(6.65))(range_t(6.74))(range_t(6.82))
 | 
			
		||||
         (range_t(6.90))(range_t(6.99))(range_t(7.08))(range_t(7.16))(range_t(7.25))
 | 
			
		||||
         (range_t(7.34))(range_t(7.43))(range_t(7.52))(range_t(7.61))(range_t(7.71))
 | 
			
		||||
         (range_t(7.80))(range_t(7.90))(range_t(7.99))(range_t(8.09))(range_t(8.19))
 | 
			
		||||
         (range_t(8.29))(range_t(8.39))(range_t(8.49))(range_t(8.59))(range_t(8.69))
 | 
			
		||||
         (range_t(8.80))(range_t(8.91))(range_t(9.01))(range_t(9.12))(range_t(9.23))
 | 
			
		||||
         (range_t(9.35))(range_t(9.46))(range_t(9.57))(range_t(9.69))(range_t(9.81))
 | 
			
		||||
         (range_t(9.93))(range_t(10.05))(range_t(10.17))(range_t(10.30))(range_t(10.43))
 | 
			
		||||
         (range_t(10.55))(range_t(10.69))(range_t(10.82))(range_t(10.95))(range_t(11.09))
 | 
			
		||||
         (range_t(11.23))(range_t(11.37))(range_t(11.51))(range_t(11.66))(range_t(11.81))
 | 
			
		||||
         (range_t(11.96))(range_t(12.11))(range_t(12.27))(range_t(12.43))(range_t(12.59))
 | 
			
		||||
         (range_t(12.76))(range_t(12.92))(range_t(13.10))(range_t(13.27))(range_t(13.45))
 | 
			
		||||
         (range_t(13.63))(range_t(13.82))(range_t(14.01))(range_t(14.21))(range_t(14.41))
 | 
			
		||||
         (range_t(14.61))(range_t(14.82))(range_t(15.03))(range_t(15.25))(range_t(15.48))
 | 
			
		||||
         (range_t(15.71))(range_t(15.95))(range_t(16.19))(range_t(16.45))(range_t(16.71))
 | 
			
		||||
         (range_t(16.97))(range_t(17.25))(range_t(17.53))(range_t(17.83))(range_t(18.13))
 | 
			
		||||
         (range_t(18.45))(range_t(18.78))(range_t(19.12))(range_t(19.47))(range_t(19.84))
 | 
			
		||||
         (range_t(20.23))(range_t(20.63))(range_t(21.06))(range_t(21.50))(range_t(21.97))
 | 
			
		||||
         (range_t(22.47))(range_t(22.99))(range_t(23.55))(range_t(24.15))(range_t(24.80))
 | 
			
		||||
         (range_t(25.49))(range_t(26.25))(range_t(27.08))(range_t(27.99))(range_t(29.01))
 | 
			
		||||
         (range_t(30.17))
 | 
			
		||||
         ))
 | 
			
		||||
	// We limit Rx VGA2 to 30dB, as docs say higher values are dangerous
 | 
			
		||||
    ("VGA2", gain_range_t(0, 30, double(3.0)))
 | 
			
		||||
// ToDo: Add LNA control here
 | 
			
		||||
@@ -127,11 +155,13 @@ public:
 | 
			
		||||
 | 
			
		||||
    uhd::sensor_value_t get_rx_pll_locked()
 | 
			
		||||
    {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        return uhd::sensor_value_t("LO", lms.get_rx_pll_locked(), "locked", "unlocked");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uhd::sensor_value_t get_tx_pll_locked()
 | 
			
		||||
    {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        return uhd::sensor_value_t("LO", lms.get_tx_pll_locked(), "locked", "unlocked");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -187,17 +217,20 @@ public:
 | 
			
		||||
 | 
			
		||||
    uint8_t get_tx_vga1dc_i_int(void)
 | 
			
		||||
    {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        return lms.get_tx_vga1dc_i_int();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t get_tx_vga1dc_q_int(void)
 | 
			
		||||
    {
 | 
			
		||||
        return lms.get_tx_vga1dc_i_int();
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        return lms.get_tx_vga1dc_q_int();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
    double set_freq(dboard_iface::unit_t unit, double f) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_freq(%f)\n", f);
 | 
			
		||||
        unsigned ref_freq = _clock_rate;
 | 
			
		||||
        double actual_freq = 0;
 | 
			
		||||
@@ -227,6 +260,7 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool set_enabled(dboard_iface::unit_t unit, bool en) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_enabled(%d)\n", en);
 | 
			
		||||
        if (unit==dboard_iface::UNIT_RX) {
 | 
			
		||||
            if (en)
 | 
			
		||||
@@ -244,6 +278,7 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double set_rx_gain(double gain, const std::string &name) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_rx_gain(%f, %s)\n", gain, name.c_str());
 | 
			
		||||
        assert_has(lms_rx_gain_ranges.keys(), name, "LMS6002D rx gain name");
 | 
			
		||||
        if(name == "VGA1"){
 | 
			
		||||
@@ -257,6 +292,7 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void set_rx_ant(const std::string &ant) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_rx_ant(%s)\n", ant.c_str());
 | 
			
		||||
        //validate input
 | 
			
		||||
        assert_has(lms_rx_antennas, ant, "LMS6002D rx antenna name");
 | 
			
		||||
@@ -290,6 +326,7 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double set_rx_bandwidth(double bandwidth) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_rx_bandwidth(%f)\n", bandwidth);
 | 
			
		||||
        // Get the closest available bandwidth
 | 
			
		||||
        bandwidth = lms_bandwidth_range.clip(bandwidth);
 | 
			
		||||
@@ -301,6 +338,7 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double set_tx_gain(double gain, const std::string &name) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_tx_gain(%f, %s)\n", gain, name.c_str());
 | 
			
		||||
        //validate input
 | 
			
		||||
        assert_has(lms_tx_gain_ranges.keys(), name, "LMS6002D tx gain name");
 | 
			
		||||
@@ -322,6 +360,7 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void set_tx_ant(const std::string &ant) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_tx_ant(%s)\n", ant.c_str());
 | 
			
		||||
        //validate input
 | 
			
		||||
        assert_has(lms_tx_antennas, ant, "LMS6002D tx antenna ant");
 | 
			
		||||
@@ -341,6 +380,7 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double set_tx_bandwidth(double bandwidth) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_tx_bandwidth(%f)\n", bandwidth);
 | 
			
		||||
        // Get the closest available bandwidth
 | 
			
		||||
        bandwidth = lms_bandwidth_range.clip(bandwidth);
 | 
			
		||||
@@ -352,12 +392,14 @@ protected:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t _set_tx_vga1dc_i_int(uint8_t offset) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_tx_vga1dc_i_int(%d)\n", offset);
 | 
			
		||||
        lms.set_tx_vga1dc_i_int(offset);
 | 
			
		||||
        return offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t _set_tx_vga1dc_q_int(uint8_t offset) {
 | 
			
		||||
        boost::recursive_mutex::scoped_lock l(_mutex);
 | 
			
		||||
        if (verbosity>0) printf("lms6002d_ctrl_impl::set_tx_vga1dc_q_int(%d)\n", offset);
 | 
			
		||||
        lms.set_tx_vga1dc_q_int(offset);
 | 
			
		||||
        return offset;
 | 
			
		||||
@@ -375,6 +417,8 @@ private:
 | 
			
		||||
    const int _lms_spi_number;
 | 
			
		||||
    const int _adf4350_spi_number;
 | 
			
		||||
    const double _clock_rate;
 | 
			
		||||
 | 
			
		||||
    boost::recursive_mutex _mutex;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
lms6002d_ctrl::sptr lms6002d_ctrl::make(uhd::spi_iface::sptr spiface, const int lms_spi_number, const int adf4350_spi_number, const double clock_rate)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										280
									
								
								host/power_amp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								host/power_amp.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,280 @@
 | 
			
		||||
#include "power_amp.hpp"
 | 
			
		||||
#include <uhd/utils/msg.hpp>
 | 
			
		||||
#include <uhd/exception.hpp>
 | 
			
		||||
#include <boost/assign/list_of.hpp>
 | 
			
		||||
#include <boost/foreach.hpp>
 | 
			
		||||
#include <boost/algorithm/string.hpp>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
 | 
			
		||||
using namespace uhd;
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Declarations
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
 | 
			
		||||
class power_amp_impl : public power_amp {
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    // Voltage to Watts curves
 | 
			
		||||
    typedef std::map<double,double> pa_curve_t;
 | 
			
		||||
 | 
			
		||||
    power_amp_impl(pa_type_t pa_type, const pa_curve_t &v2w, const pa_curve_t &w2v);
 | 
			
		||||
    virtual ~power_amp_impl();
 | 
			
		||||
 | 
			
		||||
    // Get the PA type
 | 
			
		||||
    virtual pa_type_t get_pa_type() const {return _pa_type;}
 | 
			
		||||
 | 
			
		||||
    // Get the PA type as a string
 | 
			
		||||
    virtual std::string get_pa_type_str() const {return pa_type_to_str(_pa_type);}
 | 
			
		||||
 | 
			
		||||
    // Minimum and maximum supported output power in watts
 | 
			
		||||
    virtual double min_power_w() const;
 | 
			
		||||
    virtual double max_power_w() const;
 | 
			
		||||
    // Minimum and maximum supported output power in dBm
 | 
			
		||||
    virtual double min_power_dBm() const;
 | 
			
		||||
    virtual double max_power_dBm() const;
 | 
			
		||||
 | 
			
		||||
    // Get output power in watts for a given voltage
 | 
			
		||||
    virtual double v2w(double v) const;
 | 
			
		||||
    // Get input voltage required to generate given output power (in watts)
 | 
			
		||||
    virtual double w2v(double w) const;
 | 
			
		||||
    // Get output power in dBm for a given voltage
 | 
			
		||||
    virtual double v2dBm(double v) const;
 | 
			
		||||
    // Get input voltage required to generate given output power (in dBm)
 | 
			
		||||
    virtual double dBm2v(double dBm) const;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
    // The PA type
 | 
			
		||||
    pa_type_t _pa_type;
 | 
			
		||||
 | 
			
		||||
    // Curves
 | 
			
		||||
    const pa_curve_t &_v2w_curve;
 | 
			
		||||
    const pa_curve_t _w2v_curve;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Interpolate curve values
 | 
			
		||||
static double pa_interpolate_curve(const power_amp_impl::pa_curve_t &curve, double v);
 | 
			
		||||
// Convert an A->B map into a B->A map
 | 
			
		||||
template<typename map_t> static map_t map_reverse(map_t curve);
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Constants
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
 | 
			
		||||
// NOTE: All names MUST be uppercase for pa_str_to_type() to work correctly
 | 
			
		||||
const power_amp::pa_type_map_pair_t power_amp::_pa_type_map[] = {
 | 
			
		||||
    {power_amp::PA_NONE, "NONE"}, // Also serves as the default
 | 
			
		||||
    {power_amp::PA_EPA881F40A, "EPA881F40A"},
 | 
			
		||||
    {power_amp::PA_EPA942H40A, "EPA942H40A"},
 | 
			
		||||
    {power_amp::PA_EPA1800F37A, "EPA1800F37A"}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const power_amp_impl::pa_curve_t EPA942H40A_v2w_curve = boost::assign::map_list_of
 | 
			
		||||
    (9.5,   1.15)
 | 
			
		||||
    (10,    1.31)
 | 
			
		||||
    (11,    1.6)
 | 
			
		||||
    (12,    1.9)
 | 
			
		||||
    (12.5,  2.1)
 | 
			
		||||
    (13,    2.25)
 | 
			
		||||
    (13.5,  2.44)
 | 
			
		||||
    (14,    2.6)
 | 
			
		||||
    (14.5,  2.8)
 | 
			
		||||
    (15,    3.0)
 | 
			
		||||
    (15.5,  3.2)
 | 
			
		||||
    (16,    3.45)
 | 
			
		||||
    (16.5,  3.7)
 | 
			
		||||
    (17,    3.9)
 | 
			
		||||
    (17.5,  4.1)
 | 
			
		||||
    (18,    4.35)
 | 
			
		||||
    (18.5,  4.6)
 | 
			
		||||
    (19,    4.8)
 | 
			
		||||
    (19.5,  5.1)
 | 
			
		||||
    (20,    5.4)
 | 
			
		||||
    (20.5,  5.65)
 | 
			
		||||
    (21.1,  6.0)
 | 
			
		||||
    (21.6,  6.2)
 | 
			
		||||
    (22.1,  6.5)
 | 
			
		||||
    (22.6,  6.8)
 | 
			
		||||
    (23.1,  7.1)
 | 
			
		||||
    (23.4,  7.25)
 | 
			
		||||
    (23.7,  7.4)
 | 
			
		||||
    (24,    7.55)
 | 
			
		||||
    (24.2,  7.7)
 | 
			
		||||
    (24.5,  7.9)
 | 
			
		||||
    (24.8,  8.0)
 | 
			
		||||
    (25.2,  8.25)
 | 
			
		||||
    (25.5,  8.45)
 | 
			
		||||
    (25.9,  8.65)
 | 
			
		||||
    (26.2,  8.9)
 | 
			
		||||
    (26.6,  9.1)
 | 
			
		||||
    (28,   10.0);
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Static functions
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
 | 
			
		||||
template<typename map_t>
 | 
			
		||||
static map_t map_reverse(map_t curve)
 | 
			
		||||
{
 | 
			
		||||
    map_t reversed;
 | 
			
		||||
 | 
			
		||||
    for (typename map_t::iterator i = curve.begin(); i != curve.end(); ++i)
 | 
			
		||||
        reversed[i->second] = i->first;
 | 
			
		||||
 | 
			
		||||
    return reversed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static double pa_interpolate_curve(const power_amp_impl::pa_curve_t &curve, double v)
 | 
			
		||||
{
 | 
			
		||||
    power_amp_impl::pa_curve_t::const_iterator i = curve.upper_bound(v);
 | 
			
		||||
    if (i == curve.end())
 | 
			
		||||
    {
 | 
			
		||||
        return (--i)->second;
 | 
			
		||||
    }
 | 
			
		||||
    if (i == curve.begin())
 | 
			
		||||
    {
 | 
			
		||||
        return i->second;
 | 
			
		||||
    }
 | 
			
		||||
    power_amp_impl::pa_curve_t::const_iterator l=i;
 | 
			
		||||
    --l;
 | 
			
		||||
 | 
			
		||||
    const double delta=(v - l->first) / (i->first - l->first);
 | 
			
		||||
    return delta*i->second + (1-delta)*l->second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Make
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
power_amp::sptr power_amp::make(pa_type_t pa_type) {
 | 
			
		||||
    switch (pa_type) {
 | 
			
		||||
    case PA_NONE:
 | 
			
		||||
        return power_amp::sptr();
 | 
			
		||||
    case PA_EPA881F40A:
 | 
			
		||||
    case PA_EPA942H40A:
 | 
			
		||||
    case PA_EPA1800F37A:
 | 
			
		||||
    default:
 | 
			
		||||
        return power_amp::sptr(new power_amp_impl(pa_type, EPA942H40A_v2w_curve,
 | 
			
		||||
                                                  map_reverse(EPA942H40A_v2w_curve)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * power_amp methods
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
 | 
			
		||||
power_amp::~power_amp()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string power_amp::pa_type_to_str(pa_type_t pa)
 | 
			
		||||
{
 | 
			
		||||
    BOOST_FOREACH(const power_amp::pa_type_map_pair_t &pa_map_pair, _pa_type_map)
 | 
			
		||||
    {
 | 
			
		||||
        if (pa_map_pair.type == pa)
 | 
			
		||||
            return pa_map_pair.name;
 | 
			
		||||
    }
 | 
			
		||||
    throw uhd::environment_error("Can't map PA type to a string.");
 | 
			
		||||
    return "NONE";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
power_amp::pa_type_t power_amp::pa_str_to_type(const std::string &pa_str)
 | 
			
		||||
{
 | 
			
		||||
    std::string pa_str_upper = boost::to_upper_copy(pa_str);
 | 
			
		||||
    BOOST_FOREACH(const power_amp::pa_type_map_pair_t &pa_map_pair, _pa_type_map)
 | 
			
		||||
    {
 | 
			
		||||
        if (pa_map_pair.name == pa_str_upper)
 | 
			
		||||
            return pa_map_pair.type;
 | 
			
		||||
    }
 | 
			
		||||
    UHD_MSG(error) << "PA name " << pa_str << " is not recognized. "
 | 
			
		||||
                   << "Setting PA type to NONE." << std::endl;
 | 
			
		||||
    return power_amp::PA_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::list<power_amp::pa_type_t> power_amp::list_pa_type()
 | 
			
		||||
{
 | 
			
		||||
    std::list<power_amp::pa_type_t> list;
 | 
			
		||||
 | 
			
		||||
    BOOST_FOREACH(const power_amp::pa_type_map_pair_t &pa_map_pair, _pa_type_map) {
 | 
			
		||||
        list.push_back(pa_map_pair.type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::list<std::string> power_amp::list_pa_str()
 | 
			
		||||
{
 | 
			
		||||
    std::list<std::string> list;
 | 
			
		||||
 | 
			
		||||
    BOOST_FOREACH(const power_amp::pa_type_map_pair_t &pa_map_pair, _pa_type_map) {
 | 
			
		||||
        list.push_back(pa_map_pair.name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp::w2dBm(double w)
 | 
			
		||||
{
 | 
			
		||||
    return 10*log10(w) + 30;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp::dBm2w(double dBm)
 | 
			
		||||
{
 | 
			
		||||
    return pow(10, (dBm-30)/10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * power_amp_impl methods
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
 | 
			
		||||
power_amp_impl::power_amp_impl(pa_type_t pa_type, const pa_curve_t &v2w, const pa_curve_t &w2v)
 | 
			
		||||
    : _pa_type(pa_type)
 | 
			
		||||
    , _v2w_curve(v2w)
 | 
			
		||||
    , _w2v_curve(w2v)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
power_amp_impl::~power_amp_impl()
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::min_power_w() const
 | 
			
		||||
{
 | 
			
		||||
    return _w2v_curve.begin()->first;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::max_power_w() const
 | 
			
		||||
{
 | 
			
		||||
    return _w2v_curve.rbegin()->first;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::min_power_dBm() const
 | 
			
		||||
{
 | 
			
		||||
    return w2dBm(min_power_w());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::max_power_dBm() const
 | 
			
		||||
{
 | 
			
		||||
    return w2dBm(max_power_w());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::v2w(double v) const
 | 
			
		||||
{
 | 
			
		||||
    return pa_interpolate_curve(_v2w_curve, v);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::w2v(double w) const
 | 
			
		||||
{
 | 
			
		||||
    return pa_interpolate_curve(_w2v_curve, w);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::v2dBm(double v) const
 | 
			
		||||
{
 | 
			
		||||
    return w2dBm(v2w(v));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double power_amp_impl::dBm2v(double dBm) const
 | 
			
		||||
{
 | 
			
		||||
    return w2v(dBm2w(dBm));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								host/power_amp.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								host/power_amp.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
#ifndef POWER_AMP_HPP
 | 
			
		||||
#define POWER_AMP_HPP
 | 
			
		||||
 | 
			
		||||
#include <uhd/config.hpp>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <list>
 | 
			
		||||
#include <boost/shared_ptr.hpp>
 | 
			
		||||
 | 
			
		||||
namespace uhd {
 | 
			
		||||
 | 
			
		||||
class power_amp {
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    // Supported Power Amplifiers
 | 
			
		||||
    enum pa_type_t {
 | 
			
		||||
        PA_NONE,         // No PA connected
 | 
			
		||||
        PA_EPA881F40A,   // EPA881F40A GSM850
 | 
			
		||||
        PA_EPA942H40A,   // EPA942H40A EGSM900
 | 
			
		||||
        PA_EPA1800F37A   // EPA1800F37A DCS1800
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    typedef boost::shared_ptr<power_amp> sptr;
 | 
			
		||||
    static power_amp::sptr make(pa_type_t pa_type);
 | 
			
		||||
    virtual ~power_amp();
 | 
			
		||||
 | 
			
		||||
    // Get the PA type
 | 
			
		||||
    virtual pa_type_t get_pa_type() const =0;
 | 
			
		||||
    // Get the PA type as a string
 | 
			
		||||
    virtual std::string get_pa_type_str() const =0;
 | 
			
		||||
 | 
			
		||||
    // Convert PA type to a string
 | 
			
		||||
    static std::string pa_type_to_str(pa_type_t pa);
 | 
			
		||||
    // Convert a string to a PA type
 | 
			
		||||
    static pa_type_t pa_str_to_type(const std::string &pa_str);
 | 
			
		||||
 | 
			
		||||
	// Return a list of PA types
 | 
			
		||||
	static std::list<pa_type_t> list_pa_type();
 | 
			
		||||
	// Return a list of PA type strings
 | 
			
		||||
	static std::list<std::string> list_pa_str();
 | 
			
		||||
 | 
			
		||||
    // Convert watts -> dBm
 | 
			
		||||
    static double w2dBm(double w);
 | 
			
		||||
    // Convert dBm -> watts
 | 
			
		||||
    static double dBm2w(double dBm);
 | 
			
		||||
 | 
			
		||||
    // Minimum and maximum supported output power in watts
 | 
			
		||||
    virtual double min_power_w() const =0;
 | 
			
		||||
    virtual double max_power_w() const =0;
 | 
			
		||||
    // Minimum and maximum supported output power in dBm
 | 
			
		||||
    virtual double min_power_dBm() const =0;
 | 
			
		||||
    virtual double max_power_dBm() const =0;
 | 
			
		||||
 | 
			
		||||
    // Get output power in watts for a given voltage
 | 
			
		||||
    virtual double v2w(double v) const =0;
 | 
			
		||||
    // Get input voltage required to generate given output power (in watts)
 | 
			
		||||
    virtual double w2v(double w) const =0;
 | 
			
		||||
    // Get output power in dBm for a given voltage
 | 
			
		||||
    virtual double v2dBm(double v) const =0;
 | 
			
		||||
    // Get input voltage required to generate given output power (in dBm)
 | 
			
		||||
    virtual double dBm2v(double dBm) const =0;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
    // Map PA types to string names
 | 
			
		||||
    struct pa_type_map_pair_t {
 | 
			
		||||
        pa_type_t type;
 | 
			
		||||
        std::string name;
 | 
			
		||||
    };
 | 
			
		||||
    static const pa_type_map_pair_t _pa_type_map[];
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // POWER_AMP_HPP
 | 
			
		||||
@@ -44,13 +44,15 @@ static const boost::uint32_t MAX_SEQS_OUT = 15;
 | 
			
		||||
class umtrx_fifo_ctrl_impl : public umtrx_fifo_ctrl{
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
    umtrx_fifo_ctrl_impl(zero_copy_if::sptr xport, const boost::uint32_t sid):
 | 
			
		||||
    umtrx_fifo_ctrl_impl(zero_copy_if::sptr xport, const boost::uint32_t sid, const boost::uint32_t window_size):
 | 
			
		||||
        _xport(xport),
 | 
			
		||||
        _sid(sid),
 | 
			
		||||
        _window_size(std::min(window_size, MAX_SEQS_OUT)),
 | 
			
		||||
        _seq_out(0),
 | 
			
		||||
        _seq_ack(0),
 | 
			
		||||
        _timeout(ACK_TIMEOUT)
 | 
			
		||||
    {
 | 
			
		||||
        UHD_MSG(status) << "fifo_ctrl.window_size = " << _window_size << std::endl;
 | 
			
		||||
        while (_xport->get_recv_buff(0.0)){} //flush
 | 
			
		||||
        this->set_time(uhd::time_spec_t(0.0));
 | 
			
		||||
        this->set_tick_rate(1.0); //something possible but bogus
 | 
			
		||||
@@ -72,7 +74,7 @@ public:
 | 
			
		||||
 | 
			
		||||
        this->send_pkt((addr - SETTING_REGS_BASE)/4, data, POKE32_CMD);
 | 
			
		||||
 | 
			
		||||
        this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
 | 
			
		||||
        this->wait_for_ack(_seq_out-_window_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boost::uint32_t peek32(wb_addr_type addr){
 | 
			
		||||
@@ -101,7 +103,7 @@ public:
 | 
			
		||||
        boost::mutex::scoped_lock lock(_mutex);
 | 
			
		||||
 | 
			
		||||
        this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD);
 | 
			
		||||
        this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
 | 
			
		||||
        this->wait_for_ack(_seq_out-_window_size);
 | 
			
		||||
 | 
			
		||||
        _ctrl_word_cache = 0; // force update first time around
 | 
			
		||||
    }
 | 
			
		||||
@@ -128,13 +130,13 @@ public:
 | 
			
		||||
        //conditionally send control word
 | 
			
		||||
        if (_ctrl_word_cache != ctrl_word){
 | 
			
		||||
            this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD);
 | 
			
		||||
            this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
 | 
			
		||||
            this->wait_for_ack(_seq_out-_window_size);
 | 
			
		||||
            _ctrl_word_cache = ctrl_word;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //send data word
 | 
			
		||||
        this->send_pkt(SPI_DATA, data_out, POKE32_CMD);
 | 
			
		||||
        this->wait_for_ack(_seq_out-MAX_SEQS_OUT);
 | 
			
		||||
        this->wait_for_ack(_seq_out-_window_size);
 | 
			
		||||
 | 
			
		||||
        //conditional readback
 | 
			
		||||
        if (readback){
 | 
			
		||||
@@ -230,6 +232,7 @@ private:
 | 
			
		||||
 | 
			
		||||
    zero_copy_if::sptr _xport;
 | 
			
		||||
    const boost::uint32_t _sid;
 | 
			
		||||
    const boost::uint32_t _window_size;
 | 
			
		||||
    boost::mutex _mutex;
 | 
			
		||||
    boost::uint16_t _seq_out;
 | 
			
		||||
    boost::uint16_t _seq_ack;
 | 
			
		||||
@@ -241,6 +244,6 @@ private:
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
umtrx_fifo_ctrl::sptr umtrx_fifo_ctrl::make(zero_copy_if::sptr xport, const boost::uint32_t sid){
 | 
			
		||||
    return sptr(new umtrx_fifo_ctrl_impl(xport, sid));
 | 
			
		||||
umtrx_fifo_ctrl::sptr umtrx_fifo_ctrl::make(zero_copy_if::sptr xport, const boost::uint32_t sid, const size_t window_size){
 | 
			
		||||
    return sptr(new umtrx_fifo_ctrl_impl(xport, sid, boost::uint32_t(window_size)));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ public:
 | 
			
		||||
    typedef boost::shared_ptr<umtrx_fifo_ctrl> sptr;
 | 
			
		||||
 | 
			
		||||
    //! Make a new FIFO control object
 | 
			
		||||
    static sptr make(uhd::transport::zero_copy_if::sptr xport, const boost::uint32_t sid);
 | 
			
		||||
    static sptr make(uhd::transport::zero_copy_if::sptr xport, const boost::uint32_t sid, const size_t window_size);
 | 
			
		||||
 | 
			
		||||
    //! Set the command time that will activate
 | 
			
		||||
    virtual void set_time(const uhd::time_spec_t &time) = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,55 @@ using namespace uhd::usrp;
 | 
			
		||||
using namespace uhd::transport;
 | 
			
		||||
namespace asio = boost::asio;
 | 
			
		||||
 | 
			
		||||
// Values recommended by Andrey Sviyazov
 | 
			
		||||
const int umtrx_impl::UMTRX_VGA1_DEF = -20;
 | 
			
		||||
const int umtrx_impl::UMTRX_VGA2_DEF = 22;
 | 
			
		||||
const int umtrx_impl::UMTRX_VGA2_MIN = 0;
 | 
			
		||||
 | 
			
		||||
static const double _dcdc_val_to_volt_init[256] =
 | 
			
		||||
{
 | 
			
		||||
     9.38,  9.38,  9.40,  9.42,  9.42,  9.44,  9.46,  9.46,  9.48,  9.50, // 10
 | 
			
		||||
     9.50,  9.52,  9.54,  9.54,  9.56,  9.58,  9.58,  9.60,  9.60,  9.62, // 20
 | 
			
		||||
     9.64,  9.66,  9.66,  9.68,  9.70,  9.70,  9.72,  9.74,  9.76,  9.76, // 30
 | 
			
		||||
     9.78,  9.80,  9.82,  9.82,  9.84,  9.86,  9.88,  9.90,  9.92,  9.92, // 40
 | 
			
		||||
     9.94,  9.96,  9.98,  9.98, 10.00, 10.02, 10.04, 10.06, 10.06, 10.08, // 50
 | 
			
		||||
    10.10, 10.12, 10.14, 10.16, 10.18, 10.20, 10.20, 10.24, 10.24, 10.28, // 60
 | 
			
		||||
    10.30, 10.32, 10.34, 10.34, 10.36, 10.38, 10.40, 10.42, 10.44, 10.46, // 70
 | 
			
		||||
    10.48, 10.50, 10.52, 10.54, 10.56, 10.60, 10.62, 10.64, 10.66, 10.68, // 80
 | 
			
		||||
    10.70, 10.72, 10.74, 10.76, 10.78, 10.80, 10.84, 10.86, 10.88, 10.90, // 90
 | 
			
		||||
    10.94, 10.96, 10.98, 11.00, 11.02, 11.06, 11.06, 11.10, 11.12, 11.16, // 100
 | 
			
		||||
    11.18, 11.20, 11.24, 11.26, 11.28, 11.32, 11.34, 11.38, 11.40, 11.44, // 110
 | 
			
		||||
    11.46, 11.50, 11.50, 11.54, 11.58, 11.60, 11.64, 11.66, 11.70, 11.74, // 120
 | 
			
		||||
    11.76, 11.80, 11.84, 11.86, 11.90, 11.94, 11.98, 12.00, 12.02, 12.06, // 130
 | 
			
		||||
    12.10, 12.14, 12.18, 12.22, 12.26, 12.28, 12.32, 12.36, 12.40, 12.44, // 140
 | 
			
		||||
    12.48, 12.54, 12.58, 12.62, 12.64, 12.68, 12.72, 12.76, 12.82, 12.86, // 150
 | 
			
		||||
    12.90, 12.96, 13.00, 13.04, 13.10, 13.14, 13.20, 13.24, 13.30, 13.34, // 160
 | 
			
		||||
    13.38, 13.44, 13.48, 13.54, 13.60, 13.66, 13.72, 13.76, 13.82, 13.88, // 170
 | 
			
		||||
    13.94, 14.02, 14.06, 14.14, 14.20, 14.26, 14.30, 14.36, 14.42, 14.50, // 180
 | 
			
		||||
    14.56, 14.64, 14.72, 14.78, 14.86, 14.92, 15.00, 15.08, 15.16, 15.24, // 190
 | 
			
		||||
    15.32, 15.40, 15.46, 15.54, 15.62, 15.72, 15.80, 15.90, 16.00, 16.08, // 200
 | 
			
		||||
    16.18, 16.28, 16.38, 16.48, 16.58, 16.68, 16.80, 16.90, 16.96, 17.08, // 210
 | 
			
		||||
    17.20, 17.32, 17.44, 17.56, 17.68, 17.82, 17.94, 18.06, 18.20, 18.36, // 220
 | 
			
		||||
    18.48, 18.64, 18.78, 18.94, 19.02, 19.18, 19.34, 19.50, 19.68, 19.84, // 230
 | 
			
		||||
    20.02, 20.20, 20.38, 20.58, 20.76, 20.96, 21.18, 21.38, 21.60, 21.82, // 240
 | 
			
		||||
    21.92, 22.16, 22.40, 22.66, 22.92, 23.18, 23.46, 23.74, 24.02, 24.30, // 250
 | 
			
		||||
    24.62, 24.94, 25.28, 25.62, 25.98, 26.34
 | 
			
		||||
};
 | 
			
		||||
const std::vector<double> umtrx_impl::_dcdc_val_to_volt(_dcdc_val_to_volt_init, &_dcdc_val_to_volt_init[256]);
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Property tree "alias" function
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
 | 
			
		||||
template <typename T> property<T> &property_alias(uhd::property_tree::sptr &_tree,
 | 
			
		||||
                                                  const uhd::fs_path &orig, const uhd::fs_path &alias)
 | 
			
		||||
{
 | 
			
		||||
    // By default route each chanel to its own antenna
 | 
			
		||||
    return _tree->create<T>(alias)
 | 
			
		||||
        .subscribe(boost::bind(&uhd::property<T>::set, boost::ref(_tree->access<T>(orig)), _1))
 | 
			
		||||
        .publish(boost::bind(&uhd::property<T>::get, boost::ref(_tree->access<T>(orig))));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Make
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
@@ -183,7 +232,8 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
 | 
			
		||||
    // high performance settings control
 | 
			
		||||
    ////////////////////////////////////////////////////////////////
 | 
			
		||||
    _iface->poke32(U2_REG_MISC_CTRL_SFC_CLEAR, 1); //clear settings fifo control state machine
 | 
			
		||||
    _ctrl = umtrx_fifo_ctrl::make(this->make_xport(UMTRX_CTRL_FRAMER, device_addr_t()), UMTRX_CTRL_SID);
 | 
			
		||||
    const size_t fifo_ctrl_window(device_addr.cast<size_t>("fifo_ctrl_window", 1024)); //default gets clipped to hardware maximum
 | 
			
		||||
    _ctrl = umtrx_fifo_ctrl::make(this->make_xport(UMTRX_CTRL_FRAMER, device_addr_t()), UMTRX_CTRL_SID, fifo_ctrl_window);
 | 
			
		||||
    _ctrl->peek32(0); //test readback
 | 
			
		||||
    _tree->create<time_spec_t>(mb_path / "time/cmd")
 | 
			
		||||
        .subscribe(boost::bind(&umtrx_fifo_ctrl::set_time, _ctrl, _1));
 | 
			
		||||
@@ -220,16 +270,76 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
 | 
			
		||||
    _tree->create<std::string>(mb_path / "hwrev").set(get_hw_rev());
 | 
			
		||||
    UHD_MSG(status) << "Detected UmTRX " << get_hw_rev() << std::endl;
 | 
			
		||||
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////////
 | 
			
		||||
    // configure diversity switches
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    // note: the control is also aliased to RF frontend later
 | 
			
		||||
    _tree->create<bool>(mb_path / "divsw1")
 | 
			
		||||
            .subscribe(boost::bind(&umtrx_impl::set_divsw1, this, _1));
 | 
			
		||||
            .subscribe(boost::bind(&umtrx_impl::set_diversity, this, _1, 0))
 | 
			
		||||
            .set(device_addr.cast<bool>("divsw1", false));
 | 
			
		||||
    UHD_MSG(status) << "Diversity switch for channel 1: "
 | 
			
		||||
                    << (_tree->access<bool>(mb_path / "divsw1").get()?"true":"false")
 | 
			
		||||
                    << std::endl;
 | 
			
		||||
    _tree->create<bool>(mb_path / "divsw2")
 | 
			
		||||
            .subscribe(boost::bind(&umtrx_impl::set_divsw2, this, _1));
 | 
			
		||||
            .subscribe(boost::bind(&umtrx_impl::set_diversity, this, _1, 1))
 | 
			
		||||
            .set(device_addr.cast<bool>("divsw2", false));
 | 
			
		||||
    UHD_MSG(status) << "Diversity switch for channel 2: "
 | 
			
		||||
                    << (_tree->access<bool>(mb_path / "divsw2").get()?"true":"false")
 | 
			
		||||
                    << std::endl;
 | 
			
		||||
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////////
 | 
			
		||||
    // set PLL divider
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
    // TODO: Add EEPROM cell to manually override this
 | 
			
		||||
    _pll_div = 1;
 | 
			
		||||
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////
 | 
			
		||||
    // get the atached PA type
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////
 | 
			
		||||
    std::list<std::string> pa_types = power_amp::list_pa_str();
 | 
			
		||||
    std::string pa_list_str;
 | 
			
		||||
    BOOST_FOREACH(const std::string &pa_str, pa_types)
 | 
			
		||||
    {
 | 
			
		||||
        pa_list_str += pa_str + " ";
 | 
			
		||||
    }
 | 
			
		||||
    UHD_MSG(status) << "Known PA types: " << pa_list_str << std::endl;
 | 
			
		||||
 | 
			
		||||
    power_amp::pa_type_t pa_type = power_amp::pa_str_to_type(device_addr.cast<std::string>("pa", "NONE"));
 | 
			
		||||
    if (_hw_rev < UMTRX_VER_2_3_1 and pa_type != power_amp::PA_NONE)
 | 
			
		||||
    {
 | 
			
		||||
        UHD_MSG(error) << "PA type " << power_amp::pa_type_to_str(pa_type) << " is not supported for UmTRX "
 | 
			
		||||
                       << get_hw_rev() << ". Setting PA type to NONE." << std::endl;
 | 
			
		||||
        pa_type = power_amp::PA_NONE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (char name = 'A'; name <= 'B'; name++)
 | 
			
		||||
    {
 | 
			
		||||
        std::string name_str = std::string(1, name);
 | 
			
		||||
        _pa[name_str] = power_amp::make(pa_type);
 | 
			
		||||
        UHD_MSG(status) << "Installed PA for side" << name_str << ": " << power_amp::pa_type_to_str(pa_type) << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (_pa["A"])
 | 
			
		||||
    {
 | 
			
		||||
        _pa_power_max_dBm = _pa["A"]->max_power_dBm();
 | 
			
		||||
 | 
			
		||||
        double limit_w = device_addr.cast<double>("pa_power_max_w", _pa["A"]->max_power_w());
 | 
			
		||||
        if (limit_w != _pa["A"]->max_power_w()) {
 | 
			
		||||
            _pa_power_max_dBm = power_amp::w2dBm(limit_w);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        double limit_dbm = device_addr.cast<double>("pa_power_max_dbm", _pa["A"]->max_power_dBm());
 | 
			
		||||
        if (limit_dbm != _pa["A"]->max_power_dBm()) {
 | 
			
		||||
            _pa_power_max_dBm = limit_dbm;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_pa_power_max_dBm != _pa["A"]->max_power_dBm()) {
 | 
			
		||||
            UHD_MSG(status) << "Limiting PA output power to: " << _pa_power_max_dBm << "dBm (" << power_amp::dBm2w(_pa_power_max_dBm) << "W)" << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////
 | 
			
		||||
    // create codec control objects
 | 
			
		||||
    ////////////////////////////////////////////////////////////////////
 | 
			
		||||
@@ -429,6 +539,9 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //tx gains
 | 
			
		||||
        if (!_pa[fe_name])
 | 
			
		||||
        {
 | 
			
		||||
            // Use internal LMS gain control if we don't have a PA
 | 
			
		||||
            BOOST_FOREACH(const std::string &name, ctrl->get_tx_gains())
 | 
			
		||||
            {
 | 
			
		||||
                _tree->create<meta_range_t>(tx_rf_fe_path / "gains" / name / "range")
 | 
			
		||||
@@ -438,6 +551,22 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
 | 
			
		||||
                    .coerce(boost::bind(&lms6002d_ctrl::set_tx_gain, ctrl, _1, name))
 | 
			
		||||
                    .set((ctrl->get_tx_gain_range(name).start() + ctrl->get_tx_gain_range(name).stop())/2.0);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Set LMS internal VGA1 gain to optimal value
 | 
			
		||||
            // VGA2 will be set in the set_tx_power()
 | 
			
		||||
            ctrl->set_tx_gain(UMTRX_VGA1_DEF, "VGA1");
 | 
			
		||||
            _tx_power_range[fe_name] = generate_tx_power_range(fe_name);
 | 
			
		||||
 | 
			
		||||
            // Use PA control to control output power
 | 
			
		||||
            _tree->create<meta_range_t>(tx_rf_fe_path / "gains" / "PA" / "range")
 | 
			
		||||
                .publish(boost::bind(&umtrx_impl::get_tx_power_range, this, fe_name));
 | 
			
		||||
 | 
			
		||||
            _tree->create<double>(tx_rf_fe_path / "gains" / "PA" / "value")
 | 
			
		||||
                .coerce(boost::bind(&umtrx_impl::set_tx_power, this, _1, fe_name))
 | 
			
		||||
                // Set default output power to maximum
 | 
			
		||||
                .set(get_tx_power_range(fe_name).stop());
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //rx freq
 | 
			
		||||
        _tree->create<double>(rx_rf_fe_path / "freq" / "value")
 | 
			
		||||
@@ -517,6 +646,9 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
 | 
			
		||||
            .publish(boost::bind(&umtrx_impl::get_dc_offset_correction, this, fe_name))
 | 
			
		||||
            .subscribe(boost::bind(&umtrx_impl::set_dc_offset_correction, this, fe_name, _1))
 | 
			
		||||
            .set(std::complex<double>(dc_i, dc_q));
 | 
			
		||||
 | 
			
		||||
        // Alias diversity switch control from mb_path
 | 
			
		||||
        property_alias<bool>(_tree, mb_path / "divsw"+(fe_name=="A"?"1":"2"), rx_rf_fe_path / "diversity");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //set TCXO DAC calibration value, which is read from mboard EEPROM
 | 
			
		||||
@@ -557,10 +689,15 @@ umtrx_impl::umtrx_impl(const device_addr_t &device_addr)
 | 
			
		||||
 | 
			
		||||
    _tree->access<std::string>(mb_path / "clock_source" / "value").set("internal");
 | 
			
		||||
    _tree->access<std::string>(mb_path / "time_source" / "value").set("none");
 | 
			
		||||
 | 
			
		||||
    //create status monitor and client handler
 | 
			
		||||
    this->status_monitor_start(device_addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
umtrx_impl::~umtrx_impl(void)
 | 
			
		||||
{
 | 
			
		||||
    this->status_monitor_stop();
 | 
			
		||||
 | 
			
		||||
    BOOST_FOREACH(const std::string &fe_name, _lms_ctrl.keys())
 | 
			
		||||
    {
 | 
			
		||||
        lms6002d_ctrl::sptr ctrl = _lms_ctrl[fe_name];
 | 
			
		||||
@@ -573,15 +710,105 @@ umtrx_impl::~umtrx_impl(void)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int umtrx_impl::volt_to_dcdc_r(double v)
 | 
			
		||||
{
 | 
			
		||||
    if (v <= _dcdc_val_to_volt[0])
 | 
			
		||||
        return 0;
 | 
			
		||||
    else if (v >= _dcdc_val_to_volt[255])
 | 
			
		||||
        return 255;
 | 
			
		||||
    else
 | 
			
		||||
        return std::lower_bound(_dcdc_val_to_volt.begin(), _dcdc_val_to_volt.end(), v) - _dcdc_val_to_volt.begin();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::set_pa_dcdc_r(uint8_t val)
 | 
			
		||||
{
 | 
			
		||||
    boost::recursive_mutex::scoped_lock l(_i2c_mutex);
 | 
			
		||||
    // AD5245 control
 | 
			
		||||
    if (_hw_rev >= UMTRX_VER_2_3_1)
 | 
			
		||||
    {
 | 
			
		||||
        _pa_dcdc_r = val;
 | 
			
		||||
        _iface->write_i2c(BOOST_BINARY(0101100), boost::assign::list_of(0)(val));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uhd::gain_range_t umtrx_impl::generate_tx_power_range(const std::string &which) const
 | 
			
		||||
{
 | 
			
		||||
    // Native PA range plus LMS6 VGA2 control. We keep LMS6 VGA1 constant to
 | 
			
		||||
    // maintain high signal quality.
 | 
			
		||||
    uhd::gain_range_t pa_range = generate_pa_power_range(which);
 | 
			
		||||
//    UHD_MSG(status) << "Original PA output power range: " << pa_range.to_pp_string() << std::endl;
 | 
			
		||||
    uhd::gain_range_t vga_range(pa_range.start() - (UMTRX_VGA2_DEF-UMTRX_VGA2_MIN), pa_range.start()-1, 1.0);
 | 
			
		||||
    uhd::gain_range_t res_range(vga_range);
 | 
			
		||||
    res_range.insert(res_range.end(), pa_range.begin(), pa_range.end());
 | 
			
		||||
//    UHD_MSG(status) << "Generated Tx output power range: " << res_range.to_pp_string() << std::endl;
 | 
			
		||||
    return res_range;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uhd::gain_range_t umtrx_impl::generate_pa_power_range(const std::string &which) const
 | 
			
		||||
{
 | 
			
		||||
    double min_power = _pa[which]->min_power_dBm();
 | 
			
		||||
    double max_power = _pa_power_max_dBm;
 | 
			
		||||
    return uhd::gain_range_t(min_power, max_power, 0.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const uhd::gain_range_t &umtrx_impl::get_tx_power_range(const std::string &which) const
 | 
			
		||||
{
 | 
			
		||||
    return _tx_power_range[which];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double umtrx_impl::set_tx_power(double power, const std::string &which)
 | 
			
		||||
{
 | 
			
		||||
    double min_pa_power = _pa[which]->min_power_dBm();
 | 
			
		||||
    double actual_power;
 | 
			
		||||
 | 
			
		||||
    if (power >= min_pa_power)
 | 
			
		||||
    {
 | 
			
		||||
        UHD_MSG(status) << "Setting Tx power using PA (VGA2=" << UMTRX_VGA2_DEF << ", PA=" << power << ")" << std::endl;
 | 
			
		||||
        // Set VGA2 to the recommended value and use PA to control Tx power
 | 
			
		||||
        _lms_ctrl[which]->set_tx_gain(UMTRX_VGA2_DEF, "VGA2");
 | 
			
		||||
        actual_power = set_pa_power(power, which);
 | 
			
		||||
    } else {
 | 
			
		||||
        double vga2_gain = UMTRX_VGA2_DEF - (min_pa_power-power);
 | 
			
		||||
        UHD_MSG(status) << "Setting Tx power using VGA2 (VGA2=" << vga2_gain << ", PA=" << min_pa_power << ")" << std::endl;
 | 
			
		||||
        // Set PA output power to minimum and use VGA2 to control Tx power
 | 
			
		||||
        actual_power = _lms_ctrl[which]->set_tx_gain(vga2_gain, "VGA2");
 | 
			
		||||
        actual_power = set_pa_power(min_pa_power, which) - (UMTRX_VGA2_DEF-actual_power);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return actual_power;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double umtrx_impl::set_pa_power(double power, const std::string &which)
 | 
			
		||||
{
 | 
			
		||||
    boost::recursive_mutex::scoped_lock l(_i2c_mutex);
 | 
			
		||||
    // TODO:: Use DCDC bypass for maximum output power
 | 
			
		||||
    // TODO:: Limit output power for UmSITE-TM3
 | 
			
		||||
 | 
			
		||||
    // Find voltage required for the requested output power
 | 
			
		||||
    double v = _pa[which]->dBm2v(power);
 | 
			
		||||
    uint8_t dcdc_val = volt_to_dcdc_r(v);
 | 
			
		||||
    // Set the value
 | 
			
		||||
    set_nlow(true);
 | 
			
		||||
    set_pa_dcdc_r(dcdc_val);
 | 
			
		||||
 | 
			
		||||
    // Check what power do we actually have by reading the DCDC voltage
 | 
			
		||||
    // and converting it to the PA power
 | 
			
		||||
    double v_actual = read_dc_v("DCOUT").to_real();
 | 
			
		||||
    double power_actual = _pa[which]->v2dBm(v_actual);
 | 
			
		||||
 | 
			
		||||
    // TODO:: Check that power is actually there by reading VSWR sensor.
 | 
			
		||||
 | 
			
		||||
    UHD_MSG(status) << "Setting PA power: Requested: " << power << "dBm = " << power_amp::dBm2w(power) << "W "
 | 
			
		||||
                    << "(" << v << "V dcdc_r=" << int(dcdc_val) << "). "
 | 
			
		||||
                    << "Actual: " << power_actual << "dBm = " << power_amp::dBm2w(power_actual) <<"W "
 | 
			
		||||
                    << "(" << v_actual << "V)" << std::endl;
 | 
			
		||||
 | 
			
		||||
    return power_actual;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::set_mb_eeprom(const uhd::i2c_iface::sptr &iface, const uhd::usrp::mboard_eeprom_t &eeprom)
 | 
			
		||||
{
 | 
			
		||||
    boost::recursive_mutex::scoped_lock l(_i2c_mutex);
 | 
			
		||||
    store_umtrx_eeprom(eeprom, *iface);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -646,6 +873,7 @@ uint8_t umtrx_impl::dc_offset_double2int(double corr)
 | 
			
		||||
 | 
			
		||||
uhd::sensor_value_t umtrx_impl::read_temp_c(const std::string &which)
 | 
			
		||||
{
 | 
			
		||||
    boost::recursive_mutex::scoped_lock l(_i2c_mutex);
 | 
			
		||||
    double temp = (which == "A") ? _temp_side_a.get_temp() :
 | 
			
		||||
                                   _temp_side_b.get_temp();
 | 
			
		||||
    return uhd::sensor_value_t("Temp"+which, temp, "C");
 | 
			
		||||
@@ -653,6 +881,7 @@ uhd::sensor_value_t umtrx_impl::read_temp_c(const std::string &which)
 | 
			
		||||
 | 
			
		||||
uhd::sensor_value_t umtrx_impl::read_pa_v(const std::string &which)
 | 
			
		||||
{
 | 
			
		||||
    boost::recursive_mutex::scoped_lock l(_i2c_mutex);
 | 
			
		||||
    unsigned i;
 | 
			
		||||
    for (i = 0; i < 4; i++) {
 | 
			
		||||
        if (which == power_sensors[i])
 | 
			
		||||
@@ -668,6 +897,7 @@ uhd::sensor_value_t umtrx_impl::read_pa_v(const std::string &which)
 | 
			
		||||
 | 
			
		||||
uhd::sensor_value_t umtrx_impl::read_dc_v(const std::string &which)
 | 
			
		||||
{
 | 
			
		||||
    boost::recursive_mutex::scoped_lock l(_i2c_mutex);
 | 
			
		||||
    unsigned i;
 | 
			
		||||
    for (i = 0; i < 4; i++) {
 | 
			
		||||
        if (which == dc_sensors[i])
 | 
			
		||||
@@ -813,14 +1043,11 @@ void umtrx_impl::set_nlow(bool en)
 | 
			
		||||
    _pa_nlow = en; commit_pa_state();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::set_divsw1(bool en)
 | 
			
		||||
void umtrx_impl::set_diversity(bool en, int chan)
 | 
			
		||||
{
 | 
			
		||||
    _iface->poke32(U2_REG_SR_ADDR(SR_DIVSW+0), (en) ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::set_divsw2(bool en)
 | 
			
		||||
{
 | 
			
		||||
    _iface->poke32(U2_REG_SR_ADDR(SR_DIVSW+1), (en) ? 1 : 0);
 | 
			
		||||
    // chan 0 has inversed switch polarity
 | 
			
		||||
    // chan 1 has straight switch polarity
 | 
			
		||||
    _iface->poke32(U2_REG_SR_ADDR(SR_DIVSW+chan), (en != (chan==1)) ? 0 : 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char* umtrx_impl::get_hw_rev() const
 | 
			
		||||
@@ -834,4 +1061,3 @@ const char* umtrx_impl::get_hw_rev() const
 | 
			
		||||
    default:               return "[unknown]";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
#include "cores/time64_core_200.hpp"
 | 
			
		||||
#include "ads1015_ctrl.hpp"
 | 
			
		||||
#include "tmp102_ctrl.hpp"
 | 
			
		||||
#include "power_amp.hpp"
 | 
			
		||||
#include <uhd/usrp/mboard_eeprom.hpp>
 | 
			
		||||
#include <uhd/property_tree.hpp>
 | 
			
		||||
#include <uhd/device.hpp>
 | 
			
		||||
@@ -47,6 +48,8 @@
 | 
			
		||||
#include <uhd/transport/udp_simple.hpp>
 | 
			
		||||
#include <uhd/transport/udp_zero_copy.hpp>
 | 
			
		||||
#include <uhd/transport/bounded_buffer.hpp>
 | 
			
		||||
#include <boost/thread/recursive_mutex.hpp>
 | 
			
		||||
#include <boost/property_tree/json_parser.hpp>
 | 
			
		||||
#include <uhd/types/ranges.hpp>
 | 
			
		||||
#include <uhd/exception.hpp>
 | 
			
		||||
#include <uhd/utils/static.hpp>
 | 
			
		||||
@@ -57,6 +60,7 @@
 | 
			
		||||
#include <boost/weak_ptr.hpp>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
#include <boost/thread/mutex.hpp>
 | 
			
		||||
#include <uhd/utils/tasks.hpp>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Halfthe size of USRP2 SRAM, because we split the same SRAM into buffers for two Tx channels instead of one.
 | 
			
		||||
@@ -119,10 +123,15 @@ private:
 | 
			
		||||
    unsigned _pll_div;
 | 
			
		||||
    const char* get_hw_rev() const;
 | 
			
		||||
 | 
			
		||||
    //communication interfaces
 | 
			
		||||
    std::string _device_ip_addr;
 | 
			
		||||
    umtrx_iface::sptr _iface;
 | 
			
		||||
    umtrx_fifo_ctrl::sptr _ctrl;
 | 
			
		||||
    // Optimal VGA settings for GSM signal, according to our measurements.
 | 
			
		||||
    static const int UMTRX_VGA1_DEF;
 | 
			
		||||
    static const int UMTRX_VGA2_DEF;
 | 
			
		||||
    static const int UMTRX_VGA2_MIN;
 | 
			
		||||
 | 
			
		||||
    // Conversion table for converting DCDC_R values to actual voltages.
 | 
			
		||||
    static const std::vector<double> _dcdc_val_to_volt;
 | 
			
		||||
    // Find a dcdc_r value to approximate requested Vout voltage
 | 
			
		||||
    static int volt_to_dcdc_r(double v);
 | 
			
		||||
 | 
			
		||||
    ads1015_ctrl _sense_pwr;
 | 
			
		||||
    ads1015_ctrl _sense_dc;
 | 
			
		||||
@@ -133,9 +142,21 @@ private:
 | 
			
		||||
    bool _pa_nlow;
 | 
			
		||||
    bool _pa_en1;
 | 
			
		||||
    bool _pa_en2;
 | 
			
		||||
    uint8_t _pa_dcdc_r;
 | 
			
		||||
    double _pa_power_max_dBm; // Artifical PA output power limit, dBm
 | 
			
		||||
 | 
			
		||||
    void set_pa_dcdc_r(uint8_t val);
 | 
			
		||||
    uint8_t get_pa_dcdc_r() const {return _pa_dcdc_r;}
 | 
			
		||||
 | 
			
		||||
    //communication interfaces
 | 
			
		||||
    std::string _device_ip_addr;
 | 
			
		||||
    umtrx_iface::sptr _iface;
 | 
			
		||||
    umtrx_fifo_ctrl::sptr _ctrl;
 | 
			
		||||
 | 
			
		||||
    //controls for perifs
 | 
			
		||||
    uhd::dict<std::string, lms6002d_ctrl::sptr> _lms_ctrl;
 | 
			
		||||
    uhd::dict<std::string, uhd::power_amp::sptr> _pa;
 | 
			
		||||
    uhd::dict<std::string, uhd::gain_range_t> _tx_power_range; // Tx output power range
 | 
			
		||||
 | 
			
		||||
    //control for FPGA cores
 | 
			
		||||
    std::vector<rx_frontend_core_200::sptr> _rx_fes;
 | 
			
		||||
@@ -145,7 +166,6 @@ private:
 | 
			
		||||
    time64_core_200::sptr _time64;
 | 
			
		||||
 | 
			
		||||
    //helper routines
 | 
			
		||||
    void set_pa_dcdc_r(uint8_t val);
 | 
			
		||||
    void set_mb_eeprom(const uhd::i2c_iface::sptr &, const uhd::usrp::mboard_eeprom_t &);
 | 
			
		||||
    double get_master_clock_rate(void) const { return 26e6; }
 | 
			
		||||
    double get_master_dsp_rate(void) const { return get_master_clock_rate()/2; }
 | 
			
		||||
@@ -165,8 +185,12 @@ private:
 | 
			
		||||
    void set_enpa1(bool en);
 | 
			
		||||
    void set_enpa2(bool en);
 | 
			
		||||
    void set_nlow(bool en);
 | 
			
		||||
    void set_divsw1(bool en);
 | 
			
		||||
    void set_divsw2(bool en);
 | 
			
		||||
    void set_diversity(bool en, int chan);
 | 
			
		||||
    uhd::gain_range_t generate_tx_power_range(const std::string &which) const;
 | 
			
		||||
    uhd::gain_range_t generate_pa_power_range(const std::string &which) const;
 | 
			
		||||
    const uhd::gain_range_t &get_tx_power_range(const std::string &which) const;
 | 
			
		||||
    double set_tx_power(double power, const std::string &which);
 | 
			
		||||
    double set_pa_power(double power, const std::string &which);
 | 
			
		||||
    uint16_t get_tcxo_dac(const umtrx_iface::sptr &);
 | 
			
		||||
    uhd::transport::zero_copy_if::sptr make_xport(const size_t which, const uhd::device_addr_t &args);
 | 
			
		||||
    std::complex<double> get_dc_offset_correction(const std::string &which) const;
 | 
			
		||||
@@ -179,6 +203,21 @@ private:
 | 
			
		||||
    uhd::sensor_value_t read_temp_c(const std::string &which);
 | 
			
		||||
    uhd::sensor_value_t read_pa_v(const std::string &which);
 | 
			
		||||
    uhd::sensor_value_t read_dc_v(const std::string &which);
 | 
			
		||||
    boost::recursive_mutex _i2c_mutex;
 | 
			
		||||
 | 
			
		||||
    //status monitoring
 | 
			
		||||
    void status_monitor_start(const uhd::device_addr_t &device_addr);
 | 
			
		||||
    void status_monitor_stop(void);
 | 
			
		||||
    uhd::task::sptr _status_monitor_task;
 | 
			
		||||
    void status_monitor_handler(void);
 | 
			
		||||
 | 
			
		||||
    //tcp query server
 | 
			
		||||
    uhd::task::sptr _server_query_task;
 | 
			
		||||
    void server_query_handler(void);
 | 
			
		||||
    boost::asio::io_service _server_query_io_service;
 | 
			
		||||
    boost::shared_ptr<boost::asio::ip::tcp::acceptor> _server_query_tcp_acceptor;
 | 
			
		||||
    void client_query_handle(boost::shared_ptr<boost::asio::ip::tcp::socket>);
 | 
			
		||||
    void client_query_handle1(const boost::property_tree::ptree &request, boost::property_tree::ptree &response);
 | 
			
		||||
 | 
			
		||||
    //streaming
 | 
			
		||||
    std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										246
									
								
								host/umtrx_monitor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								host/umtrx_monitor.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,246 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright 2015-2015 Fairwaves 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 "umtrx_impl.hpp"
 | 
			
		||||
#include <uhd/utils/msg.hpp>
 | 
			
		||||
#include <uhd/types/sensors.hpp>
 | 
			
		||||
#include <uhd/types/ranges.hpp>
 | 
			
		||||
#include <boost/asio.hpp>
 | 
			
		||||
 | 
			
		||||
using namespace uhd;
 | 
			
		||||
using namespace uhd::usrp;
 | 
			
		||||
namespace asio = boost::asio;
 | 
			
		||||
 | 
			
		||||
/*!
 | 
			
		||||
 * Querying sensors using the status monitor server.
 | 
			
		||||
 * Requests are encoded in JSON and end in a newline.
 | 
			
		||||
 * Responses are encoded in JSON and end in a newline.
 | 
			
		||||
 *
 | 
			
		||||
 * Start UmTRX driver with status_port set in args
 | 
			
		||||
 * ./some_application --args="status_port=12345"
 | 
			
		||||
 *
 | 
			
		||||
 * import json
 | 
			
		||||
 * import socket
 | 
			
		||||
 * s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
			
		||||
 * s.connect(("localhost", 12345))
 | 
			
		||||
 * f = s.makefile() #gives us readline()
 | 
			
		||||
 *
 | 
			
		||||
 * #list branches in the property tree
 | 
			
		||||
 * s.send(json.dumps(dict(action='LIST', path='/mboards/0/sensors'))+'\n')
 | 
			
		||||
 * print json.loads(f.readline())
 | 
			
		||||
 * {u'result': [u'tempA']}
 | 
			
		||||
 *
 | 
			
		||||
 * #check if the specified path exists
 | 
			
		||||
 * s.send(json.dumps(dict(action='HAS', path='/mboards/0/sensors/tempA'))+'\n')
 | 
			
		||||
 * print json.loads(f.readline())
 | 
			
		||||
 * {u'result': u'true'}
 | 
			
		||||
 *
 | 
			
		||||
 * #get the value of a tree entry, types can be BOOL, INT, DOUBLE, 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
 | 
			
		||||
 * 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
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::status_monitor_start(const uhd::device_addr_t &device_addr)
 | 
			
		||||
{
 | 
			
		||||
    if (device_addr.has_key("status_port"))
 | 
			
		||||
    {
 | 
			
		||||
        UHD_MSG(status) << "Creating TCP monitor on port " << device_addr.get("status_port") << std::endl;
 | 
			
		||||
        _server_query_tcp_acceptor.reset(new asio::ip::tcp::acceptor(
 | 
			
		||||
            _server_query_io_service, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), device_addr.cast<int>("status_port", 0))));
 | 
			
		||||
        _server_query_task = task::make(boost::bind(&umtrx_impl::server_query_handler, this));
 | 
			
		||||
    }
 | 
			
		||||
    _status_monitor_task = task::make(boost::bind(&umtrx_impl::status_monitor_handler, this));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::status_monitor_stop(void)
 | 
			
		||||
{
 | 
			
		||||
    _status_monitor_task.reset();
 | 
			
		||||
    _server_query_task.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool wait_read_sockfd(const int sockfd, const size_t timeoutMs)
 | 
			
		||||
{
 | 
			
		||||
    //setup timeval for timeout
 | 
			
		||||
    timeval tv;
 | 
			
		||||
    tv.tv_sec = 0;
 | 
			
		||||
    tv.tv_usec = timeoutMs*1000;
 | 
			
		||||
 | 
			
		||||
    //setup rset for timeout
 | 
			
		||||
    fd_set rset;
 | 
			
		||||
    FD_ZERO(&rset);
 | 
			
		||||
    FD_SET(sockfd, &rset);
 | 
			
		||||
 | 
			
		||||
    //call select with timeout on receive socket
 | 
			
		||||
    return ::select(sockfd+1, &rset, NULL, NULL, &tv) > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::status_monitor_handler(void)
 | 
			
		||||
{
 | 
			
		||||
    //TODO read the sensors and react...
 | 
			
		||||
    //read_dc_v, etc...
 | 
			
		||||
    //UHD_MSG(status) << this->read_temp_c("A").to_pp_string() << std::endl;
 | 
			
		||||
 | 
			
		||||
    //TODO shutdown frontend when temp > thresh
 | 
			
		||||
    //ctrl->set_rx_enabled(false);
 | 
			
		||||
    //ctrl->set_tx_enabled(false);
 | 
			
		||||
 | 
			
		||||
    //this sleep defines the polling time between status checks
 | 
			
		||||
    //when the handler completes, it will be called again asap
 | 
			
		||||
    //if the task is canceled, this sleep in interrupted for exit
 | 
			
		||||
    boost::this_thread::sleep(boost::posix_time::milliseconds(1500));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::server_query_handler(void)
 | 
			
		||||
{
 | 
			
		||||
    //accept the client socket (timeout is 100 ms, task is called again)
 | 
			
		||||
    if (not wait_read_sockfd(_server_query_tcp_acceptor->native(), 100)) return;
 | 
			
		||||
    boost::shared_ptr<asio::ip::tcp::socket> socket(new asio::ip::tcp::socket(_server_query_io_service));
 | 
			
		||||
    _server_query_tcp_acceptor->accept(*socket);
 | 
			
		||||
 | 
			
		||||
    //create a new thread to handle the client
 | 
			
		||||
    boost::thread handler(boost::bind(&umtrx_impl::client_query_handle, this, socket));
 | 
			
		||||
    handler.detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::client_query_handle(boost::shared_ptr<boost::asio::ip::tcp::socket> socket)
 | 
			
		||||
{
 | 
			
		||||
    while (not boost::this_thread::interruption_requested())
 | 
			
		||||
    {
 | 
			
		||||
        boost::property_tree::ptree request, response;
 | 
			
		||||
 | 
			
		||||
        //receive the request in JSON markup
 | 
			
		||||
        boost::asio::streambuf requestBuff;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            boost::asio::read_until(*socket, requestBuff, "\n");
 | 
			
		||||
            std::istream is(&requestBuff);
 | 
			
		||||
            boost::property_tree::read_json(is, request);
 | 
			
		||||
        }
 | 
			
		||||
        catch (const std::exception &ex)
 | 
			
		||||
        {
 | 
			
		||||
            if (requestBuff.size() == 0) return; //client ended
 | 
			
		||||
            response.put("error", "request parser error: " + std::string(ex.what()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //handle the request
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            this->client_query_handle1(request, response);
 | 
			
		||||
        }
 | 
			
		||||
        catch (const std::exception &ex)
 | 
			
		||||
        {
 | 
			
		||||
            response.put("error", "failed to handle request: " + std::string(ex.what()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //send the response
 | 
			
		||||
        boost::asio::streambuf responseBuff;
 | 
			
		||||
        std::ostream os(&responseBuff);
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            boost::property_tree::write_json(os, response, false/*not pretty required*/);
 | 
			
		||||
            boost::asio::write(*socket, responseBuff);
 | 
			
		||||
        }
 | 
			
		||||
        catch (const std::exception &ex)
 | 
			
		||||
        {
 | 
			
		||||
            UHD_MSG(error) << "client_query_handle send response failed, exit client thread: " << ex.what() << std::endl;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void umtrx_impl::client_query_handle1(const boost::property_tree::ptree &request, boost::property_tree::ptree &response)
 | 
			
		||||
{
 | 
			
		||||
    const std::string action = request.get("action", "");
 | 
			
		||||
    const std::string path = request.get("path", "");
 | 
			
		||||
    if (response.count("error") != 0)
 | 
			
		||||
    {
 | 
			
		||||
        //already in error
 | 
			
		||||
    }
 | 
			
		||||
    else if (path.empty())
 | 
			
		||||
    {
 | 
			
		||||
        response.put("error", "path field not specified");
 | 
			
		||||
    }
 | 
			
		||||
    else if (action.empty())
 | 
			
		||||
    {
 | 
			
		||||
        response.put("error", "action field not specified: GET, SET, HAS, LIST");
 | 
			
		||||
    }
 | 
			
		||||
    else if (action == "GET")
 | 
			
		||||
    {
 | 
			
		||||
        const std::string type = request.get("type", "");
 | 
			
		||||
        if (type.empty()) response.put("error", "type field not specified: BOOL, INT, DOUBLE, SENSOR, RANGE");
 | 
			
		||||
        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 == "SENSOR")
 | 
			
		||||
        {
 | 
			
		||||
            boost::property_tree::ptree result;
 | 
			
		||||
            const sensor_value_t sensor = _tree->access<sensor_value_t>(path).get();
 | 
			
		||||
            result.put("name", sensor.name);
 | 
			
		||||
            result.put("value", sensor.value);
 | 
			
		||||
            result.put("unit", sensor.unit);
 | 
			
		||||
            response.add_child("result", result);
 | 
			
		||||
        }
 | 
			
		||||
        else if (type == "RANGE")
 | 
			
		||||
        {
 | 
			
		||||
            boost::property_tree::ptree result;
 | 
			
		||||
            BOOST_FOREACH(const range_t &range, _tree->access<meta_range_t>(path).get())
 | 
			
		||||
            {
 | 
			
		||||
                boost::property_tree::ptree rangeData;
 | 
			
		||||
                rangeData.put("start", range.start());
 | 
			
		||||
                rangeData.put("stop", range.stop());
 | 
			
		||||
                rangeData.put("step", range.step());
 | 
			
		||||
                result.push_back(std::make_pair("", rangeData));
 | 
			
		||||
            }
 | 
			
		||||
            response.add_child("result", result);
 | 
			
		||||
        }
 | 
			
		||||
        else response.put("error", "unknown type: " + type);
 | 
			
		||||
    }
 | 
			
		||||
    else if (action == "SET")
 | 
			
		||||
    {
 | 
			
		||||
        const std::string type = request.get("type", "");
 | 
			
		||||
        if (type.empty()) response.put("error", "type field not specified: BOOL, INT, DOUBLE");
 | 
			
		||||
        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 response.put("error", "unknown type: " + type);
 | 
			
		||||
    }
 | 
			
		||||
    else if (action == "HAS")
 | 
			
		||||
    {
 | 
			
		||||
        response.put("result", _tree->exists(path));
 | 
			
		||||
    }
 | 
			
		||||
    else if (action == "LIST")
 | 
			
		||||
    {
 | 
			
		||||
        boost::property_tree::ptree result;
 | 
			
		||||
        BOOST_FOREACH(const std::string &branchName, _tree->list(path))
 | 
			
		||||
        {
 | 
			
		||||
            boost::property_tree::ptree branchData;
 | 
			
		||||
            branchData.put("", branchName);
 | 
			
		||||
            result.push_back(std::make_pair("", branchData));
 | 
			
		||||
        }
 | 
			
		||||
        response.add_child("result", result);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        response.put("error", "unknown action: " + action);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,10 @@
 | 
			
		||||
########################################################################
 | 
			
		||||
INSTALL(PROGRAMS
 | 
			
		||||
    umtrx_net_burner
 | 
			
		||||
    umtrx_nmea
 | 
			
		||||
    umtrx_gps_coord
 | 
			
		||||
    umtrx_auto_calibration
 | 
			
		||||
    umtrx_firmware
 | 
			
		||||
    DESTINATION bin
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -18,6 +22,10 @@ add_executable(umtrx_cal_tx_dc_offset umtrx_cal_tx_dc_offset.cpp)
 | 
			
		||||
target_link_libraries(umtrx_cal_tx_dc_offset ${UMTRX_LIBRARIES})
 | 
			
		||||
install(TARGETS umtrx_cal_tx_dc_offset DESTINATION bin)
 | 
			
		||||
 | 
			
		||||
add_executable(umtrx_cal_tx_iq_balance umtrx_cal_tx_iq_balance.cpp)
 | 
			
		||||
target_link_libraries(umtrx_cal_tx_iq_balance ${UMTRX_LIBRARIES})
 | 
			
		||||
install(TARGETS umtrx_cal_tx_iq_balance DESTINATION bin)
 | 
			
		||||
 | 
			
		||||
add_executable(umtrx_pa_ctrl umtrx_pa_ctrl.cpp)
 | 
			
		||||
target_link_libraries(umtrx_pa_ctrl ${UMTRX_LIBRARIES})
 | 
			
		||||
install(TARGETS umtrx_pa_ctrl DESTINATION bin)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										134
									
								
								host/utils/umtrx_auto_calibration
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										134
									
								
								host/utils/umtrx_auto_calibration
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,134 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
if [ "$#" -lt "1" ] ; then
 | 
			
		||||
  echo "Automatic calibration of an UmTRX for a set of given presets (bands)."
 | 
			
		||||
  echo
 | 
			
		||||
  echo "Usage:"
 | 
			
		||||
  echo "  umtrx_cal <preset> [<preset>] [<preset>] ..."
 | 
			
		||||
  echo
 | 
			
		||||
  echo "  preset - GSM850, EGSM900 (same as GSM900), GSM1800 (same as DCS1800), GSM1900 (same as PCS1900)."
 | 
			
		||||
  echo
 | 
			
		||||
  echo "Calibrations to be performed:"
 | 
			
		||||
  echo "  - Tx DC offset calibration"
 | 
			
		||||
  echo "  - Tx IQ balance calibration"
 | 
			
		||||
  echo
 | 
			
		||||
  echo "The result of the calibration is stored in the DIR/.uhd/cal/ directory. DIR is one of the \$APPDATA, \$HOME and /tmp,"
 | 
			
		||||
  echo "whichever is defined. Make sure you run calibration from the same user as the one who runs applications or define"
 | 
			
		||||
  echo "\$APPDATA or \$HOME appropriately. Calibration files will be loaded by the application automatically on startup."
 | 
			
		||||
  echo "Old calibration files are renamed when you run a calibration to avoid overwriting."
 | 
			
		||||
  echo
 | 
			
		||||
  echo "Calibration is permanent and only depends on temperature. If the temperature of the system is stable, you need to"
 | 
			
		||||
  echo "run the calibration only once."
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
presets=$*
 | 
			
		||||
 | 
			
		||||
sides="A B"
 | 
			
		||||
uhd_args="--args=fifo_ctrl_window=0"
 | 
			
		||||
report=""
 | 
			
		||||
 | 
			
		||||
run_cal() {
 | 
			
		||||
  what=$1 ; shift
 | 
			
		||||
  freq_start=$1 ; shift
 | 
			
		||||
  freq_stop=$1 ; shift
 | 
			
		||||
  other_args=$*
 | 
			
		||||
 | 
			
		||||
  freq_step=$(python -c "print $freq_stop - $freq_start if $freq_stop != $freq_start else 1e3")
 | 
			
		||||
 | 
			
		||||
  if [ "$what" = "dc" ] ; then
 | 
			
		||||
    cmd="umtrx_cal_tx_dc_offset"
 | 
			
		||||
  elif [ "$what" = "iq" ] ; then
 | 
			
		||||
    cmd="umtrx_cal_tx_iq_balance"
 | 
			
		||||
  else
 | 
			
		||||
    echo "Unknown calibration type \"$what\""
 | 
			
		||||
    return 1
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  for side in $sides ; do
 | 
			
		||||
 | 
			
		||||
    echo
 | 
			
		||||
    echo "------------------------------------------------------------------"
 | 
			
		||||
    echo "       Calibrating $what from $freq_start to $freq_stop for side $side"
 | 
			
		||||
    echo "------------------------------------------------------------------"
 | 
			
		||||
    echo
 | 
			
		||||
 | 
			
		||||
    res=255
 | 
			
		||||
    i=0
 | 
			
		||||
    while [ $i -lt 10 -a $res -ne 0 ] ; do
 | 
			
		||||
      i=$(expr $i + 1)
 | 
			
		||||
      cmd_full="$cmd $uhd_args --freq_start $freq_start --freq_stop $freq_stop --freq_step $freq_step --which $side $other_args"
 | 
			
		||||
      echo $cmd_full
 | 
			
		||||
      $cmd_full
 | 
			
		||||
      res=$(echo $?)
 | 
			
		||||
    done
 | 
			
		||||
 | 
			
		||||
    text_res="Calibration type $what side $side from $freq_start to $freq_stop:"
 | 
			
		||||
    if [ $res -ne 0 ] ; then
 | 
			
		||||
      text_res="$text_res FAIL"
 | 
			
		||||
    else
 | 
			
		||||
      text_res="$text_res SUCCESS"
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    echo
 | 
			
		||||
    echo "$text_res"
 | 
			
		||||
    echo
 | 
			
		||||
 | 
			
		||||
    report="$report$text_res\n"
 | 
			
		||||
  done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
run_preset() {
 | 
			
		||||
  preset=$1
 | 
			
		||||
 | 
			
		||||
  echo
 | 
			
		||||
  echo "===================================================================="
 | 
			
		||||
  echo "                       Running preset $preset"
 | 
			
		||||
  echo "===================================================================="
 | 
			
		||||
  echo
 | 
			
		||||
 | 
			
		||||
  case $preset in
 | 
			
		||||
  GSM850)
 | 
			
		||||
    run_cal dc 869e6 894e6
 | 
			
		||||
    # The band completely falls into the 810-930 VCO range
 | 
			
		||||
    run_cal iq 869e6 894e6
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  GSM900|EGSM900)
 | 
			
		||||
    run_cal dc 925e6 960e6
 | 
			
		||||
    # The band spans VCO ranges 810-930 and 930-11425
 | 
			
		||||
    run_cal iq 925e6 930e6
 | 
			
		||||
    run_cal iq 930.001e6 960e6 --append
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  GSM1800|DCS1800)
 | 
			
		||||
    run_cal dc 1805e6 1880e6
 | 
			
		||||
    # The band spans VCO ranges 1620-1860 and 1860-2285
 | 
			
		||||
    run_cal iq 1805e6 1860e6
 | 
			
		||||
    run_cal iq 1860.001e6 1880e6 --append
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  GSM1900|PCS1900)
 | 
			
		||||
    run_cal dc 1930e6 1990e6
 | 
			
		||||
    # The band completely falls into the 1860-2285 VCO range
 | 
			
		||||
    run_cal iq 1930e6 1990e6
 | 
			
		||||
    ;;
 | 
			
		||||
 | 
			
		||||
  *)
 | 
			
		||||
    echo "Unknown preset"
 | 
			
		||||
    exit 2
 | 
			
		||||
    ;;
 | 
			
		||||
  esac
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for preset in $presets ; do
 | 
			
		||||
  run_preset $preset
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
echo
 | 
			
		||||
echo "===================================================================="
 | 
			
		||||
echo "                               Result"
 | 
			
		||||
echo "===================================================================="
 | 
			
		||||
echo
 | 
			
		||||
echo "$report"
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright 2010 Ettus Research LLC
 | 
			
		||||
// Copyright 2012 Fairwaves LLC
 | 
			
		||||
// Copyright 2012-1015 Fairwaves, Inc
 | 
			
		||||
//
 | 
			
		||||
// 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
 | 
			
		||||
@@ -17,16 +17,10 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include "usrp_cal_utils.hpp"
 | 
			
		||||
#include <uhd/utils/thread_priority.hpp>
 | 
			
		||||
#include <uhd/utils/safe_main.hpp>
 | 
			
		||||
#include <uhd/utils/paths.hpp>
 | 
			
		||||
#include <uhd/utils/algorithm.hpp>
 | 
			
		||||
#include <uhd/usrp/multi_usrp.hpp>
 | 
			
		||||
#include <boost/program_options.hpp>
 | 
			
		||||
#include <boost/format.hpp>
 | 
			
		||||
#include <boost/thread/thread.hpp>
 | 
			
		||||
#include <boost/math/special_functions/round.hpp>
 | 
			
		||||
#include <boost/random.hpp>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <complex>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
@@ -35,77 +29,271 @@
 | 
			
		||||
namespace po = boost::program_options;
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Transmit thread
 | 
			
		||||
 * Calibration utility class
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static void tx_thread(uhd::usrp::multi_usrp::sptr usrp, const double tx_wave_freq, const double tx_wave_ampl){
 | 
			
		||||
    uhd::set_thread_priority_safe();
 | 
			
		||||
class dc_cal_t {
 | 
			
		||||
public:
 | 
			
		||||
    dc_cal_t(uhd::property<uint8_t> &dc_i_prop, uhd::property<uint8_t> &dc_q_prop,
 | 
			
		||||
             uhd::rx_streamer::sptr rx_stream,
 | 
			
		||||
             const size_t nsamps,
 | 
			
		||||
             double bb_dc_freq,
 | 
			
		||||
             double rx_rate,
 | 
			
		||||
             int verbose,
 | 
			
		||||
             bool debug_raw_data,
 | 
			
		||||
             int init_dc_i=128, int init_dc_q=128);
 | 
			
		||||
 | 
			
		||||
    //create a transmit streamer
 | 
			
		||||
    uhd::stream_args_t stream_args("fc32"); //complex floats
 | 
			
		||||
    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args);
 | 
			
		||||
    double init();
 | 
			
		||||
    void run_q(int dc_q);
 | 
			
		||||
    void run_i(int dc_i);
 | 
			
		||||
    void run_iq(int dc_i, int dc_q);
 | 
			
		||||
 | 
			
		||||
    //setup variables and allocate buffer
 | 
			
		||||
    uhd::tx_metadata_t md;
 | 
			
		||||
    md.has_time_spec = false;
 | 
			
		||||
    std::vector<samp_type> buff(tx_stream->get_max_num_samps()*10);
 | 
			
		||||
    void set_dc_i(double i) {prop_set_check(_dc_i_prop, i);}
 | 
			
		||||
    void set_dc_q(double q) {prop_set_check(_dc_q_prop, q);}
 | 
			
		||||
    void set_dc_i_best() {set_dc_i(_best_dc_i);}
 | 
			
		||||
    void set_dc_q_best() {set_dc_q(_best_dc_q);}
 | 
			
		||||
 | 
			
		||||
    //values for the wave table lookup
 | 
			
		||||
    size_t index = 0;
 | 
			
		||||
    const double tx_rate = usrp->get_tx_rate();
 | 
			
		||||
    const size_t step = boost::math::iround(wave_table_len * tx_wave_freq/tx_rate);
 | 
			
		||||
    wave_table table(tx_wave_ampl);
 | 
			
		||||
    double get_lowest_offset() const {return _lowest_offset;}
 | 
			
		||||
    int get_best_dc_i() const {return _best_dc_i;}
 | 
			
		||||
    int get_best_dc_q() const {return _best_dc_q;}
 | 
			
		||||
 | 
			
		||||
    //fill buff and send until interrupted
 | 
			
		||||
    while (not boost::this_thread::interruption_requested()){
 | 
			
		||||
        for (size_t i = 0; i < buff.size(); i++){
 | 
			
		||||
            buff[i] = table(index += step);
 | 
			
		||||
            buff[i] = samp_type(0, 0); //using no-power transmit to cal with
 | 
			
		||||
        }
 | 
			
		||||
        tx_stream->send(&buff.front(), buff.size(), md);
 | 
			
		||||
    }
 | 
			
		||||
protected:
 | 
			
		||||
    double _lowest_offset;
 | 
			
		||||
    int _best_dc_i;
 | 
			
		||||
    int _best_dc_q;
 | 
			
		||||
    uhd::property<uint8_t> &_dc_i_prop;
 | 
			
		||||
    uhd::property<uint8_t> &_dc_q_prop;
 | 
			
		||||
 | 
			
		||||
    //send a mini EOB packet
 | 
			
		||||
    md.end_of_burst = true;
 | 
			
		||||
    tx_stream->send("", 0, md);
 | 
			
		||||
}
 | 
			
		||||
    uhd::rx_streamer::sptr _rx_stream;
 | 
			
		||||
    std::vector<samp_type> _buff;
 | 
			
		||||
    const size_t _nsamps;
 | 
			
		||||
    double _bb_dc_freq;
 | 
			
		||||
    double _rx_rate;
 | 
			
		||||
    int _verbose;
 | 
			
		||||
    bool _debug_raw_data;
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Tune RX and TX routine
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static double tune_rx_and_tx(uhd::usrp::multi_usrp::sptr usrp, const double tx_lo_freq, const double rx_offset){
 | 
			
		||||
    //tune the transmitter with no cordic
 | 
			
		||||
    uhd::tune_request_t tx_tune_req(tx_lo_freq);
 | 
			
		||||
    tx_tune_req.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
 | 
			
		||||
    tx_tune_req.dsp_freq = 0;
 | 
			
		||||
    usrp->set_tx_freq(tx_tune_req);
 | 
			
		||||
    void prop_set_check(uhd::property<uint8_t> &prop, uint8_t val);
 | 
			
		||||
 | 
			
		||||
    //tune the receiver
 | 
			
		||||
    usrp->set_rx_freq(uhd::tune_request_t(usrp->get_tx_freq(), rx_offset));
 | 
			
		||||
    double get_dbrms();
 | 
			
		||||
    bool run_x();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    boost::this_thread::sleep(boost::posix_time::milliseconds(10));
 | 
			
		||||
    return usrp->get_tx_freq();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * random uniform int
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static int uniform_rand(const int low, const int high)
 | 
			
		||||
dc_cal_t::dc_cal_t(uhd::property<uint8_t> &dc_i_prop, uhd::property<uint8_t> &dc_q_prop,
 | 
			
		||||
                   uhd::rx_streamer::sptr rx_stream,
 | 
			
		||||
                   const size_t nsamps,
 | 
			
		||||
                   double bb_dc_freq,
 | 
			
		||||
                   double rx_rate,
 | 
			
		||||
                   int verbose,
 | 
			
		||||
                   bool debug_raw_data,
 | 
			
		||||
                   int init_dc_i,
 | 
			
		||||
                   int init_dc_q)
 | 
			
		||||
    : _best_dc_i(init_dc_i), _best_dc_q(init_dc_q)
 | 
			
		||||
    , _dc_i_prop(dc_i_prop), _dc_q_prop(dc_q_prop)
 | 
			
		||||
    , _rx_stream(rx_stream)
 | 
			
		||||
    , _nsamps(nsamps)
 | 
			
		||||
    , _bb_dc_freq(bb_dc_freq)
 | 
			
		||||
    , _rx_rate(rx_rate)
 | 
			
		||||
    , _verbose(verbose)
 | 
			
		||||
    , _debug_raw_data(debug_raw_data)
 | 
			
		||||
{
 | 
			
		||||
    static boost::random::mt19937 rng;
 | 
			
		||||
    boost::random::uniform_int_distribution<> dist(low, high);
 | 
			
		||||
    return dist(rng);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double dc_cal_t::init()
 | 
			
		||||
{
 | 
			
		||||
    set_dc_i_best();
 | 
			
		||||
    set_dc_q_best();
 | 
			
		||||
 | 
			
		||||
    //get the DC offset tone size
 | 
			
		||||
    _lowest_offset = get_dbrms();
 | 
			
		||||
 | 
			
		||||
    if (_verbose) printf("initial_dc_dbrms = %2.0f dB\n", _lowest_offset);
 | 
			
		||||
    if (_debug_raw_data) write_samples_to_file(_buff, "initial_samples.dat");
 | 
			
		||||
 | 
			
		||||
    return _lowest_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dc_cal_t::run_q(int dc_q)
 | 
			
		||||
{
 | 
			
		||||
    if (_verbose) printf("      dc_q = %d", dc_q);
 | 
			
		||||
    set_dc_q(dc_q);
 | 
			
		||||
    if (run_x())
 | 
			
		||||
        _best_dc_q = dc_q;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dc_cal_t::run_i(int dc_i)
 | 
			
		||||
{
 | 
			
		||||
    if (_verbose) printf("      dc_i = %d", dc_i);
 | 
			
		||||
    set_dc_i(dc_i);
 | 
			
		||||
    if (run_x())
 | 
			
		||||
        _best_dc_i = dc_i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dc_cal_t::run_iq(int dc_i, int dc_q)
 | 
			
		||||
{
 | 
			
		||||
    if (_verbose) printf("      dc_i = %d dc_q = %d", dc_i, dc_q);
 | 
			
		||||
    set_dc_i(dc_i);
 | 
			
		||||
    set_dc_q(dc_q);
 | 
			
		||||
    if (run_x()) {
 | 
			
		||||
        _best_dc_i = dc_i;
 | 
			
		||||
        _best_dc_q = dc_q;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void dc_cal_t::prop_set_check(uhd::property<uint8_t> &prop, uint8_t val)
 | 
			
		||||
{
 | 
			
		||||
    prop.set(val);
 | 
			
		||||
    uint8_t val_read = prop.get();
 | 
			
		||||
    if (val_read != val)
 | 
			
		||||
        throw std::runtime_error(
 | 
			
		||||
            str(boost::format("Calibration property sets incorrectly. Requested %d, read back %d")
 | 
			
		||||
                          % int(val) % int(val_read)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double dc_cal_t::get_dbrms()
 | 
			
		||||
{
 | 
			
		||||
    //receive some samples
 | 
			
		||||
    capture_samples(_rx_stream, _buff, _nsamps);
 | 
			
		||||
    //calculate dB rms
 | 
			
		||||
    return compute_tone_dbrms(_buff, _bb_dc_freq/_rx_rate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool dc_cal_t::run_x()
 | 
			
		||||
{
 | 
			
		||||
    bool better = false;
 | 
			
		||||
 | 
			
		||||
    //get the DC offset tone size
 | 
			
		||||
    const double dc_dbrms = get_dbrms();
 | 
			
		||||
    if (_verbose) printf("    dc_dbrms = %2.0f dB", dc_dbrms);
 | 
			
		||||
 | 
			
		||||
    if (dc_dbrms < _lowest_offset){
 | 
			
		||||
        _lowest_offset = dc_dbrms;
 | 
			
		||||
        better = true;
 | 
			
		||||
        if (_verbose) printf("    *");
 | 
			
		||||
        if (_debug_raw_data) write_samples_to_file(_buff, "best_samples.dat");
 | 
			
		||||
    }
 | 
			
		||||
    if (_verbose) printf("\n");
 | 
			
		||||
 | 
			
		||||
    return better;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Calibration method: Downhill
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static result_t calibrate_downhill(dc_cal_t &dc_cal,
 | 
			
		||||
                                   double tx_lo,
 | 
			
		||||
                                   int verbose)
 | 
			
		||||
{
 | 
			
		||||
    //bounds and results from searching
 | 
			
		||||
    int dc_i_start, dc_i_stop, dc_i_step;
 | 
			
		||||
    int dc_q_start, dc_q_stop, dc_q_step;
 | 
			
		||||
 | 
			
		||||
    //capture initial uncorrected value
 | 
			
		||||
    const double initial_dc_dbrms = dc_cal.init();
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < 6; i++)
 | 
			
		||||
    {
 | 
			
		||||
        if (verbose) printf("  iteration %ld  best_i = %d  best_q = %d\n", i, dc_cal.get_best_dc_i(), dc_cal.get_best_dc_q());
 | 
			
		||||
 | 
			
		||||
        switch (i) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            dc_i_start = 0;
 | 
			
		||||
            dc_i_stop  = 256;
 | 
			
		||||
            dc_q_start = 0;
 | 
			
		||||
            dc_q_stop  = 256;
 | 
			
		||||
            dc_i_step = 10;
 | 
			
		||||
            dc_q_step = 10;
 | 
			
		||||
            break;
 | 
			
		||||
        case 1:
 | 
			
		||||
            dc_i_start = dc_cal.get_best_dc_i() - 15;
 | 
			
		||||
            dc_i_stop  = dc_cal.get_best_dc_i() + 15;
 | 
			
		||||
            dc_q_start = dc_cal.get_best_dc_q() - 15;
 | 
			
		||||
            dc_q_stop  = dc_cal.get_best_dc_q() + 15;
 | 
			
		||||
            dc_i_step = 1;
 | 
			
		||||
            dc_q_step = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        case 2:
 | 
			
		||||
        case 3:
 | 
			
		||||
            dc_i_start = dc_cal.get_best_dc_i() - 3;
 | 
			
		||||
            dc_i_stop  = dc_cal.get_best_dc_i() + 3;
 | 
			
		||||
            dc_q_start = dc_cal.get_best_dc_q() - 3;
 | 
			
		||||
            dc_q_stop  = dc_cal.get_best_dc_q() + 3;
 | 
			
		||||
            dc_i_step = 1;
 | 
			
		||||
            dc_q_step = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            dc_i_start = dc_cal.get_best_dc_i() - 1;
 | 
			
		||||
            dc_i_stop  = dc_cal.get_best_dc_i() + 1;
 | 
			
		||||
            dc_q_start = dc_cal.get_best_dc_q() - 1;
 | 
			
		||||
            dc_q_stop  = dc_cal.get_best_dc_q() + 1;
 | 
			
		||||
            dc_i_step = 1;
 | 
			
		||||
            dc_q_step = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (i <= 2) {
 | 
			
		||||
            // Itereate through I and Q sequentially
 | 
			
		||||
 | 
			
		||||
            if (verbose) printf("    I in [%d; %d] step %d Q = %d\n",
 | 
			
		||||
                                            dc_i_start, dc_i_stop, dc_i_step, dc_cal.get_best_dc_q());
 | 
			
		||||
            dc_cal.set_dc_q_best();
 | 
			
		||||
            for (int dc_i = dc_i_start; dc_i <= dc_i_stop; dc_i += dc_i_step){
 | 
			
		||||
                dc_cal.run_i(dc_i);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (verbose) printf("    I = %d Q in [%d; %d] step %d\n",
 | 
			
		||||
                                            dc_cal.get_best_dc_i(), dc_q_start, dc_q_stop, dc_q_step);
 | 
			
		||||
            dc_cal.set_dc_i_best();
 | 
			
		||||
            for (int dc_q = dc_q_start; dc_q <= dc_q_stop; dc_q += dc_q_step){
 | 
			
		||||
                dc_cal.run_q(dc_q);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // Itereate through all combinations of I and Q
 | 
			
		||||
 | 
			
		||||
            if (verbose) printf("    I in [%d; %d] step %d Q in [%d; %d] step %d\n",
 | 
			
		||||
                                dc_i_start, dc_i_stop, dc_i_step,
 | 
			
		||||
                                dc_q_start, dc_q_stop, dc_q_step);
 | 
			
		||||
            for (int dc_i = dc_i_start; dc_i <= dc_i_stop; dc_i += dc_i_step) {
 | 
			
		||||
                for (int dc_q = dc_q_start; dc_q <= dc_q_stop; dc_q += dc_q_step) {
 | 
			
		||||
                    dc_cal.run_iq(dc_i, dc_q);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Calibration result
 | 
			
		||||
    result_t result;
 | 
			
		||||
    result.freq = tx_lo;
 | 
			
		||||
    result.real_corr = dc_cal.get_best_dc_i();
 | 
			
		||||
    result.imag_corr = dc_cal.get_best_dc_q();
 | 
			
		||||
    result.best = dc_cal.get_lowest_offset();
 | 
			
		||||
    result.delta = initial_dc_dbrms - result.best;
 | 
			
		||||
 | 
			
		||||
    // Output to console
 | 
			
		||||
    std::cout
 | 
			
		||||
        << result.freq/1e6 << " MHz "
 | 
			
		||||
        << "I/Q = " << result.real_corr << "/" << result.imag_corr << " "
 | 
			
		||||
        << "(" << dc_offset_int2double(result.real_corr) << "/"
 | 
			
		||||
        <<  dc_offset_int2double(result.imag_corr) << ") "
 | 
			
		||||
        << "leakage = " << result.best << " dB, "
 | 
			
		||||
        << "improvement = " << result.delta << " dB\n"
 | 
			
		||||
        << std::flush
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Main
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
    std::string args;
 | 
			
		||||
    std::string args, which, serial;
 | 
			
		||||
    int verbose;
 | 
			
		||||
    int vga1_gain, vga2_gain, rx_gain;
 | 
			
		||||
    double tx_wave_freq, tx_wave_ampl, rx_offset;
 | 
			
		||||
    double freq_start, freq_stop, freq_step;
 | 
			
		||||
    size_t nsamps;
 | 
			
		||||
    size_t ntrials;
 | 
			
		||||
    std::string which;
 | 
			
		||||
    int single_test_i, single_test_q;
 | 
			
		||||
 | 
			
		||||
    po::options_description desc("Allowed options");
 | 
			
		||||
    desc.add_options()
 | 
			
		||||
@@ -113,15 +301,22 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
        ("verbose", "enable some verbose")
 | 
			
		||||
        ("debug_raw_data", "save raw captured signals to files")
 | 
			
		||||
        ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]")
 | 
			
		||||
        ("which", po::value<std::string>(&which)->default_value("A"), "Which chain A or B?")
 | 
			
		||||
        ("vga1", po::value<int>(&vga1_gain)->default_value(-20), "LMS6002D Tx VGA1 gain [-35 to -4]")
 | 
			
		||||
        ("vga2", po::value<int>(&vga2_gain)->default_value(22), "LMS6002D Tx VGA2 gain [0 to 25]")
 | 
			
		||||
        ("rx_gain", po::value<int>(&rx_gain)->default_value(50), "LMS6002D Rx combined gain [0 to 156]")
 | 
			
		||||
        ("tx_wave_freq", po::value<double>(&tx_wave_freq)->default_value(50e3), "Transmit wave frequency in Hz")
 | 
			
		||||
        ("tx_wave_ampl", po::value<double>(&tx_wave_ampl)->default_value(0.7), "Transmit wave amplitude in counts")
 | 
			
		||||
        ("rx_offset", po::value<double>(&rx_offset)->default_value(.9344e6), "RX LO offset from the TX LO in Hz")
 | 
			
		||||
        ("rx_offset", po::value<double>(&rx_offset)->default_value(300e3), "RX LO offset from the TX LO in Hz")
 | 
			
		||||
        ("freq_start", po::value<double>(&freq_start), "Frequency start in Hz (do not specify for default)")
 | 
			
		||||
        ("freq_stop", po::value<double>(&freq_stop), "Frequency stop in Hz (do not specify for default)")
 | 
			
		||||
        ("freq_step", po::value<double>(&freq_step)->default_value(default_freq_step), "Step size for LO sweep in Hz")
 | 
			
		||||
        ("nsamps", po::value<size_t>(&nsamps)->default_value(default_num_samps), "Samples per data capture")
 | 
			
		||||
        ("ntrials", po::value<size_t>(&ntrials)->default_value(1), "Num trials per TX LO")
 | 
			
		||||
        ("which", po::value<std::string>(&which)->default_value("A"), "Which chain A or B?")
 | 
			
		||||
        ("single_test", "Perform a single measurement and exit (freq = freq_start, I = single_test_i, Q = single_test_q]")
 | 
			
		||||
        ("single_test_i", po::value<int>(&single_test_i)->default_value(128), "Only in the single test mode! I channel calibration value [0 to 255]")
 | 
			
		||||
        ("single_test_q", po::value<int>(&single_test_q)->default_value(128), "Only in the single test mode! Q channel calibration value [0 to 255]")
 | 
			
		||||
        ("append", "Append measurements to the calibratoin file instead of rewriting [default=overwrite]")
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    po::variables_map vm;
 | 
			
		||||
@@ -130,31 +325,17 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
 | 
			
		||||
    //print the help message
 | 
			
		||||
    if (vm.count("help")){
 | 
			
		||||
        std::cout << boost::format("USRP Generate TX DC Offset Calibration Table %s") % desc << std::endl;
 | 
			
		||||
        std::cout << boost::format("UmTRX Generate TX DC Offset Calibration Table %s") % desc << std::endl;
 | 
			
		||||
        std::cout <<
 | 
			
		||||
            "This application measures leakage between RX and TX on an XCVR daughterboard to self-calibrate.\n"
 | 
			
		||||
            "This application measures leakage between RX and TX using LMS6002D internal RF loopback to self-calibrate.\n"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
        return ~0;
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //create a usrp device
 | 
			
		||||
    std::cout << std::endl;
 | 
			
		||||
    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;
 | 
			
		||||
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
 | 
			
		||||
    verbose = vm.count("verbose");
 | 
			
		||||
 | 
			
		||||
    //set subdev spec
 | 
			
		||||
    usrp->set_rx_subdev_spec(which+":0");
 | 
			
		||||
    usrp->set_tx_subdev_spec(which+":0");
 | 
			
		||||
 | 
			
		||||
    //set the antennas to cal
 | 
			
		||||
    if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){
 | 
			
		||||
        throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate.");
 | 
			
		||||
    }
 | 
			
		||||
    usrp->set_rx_antenna("CAL");
 | 
			
		||||
    usrp->set_tx_antenna("CAL");
 | 
			
		||||
 | 
			
		||||
    //set optimum defaults
 | 
			
		||||
    set_optimum_defaults(usrp);
 | 
			
		||||
    // Create a USRP device
 | 
			
		||||
    uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, which, serial, vga1_gain, vga2_gain, rx_gain, verbose);
 | 
			
		||||
 | 
			
		||||
    //create a receive streamer
 | 
			
		||||
    uhd::stream_args_t stream_args("fc32"); //complex floats
 | 
			
		||||
@@ -164,18 +345,18 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
    boost::thread_group threads;
 | 
			
		||||
    threads.create_thread(boost::bind(&tx_thread, usrp, tx_wave_freq, tx_wave_ampl));
 | 
			
		||||
 | 
			
		||||
    //re-usable buffer for samples
 | 
			
		||||
    std::vector<samp_type> buff;
 | 
			
		||||
 | 
			
		||||
    //store the results here
 | 
			
		||||
    std::vector<result_t> results;
 | 
			
		||||
 | 
			
		||||
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
 | 
			
		||||
    const uhd::fs_path tx_fe_path = "/mboards/0/dboards/"+which+"/tx_frontends/0";
 | 
			
		||||
    uhd::property<uint8_t> &dc_i_prop = usrp->get_device()->get_tree()->access<uint8_t>(tx_fe_path / "lms6002d/tx_dc_i/value");
 | 
			
		||||
    uhd::property<uint8_t> &dc_q_prop = usrp->get_device()->get_tree()->access<uint8_t>(tx_fe_path / "lms6002d/tx_dc_q/value");
 | 
			
		||||
    uhd::property<uint8_t> &dc_i_prop = tree->access<uint8_t>(tx_fe_path / "lms6002d/tx_dc_i/value");
 | 
			
		||||
    uhd::property<uint8_t> &dc_q_prop = tree->access<uint8_t>(tx_fe_path / "lms6002d/tx_dc_q/value");
 | 
			
		||||
 | 
			
		||||
    if (not vm.count("freq_start")) freq_start = usrp->get_tx_freq_range().start() + 50e6;
 | 
			
		||||
    if (not vm.count("freq_stop")) freq_stop = usrp->get_tx_freq_range().stop() - 50e6;
 | 
			
		||||
    UHD_MSG(status) << boost::format("Calibration frequency type: DC offset") << std::endl;
 | 
			
		||||
    UHD_MSG(status) << boost::format("Calibration frequency range: %d MHz -> %d MHz") % (freq_start/1e6) % (freq_stop/1e6) << std::endl;
 | 
			
		||||
 | 
			
		||||
    for (double tx_lo_i = freq_start; tx_lo_i <= freq_stop; tx_lo_i += freq_step){
 | 
			
		||||
        const double tx_lo = tune_rx_and_tx(usrp, tx_lo_i, rx_offset);
 | 
			
		||||
@@ -185,78 +366,37 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
        const double actual_tx_freq = usrp->get_tx_freq();
 | 
			
		||||
        const double actual_rx_freq = usrp->get_rx_freq();
 | 
			
		||||
        const double bb_dc_freq = actual_tx_freq - actual_rx_freq;
 | 
			
		||||
        if (vm.count("verbose")) printf("actual_rx_rate = %0.2f MHz\n", actual_rx_rate/1e6);
 | 
			
		||||
        if (vm.count("verbose")) printf("actual_tx_freq = %0.2f MHz\n", actual_tx_freq/1e6);
 | 
			
		||||
        if (vm.count("verbose")) printf("actual_rx_freq = %0.2f MHz\n", actual_rx_freq/1e6);
 | 
			
		||||
        if (vm.count("verbose")) printf("bb_dc_freq = %0.2f MHz\n", bb_dc_freq/1e6);
 | 
			
		||||
        if (verbose) printf("actual_rx_rate = %0.2f MHz\n", actual_rx_rate/1e6);
 | 
			
		||||
        if (verbose) printf("actual_tx_freq = %0.2f MHz\n", actual_tx_freq/1e6);
 | 
			
		||||
        if (verbose) printf("actual_rx_freq = %0.2f MHz\n", actual_rx_freq/1e6);
 | 
			
		||||
        if (verbose) printf("bb_dc_freq = %0.2f MHz\n", bb_dc_freq/1e6);
 | 
			
		||||
 | 
			
		||||
        for (size_t trial_no = 0; trial_no < ntrials; trial_no++)
 | 
			
		||||
        {
 | 
			
		||||
            //bounds and results from searching
 | 
			
		||||
            double lowest_offset;
 | 
			
		||||
            int best_dc_i = 128, best_dc_q = 128;
 | 
			
		||||
 | 
			
		||||
            //capture initial uncorrected value
 | 
			
		||||
            dc_i_prop.set(best_dc_i);
 | 
			
		||||
            dc_q_prop.set(best_dc_q);
 | 
			
		||||
            capture_samples(rx_stream, buff, nsamps);
 | 
			
		||||
            const double initial_dc_dbrms = compute_tone_dbrms(buff, bb_dc_freq/actual_rx_rate);
 | 
			
		||||
            lowest_offset = initial_dc_dbrms;
 | 
			
		||||
            if (vm.count("verbose")) printf("initial_dc_dbrms = %2.0f dB\n", initial_dc_dbrms);
 | 
			
		||||
 | 
			
		||||
            if (vm.count("debug_raw_data")) write_samples_to_file(buff, "initial_samples.dat");
 | 
			
		||||
 | 
			
		||||
            for (int bound = 256; bound >= 8; bound /= 2) //how many bits of precision to care about for the search
 | 
			
		||||
            if (vm.count("single_test"))
 | 
			
		||||
            {
 | 
			
		||||
                if (vm.count("verbose")) printf("  iteration %du\n", bound);
 | 
			
		||||
                int this_dc_i = best_dc_i, this_dc_q = best_dc_q;
 | 
			
		||||
                dc_cal_t dc_cal(dc_i_prop, dc_q_prop,
 | 
			
		||||
                                rx_stream,
 | 
			
		||||
                                nsamps,
 | 
			
		||||
                                bb_dc_freq,
 | 
			
		||||
                                actual_rx_rate,
 | 
			
		||||
                                verbose,
 | 
			
		||||
                                vm.count("debug_raw_data"),
 | 
			
		||||
                                single_test_i, single_test_q);
 | 
			
		||||
 | 
			
		||||
                bool has_improvement = false;
 | 
			
		||||
                for (int rand_search_no = 0; rand_search_no < bound/4; rand_search_no++) //how many random points to inspect
 | 
			
		||||
                {
 | 
			
		||||
                    int dc_i = uniform_rand(std::max(0, this_dc_i-bound/2), std::min(256, this_dc_i+bound/2));
 | 
			
		||||
                    int dc_q = uniform_rand(std::max(0, this_dc_q-bound/2), std::min(256, this_dc_q+bound/2));
 | 
			
		||||
                    if (vm.count("verbose")) std::cout << "bound " << bound << " dc_i " << dc_i << " dc_q " << dc_q << std::endl;
 | 
			
		||||
 | 
			
		||||
                    dc_i_prop.set(dc_i);
 | 
			
		||||
                    dc_q_prop.set(dc_q);
 | 
			
		||||
 | 
			
		||||
                    //receive some samples
 | 
			
		||||
                    capture_samples(rx_stream, buff, nsamps);
 | 
			
		||||
 | 
			
		||||
                    const double dc_dbrms = compute_tone_dbrms(buff, bb_dc_freq/actual_rx_rate);
 | 
			
		||||
                    if (vm.count("verbose")) printf("    dc_dbrms = %2.0f dB", dc_dbrms);
 | 
			
		||||
 | 
			
		||||
                    if (dc_dbrms < lowest_offset){
 | 
			
		||||
                        lowest_offset = dc_dbrms;
 | 
			
		||||
                        best_dc_i = dc_i;
 | 
			
		||||
                        best_dc_q = dc_q;
 | 
			
		||||
                        has_improvement = true;
 | 
			
		||||
                        if (vm.count("verbose")) printf("    *");
 | 
			
		||||
                        if (vm.count("debug_raw_data")) write_samples_to_file(buff, "best_samples.dat");
 | 
			
		||||
                    }
 | 
			
		||||
                    if (vm.count("verbose")) printf("\n");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Stop iterating if no imprevement, but do at least 3 iterations
 | 
			
		||||
                if (!has_improvement and bound < 64) break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (vm.count("verbose")) printf("  best_dc_i = %d best_dc_q = %d", best_dc_i, best_dc_q);
 | 
			
		||||
            if (vm.count("verbose")) printf("  lowest_offset = %2.0f dB  delta = %2.0f dB\n", lowest_offset, initial_dc_dbrms - lowest_offset);
 | 
			
		||||
 | 
			
		||||
            if (lowest_offset < initial_dc_dbrms){ //most likely valid, keep result
 | 
			
		||||
                result_t result;
 | 
			
		||||
                result.freq = tx_lo;
 | 
			
		||||
                result.real_corr = best_dc_i;
 | 
			
		||||
                result.imag_corr = best_dc_q;
 | 
			
		||||
                result.best = lowest_offset;
 | 
			
		||||
                result.delta = initial_dc_dbrms - lowest_offset;
 | 
			
		||||
                results.push_back(result);
 | 
			
		||||
                if (vm.count("verbose")){
 | 
			
		||||
                    std::cout << boost::format("TX DC: %f MHz: lowest offset %f dB, corrected %f dB") % (tx_lo/1e6) % result.best % result.delta << std::endl;
 | 
			
		||||
                }
 | 
			
		||||
                else std::cout << "." << std::flush;
 | 
			
		||||
                const double dc_dbrms = dc_cal.init();;
 | 
			
		||||
                printf("I = %d Q = %d ", single_test_i, single_test_q);
 | 
			
		||||
                printf("dc_dbrms = %2.1f dB\n", dc_dbrms);
 | 
			
		||||
            } else {
 | 
			
		||||
                dc_cal_t dc_cal(dc_i_prop, dc_q_prop,
 | 
			
		||||
                                rx_stream,
 | 
			
		||||
                                nsamps,
 | 
			
		||||
                                bb_dc_freq,
 | 
			
		||||
                                actual_rx_rate,
 | 
			
		||||
                                verbose,
 | 
			
		||||
                                vm.count("debug_raw_data"));
 | 
			
		||||
                // Perform normal calibration
 | 
			
		||||
                results.push_back(calibrate_downhill(dc_cal, tx_lo, verbose));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -266,7 +406,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
    threads.interrupt_all();
 | 
			
		||||
    threads.join_all();
 | 
			
		||||
 | 
			
		||||
    store_results(usrp, results, "TX", "tx", "lms_dc", which);
 | 
			
		||||
    if (not vm.count("single_test"))
 | 
			
		||||
        store_results(usrp, results, "tx", "dc", vm.count("append"));
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										187
									
								
								host/utils/umtrx_cal_tx_iq_balance.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								host/utils/umtrx_cal_tx_iq_balance.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,187 @@
 | 
			
		||||
//
 | 
			
		||||
// Copyright 2010,2012 Ettus Research LLC
 | 
			
		||||
// Copyright 2015 Fairwaves, Inc
 | 
			
		||||
//
 | 
			
		||||
// 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 "usrp_cal_utils.hpp"
 | 
			
		||||
#include <uhd/utils/safe_main.hpp>
 | 
			
		||||
#include <boost/program_options.hpp>
 | 
			
		||||
#include <boost/math/special_functions/round.hpp>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <complex>
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
 | 
			
		||||
namespace po = boost::program_options;
 | 
			
		||||
 | 
			
		||||
static const size_t num_search_steps = 5;
 | 
			
		||||
static const size_t num_search_iters = 7;
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Main
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
    std::string args, which, serial;
 | 
			
		||||
    int verbose;
 | 
			
		||||
    int vga1_gain, vga2_gain, rx_gain;
 | 
			
		||||
    double tx_wave_freq, tx_wave_ampl, rx_offset;
 | 
			
		||||
    double freq_start, freq_stop, freq_step;
 | 
			
		||||
    size_t nsamps;
 | 
			
		||||
 | 
			
		||||
    po::options_description desc("Allowed options");
 | 
			
		||||
    desc.add_options()
 | 
			
		||||
        ("help", "help message")
 | 
			
		||||
        ("verbose", "enable some verbose")
 | 
			
		||||
        ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]")
 | 
			
		||||
        ("which", po::value<std::string>(&which)->default_value("A"), "Which chain A or B?")
 | 
			
		||||
        ("vga1", po::value<int>(&vga1_gain)->default_value(-20), "LMS6002D Tx VGA1 gain [-35 to -4]")
 | 
			
		||||
        ("vga2", po::value<int>(&vga2_gain)->default_value(22), "LMS6002D Tx VGA2 gain [0 to 25]")
 | 
			
		||||
        ("rx_gain", po::value<int>(&rx_gain)->default_value(50), "LMS6002D Rx combined gain [0 to 156]")
 | 
			
		||||
        ("tx_wave_freq", po::value<double>(&tx_wave_freq)->default_value(50e3), "Transmit wave frequency in Hz")
 | 
			
		||||
        ("tx_wave_ampl", po::value<double>(&tx_wave_ampl)->default_value(0.7), "Transmit wave amplitude in counts")
 | 
			
		||||
        ("rx_offset", po::value<double>(&rx_offset)->default_value(300e3), "RX LO offset from the TX LO in Hz")
 | 
			
		||||
        ("freq_start", po::value<double>(&freq_start), "Frequency start in Hz (do not specify for default)")
 | 
			
		||||
        ("freq_stop", po::value<double>(&freq_stop), "Frequency stop in Hz (do not specify for default)")
 | 
			
		||||
        ("freq_step", po::value<double>(&freq_step)->default_value(default_freq_step), "Step size for LO sweep in Hz")
 | 
			
		||||
        ("nsamps", po::value<size_t>(&nsamps)->default_value(default_num_samps), "Samples per data capture")
 | 
			
		||||
        ("append", "Append measurements to the calibratoin file instead of rewriting [default=overwrite]")
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    po::variables_map vm;
 | 
			
		||||
    po::store(po::parse_command_line(argc, argv, desc), vm);
 | 
			
		||||
    po::notify(vm);
 | 
			
		||||
 | 
			
		||||
    //print the help message
 | 
			
		||||
    if (vm.count("help")){
 | 
			
		||||
        std::cout << boost::format("UmTRX Generate TX IQ Balance Calibration Table %s") % desc << std::endl;
 | 
			
		||||
        std::cout <<
 | 
			
		||||
            "This application measures leakage between RX and TX using LMS6002D internal RF loopback to self-calibrate.\n"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    verbose = vm.count("verbose");
 | 
			
		||||
 | 
			
		||||
    // Create a USRP device
 | 
			
		||||
    uhd::usrp::multi_usrp::sptr usrp = setup_usrp_for_cal(args, which, serial, vga1_gain, vga2_gain, rx_gain, verbose);
 | 
			
		||||
 | 
			
		||||
    //create a receive streamer
 | 
			
		||||
    uhd::stream_args_t stream_args("fc32"); //complex floats
 | 
			
		||||
    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);
 | 
			
		||||
 | 
			
		||||
    //create a transmitter thread
 | 
			
		||||
    boost::thread_group threads;
 | 
			
		||||
    threads.create_thread(boost::bind(&tx_thread, usrp, tx_wave_freq, tx_wave_ampl));
 | 
			
		||||
 | 
			
		||||
    //re-usable buffer for samples
 | 
			
		||||
    std::vector<samp_type> buff;
 | 
			
		||||
 | 
			
		||||
    //store the results here
 | 
			
		||||
    std::vector<result_t> results;
 | 
			
		||||
 | 
			
		||||
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
 | 
			
		||||
    const uhd::fs_path tx_fe_path = "/mboards/0/tx_frontends/"+which;
 | 
			
		||||
    uhd::property<std::complex<double> > &iq_prop = tree->access<std::complex<double> >(tx_fe_path / "iq_balance" / "value");
 | 
			
		||||
 | 
			
		||||
    if (not vm.count("freq_start")) freq_start = usrp->get_tx_freq_range().start() + 50e6;
 | 
			
		||||
    if (not vm.count("freq_stop")) freq_stop = usrp->get_tx_freq_range().stop() - 50e6;
 | 
			
		||||
    UHD_MSG(status) << boost::format("Calibration frequency type: IQ balance") << std::endl;
 | 
			
		||||
    UHD_MSG(status) << boost::format("Calibration frequency range: %d MHz -> %d MHz") % (freq_start/1e6) % (freq_stop/1e6) << std::endl;
 | 
			
		||||
 | 
			
		||||
    for (double tx_lo_i = freq_start; tx_lo_i <= freq_stop; tx_lo_i += freq_step){
 | 
			
		||||
        const double tx_lo = tune_rx_and_tx(usrp, tx_lo_i, rx_offset);
 | 
			
		||||
 | 
			
		||||
        //frequency constants for this tune event
 | 
			
		||||
        const double actual_rx_rate = usrp->get_rx_rate();
 | 
			
		||||
        const double actual_tx_freq = usrp->get_tx_freq();
 | 
			
		||||
        const double actual_rx_freq = usrp->get_rx_freq();
 | 
			
		||||
        const double bb_tone_freq = actual_tx_freq + tx_wave_freq - actual_rx_freq;
 | 
			
		||||
        const double bb_imag_freq = actual_tx_freq - tx_wave_freq - actual_rx_freq;
 | 
			
		||||
 | 
			
		||||
        //capture initial uncorrected value
 | 
			
		||||
        iq_prop.set(0.0);
 | 
			
		||||
        capture_samples(rx_stream, buff, nsamps);
 | 
			
		||||
        const double initial_suppression = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate) - compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate);
 | 
			
		||||
 | 
			
		||||
        //bounds and results from searching
 | 
			
		||||
        std::complex<double> best_correction;
 | 
			
		||||
        double phase_corr_start = -.3, phase_corr_stop = .3, phase_corr_step;
 | 
			
		||||
        double ampl_corr_start = -.3, ampl_corr_stop = .3, ampl_corr_step;
 | 
			
		||||
        double best_suppression = 0, best_phase_corr = 0, best_ampl_corr = 0;
 | 
			
		||||
 | 
			
		||||
        for (size_t i = 0; i < num_search_iters; i++){
 | 
			
		||||
 | 
			
		||||
            phase_corr_step = (phase_corr_stop - phase_corr_start)/(num_search_steps-1);
 | 
			
		||||
            ampl_corr_step = (ampl_corr_stop - ampl_corr_start)/(num_search_steps-1);
 | 
			
		||||
 | 
			
		||||
            for (double phase_corr = phase_corr_start; phase_corr <= phase_corr_stop + phase_corr_step/2; phase_corr += phase_corr_step){
 | 
			
		||||
            for (double ampl_corr = ampl_corr_start; ampl_corr <= ampl_corr_stop + ampl_corr_step/2; ampl_corr += ampl_corr_step){
 | 
			
		||||
 | 
			
		||||
                const std::complex<double> correction(ampl_corr, phase_corr);
 | 
			
		||||
                iq_prop.set(correction);
 | 
			
		||||
 | 
			
		||||
                //receive some samples
 | 
			
		||||
                capture_samples(rx_stream, buff, nsamps);
 | 
			
		||||
 | 
			
		||||
                const double tone_dbrms = compute_tone_dbrms(buff, bb_tone_freq/actual_rx_rate);
 | 
			
		||||
                const double imag_dbrms = compute_tone_dbrms(buff, bb_imag_freq/actual_rx_rate);
 | 
			
		||||
                const double suppression = tone_dbrms - imag_dbrms;
 | 
			
		||||
 | 
			
		||||
                if (suppression > best_suppression){
 | 
			
		||||
                    best_correction = correction;
 | 
			
		||||
                    best_suppression = suppression;
 | 
			
		||||
                    best_phase_corr = phase_corr;
 | 
			
		||||
                    best_ampl_corr = ampl_corr;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }}
 | 
			
		||||
 | 
			
		||||
            if (verbose) std::cout << "best_phase_corr " << best_phase_corr << std::endl;
 | 
			
		||||
            if (verbose) std::cout << "best_ampl_corr " << best_ampl_corr << std::endl;
 | 
			
		||||
            if (verbose) std::cout << "best_suppression " << best_suppression << std::endl;
 | 
			
		||||
 | 
			
		||||
            phase_corr_start = best_phase_corr - phase_corr_step;
 | 
			
		||||
            phase_corr_stop = best_phase_corr + phase_corr_step;
 | 
			
		||||
            ampl_corr_start = best_ampl_corr - ampl_corr_step;
 | 
			
		||||
            ampl_corr_stop = best_ampl_corr + ampl_corr_step;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (best_suppression > 30){ //most likely valid, keep result
 | 
			
		||||
            result_t result;
 | 
			
		||||
            result.freq = tx_lo;
 | 
			
		||||
            result.real_corr = best_correction.real();
 | 
			
		||||
            result.imag_corr = best_correction.imag();
 | 
			
		||||
            result.best = best_suppression;
 | 
			
		||||
            result.delta = best_suppression - initial_suppression;
 | 
			
		||||
            results.push_back(result);
 | 
			
		||||
            if (verbose){
 | 
			
		||||
                std::cout << boost::format("TX IQ: %f MHz: best suppression %f dB, corrected %f dB") % (tx_lo/1e6) % result.best % result.delta << std::endl;
 | 
			
		||||
            }
 | 
			
		||||
            else std::cout << "." << std::flush;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    std::cout << std::endl;
 | 
			
		||||
 | 
			
		||||
    //stop the transmitter
 | 
			
		||||
    threads.interrupt_all();
 | 
			
		||||
    threads.join_all();
 | 
			
		||||
 | 
			
		||||
    store_results(usrp, results, "tx", "iq", vm.count("append"));
 | 
			
		||||
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								host/utils/umtrx_firmware
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								host/utils/umtrx_firmware
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
case word in
 | 
			
		||||
    flash )
 | 
			
		||||
        umtrx_net_burner --addr=192.168.10.2 --fpga=/usr/share/umtrx/firmware/u2plus_umtrx_v2.bin --fw=/usr/share/umtrx/firmware/umtrx_txrx_uhd.bin --reset
 | 
			
		||||
        ;;
 | 
			
		||||
    check )
 | 
			
		||||
        ;;
 | 
			
		||||
    * )
 | 
			
		||||
        cat <<-EOF
 | 
			
		||||
        Usage:
 | 
			
		||||
        $0 flash  - burn packaged firmware to umtrx
 | 
			
		||||
        $0 check  - compare versions of packaged firmware and one installed on umtrx
 | 
			
		||||
EOF
 | 
			
		||||
        ;;
 | 
			
		||||
esac
 | 
			
		||||
							
								
								
									
										22
									
								
								host/utils/umtrx_gps_coord
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										22
									
								
								host/utils/umtrx_gps_coord
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
if [ "x$1" = "x-h" -o "x$1" = "x--help" ] ; then
 | 
			
		||||
  echo "Usage:"
 | 
			
		||||
  echo "  umtrx_gps_coord [umtrx_ip]"
 | 
			
		||||
  echo
 | 
			
		||||
  echo "  umtrx_ip - (optional) UmTRX IP address [default=192.168.10.2]"
 | 
			
		||||
  echo
 | 
			
		||||
  echo "Output:"
 | 
			
		||||
  echo "  hh:mm:ss.SSS UTC <lat>N <lon>W <altitude> m"
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ $# -eq 0 ] ; then
 | 
			
		||||
  UMTRX_ADDR="192.168.10.2"
 | 
			
		||||
else
 | 
			
		||||
  UMTRX_ADDR=$1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo . | nc -u $UMTRX_ADDR 49171 | \
 | 
			
		||||
  awk -F, '/\$GPGGA/ {print substr($2,0,3) ":" substr($2,3,2) ":" substr($2,5,2) "." substr($2,8,3) " UTC", (substr($3,0,2) + (substr($3,3) / 60.0)) $4,  (substr($5,0,3) + (substr($5,4) / 60.0)) $6, $10 " m"; fflush();}'
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								host/utils/umtrx_nmea
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								host/utils/umtrx_nmea
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
#!/bin/sh
 | 
			
		||||
 | 
			
		||||
if [ "x$1" = "x-h" -o "x$1" = "x--help" ] ; then
 | 
			
		||||
  echo "Usage:"
 | 
			
		||||
  echo "  umtrx_nmea [umtrx_ip]"
 | 
			
		||||
  echo
 | 
			
		||||
  echo "  umtrx_ip - (optional) UmTRX IP address [default=192.168.10.2]"
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ $# -eq 0 ] ; then
 | 
			
		||||
  UMTRX_ADDR="192.168.10.2"
 | 
			
		||||
else
 | 
			
		||||
  UMTRX_ADDR=$1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo . | nc -u $UMTRX_ADDR 49171
 | 
			
		||||
@@ -114,10 +114,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (vm.count("divsw1")) {
 | 
			
		||||
        tree->access<bool>(mb_path / "divsw1").set(divsw1 ? 1 : 0);
 | 
			
		||||
        tree->access<bool>(mb_path / "dboards" / "A" / "rx_frontends" / "0" / "diversiy").set(divsw1 ? 1 : 0);
 | 
			
		||||
    }
 | 
			
		||||
    if (vm.count("divsw2")) {
 | 
			
		||||
        tree->access<bool>(mb_path / "divsw2").set(divsw2 ? 1 : 0);
 | 
			
		||||
        tree->access<bool>(mb_path / "dboards" / "B" / "rx_frontends" / "0" / "diversiy").set(divsw1 ? 1 : 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										112
									
								
								host/utils/umtrx_property_tree.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								host/utils/umtrx_property_tree.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
###  Property tree API
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
import socket
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
class umtrx_property_tree:
 | 
			
		||||
 | 
			
		||||
  def connect(self, host="localhost", port=12345):
 | 
			
		||||
    self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
			
		||||
    self.s.connect((host, port))
 | 
			
		||||
    self.f = self.s.makefile()
 | 
			
		||||
 | 
			
		||||
  def close(self):
 | 
			
		||||
    self.s.close()
 | 
			
		||||
 | 
			
		||||
  #
 | 
			
		||||
  # Helper methods
 | 
			
		||||
  #
 | 
			
		||||
 | 
			
		||||
  def _send_request(self, action, path, value_type=None, value=None):
 | 
			
		||||
    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')
 | 
			
		||||
 | 
			
		||||
  def _recv_response(self):
 | 
			
		||||
    resp = self.f.readline().strip()
 | 
			
		||||
    if len(resp)>0:
 | 
			
		||||
      return json.loads(resp)
 | 
			
		||||
    else:
 | 
			
		||||
      return None
 | 
			
		||||
 | 
			
		||||
  #
 | 
			
		||||
  # Getters (raw)
 | 
			
		||||
  #
 | 
			
		||||
 | 
			
		||||
  def query_bool_raw(self, path):
 | 
			
		||||
    self._send_request('GET', path, value_type='BOOL')
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  def query_int_raw(self, path):
 | 
			
		||||
    self._send_request('GET', path, value_type='INT')
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  def query_double_raw(self, path):
 | 
			
		||||
    self._send_request('GET', path, value_type='DOUBLE')
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  def query_sensor_raw(self, path):
 | 
			
		||||
    self._send_request('GET', path, value_type='SENSOR')
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  def query_range_raw(self, path):
 | 
			
		||||
    self._send_request('GET', path, value_type='RANGE')
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  #
 | 
			
		||||
  # Getters (value)
 | 
			
		||||
  #
 | 
			
		||||
 | 
			
		||||
  def query_bool_value(self, path):
 | 
			
		||||
    res = self.query_bool_raw(path)
 | 
			
		||||
    return res['result']['value']
 | 
			
		||||
 | 
			
		||||
  def query_int_value(self, path):
 | 
			
		||||
    res = self.query_int_raw(path)
 | 
			
		||||
    return res['result']['value']
 | 
			
		||||
 | 
			
		||||
  def query_double_value(self, path):
 | 
			
		||||
    res = self.query_double_raw(path)
 | 
			
		||||
    return res['result']['value']
 | 
			
		||||
 | 
			
		||||
  def query_sensor_value(self, path):
 | 
			
		||||
    res = self.query_sensor_raw(path)
 | 
			
		||||
    return res['result']['value']
 | 
			
		||||
 | 
			
		||||
  def query_range_value(self, path):
 | 
			
		||||
    res = self.query_range_raw(path)
 | 
			
		||||
    return res['result']
 | 
			
		||||
 | 
			
		||||
  #
 | 
			
		||||
  # Setters
 | 
			
		||||
  #
 | 
			
		||||
 | 
			
		||||
  def set_bool(self, path, val):
 | 
			
		||||
    self._send_request('SET', path, value_type='BOOL', value=val)
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  def set_int(self, path, val):
 | 
			
		||||
    self._send_request('SET', path, value_type='INT', value=val)
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  def set_double(self, path, val):
 | 
			
		||||
    self._send_request('SET', path, value_type='DOUBLE', value=val)
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  #
 | 
			
		||||
  # Check path presence and list paths
 | 
			
		||||
  #
 | 
			
		||||
 | 
			
		||||
  def has_path_raw(self, path):
 | 
			
		||||
    self._send_request('HAS', path)
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
 | 
			
		||||
  def list_path_raw(self, path):
 | 
			
		||||
    self._send_request('LIST', path)
 | 
			
		||||
    return self._recv_response()
 | 
			
		||||
							
								
								
									
										49
									
								
								host/utils/umtrx_query_sensors.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										49
									
								
								host/utils/umtrx_query_sensors.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
###  Query sensors
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
from umtrx_property_tree import umtrx_property_tree
 | 
			
		||||
from umtrx_vswr import umtrx_vswr
 | 
			
		||||
 | 
			
		||||
s = umtrx_property_tree()
 | 
			
		||||
s.connect()
 | 
			
		||||
 | 
			
		||||
sensors_path="/mboards/0/sensors"
 | 
			
		||||
res = s.list_path_raw(sensors_path)
 | 
			
		||||
sensors_list = res.get('result', [])
 | 
			
		||||
 | 
			
		||||
print "Sensors:"
 | 
			
		||||
for sensor in sensors_list:
 | 
			
		||||
  reply = s.query_sensor_raw(sensors_path+"/"+sensor)
 | 
			
		||||
  if reply.has_key('result'):
 | 
			
		||||
    res = reply['result']
 | 
			
		||||
    print "  %15s = %9s %s" % (res['name'], res['value'], res['unit'])
 | 
			
		||||
  else:
 | 
			
		||||
    print "Can't read sensor %s" % sensor
 | 
			
		||||
 | 
			
		||||
#vswr_calibration = TM10_VSWR_cal
 | 
			
		||||
#vswr_calibration = TM3_VSWR_cal
 | 
			
		||||
vswr_calibration = 0
 | 
			
		||||
for num in [1, 2]:
 | 
			
		||||
  vpr_name = 'voltagePR'+str(num)
 | 
			
		||||
  vpf_name = 'voltagePF'+str(num)
 | 
			
		||||
  if vpr_name in sensors_list and vpf_name in sensors_list:
 | 
			
		||||
    vpr = float(s.query_sensor_value(sensors_path+'/'+vpr_name))
 | 
			
		||||
    vpf = float(s.query_sensor_value(sensors_path+'/'+vpf_name))
 | 
			
		||||
    vswr = umtrx_vswr(vpf, vpr, vswr_calibration)
 | 
			
		||||
    print "TRX %d power detector:" % num
 | 
			
		||||
    print "              VPF =  %5.2f  V"   % vpf
 | 
			
		||||
    print "               PF = %5.1f   dBm" % vswr.pf()
 | 
			
		||||
    print "              VPR =  %5.2f  V"   % vpr
 | 
			
		||||
    print "               PR = %5.1f   dBm" % vswr.pr()
 | 
			
		||||
    print "             VSWR = %6.2f"       % vswr.vswr()
 | 
			
		||||
    print "            Gamma =   %5.3f"     % vswr.gamma()
 | 
			
		||||
    print "      Return Loss = %5.1f   dB"  % vswr.return_loss()
 | 
			
		||||
    print "    Mismatch Loss =   %5.3f dB"  % vswr.mismatch_loss()
 | 
			
		||||
    print "    Through power =  %5.2f  %%"  % (100.0*vswr.pf_rate())
 | 
			
		||||
    print "  Reflected power =  %5.2f  %%"  % (100.0*vswr.pr_rate())
 | 
			
		||||
 | 
			
		||||
s.close()
 | 
			
		||||
							
								
								
									
										68
									
								
								host/utils/umtrx_vswr.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								host/utils/umtrx_vswr.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
#!/usr/bin/env python
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
##########################
 | 
			
		||||
###  VSWR calculations
 | 
			
		||||
##########################
 | 
			
		||||
 | 
			
		||||
# Implemented as described at:
 | 
			
		||||
# http://www.markimicrowave.com/assets/data/return%20loss%20to%20vswr.pdf
 | 
			
		||||
 | 
			
		||||
import math
 | 
			
		||||
 | 
			
		||||
# TODO: requires better calibration
 | 
			
		||||
TM10_VSWR_cal=0.3/2
 | 
			
		||||
 | 
			
		||||
class umtrx_vswr:
 | 
			
		||||
  def __init__(self, VPF, VPR, calibration=0, coef=0.05):
 | 
			
		||||
    self.vpf = VPF
 | 
			
		||||
    self.vpr = VPR
 | 
			
		||||
    self.calibration = calibration
 | 
			
		||||
    self.coef = coef
 | 
			
		||||
    self._gamma = self._calc_gamma()
 | 
			
		||||
 | 
			
		||||
  def _calc_gamma(self):
 | 
			
		||||
    ''' Internal function: calculate Gamma '''
 | 
			
		||||
    return math.pow(10, -self.return_loss()/20.0)
 | 
			
		||||
 | 
			
		||||
  def pf(self):
 | 
			
		||||
    ''' Estimated through power, dBm '''
 | 
			
		||||
    return (self.vpf-self.calibration)/self.coef
 | 
			
		||||
 | 
			
		||||
  def pr(self):
 | 
			
		||||
    ''' Estimated reflected power, dBm '''
 | 
			
		||||
    return (self.vpr-self.calibration)/self.coef
 | 
			
		||||
 | 
			
		||||
  def return_loss(self):
 | 
			
		||||
    ''' Estimated return loss, dB '''
 | 
			
		||||
    return self.pf()-self.pr()
 | 
			
		||||
 | 
			
		||||
  def gamma(self):
 | 
			
		||||
    ''' Estimated Gamma '''
 | 
			
		||||
    return self._gamma
 | 
			
		||||
 | 
			
		||||
  def vswr(self):
 | 
			
		||||
    ''' Estimated VSWR '''
 | 
			
		||||
    gamma = self._gamma
 | 
			
		||||
    if gamma == 1.0:
 | 
			
		||||
      return float("inf")
 | 
			
		||||
    else:
 | 
			
		||||
      return (1+gamma)/(1-gamma)
 | 
			
		||||
 | 
			
		||||
  def mismatch_loss(self):
 | 
			
		||||
    ''' Estimated mismatch loss, dB '''
 | 
			
		||||
    gamma = self._gamma
 | 
			
		||||
    if gamma == 1.0:
 | 
			
		||||
      return float("-inf")
 | 
			
		||||
    else:
 | 
			
		||||
      return -10.0 * math.log(1.0-gamma*gamma, 10)
 | 
			
		||||
 | 
			
		||||
  def pf_rate(self):
 | 
			
		||||
    ''' Estimated reflected power rate, % '''
 | 
			
		||||
    gamma = self._gamma
 | 
			
		||||
    return 1.0 - gamma*gamma
 | 
			
		||||
 | 
			
		||||
  def pr_rate(self):
 | 
			
		||||
    ''' Estimated reflected power rate, % '''
 | 
			
		||||
    gamma = self._gamma
 | 
			
		||||
    return gamma*gamma
 | 
			
		||||
@@ -16,10 +16,16 @@
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
#include <uhd/utils/paths.hpp>
 | 
			
		||||
#include <uhd/utils/thread_priority.hpp>
 | 
			
		||||
#include <uhd/utils/algorithm.hpp>
 | 
			
		||||
#include <uhd/utils/msg.hpp>
 | 
			
		||||
#include <uhd/property_tree.hpp>
 | 
			
		||||
#include <uhd/usrp/multi_usrp.hpp>
 | 
			
		||||
#include <uhd/usrp/dboard_eeprom.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include <boost/algorithm/string.hpp>
 | 
			
		||||
#include <boost/thread/thread.hpp>
 | 
			
		||||
#include <boost/math/special_functions/round.hpp>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <complex>
 | 
			
		||||
@@ -37,67 +43,9 @@ typedef std::complex<float> samp_type;
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static const double tau = 6.28318531;
 | 
			
		||||
static const size_t wave_table_len = 8192;
 | 
			
		||||
static const size_t num_search_steps = 5;
 | 
			
		||||
static const size_t num_search_iters = 7;
 | 
			
		||||
static const double default_freq_step = 1e6;
 | 
			
		||||
static const size_t default_num_samps = 10000;
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Set standard defaults for devices
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static inline void set_optimum_defaults(uhd::usrp::multi_usrp::sptr usrp){
 | 
			
		||||
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
 | 
			
		||||
 | 
			
		||||
    const uhd::fs_path mb_path = "/mboards/0";
 | 
			
		||||
    const std::string mb_name = tree->access<std::string>(mb_path / "name").get();
 | 
			
		||||
    if (mb_name.find("USRP2") != std::string::npos or mb_name.find("N200") != std::string::npos or mb_name.find("N210") != std::string::npos){
 | 
			
		||||
        usrp->set_tx_rate(12.5e6);
 | 
			
		||||
        usrp->set_rx_rate(12.5e6);
 | 
			
		||||
    }
 | 
			
		||||
    else if (mb_name.find("UMTRX") != std::string::npos){
 | 
			
		||||
        usrp->set_tx_rate(13e6/2);
 | 
			
		||||
        usrp->set_tx_bandwidth(5e6);
 | 
			
		||||
        usrp->set_rx_rate(13e6/2);
 | 
			
		||||
        usrp->set_rx_bandwidth(5e6);
 | 
			
		||||
    }
 | 
			
		||||
    else if (mb_name.find("B100") != std::string::npos){
 | 
			
		||||
        usrp->set_tx_rate(4e6);
 | 
			
		||||
        usrp->set_rx_rate(4e6);
 | 
			
		||||
    }
 | 
			
		||||
    else if (mb_name.find("E100") != std::string::npos or mb_name.find("E110") != std::string::npos){
 | 
			
		||||
        usrp->set_tx_rate(4e6);
 | 
			
		||||
        usrp->set_rx_rate(8e6);
 | 
			
		||||
    }
 | 
			
		||||
    else{
 | 
			
		||||
        throw std::runtime_error("self-calibration is not supported for this hardware");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const uhd::fs_path tx_fe_path = "/mboards/0/dboards/A/tx_frontends/0";
 | 
			
		||||
    const std::string tx_name = tree->access<std::string>(tx_fe_path / "name").get();
 | 
			
		||||
    if (tx_name.find("WBX") != std::string::npos or tx_name.find("SBX") != std::string::npos){
 | 
			
		||||
        usrp->set_tx_gain(0);
 | 
			
		||||
    }
 | 
			
		||||
    else if (tx_name.find("LMS6002D") != std::string::npos){
 | 
			
		||||
        usrp->set_tx_gain(10);
 | 
			
		||||
    }
 | 
			
		||||
    else{
 | 
			
		||||
        throw std::runtime_error("self-calibration is not supported for this hardware");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const uhd::fs_path rx_fe_path = "/mboards/0/dboards/A/tx_frontends/0";
 | 
			
		||||
    const std::string rx_name = tree->access<std::string>(rx_fe_path / "name").get();
 | 
			
		||||
    if (rx_name.find("WBX") != std::string::npos or rx_name.find("SBX") != std::string::npos){
 | 
			
		||||
        usrp->set_rx_gain(25);
 | 
			
		||||
    }
 | 
			
		||||
    else if (rx_name.find("LMS6002D") != std::string::npos){
 | 
			
		||||
        usrp->set_rx_gain(10);
 | 
			
		||||
    }
 | 
			
		||||
    else{
 | 
			
		||||
        throw std::runtime_error("self-calibration is not supported for this hardware");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Sinusoid wave table
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
@@ -146,50 +94,91 @@ static inline void write_samples_to_file(
 | 
			
		||||
    outfile.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Retrieve d'board serial
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static std::string get_serial(
 | 
			
		||||
    uhd::usrp::multi_usrp::sptr usrp,
 | 
			
		||||
    const std::string &tx_rx
 | 
			
		||||
){
 | 
			
		||||
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
 | 
			
		||||
    // Will work on 1st subdev, top-level must make sure it's the right one
 | 
			
		||||
    uhd::usrp::subdev_spec_t subdev_spec = usrp->get_rx_subdev_spec();
 | 
			
		||||
    const uhd::fs_path db_path = "/mboards/0/dboards/" + subdev_spec[0].db_name + "/" + tx_rx + "_eeprom";
 | 
			
		||||
    const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();
 | 
			
		||||
    return db_eeprom.serial;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Convert integer calibration values to floats
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static double dc_offset_int2double(uint8_t corr)
 | 
			
		||||
{
 | 
			
		||||
    return (corr-128)/128.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Store data to file
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static void store_results(
 | 
			
		||||
    uhd::usrp::multi_usrp::sptr usrp,
 | 
			
		||||
    const std::vector<result_t> &results,
 | 
			
		||||
    const std::string &XX,
 | 
			
		||||
    const std::string &xx,
 | 
			
		||||
    const std::string &what,
 | 
			
		||||
    const std::string &which
 | 
			
		||||
    const std::string &rx_tx, // "tx" or "rx"
 | 
			
		||||
    const std::string &what,  // Type of test, e.g. "iq"
 | 
			
		||||
    bool append
 | 
			
		||||
){
 | 
			
		||||
    //extract eeprom serial
 | 
			
		||||
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
 | 
			
		||||
    const uhd::fs_path db_path = "/mboards/0/dboards/"+which+"/" + xx + "_eeprom";
 | 
			
		||||
    const uhd::usrp::dboard_eeprom_t db_eeprom = tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get();
 | 
			
		||||
    if (db_eeprom.serial.empty()) throw std::runtime_error(XX + " dboard has empty serial!");
 | 
			
		||||
    std::ofstream cal_data;
 | 
			
		||||
    bool write_header=true;
 | 
			
		||||
    std::string rx_tx_upper = boost::to_upper_copy(rx_tx);
 | 
			
		||||
    std::string serial = get_serial(usrp, rx_tx);
 | 
			
		||||
 | 
			
		||||
    //make the calibration file path
 | 
			
		||||
    fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd";
 | 
			
		||||
    fs::create_directory(cal_data_path);
 | 
			
		||||
    cal_data_path = cal_data_path / "cal";
 | 
			
		||||
    fs::create_directory(cal_data_path);
 | 
			
		||||
    cal_data_path = cal_data_path / str(boost::format("%s_%s_cal_v0.1_%s.csv") % xx % what % db_eeprom.serial);
 | 
			
		||||
    cal_data_path = cal_data_path / str(boost::format("%s_%s_cal_v0.2_%s.csv") % rx_tx % what % serial);
 | 
			
		||||
    if (fs::exists(cal_data_path)){
 | 
			
		||||
        if (append)
 | 
			
		||||
            write_header = false;
 | 
			
		||||
        else
 | 
			
		||||
            fs::rename(cal_data_path, cal_data_path.string() + str(boost::format(".%d") % time(NULL)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    cal_data.open(cal_data_path.string().c_str(), std::ofstream::out | std::ofstream::app);
 | 
			
		||||
 | 
			
		||||
    if (write_header)
 | 
			
		||||
    {
 | 
			
		||||
        //fill the calibration file
 | 
			
		||||
    std::ofstream cal_data(cal_data_path.string().c_str());
 | 
			
		||||
    cal_data << boost::format("name, %s Frontend Calibration\n") % XX;
 | 
			
		||||
    cal_data << boost::format("serial, %s\n") % db_eeprom.serial;
 | 
			
		||||
        cal_data << boost::format("name, %s Frontend Calibration\n") % rx_tx_upper;
 | 
			
		||||
        cal_data << boost::format("serial, %s\n") % serial;
 | 
			
		||||
        cal_data << boost::format("timestamp, %d\n") % time(NULL);
 | 
			
		||||
        cal_data << boost::format("version, 0, 1\n");
 | 
			
		||||
        cal_data << boost::format("DATA STARTS HERE\n");
 | 
			
		||||
        // For DC calibration we also store LMS6002D integer values
 | 
			
		||||
        if (what == "dc")
 | 
			
		||||
            cal_data << "lo_frequency, correction_real, correction_imag, measured, delta, int_i, int_q\n";
 | 
			
		||||
        else
 | 
			
		||||
            cal_data << "lo_frequency, correction_real, correction_imag, measured, delta\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < results.size(); i++){
 | 
			
		||||
        cal_data
 | 
			
		||||
            << results[i].freq << ", "
 | 
			
		||||
            << results[i].real_corr << ", "
 | 
			
		||||
            << results[i].imag_corr << ", "
 | 
			
		||||
            << results[i].best << ", "
 | 
			
		||||
            << results[i].delta << "\n"
 | 
			
		||||
        ;
 | 
			
		||||
        // Write to file
 | 
			
		||||
        cal_data << results[i].freq;
 | 
			
		||||
        if (what == "dc") {
 | 
			
		||||
            cal_data << ", " << dc_offset_int2double(results[i].real_corr);
 | 
			
		||||
            cal_data << ", " << dc_offset_int2double(results[i].imag_corr);
 | 
			
		||||
        } else {
 | 
			
		||||
            cal_data << ", " << results[i].real_corr;
 | 
			
		||||
            cal_data << ", " << results[i].imag_corr;
 | 
			
		||||
        }
 | 
			
		||||
        cal_data << ", " << results[i].best;
 | 
			
		||||
        cal_data << ", " << results[i].delta;
 | 
			
		||||
        if (what == "dc") {
 | 
			
		||||
            cal_data << ", " << results[i].real_corr;
 | 
			
		||||
            cal_data << ", " << results[i].imag_corr;
 | 
			
		||||
        }
 | 
			
		||||
        cal_data << "\n";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::cout << "wrote cal data to " << cal_data_path << std::endl;
 | 
			
		||||
@@ -234,3 +223,105 @@ static void capture_samples(
 | 
			
		||||
        throw std::runtime_error("did not get all the samples requested");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Transmit thread
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static void tx_thread(uhd::usrp::multi_usrp::sptr usrp, const double tx_wave_freq, const double tx_wave_ampl){
 | 
			
		||||
    uhd::set_thread_priority_safe();
 | 
			
		||||
 | 
			
		||||
    //create a transmit streamer
 | 
			
		||||
    uhd::stream_args_t stream_args("fc32"); //complex floats
 | 
			
		||||
    uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args);
 | 
			
		||||
 | 
			
		||||
    //setup variables and allocate buffer
 | 
			
		||||
    uhd::tx_metadata_t md;
 | 
			
		||||
    md.has_time_spec = false;
 | 
			
		||||
    std::vector<samp_type> buff(tx_stream->get_max_num_samps()*10);
 | 
			
		||||
 | 
			
		||||
    //values for the wave table lookup
 | 
			
		||||
    size_t index = 0;
 | 
			
		||||
    const double tx_rate = usrp->get_tx_rate();
 | 
			
		||||
    const size_t step = boost::math::iround(wave_table_len * tx_wave_freq/tx_rate);
 | 
			
		||||
    wave_table table(tx_wave_ampl);
 | 
			
		||||
 | 
			
		||||
    //fill buff and send until interrupted
 | 
			
		||||
    while (not boost::this_thread::interruption_requested()){
 | 
			
		||||
        for (size_t i = 0; i < buff.size(); i++){
 | 
			
		||||
            buff[i] = table(index += step);
 | 
			
		||||
        }
 | 
			
		||||
        tx_stream->send(&buff.front(), buff.size(), md);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //send a mini EOB packet
 | 
			
		||||
    md.end_of_burst = true;
 | 
			
		||||
    tx_stream->send("", 0, md);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Tune RX and TX routine
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static double tune_rx_and_tx(uhd::usrp::multi_usrp::sptr usrp, const double tx_lo_freq, const double rx_offset){
 | 
			
		||||
    //tune the transmitter with no cordic
 | 
			
		||||
    uhd::tune_request_t tx_tune_req(tx_lo_freq);
 | 
			
		||||
    tx_tune_req.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
 | 
			
		||||
    tx_tune_req.dsp_freq = 0;
 | 
			
		||||
    usrp->set_tx_freq(tx_tune_req);
 | 
			
		||||
 | 
			
		||||
    //tune the receiver
 | 
			
		||||
    usrp->set_rx_freq(uhd::tune_request_t(usrp->get_tx_freq(), rx_offset));
 | 
			
		||||
 | 
			
		||||
    boost::this_thread::sleep(boost::posix_time::milliseconds(10));
 | 
			
		||||
    return usrp->get_tx_freq();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/***********************************************************************
 | 
			
		||||
 * Setup function
 | 
			
		||||
 **********************************************************************/
 | 
			
		||||
static uhd::usrp::multi_usrp::sptr setup_usrp_for_cal(const std::string &args, const std::string &which, std::string &serial,
 | 
			
		||||
                                                      int vga1_gain, int vga2_gain, int rx_gain, int verbose)
 | 
			
		||||
{
 | 
			
		||||
    std::cout << std::endl;
 | 
			
		||||
    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;
 | 
			
		||||
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
 | 
			
		||||
 | 
			
		||||
    // Do we have an UmTRX here?
 | 
			
		||||
    uhd::property_tree::sptr tree = usrp->get_device()->get_tree();
 | 
			
		||||
    const uhd::fs_path mb_path = "/mboards/0";
 | 
			
		||||
    const std::string mb_name = tree->access<std::string>(mb_path / "name").get();
 | 
			
		||||
    if (mb_name.find("UMTRX") == std::string::npos){
 | 
			
		||||
        throw std::runtime_error("This utility supports only UmTRX hardware.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //set subdev spec
 | 
			
		||||
    usrp->set_rx_subdev_spec(which+":0");
 | 
			
		||||
    usrp->set_tx_subdev_spec(which+":0");
 | 
			
		||||
 | 
			
		||||
    UHD_MSG(status) << "Running calibration for " << usrp->get_tx_subdev_name(0) << std::endl;
 | 
			
		||||
    serial = get_serial(usrp, "tx");
 | 
			
		||||
    UHD_MSG(status) << "Daughterboard serial: " << serial << std::endl;
 | 
			
		||||
 | 
			
		||||
    //set the antennas to cal
 | 
			
		||||
    if (not uhd::has(usrp->get_rx_antennas(), "CAL") or not uhd::has(usrp->get_tx_antennas(), "CAL")){
 | 
			
		||||
        throw std::runtime_error("This board does not have the CAL antenna option, cannot self-calibrate.");
 | 
			
		||||
    }
 | 
			
		||||
    usrp->set_rx_antenna("CAL");
 | 
			
		||||
    usrp->set_tx_antenna("CAL");
 | 
			
		||||
 | 
			
		||||
    //set optimum defaults
 | 
			
		||||
    //  GSM symbol rate * 4
 | 
			
		||||
    usrp->set_tx_rate(13e6/12);
 | 
			
		||||
    usrp->set_rx_rate(13e6/12);
 | 
			
		||||
    //  500kHz LPF
 | 
			
		||||
    usrp->set_tx_bandwidth(1e6);
 | 
			
		||||
    usrp->set_rx_bandwidth(1e6);
 | 
			
		||||
    // Our recommended VGA1/VGA2
 | 
			
		||||
    usrp->set_tx_gain(vga1_gain, "VGA1");
 | 
			
		||||
    usrp->set_tx_gain(vga2_gain, "VGA2");
 | 
			
		||||
    usrp->set_rx_gain(rx_gain);
 | 
			
		||||
    if (verbose) printf("actual Tx VGA1 gain = %.0f dB\n", usrp->get_tx_gain("VGA1"));
 | 
			
		||||
    if (verbose) printf("actual Tx VGA2 gain = %.0f dB\n", usrp->get_tx_gain("VGA2"));
 | 
			
		||||
    if (verbose) printf("actual Rx gain = %.0f dB\n", usrp->get_rx_gain());
 | 
			
		||||
 | 
			
		||||
    return usrp;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user