45 Commits
1.0.3 ... 1.0.5

Author SHA1 Message Date
Kirill Zakharenko
2a89674c56 Bump version to 1.0.5 2015-11-23 15:09:10 +03:00
Josh Blum
77e9066bf8 fpga: updated 4x ddc image for spi work 2015-08-11 23:45:58 -07:00
Josh Blum
1e43f04790 Merge branch 'axi_shared_spi' 2015-08-11 17:04:51 -07:00
Kirill Zakharenko
cbff81745b debian: added firmware to package and umtrx_firmware script to handle it 2015-08-11 13:29:52 +03:00
Josh Blum
57d5ca4b51 fpga: simplify spi setting regs with generate loop 2015-07-29 19:21:28 -07:00
Josh Blum
b78ceeb9b1 fpga: connect both spi settings drivers 2015-07-29 01:59:54 -07:00
Josh Blum
fcd92d8f50 fpga: use axi stream spi core (still single dest) 2015-07-28 16:32:17 -07:00
Josh Blum
2727f62ab8 fpga: created axi stream controled spi core 2015-07-28 16:31:29 -07:00
Alexander Chemeris
75c3380ccf host: Fix getters in umtrx_property_tree.py. 2015-07-27 20:42:23 -04:00
Josh Blum
80e65f35cf host: disable umtrx_fifo_ctrl cache of spi config
This register can be modified by the firmware,
which invalidates the cached setting.
2015-07-24 00:50:35 -07:00
Kirill Zakharenko
f16599cfa9 debian: conflicts/replaces stanzas to replace renamed package
otherwise new packages will be installed alongside older one and we'll
get a file conflict
2015-07-23 03:00:19 +02:00
Alexander Chemeris
e6b82c85e6 debian: 1.0.4 release changelog. 2015-07-21 21:09:25 -04:00
Alexander Chemeris
8e5b9e8cba utils: Prettier output for umtrx_auto_calibration 2015-07-21 21:09:25 -04:00
Alexander Chemeris
fc9ecf7709 host: Fix a typo in the DC offset property tree setter. 2015-07-21 21:09:25 -04:00
Alexander Chemeris
913f19357b utils: Verify that calibration property has been set correctly.
Sometimes setting an LMS register fails if UmTRX has bad power supply. We want
to make sure we catch this situation and abort calibration.
2015-07-21 18:48:43 -04:00
Alexander Chemeris
d96cea8000 utils: Set default Rx gain to 50 instead of 100 in calibration utils.
A few commits ago we changed Rx gain to real dB instead of abstract values,
which changed the gain settings range - old 100 is about current 50.
2015-07-21 00:38:09 -04:00
Alexander Chemeris
c401071ea6 scripts: Fix a nasty typo in umtrx_auto_calibration.
We were executing each calibration comand twice, because $echo was not defined.
2015-07-20 22:43:01 -04:00
Josh Blum
b5b351e535 debian: split umtrx into module and runtime packages
* uhd-umtrx for uhd plugin module only (multi-arch lib dir)
* umtrx - runtime utility applications (depends on uhd-umtrx)

