Skip to content

Getting Started: XB200 Transverter Board

Jon Szymaniak edited this page Jun 22, 2014 · 45 revisions

This page provides an overview of the XB-200 and provides some information on its use.

Table of Contents

Introduction

The XB-200 transverter board is a block up-down converter that expands the bladeRF’s lower frequency range, allowing the bladeRF to be used in HF/VHF applications.

The RX and TX paths each have a set of 3 filters, at the 50MHz-54MHz (6 meter) band, 149MHz-159MHz (2 meter) band, and 206-235MHz (includes 1.25m) bands. There are also pairs of SMA connectors that will let users plug their own band filters into the RF path.

The XB-200 mates to the top of the bladeRF as follows. (There is only one possible orientation)

  • XB-200 U20 to bladeRF U74
  • XB-200 J5 to bladeRF J61
  • XB-200 J6 to bladeRF J60
For additional information, please see:

Hardware

Architecture

The transverter was meant to extend the range of the bladeRF without impairing the current frequency capabilities. As such, the transverter has a bypass path as well as a mixed path. The bypass path just connects the antenna port to the IF port directly without any modification to the signal.

The mixed path first has a filterbank for selectivity filtering. This filterbank consists of 4 separate paths: 50MHz filter, 144MHz filter, 222MHz filter and a custom filter. The custom filter can be put in line using the filter SMA connections on the transverter.

Each of the filters was designed to try to notch the terrestrial FM band as much as possible. To be able to receive normal FM, the simplest way is to put an SMA jumper in the custom path (i.e., between the RX/TX FILT and FILT-ANT SMA connectors).

The block converter stage uses an ADF4351 to produce a 1248MHz high-side injection tone. The mixing frequency was chosen because of it being about 3x higher than the 300MHz highest frequency we want to use and we can run the ADF4351 in integer-N mode, reducing spurs which may have resulted from a fractional-N mode of operation. The output of the ADF4351 is always divided by 2x, so 38.4MHz*32.5 = 1248MHz.

Since high side injection is used, the LMS6002D on the bladeRF is tuning to 1248MHz - (desired frequency). Moreover, there is a spectral inversion that is occurring due to the choice of high-side injection. The LMS6002D is programmed to swap I and Q on the data bus being presented to the FPGA to correct for the flip.

SMA Connectors

There are 10 SMA connectors on the XB-200. Below are brief descriptions of each.

RX

  • RXIF (J3): RX Intermediate Frequency. Connect this to the bladeRF RX port (J53) via an SMA cable.
  • RXANT (J12): RX Input. Connect an antenna here.
  • RXFILT (J8): Mixer-side of the RX custom filter path. Connect this directly to RXFILT-ANT to use no filter, or connect this to one end of your custom filter, and the other end of your filter to RXFILT-ANT.
  • RXFILT-ANT (J9): Antenna-side of the RX custom filter path.
  • ADC (J14): This SMA exposes the direct ADC sampling path (skipping the LMS6002). This can also be accessed without the XB-200 via J61 on the bladeRF.
TX
  • TXIF (J1): TX Intermediate Frequency. Connect this to the bladeRF TX port (J54) via an SMA cable.
  • TXANT (J2): TX output. Connect an antenna here. When not transmitting, it is recommended to keep a dummy load or attenuator on this port.
  • TXFILT (J11): Mixer-side of the TX custom filter path. Connect this directly to TXFILT-ANT to use no filter, or connect this to one end of your custom filter, and the other end of your filter to TXFILT-ANT.
  • TXFILT-ANT (J10): Antenna-side of the TX custom filter path.
  • DAC (J15): This SMA exposes the DAC direct sampling path (skipping the LMS6002). This can also be accessed without the XB-200 via J60 on the bladeRF.

GPIO ports

GPIO pins are exposed on the XB-200 via J1, J13, and J16. libbladeRF API calls to manipulate these pins are a work in progress.

TO DO: provide jumper pinout and FPGA pin associations.

Software Support

