/* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. * * This file is part of PortaPack. * * 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 2, 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; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ #include "rf_path.hpp" #include <array> #include <initializer_list> #include "hackrf_gpio.hpp" using namespace hackrf::one; #include "utility.hpp" namespace rf { namespace path { namespace { using GPIOs = std::array<GPIO, 13>; /* TODO: ARM GCC 4.8 2014q3 doesn't like this array inside struct Config. * No idea why. */ constexpr GPIOs gpios { gpio_tx, gpio_rx, gpio_mix_bypass, gpio_not_mix_bypass, gpio_tx_mix_bp, gpio_rx_mix_bp, gpio_hp, gpio_lp, gpio_amp_bypass, gpio_tx_amp, gpio_not_tx_amp_pwr, gpio_rx_amp, gpio_not_rx_amp_pwr, }; struct Config { using base_type = uint16_t; union { struct { bool tx : 1; bool rx : 1; bool mix_bypass : 1; bool not_mix_bypass : 1; bool tx_mix_bp : 1; bool rx_mix_bp : 1; bool hp : 1; bool lp : 1; bool amp_bypass : 1; bool tx_amp : 1; bool not_tx_amp : 1; bool rx_amp : 1; bool not_rx_amp : 1; }; base_type w; }; constexpr Config( const Direction direction, const Band band, const bool amplify ) : tx(direction == Direction::Transmit), rx(direction == Direction::Receive), mix_bypass(band == Band::Mid), not_mix_bypass(band != Band::Mid), tx_mix_bp((direction == Direction::Transmit) && (band == Band::Mid)), rx_mix_bp((direction == Direction::Receive) && (band == Band::Mid)), hp(band == Band::High), lp(band == Band::Low), amp_bypass(!amplify), tx_amp((direction == Direction::Transmit) && amplify), not_tx_amp(!tx_amp), rx_amp((direction == Direction::Receive) && amplify), not_rx_amp(!rx_amp) { } constexpr Config( ) : Config(Direction::Receive, Band::Mid, false) { } constexpr Config( const base_type w ) : w(w) { } constexpr Config operator^(const Config& r) const { return w ^ r.w; } constexpr Config operator&(const Config& r) const { return w & r.w; } constexpr bool operator[](const size_t n) const { return (w >> n) & 1; } static void gpio_init() { for(auto gpio : gpios) { gpio.output(); } } void apply() const { /* NOTE: Assumes order in gpios[] and Config bitfield match. */ for(size_t n=0; n<gpios.size(); n++) { gpios[n].write((*this)[n]); } } }; using ConfigAmp = std::array<Config, 2>; using ConfigDirection = std::array<ConfigAmp, 2>; using ConfigBand = std::array<ConfigDirection, 3>; constexpr ConfigAmp config_amp( const Direction direction, const Band band ) { return { { { .direction = direction, .band = band, .amplify = false }, { .direction = direction, .band = band, .amplify = true }, } }; } constexpr ConfigDirection config_rx_tx( const Band band ) { return { config_amp(Direction::Receive, band), config_amp(Direction::Transmit, band), }; } constexpr ConfigBand config_band() { return { config_rx_tx(Band::Low), config_rx_tx(Band::Mid), config_rx_tx(Band::High), }; } constexpr ConfigBand config_table = config_band(); static_assert(sizeof(config_table) == sizeof(Config::base_type) * 3 * 2 * 2, "rf path config table unexpected size"); constexpr Config get_config( const Direction direction, const Band band, const bool amplify ) { return config_table[toUType(band)][toUType(direction)][amplify ? 1 : 0]; } } /* namespace */ void Path::init() { update(); Config::gpio_init(); } void Path::set_direction(const Direction new_direction) { direction = new_direction; update(); } void Path::set_band(const Band new_band) { band = new_band; update(); } void Path::set_rf_amp(const bool new_rf_amp) { rf_amp = new_rf_amp; update(); } void Path::update() { /* 0 ^ 0 => 0 & 0 = 0 ^ 0 = 0 (no change) * 0 ^ 1 => 1 & 0 = 0 ^ 0 = 0 (ignore change to 1) * 1 ^ 0 => 1 & 1 = 1 ^ 1 = 0 (allow change to 0) * 1 ^ 1 => 0 & 1 = 0 ^ 1 = 1 (no change) */ //const Config changed = _config ^ config_next; //const Config turned_off = _config & changed; /* In transition, ignore the bits that are turning on. So this transition phase * only turns off signals. It doesn't turn on signals. */ //const Config transition_config = _config ^ turned_off; //update_signals(transition_config); /* Move to the final state by turning on required signals. */ const auto config = get_config(direction, band, rf_amp); config.apply(); } } /* path */ } /* rf */