The idea is to support multi-arch installs by separating out the plugin module
(which can exist in a multi-arch install) from the /usr/bin/ executables.
It may also be useful for users to have a minimal install (plugin only).
2015-07-12 00:02:27 -07:00
Josh Blum
1cf16e57f7 host: fixed missing boost math include (boost 1.46)
usrp_cal_utils.hpp used boost round but did not include the header.
This caused a build error on ubuntu 12.04 LTS w/ boost boost 1.46
2015-07-10 14:30:39 -07:00
Alexander Chemeris
b78a96661a host: FIx SET handling for the property tree JSON API. 2015-07-05 19:30:13 -04:00
Alexander Chemeris
5452567fd9 host: There is no need to explicitely support coerce() in property_alias()
Coerce is called automatically inside of the set() of the original property.
2015-07-05 12:47:56 -04:00
Alexander Chemeris
47b3e1aaea host: Complete calculations for VSWR in Python utils + better sensors output. 2015-07-05 10:01:37 -04:00
Alexander Chemeris
1e042157be Merge branch 'status_monitor'
Implements a TCP server inside UHD which gives access to a device's property
tree through a JSON API.
2015-07-04 19:27:14 -04:00
Alexander Chemeris
4cb89755bb host: A Python library for the property tree API and sensors query utility. 2015-07-04 19:19:39 -04:00
Alexander Chemeris
efba897843 host: No need to explicitely insert EOL, it's already there. 2015-07-04 19:19:39 -04:00
Alexander Chemeris
35ccac9f54 host: Fix typo in a comment s/GET/SET/. 2015-07-04 19:19:39 -04:00
Josh Blum
963f3e90dc umtrx: remove sleep used for testing 2015-07-04 19:19:39 -04:00
Josh Blum
0e8abb2068 umtrx: generic JSON property tree service 2015-07-04 19:19:39 -04:00
Josh Blum
e069b6d39c umtrx: client handler thread to keep socket open 2015-07-04 19:19:39 -04:00
Josh Blum
28f423567b umtrx: changes to tcp query server
* tcp query server is on its own thread
* tcp query take full sensor path (simpler)
* select on client socket to wait for data
2015-07-04 19:19:39 -04:00
Josh Blum
67303bcbcc umtrx: mutex for i2c-based interfaces 2015-07-04 19:19:38 -04:00
Josh Blum
0fc60d2275 umtrx: sensor query support and comments 2015-07-04 19:19:38 -04:00
Josh Blum
ce5f31f713 umtrx: mutex for lms ctrl wrapper class 2015-07-04 19:19:38 -04:00
Josh Blum
802b24c565 umtrx: work on status monitor tcp server 2015-07-04 19:19:38 -04:00
Josh Blum
56c4174ad4 umtrx: skeleton for status monitor thread 2015-07-04 19:19:38 -04:00
Alexander Chemeris
0073096b27 Merge branch 'achemeris/divsw' 2015-07-04 19:14:26 -04:00
Alexander Chemeris
7727cd283c host: By default route each Rx channel to it's own antenna. 2015-07-04 19:14:10 -04:00
Alexander Chemeris
ff13c7d73a host: Add ability to set diversity switches position from UHD args.
Diviersity switch controls are also aliased RF frontends property subtrees now.
2015-07-04 19:12:34 -04:00
Alexander Chemeris
0bee662b81 host: Implement property_alias() function which creates an alias at the proprty tree. 2015-07-04 19:01:23 -04:00
Kirill Zakharenko
7b955574a5 minor debianization fixes: +x debian/rules, correct debian/source/format 2015-06-29 11:45:45 +03:00
Josh Blum
4b102f0c4c Merge branch 'debian' 2015-06-20 16:45:07 -07:00
Josh Blum
0d7b517923 debian: changelog entry for 1.0.3 release 2015-06-20 16:44:57 -07:00
Josh Blum
aec754eb07 umtrx: strip leading g from describe hash 2015-06-04 20:44:31 -04:00
Alexander Chemeris
756cb9a74c utils: Better documentation for umtrx_auto_calibration. 2015-06-03 13:51:32 -04:00
Josh Blum
471eedbb6a debian: first shot at umtrx debianization 2015-05-26 17:12:04 -07:00
29 changed files with 1044 additions and 50 deletions

30
debian/changelog vendored Normal file
View 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
View File

@@ -0,0 +1 @@
9

33
debian/control vendored Normal file
View 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
View 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
View File

@@ -0,0 +1 @@
README

18
debian/rules vendored Executable file
View 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
View File

@@ -0,0 +1 @@
3.0 (native)

1
debian/uhd-umtrx.install vendored Normal file
View File