libbladeRF

The general procedure for using the XB-200 programmatically, via libbladeRF is as follows:

bladeRF-cli

TODO Describe the commands involved with the XB-200

gr-osmosdr

Fetching the pending patches

A set of preliminary patches have been submitted to the gr-osmosdr maintainers, and are pending further review. These patches are subject to change.

Below are the two patches and a quick overview of how to apply them. Save these to the filenames indicated below.

# Enter the directory containing the gr-osmosdr source
$ cd path/to/gr-osmosdr/source

# Ensure you have the latest and greatest. This is required.
$ git checkout master
$ git pull

# Create a nuand-xb200-pending branch. In the future, you can return 
# to master and delete this branch via:
#   git checkout master && git branch -D nuand-xb200-pending
$ git checkout -b nuand-xb200-pending

# Apply the patches 
$ git am 0001-bladeRF-Add-XB-200-support.patch 0002-bladeRF-auto-filterbank-selection.patch

# Now rebuild and re-install as you normally would

0001-bladeRF-Add-XB-200-support.patch

From e6458f30625ed17a944b00ffc97076933a7436ed Mon Sep 17 00:00:00 2001
From: Robert Ghilduta <[email protected]>
Date: Wed, 11 Jun 2014 01:11:09 -0700
Subject: [PATCH 1/2] bladeRF: Add XB-200 support

This commit adds support for the bladeRF XB-200 transverter expansion board. To
enable the expansion board and to allow the osmocom source to down to 0Hz,
parameter XB-200 has to be set. Additionally the XB-200 comes with 4 filter
banks that can be set via configuration option, xb200filt. The following values
are valid:
   "50M"     :  50MHz band
   "144M"    : 144MHz band
   "222M"    : 222MHz band
   [default] : Custom filter
---
 lib/bladerf/bladerf_common.cc | 26 ++++++++++++++++++++++++--
 lib/bladerf/bladerf_common.h  |  2 ++
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/lib/bladerf/bladerf_common.cc b/lib/bladerf/bladerf_common.cc
index 7516020..ac56747 100644
--- a/lib/bladerf/bladerf_common.cc
+++ b/lib/bladerf/bladerf_common.cc
@@ -48,7 +48,7 @@ using namespace boost::assign;
 boost::mutex bladerf_common::_devs_mutex;
 std::list<boost::weak_ptr<struct bladerf> > bladerf_common::_devs;
 
-bladerf_common::bladerf_common() : _conv_buf(NULL), _conv_buf_size(4096) {}
+bladerf_common::bladerf_common() : _conv_buf(NULL), _conv_buf_size(4096), xb_200_attached(false) {}
 
 bladerf_common::~bladerf_common()
 {
@@ -305,6 +305,28 @@ void bladerf_common::init(dict_t &dict, bladerf_module module)
 
   }
 
+  if ( dict.count("xb200")) {
+    if (bladerf_expansion_attach(_dev.get(), BLADERF_XB_200)) {
+       std::cerr << "Could not attach XB-200";
+    } else {
+       _xb_200_attached = true;
+    }
+  }
+
+  bladerf_xb200_filter filter = BLADERF_XB200_CUSTOM;
+  if ( dict.count("xb200filt")) {
+      if ( dict["xb200filt"] == "50M") {
+          filter = BLADERF_XB200_50M;
+      } else if ( dict["xb200filt"] == "144M") {
+          filter = BLADERF_XB200_144M;
+      } else if ( dict["xb200filt"] == "222M") {
+          filter = BLADERF_XB200_222M;
+      }
+  }
+
+  if (bladerf_xb200_set_filterbank(_dev.get(), module, filter)) {
+       std::cerr << "Could not set XB-200 filter";
+  }
 
   /* Show some info about the device we've opened */
   std::cerr << _pfx << "Using nuand LLC bladeRF #" << device_number;
