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();
+}