@@ -0,0 +1 @@
usr/lib/*/uhd/modules/

2
debian/umtrx.install vendored Normal file
View File

@@ -0,0 +1,2 @@
usr/bin
images/u2plus_umtrx_v2.bin images/umtrx_txrx_uhd.bin usr/share/umtrx/firmware

View File

@@ -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 \
))

View 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

View File

@@ -174,6 +174,10 @@ module umtrx_core
wire [7:0] set_addr, set_addr_dsp, set_addr_sys, set_addr_fe, set_addr_udp_wb, set_addr_udp_sys;
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;
@@ -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'd1}; //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),

View File

@@ -26,6 +26,7 @@ project(UmTRX-UHD)
########################################################################
include(${PROJECT_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake)
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

View File

@@ -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>
@@ -154,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");
}
@@ -214,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;
@@ -254,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)
@@ -271,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"){
@@ -284,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");
@@ -317,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);
@@ -328,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");
@@ -349,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");
@@ -368,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);
@@ -379,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;
@@ -402,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)

View File

@@ -69,6 +69,18 @@ static const double _dcdc_val_to_volt_init[256] =
};
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
@@ -258,12 +270,27 @@ 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;
@@ -619,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
@@ -659,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];
@@ -687,6 +722,7 @@ int umtrx_impl::volt_to_dcdc_r(double v)
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)
{
@@ -744,6 +780,7 @@ double umtrx_impl::set_tx_power(double power, const std::string &which)
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
@@ -771,6 +808,7 @@ double umtrx_impl::set_pa_power(double power, const std::string &which)
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);
}
@@ -835,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");
@@ -842,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])
@@ -857,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])
@@ -1002,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
@@ -1023,4 +1061,3 @@ const char* umtrx_impl::get_hw_rev() const
default: return "[unknown]";
}
}

View File

@@ -48,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>
@@ -58,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.
@@ -182,8 +185,7 @@ 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;
@@ -201,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
View 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);
}
}

View File

@@ -6,6 +6,7 @@ INSTALL(PROGRAMS
umtrx_nmea
umtrx_gps_coord
umtrx_auto_calibration
umtrx_firmware
DESTINATION bin
)

View File

@@ -2,14 +2,23 @@
if [ "$#" -lt "1" ] ; then
echo "Automatic calibration of an UmTRX for a set of given presets (bands)."
echo "Calibrations to be performed:"
echo " - Tx DC offset calibration"
echo " - Tx IQ balance calibration"
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
@@ -25,12 +34,6 @@ run_cal() {
freq_stop=$1 ; shift
other_args=$*
echo
echo "------------------------------------------------------------------"
echo " Calibrating $what from $freq_start to $freq_stop"
echo "------------------------------------------------------------------"
echo
freq_step=$(python -c "print $freq_stop - $freq_start if $freq_stop != $freq_start else 1e3")
if [ "$what" = "dc" ] ; then
@@ -43,12 +46,19 @@ run_cal() {
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
echo $cmd_full
$cmd_full
res=$(echo $?)
done

View File

@@ -47,10 +47,10 @@ public:
void run_i(int dc_i);
void run_iq(int dc_i, int dc_q);
void set_dc_i(double i) {_dc_i_prop.set(i);}
void set_dc_q(double q) {_dc_q_prop.set(q);}
void set_dc_i_best() {_dc_i_prop.set(_best_dc_i);}
void set_dc_q_best() {_dc_q_prop.set(_best_dc_q);}
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);}
double get_lowest_offset() const {return _lowest_offset;}
int get_best_dc_i() const {return _best_dc_i;}
@@ -71,6 +71,8 @@ protected:
int _verbose;
bool _debug_raw_data;
void prop_set_check(uhd::property<uint8_t> &prop, uint8_t val);
double get_dbrms();
bool run_x();
};
@@ -97,8 +99,8 @@ dc_cal_t::dc_cal_t(uhd::property<uint8_t> &dc_i_prop, uhd::property<uint8_t> &dc
double dc_cal_t::init()
{
_dc_i_prop.set(_best_dc_i);
_dc_q_prop.set(_best_dc_q);
set_dc_i_best();
set_dc_q_best();
//get the DC offset tone size
_lowest_offset = get_dbrms();
@@ -112,7 +114,7 @@ double dc_cal_t::init()
void dc_cal_t::run_q(int dc_q)
{
if (_verbose) printf(" dc_q = %d", dc_q);
_dc_q_prop.set(dc_q);
set_dc_q(dc_q);
if (run_x())
_best_dc_q = dc_q;
}
@@ -120,7 +122,7 @@ void dc_cal_t::run_q(int dc_q)
void dc_cal_t::run_i(int dc_i)
{
if (_verbose) printf(" dc_i = %d", dc_i);
_dc_i_prop.set(dc_i);
set_dc_i(dc_i);
if (run_x())
_best_dc_i = dc_i;
}
@@ -128,14 +130,24 @@ void dc_cal_t::run_i(int 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);
_dc_i_prop.set(dc_i);
_dc_q_prop.set(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
@@ -292,7 +304,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
("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(100), "LMS6002D Rx combined gain [0 to 156]")
("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")

View File

@@ -49,7 +49,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){
("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(100), "LMS6002D Rx combined gain [0 to 156]")
("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")

16
host/utils/umtrx_firmware Executable file
View 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

View File

@@ -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;

View 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()

View 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
View 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

View File

@@ -25,6 +25,7 @@
#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>

Binary file not shown.

Binary file not shown.