@@ -387,7 +409,7 @@ void bladerf_common::init(dict_t &dict, bladerf_module module)
 osmosdr::freq_range_t bladerf_common::freq_range()
 {
   /* assuming the same for RX & TX */
-  return osmosdr::freq_range_t( 300e6, 3.8e9 );
+  return osmosdr::freq_range_t( _xb_200_attached ? 0 : 300e6, 3.8e9 );
 }
 
 osmosdr::meta_range_t bladerf_common::sample_rates()
diff --git a/lib/bladerf/bladerf_common.h b/lib/bladerf/bladerf_common.h
index b13a6cf..3b1cb0b 100644
--- a/lib/bladerf/bladerf_common.h
+++ b/lib/bladerf/bladerf_common.h
@@ -82,6 +82,8 @@ protected:
 
   std::string _pfx;
 
+  bool _xb_200_attached;
+
   /* BladeRF IQ correction parameters */
   static const int16_t DCOFF_SCALE  = 2048;
   static const int16_t GAIN_SCALE   = 4096;
-- 
1.9.1

0002-bladeRF-auto-filterbank-selection.patch

From 1e3e5168361d61704726c7de26e1095b7871c6f5 Mon Sep 17 00:00:00 2001
From: Robert Ghilduta <[email protected]>
Date: Wed, 11 Jun 2014 15:01:33 -0700
Subject: [PATCH 2/2] bladeRF: auto filterbank selection

Setting xb200filt to "auto" causes the bladeRF sink and source blocks to
automatically select the appropriate filterbank based on center
frequency.
---
 lib/bladerf/bladerf_common.cc   | 12 ++++++++----
 lib/bladerf/bladerf_common.h    |  1 +
 lib/bladerf/bladerf_sink_c.cc   | 18 ++++++++++++++++++
 lib/bladerf/bladerf_source_c.cc | 18 ++++++++++++++++++
 4 files changed, 45 insertions(+), 4 deletions(-)

diff --git a/lib/bladerf/bladerf_common.cc b/lib/bladerf/bladerf_common.cc
index ac56747..6773d4d 100644
--- a/lib/bladerf/bladerf_common.cc
+++ b/lib/bladerf/bladerf_common.cc
@@ -48,7 +48,7 @@ using namespace boost::assign;
 boost::mutex bladerf_common::_devs_mutex;
 std::list<boost::weak_ptr<struct bladerf> > bladerf_common::_devs;
 
