diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index a97293ae7624f9836b763ab894e09e3df7c8db73..5247be27e4e2712959657937c2c99d66b4fd14d0 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -13,6 +13,7 @@ include(GrPlatform) #define LIB_SUFFIX
 list(APPEND gr_bladerf_srcs
     source_impl.cc
     sink_impl.cc
+    ranges.cc
 )
 
 #-pthread Adds support for multithreading with the pthreads library.
diff --git a/lib/ranges.cc b/lib/ranges.cc
new file mode 100644
index 0000000000000000000000000000000000000000..09c1daedf50b8ab152eff1cbf5ca18d3f00bfb75
--- /dev/null
+++ b/lib/ranges.cc
@@ -0,0 +1,183 @@
+//
+// Copyright 2011-2011 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/>.
+//
+
+#include <osmosdr/ranges.h>
+#include <stdexcept>
+#include <boost/math/special_functions/round.hpp>
+#include <algorithm>
+#include <sstream>
+
+using namespace osmosdr;
+
+/***********************************************************************
+ * range_t implementation code
+ **********************************************************************/
+struct range_t::impl{
+    impl(double start, double stop, double step):
+        start(start), stop(stop), step(step)
+    {
+        /* NOP */
+    }
+    double start, stop, step;
+};
+
+range_t::range_t(double value):
+    _impl(OSMOSDR_PIMPL_MAKE(impl, (value, value, 0)))
+{
+    /* NOP */
+}
+
+range_t::range_t(
+    double start, double stop, double step
+):
+    _impl(OSMOSDR_PIMPL_MAKE(impl, (start, stop, step)))
+{
+    if (stop < start){
+        throw std::runtime_error("cannot make range where stop < start");
+    }
+}
+
+double range_t::start(void) const{
+    return _impl->start;
+}
+
+double range_t::stop(void) const{
+    return _impl->stop;
+}
+
+double range_t::step(void) const{
+    return _impl->step;
+}
+
+const std::string range_t::to_pp_string(void) const{
+    std::stringstream ss;
+    ss << "(" << this->start();
+    if (this->start() != this->stop()) ss << ", " << this->stop();
+    if (this->step() != 0) ss << ", " << this->step();
+    ss << ")";
+    return ss.str();
+}
+
+/***********************************************************************
+ * meta_range_t implementation code
+ **********************************************************************/
+void check_meta_range_monotonic(const meta_range_t &mr){
+    if (mr.empty()){
+        throw std::runtime_error("meta-range cannot be empty");
+    }
+    for (size_t i = 1; i < mr.size(); i++){
+        if (mr.at(i).start() < mr.at(i-1).stop()){
+            throw std::runtime_error("meta-range is not monotonic");
+        }
+    }
+}
+
+meta_range_t::meta_range_t(void){
+    /* NOP */
+}
+
+meta_range_t::meta_range_t(
+    double start, double stop, double step
+):
+    std::vector<range_t > (1, range_t(start, stop, step))
+{
+    /* NOP */
+}
+
+double meta_range_t::start(void) const{
+    check_meta_range_monotonic(*this);
+    double min_start = this->front().start();
+    for (const range_t &r : (*this)){
+        min_start = std::min(min_start, r.start());
+    }
+    return min_start;
+}
+
+double meta_range_t::stop(void) const{
+    check_meta_range_monotonic(*this);
+    double max_stop = this->front().stop();
+    for (const range_t &r : (*this)){
+        max_stop = std::max(max_stop, r.stop());
+    }
+    return max_stop;
+}
+
+double meta_range_t::step(void) const{
+    check_meta_range_monotonic(*this);
+    std::vector<double> non_zero_steps;
+    range_t last = this->front();
+    for (const range_t &r : (*this)){
+        //steps at each range
+        if (r.step() > 0) non_zero_steps.push_back(r.step());
+        //and steps in-between ranges
+        double ibtw_step = r.start() - last.stop();
+        if (ibtw_step > 0) non_zero_steps.push_back(ibtw_step);
+        //store ref to last
+        last = r;
+    }
+    if (non_zero_steps.empty()) return 0; //all zero steps, its zero...
+    return *std::min_element(non_zero_steps.begin(), non_zero_steps.end());
+}
+
+double meta_range_t::clip(double value, bool clip_step) const{
+    check_meta_range_monotonic(*this);
+    double last_stop = this->front().stop();
+    for (const range_t &r : (*this)){
+        //in-between ranges, clip to nearest
+        if (value < r.start()){
+            return (std::abs(value - r.start()) < std::abs(value - last_stop))?
+                r.start() : last_stop;
+        }
+        //in this range, clip here
+        if (value <= r.stop()){
+            if (! clip_step || r.step() == 0) return value;
+            return boost::math::round((value - r.start())/r.step())*r.step() + r.start();
+        }
+        //continue on to the next range
+        last_stop = r.stop();
+    }
+    return last_stop;
+}
+
+std::vector<double> meta_range_t::values() const {
+    std::vector<double> values;
+
+    for (const range_t &r : (*this)) {
+        if (r.start() != r.stop()) {
+            if ( r.step() == 0 ) {
+                values.push_back( r.start() );
+                values.push_back( r.stop() );
+            } else {
+                for ( double val = r.start(); val <= r.stop(); val += r.step() ) {
+                    values.push_back( val );
+                }
+            }
+        } else {
+            values.push_back( r.start() );
+        }
+    }
+
+    return values;
+}
+
+const std::string meta_range_t::to_pp_string(void) const{
+    std::stringstream ss;
+    for (const range_t &r : (*this)){
+        ss << r.to_pp_string() << std::endl;
+    }
+    return ss.str();
+}