-bladerf_common::bladerf_common() : _conv_buf(NULL), _conv_buf_size(4096), xb_200_attached(false) {}
+bladerf_common::bladerf_common() : _conv_buf(NULL), _conv_buf_size(4096), _xb_200_attached(false), _xb_200_auto_filter(false) {}
 
 bladerf_common::~bladerf_common()
 {
@@ -315,7 +315,9 @@ void bladerf_common::init(dict_t &dict, bladerf_module module)
 
   bladerf_xb200_filter filter = BLADERF_XB200_CUSTOM;
   if ( dict.count("xb200filt")) {
-      if ( dict["xb200filt"] == "50M") {
+      if ( dict["xb200filt"] == "auto") {
+          _xb_200_auto_filter = true;
+      } else if ( dict["xb200filt"] == "50M") {
           filter = BLADERF_XB200_50M;
       } else if ( dict["xb200filt"] == "144M") {
           filter = BLADERF_XB200_144M;
@@ -324,8 +326,10 @@ void bladerf_common::init(dict_t &dict, bladerf_module module)
       }
   }
 
-  if (bladerf_xb200_set_filterbank(_dev.get(), module, filter)) {
-       std::cerr << "Could not set XB-200 filter";
+  if (_xb_200_attached && !_xb_200_auto_filter) {
+      if (bladerf_xb200_set_filterbank(_dev.get(), module, filter)) {
+          std::cerr << "Could not set XB-200 filter";
+      }
   }
 
   /* Show some info about the device we've opened */
diff --git a/lib/bladerf/bladerf_common.h b/lib/bladerf/bladerf_common.h
index 3b1cb0b..f67c5b5 100644
--- a/lib/bladerf/bladerf_common.h
+++ b/lib/bladerf/bladerf_common.h
@@ -83,6 +83,7 @@ protected:
   std::string _pfx;
 
   bool _xb_200_attached;
+  bool _xb_200_auto_filter;
 
   /* BladeRF IQ correction parameters */
   static const int16_t DCOFF_SCALE  = 2048;
diff --git a/lib/bladerf/bladerf_sink_c.cc b/lib/bladerf/bladerf_sink_c.cc
index c230e41..32fbd5e 100644
--- a/lib/bladerf/bladerf_sink_c.cc
+++ b/lib/bladerf/bladerf_sink_c.cc
@@ -183,6 +183,24 @@ double bladerf_sink_c::set_center_freq( double freq, size_t chan )
                                 boost::lexical_cast<std::string>(freq) +
                                 ":" + std::string(bladerf_strerror(ret)));
     }
+
+    if (_xb_200_auto_filter) {
+      bladerf_xb200_filter filter = BLADERF_XB200_CUSTOM;
+      if ( freq >= 50e6 && freq <= 54e6 ) {
+        filter = BLADERF_XB200_50M;
+      } else if ( freq >= 149e6 && freq <= 159e6 ) {
+        filter = BLADERF_XB200_144M;
+      } else if ( freq >= 206e6 && freq <= 235e6 ) {
+        filter = BLADERF_XB200_222M;
+      }
+
+      if (bladerf_xb200_set_filterbank(_dev.get(), BLADERF_MODULE_TX, filter)) {
+        throw std::runtime_error( std::string(__FUNCTION__) + " " +
+                                  "Failed to auto set XB-200 filter at frequency " +
+                                  boost::lexical_cast<std::string>(freq) +
+                                  ":" + std::string(bladerf_strerror(ret)));
+      }
+    }
   }
 
   return get_center_freq( chan );
diff --git a/lib/bladerf/bladerf_source_c.cc b/lib/bladerf/bladerf_source_c.cc
index d7eaeeb..3b1b5c8 100644
--- a/lib/bladerf/bladerf_source_c.cc
+++ b/lib/bladerf/bladerf_source_c.cc
@@ -232,6 +232,24 @@ double bladerf_source_c::set_center_freq( double freq, size_t chan )
                                 boost::lexical_cast<std::string>(freq) + ": " +
                                 std::string(bladerf_strerror(ret)) );
     }
+
+    if (_xb_200_auto_filter) {
+      bladerf_xb200_filter filter = BLADERF_XB200_CUSTOM;
+      if ( freq >= 50e6 && freq <= 54e6 ) {
+        filter = BLADERF_XB200_50M;
+      } else if ( freq >= 149e6 && freq <= 159e6 ) {
+        filter = BLADERF_XB200_144M;
+      } else if ( freq >= 206e6 && freq <= 235e6 ) {
+        filter = BLADERF_XB200_222M;
+      }
+
+      if (bladerf_xb200_set_filterbank(_dev.get(), BLADERF_MODULE_RX, filter)) {
+        throw std::runtime_error( std::string(__FUNCTION__) + " " +
+                                  "Failed to auto set XB-200 filter at frequency " +
+                                  boost::lexical_cast<std::string>(freq) +
+                                  ":" + std::string(bladerf_strerror(ret)));
+      }
+    }
   }
 
   return get_center_freq( chan );
-- 
1.9.1

Enabling the XB-200 and Selecting Filters

To enable the XB200 in software that utilizes gr-osmosdr, add 'xb200' to the source/sink arguments string. By default, the custom filter banks will be selected. Therefore, you must have the FILT and FILT-ANT connected through a filter, or directly connected together (no filter).

If you wish to select one of the other filter paths, append one of the following to the source/sink arguments string:

  • xb200filt=50M
  • xb200filt=144M
  • xb200filt=222M
  • xb200filt=auto
    • This option will select an appropriate filter path based upon the selected center frequency.
Clone this wiki locally