diff options
Diffstat (limited to 'host')
| -rw-r--r-- | host/include/uhd/rfnoc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/fosphor.yml | 56 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/defaults.hpp | 2 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/fosphor_block_control.hpp | 383 | ||||
| -rw-r--r-- | host/lib/rfnoc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/rfnoc/fosphor_block_control.cpp | 418 | 
6 files changed, 861 insertions, 0 deletions
| diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt index 6435ec2a6..098c79562 100644 --- a/host/include/uhd/rfnoc/CMakeLists.txt +++ b/host/include/uhd/rfnoc/CMakeLists.txt @@ -36,6 +36,7 @@ UHD_INSTALL(FILES      dmafifo_block_control.hpp      fft_block_control.hpp      fir_filter_block_control.hpp +    fosphor_block_control.hpp      null_block_control.hpp      radio_control.hpp diff --git a/host/include/uhd/rfnoc/blocks/fosphor.yml b/host/include/uhd/rfnoc/blocks/fosphor.yml new file mode 100644 index 000000000..d803dc602 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/fosphor.yml @@ -0,0 +1,56 @@ +schema: rfnoc_modtool_args +module_name: fosphor +version: 1.0 +rfnoc_version: 1.0 +chdr_width: 64 +noc_id: 0x666F0000 + +clocks: +  - name: rfnoc_chdr +    freq: "[]" +  - name: rfnoc_ctrl +    freq: "[]" +  - name: ce +    freq: "[]" + +control: +  sw_iface: nocscript +  fpga_iface: ctrlport +  interface_direction: slave +  fifo_depth: 32 +  clk_domain: ce +  ctrlport: +    byte_mode: False +    timed: False +    has_status: False + +data: +  fpga_iface: axis_data +  clk_domain: ce +  inputs: +    fft_in: +      item_width: 32 +      nipc: 1 +      info_fifo_depth: 32 +      payload_fifo_depth: 32 +      format: sc16 +      mdata_sig: ~ +  outputs: +    hist: +      item_width: 8 +      nipc: 4 +      info_fifo_depth: 32 +      payload_fifo_depth: 32 +      format: u8 +      mdata_sig: ~ +    wf: +      item_width: 8 +      nipc: 4 +      info_fifo_depth: 32 +      payload_fifo_depth: 32 +      format: u8 +      mdata_sig: ~ + +registers: + +properties: diff --git a/host/include/uhd/rfnoc/defaults.hpp b/host/include/uhd/rfnoc/defaults.hpp index bc97fd9f5..38ae57242 100644 --- a/host/include/uhd/rfnoc/defaults.hpp +++ b/host/include/uhd/rfnoc/defaults.hpp @@ -30,6 +30,7 @@ static const std::string NODE_ID_SEP("SEP");  using io_type_t                     = std::string;  static const io_type_t IO_TYPE_SC16 = "sc16"; +static const io_type_t IO_TYPE_U8   = "u8";  static const std::string ACTION_KEY_STREAM_CMD("stream_cmd");  static const std::string ACTION_KEY_RX_EVENT("rx_event"); @@ -75,5 +76,6 @@ static const noc_id_t RADIO_BLOCK      = 0x12AD1000;  static const noc_id_t DUC_BLOCK        = 0xD0C00000;  static const noc_id_t DDC_BLOCK        = 0xDDC00000;  static const noc_id_t FIR_FILTER_BLOCK = 0xf1120000; +static const noc_id_t FOSPHOR_BLOCK    = 0x666F0000;  }} // namespace uhd::rfnoc diff --git a/host/include/uhd/rfnoc/fosphor_block_control.hpp b/host/include/uhd/rfnoc/fosphor_block_control.hpp new file mode 100644 index 000000000..965b733a7 --- /dev/null +++ b/host/include/uhd/rfnoc/fosphor_block_control.hpp @@ -0,0 +1,383 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include <uhd/config.hpp> +#include <uhd/rfnoc/noc_block_base.hpp> + +namespace uhd { namespace rfnoc { + +enum class fosphor_waterfall_mode { MAX_HOLD, AVERAGE }; +enum class fosphor_waterfall_predivision_ratio { +    RATIO_1_1, +    RATIO_1_8, +    RATIO_1_64, +    RATIO_1_256 +}; + +/*! + * Fosphor Control Class + * + * The Fosphor Block is an RFNoC block that accepts FFT data as signed + * complex 16-bit data and produces two streams of eight-bit data, a + * stream of histogram data and a stream of waterfall data. + * + * \section Histogram + * + * Each time the Fosphor block receives an FFT input packet, the power values + * in each of the N frequency bins are quantized into one of 64 power bins + * (X axis represents individual FFT frequency bins; Y axis represents the + * power bins): + * + *     63                                . + *      :                              .   . + *      :                  . .       .       .     . . + *      0  . . . . . . . .     . . .           . .     . . . . . . . + *         0 1 2 3 4 5 - - - - - - - - - - - - - - - - - - - - - - N-1 + * + * Each time an FFT power value is quantized to a bin, the bin count + * is increased by one (illustrated by a '+'): + * + *     63                                + + *      :                              +   + + *      :                  + +       +       +     + + + *      0  + + + + + + + +     + + +           + +     + + + + + + + + *         0 1 2 3 4 5 - - - - - - - - - - - - - - - - - - - - - - N-1 + * + * As more FFT packets are received, the counts in each bin accumulate. + * Over time, the count in the 'closest' power bin to each sample in the FFT + * accumulates at the highest rate. However, adjacent power bins' counts may + * increase due to quantization noise and variances in the input FFT signal + * (highest counts illustrated by '*', followed by '+' and '.'): + * + *     63                              . * + + *      :          .       . .       + * . * + + *      :  + + . . + + . + * * + + + * +     * + + * * . + . + + . + *      0  * * * * * * * * + + * * * +       + * * + + * * * * * * + *         0 1 2 3 4 5 - - - - - - - - - - - - - - - - - - - - - - N-1 + * + * The Fosphor block also calculates the average power level and maximum + * power level encountered in each FFT frequency bin. The rate at which + * the accumulated counts, average power level, and maximum power level + * values rise and fall over time is configurable. + * + * An instance of histogram data output consists of 66 packets: + * + * * 64 packets, one per quantized power level, of N values, representing the + * accumulated count for each frequency bin for that particular quantized + * power level; + * * One packet of N values, representing the average power level in each + * frequency bin; and + * * One packet of N values, representing the maximum power level in each + * frequency bin. + * + * \section Waterfall + * + * The waterfall stream consists of history data of either the average or + * maximum power level values in each bin, depending on the selected waterfall + * mode. In max hold mode, each waterfall packet consists of N values, + * representing the maximum power level in each frequency bin. The rate + * that packets are produced relative to the number of input FFT packets is + * configurable via the waterfall decimation parameter. + * + * In average mode, each waterfall packet consists of N values, representing + * the _sum_ of the average power level in each frequency bin accumulated + * between packets. (Thus, if the decimation rate is increased, the values + * returned are higher than if the decimation rate is decreased.) The + * waterfall predivision ratio parameter can be used to scale the values + * prior to accumulation to counteract this effect. + * + * These streams are intended to be inputs to the GNU Radio Fosphor + * display block, which renders the streams in a entertaining graphical + * format. + */ +class UHD_API fosphor_block_control : public noc_block_base +{ +public: +    RFNOC_DECLARE_BLOCK(fosphor_block_control) + +    // Block registers +    static const uint32_t REG_ENABLE_ADDR; +    static const uint32_t REG_CLEAR_ADDR; +    static const uint32_t REG_RANDOM_ADDR; +    static const uint32_t REG_DECIM_ADDR; +    static const uint32_t REG_OFFSET_ADDR; +    static const uint32_t REG_SCALE_ADDR; +    static const uint32_t REG_TRISE_ADDR; +    static const uint32_t REG_TDECAY_ADDR; +    static const uint32_t REG_ALPHA_ADDR; +    static const uint32_t REG_EPSILON_ADDR; +    static const uint32_t REG_WF_CTRL_ADDR; +    static const uint32_t REG_WF_DECIM_ADDR; + +    /*! Set the histogram stream enable flag +     * +     * Enables or disables the stream of histogram data from the block. +     * +     * \param enable_histogram Histogram stream enable/disable flag +     */ +    virtual void set_enable_histogram(const bool enable_histogram) = 0; + +    /*! Get the histogram stream enable flag +     * +     * Returns the current histogram enable value. +     * +     * \returns Histogram stream enable/disable flag +     */ +    virtual bool get_enable_histogram() const = 0; + +    /*! Set the waterfall stream enable flag +     * +     * Enables or disables the stream of waterfall data from the block. +     * +     * \param enable_waterfall Histogram stream enable/disable flag +     */ +    virtual void set_enable_waterfall(const bool enable_waterfall) = 0; + +    /*! Get the waterfall stream enable flag +     * +     * Returns the current waterfall enable value. +     * +     * \returns Histogram stream enable/disable flag +     */ +    virtual bool get_enable_waterfall() const = 0; + +    /*! Clear the Fosphor block's stored history +     * +     * Clears the accumulated history in the Fosphor block, resetting +     * average and max hold values. +     */ +    virtual void clear_history() = 0; + +    /*! Set the dither enable flag +     * +     * Enables or disables dithering. Dithering adds quantization error +     * to the incoming signal. +     * +     * \param enable_dither Dither enable/disable flag +     */ +    virtual void set_enable_dither(const bool enable_dither) = 0; + +    /*! Get the dither enable flag +     * +     * Returns the current dither enable value. +     * +     * \returns Dither enable/disable flag +     */ +    virtual bool get_enable_dither() const = 0; + +    /*! Set the noise enable flag +     * +     * Enables or disables the addition of random noise to the incoming +     * signal. +     * +     * \param enable_noise Noise enable/disable flag +     */ +    virtual void set_enable_noise(const bool enable_noise) = 0; + +    /*! Get the noise enable flag +     * +     * Returns the current noise enable value. +     * +     * \returns Noise enable/disable flag +     */ +    virtual bool get_enable_noise() const = 0; + +    /*! Set the histogram decimation factor +     * +     * Sets the ratio of histogram outputs to FFT packet inputs. +     * For every \p decimation FFT input packets, one histogram +     * output cluster (64 histogram packets, plus a maximum and +     * average values packet) is produced. The minimum value for +     * \p decimation is 2. +     * +     * \param decimation Histogram decimation factor +     */ +    virtual void set_histogram_decimation(const uint16_t decimation) = 0; + +    /*! Get the histogram decimation factor +     * +     * Returns the current histogram decimation factor. +     * +     * \returns Histogram decimation factor +     */ +    virtual uint16_t get_histogram_decimation() const = 0; + +    /*! Set the histogram offset factor +     * +     * Sets the offset factor to apply to FFT power levels before determining +     * the appropriate histogram bin. +     * +     * \param offset The histogram offset factor to apply +     */ +    virtual void set_histogram_offset(const uint16_t offset) = 0; + +    /*! Get the histogram offset factor +     * +     * Returns the current histogram offset factor. +     * +     * \returns The histogram offset factor +     */ +    virtual uint16_t get_histogram_offset() const = 0; + +    /*! Set the histogram scale factor +     * +     * Sets the scale factor to apply to FFT power levels before determining +     * the appropriate histogram bin. The scaling factor is \p scale / 256. +     * +     * \param scale The histogram scale factor to apply +     */ +    virtual void set_histogram_scale(const uint16_t scale) = 0; + +    /*! Get the history scale factor +     * +     * Returns the current histogram scale factor. +     * +     * \returns The histogram scale factor +     */ +    virtual uint16_t get_histogram_scale() const = 0; + +    /*! Set the histogram rise rate factor +     * +     * Sets the rate at which the hit count in each frequency and power bin +     * increases when accumulating (i.e., there are hits in the particular +     * bin). The higher the value, the more quickly the values increase, +     * leading to a phosphorescent-like effect on the Fosphor display similar +     * to the gradual illumination of a CRT display in the area where the +     * electron beam is pointing. +     * +     * \param rise_rate The histogram rise rate factor to apply +     */ +    virtual void set_histogram_rise_rate(const uint16_t rise_rate) = 0; + +    /*! Get the histogram rise rate factor +     * +     * Returns the current histogram rise rate factor. +     * +     * \returns The histogram rise rate factor +     */ +    virtual uint16_t get_histogram_rise_rate() const = 0; + +    /*! Set the histogram decay rate factor +     * +     * Sets the rate at which the hit count in each frequency and power bin +     * decreases when not accumulating (i.e., there are no hits in the +     * particular bin). The lower the value, the more slowly the values +     * decrease, leading to a phosphorescent-like effect on the Fosphor +     * display similar to the gradual fading of a CRT display when the +     * electron beam is extinguished. +     * +     * \param decay_rate The histogram decay rate factor to apply +     */ +    virtual void set_histogram_decay_rate(const uint16_t decay_rate) = 0; + +    /*! Get the histogram decay rate factor +     * +     * Returns the current histogram decay rate factor. +     * +     * \returns The histogram decay rate factor +     */ +    virtual uint16_t get_histogram_decay_rate() const = 0; + +    /*! Set the power level moving average weighting +     * +     * Sets the weighing to be applied to the average power level value +     * for each FFT frequency bin. The higher the value, the higher the +     * weight is given to older samples (and thus the more slowly the average +     * values change over time in each bin). +     * +     * \param alpha The power level moving average weighting to apply +     */ +    virtual void set_spectrum_alpha(const uint16_t alpha) = 0; + +    /*! Get the power level moving average weighting +     * +     * Returns the weighting that is applied to older samples when calculating +     * the average power level for each FFT frequency bin. +     * +     * \returns The power level moving average weighting +     */ +    virtual uint16_t get_spectrum_alpha() const = 0; + +    /*! Set the maximum hold decay rate +     * +     * Sets the rate at which the maximum value for each FFT frequency +     * bin decays. The higher the value, the faster the decay rate. +     * A value of 0 retains the maximum values indefinitely. +     * +     * \param epsilon The histogram scale factor to apply +     */ +    virtual void set_spectrum_max_hold_decay(const uint16_t epsilon) = 0; + +    /*! Get the maximum hold decay rate +     * +     * Returns the rate at which the maximum value for each FFT frequency +     * bin decays. +     * +     * \returns The maximum hold decay rate +     */ +    virtual uint16_t get_spectrum_max_hold_decay() const = 0; + +    /*! Set the waterfall predivision ratio +     * +     * Sets the scaling factor applied to waterfall values. +     * +     * \param waterfall_predivision The waterfall predivision ratio to apply +     */ +    virtual void set_waterfall_predivision( +        const fosphor_waterfall_predivision_ratio waterfall_predivision) = 0; + +    /*! Get the waterfall predivision ratio +     * +     * Returns the current waterfall predivision ratio. +     * +     * \returns The waterfall predivision ratio +     */ +    virtual fosphor_waterfall_predivision_ratio get_waterfall_predivision() const = 0; + +    /*! Set the waterfall mode setting +     * +     * Sets the source of the waterfall history data. When \p waterfall_mode +     * is set to `MAX_HOLD`, the waterfall data is comprised of the max +     * power values from each FFT frequency bin. When \p waterfall_mode is set +     * to `AVERAGE`, the waterfall data is comprised of the accumulated +     * average value from each FFT frequency bin between waterfall output +     * packets. +     * +     * \param waterfall_mode The waterfall mode setting +     */ +    virtual void set_waterfall_mode(const fosphor_waterfall_mode waterfall_mode) = 0; + +    /*! Get the waterfall mode setting +     * +     * Returns the current waterfall mode setting. +     * +     * \returns The waterfall mode setting +     */ +    virtual fosphor_waterfall_mode get_waterfall_mode() const = 0; + +    /*! Set the waterfall decimation factor +     * +     * Sets the ratio of waterfall outputs to FFT packet inputs. +     * For every \p waterfall_decimation FFT input packets, one waterfall +     * output packet is produced. The minimum value for +     * \p waterfall_decimation is 2. +     * +     * \param waterfall_decimation The waterfall decimation factor to apply +     */ +    virtual void set_waterfall_decimation(const uint16_t waterfall_decimation) = 0; + +    /*! Get the histogram decimation factor +     * +     * Returns the current waterfall decimation factor. +     * +     * \returns The waterfall decimation factor +     */ +    virtual uint16_t get_waterfall_decimation() const = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt index 4edfe4009..6881a57e8 100644 --- a/host/lib/rfnoc/CMakeLists.txt +++ b/host/lib/rfnoc/CMakeLists.txt @@ -46,6 +46,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/dmafifo_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/fft_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/fir_filter_block_control.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/fosphor_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/null_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_control_impl.cpp  ) diff --git a/host/lib/rfnoc/fosphor_block_control.cpp b/host/lib/rfnoc/fosphor_block_control.cpp new file mode 100644 index 000000000..6bf621982 --- /dev/null +++ b/host/lib/rfnoc/fosphor_block_control.cpp @@ -0,0 +1,418 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/exception.hpp> +#include <uhd/rfnoc/defaults.hpp> +#include <uhd/rfnoc/fosphor_block_control.hpp> +#include <uhd/rfnoc/property.hpp> +#include <uhd/rfnoc/registry.hpp> +#include <string> + +using namespace uhd::rfnoc; + + +// Register offsets +const uint32_t fosphor_block_control::REG_ENABLE_ADDR   = 0x00; +const uint32_t fosphor_block_control::REG_CLEAR_ADDR    = 0x04; +const uint32_t fosphor_block_control::REG_RANDOM_ADDR   = 0x08; +const uint32_t fosphor_block_control::REG_DECIM_ADDR    = 0x0c; +const uint32_t fosphor_block_control::REG_OFFSET_ADDR   = 0x10; +const uint32_t fosphor_block_control::REG_SCALE_ADDR    = 0x14; +const uint32_t fosphor_block_control::REG_TRISE_ADDR    = 0x18; +const uint32_t fosphor_block_control::REG_TDECAY_ADDR   = 0x1c; +const uint32_t fosphor_block_control::REG_ALPHA_ADDR    = 0x20; +const uint32_t fosphor_block_control::REG_EPSILON_ADDR  = 0x24; +const uint32_t fosphor_block_control::REG_WF_CTRL_ADDR  = 0x28; +const uint32_t fosphor_block_control::REG_WF_DECIM_ADDR = 0x2c; + +// Mask bits +constexpr uint32_t RESET_HISTORY_BIT  = 0; +constexpr uint32_t RESET_HISTORY_MASK = (1 << RESET_HISTORY_BIT); +constexpr uint32_t RESET_CORE_BIT     = 1; +constexpr uint32_t RESET_CORE_MASK    = (1 << RESET_CORE_BIT); + +constexpr uint32_t HISTOGRAM_ENABLE_BIT  = 0; +constexpr uint32_t HISTOGRAM_ENABLE_MASK = (1 << HISTOGRAM_ENABLE_BIT); +constexpr uint32_t WATERFALL_ENABLE_BIT  = 1; +constexpr uint32_t WATERFALL_ENABLE_MASK = (1 << WATERFALL_ENABLE_BIT); + +constexpr uint32_t DITHER_ENABLE_BIT  = 0; +constexpr uint32_t DITHER_ENABLE_MASK = (1 << DITHER_ENABLE_BIT); +constexpr uint32_t NOISE_ENABLE_BIT   = 1; +constexpr uint32_t NOISE_ENABLE_MASK  = (1 << NOISE_ENABLE_BIT); + +constexpr uint32_t WATERFALL_MODE_BIT  = 7; +constexpr uint32_t WATERFALL_MODE_MASK = (1 << WATERFALL_MODE_BIT); +constexpr uint32_t PREDIV_RATIO_MASK   = (1 << 0) | (1 << 1); + +// User property names +const char* const PROP_KEY_ENABLE_HISTOGRAM     = "enable_histogram"; +const char* const PROP_KEY_ENABLE_WATERFALL     = "enable_waterfall"; +const char* const PROP_KEY_CLEAR_HISTORY        = "clear_history"; +const char* const PROP_KEY_ENABLE_DITHER        = "enable_dither"; +const char* const PROP_KEY_ENABLE_NOISE         = "enable_noise"; +const char* const PROP_KEY_HIST_DECIMATION      = "hist_decimation"; +const char* const PROP_KEY_OFFSET               = "offset"; +const char* const PROP_KEY_SCALE                = "scale"; +const char* const PROP_KEY_RISE_TIME            = "trise"; +const char* const PROP_KEY_DECAY_TIME           = "tdecay"; +const char* const PROP_KEY_ALPHA                = "alpha"; +const char* const PROP_KEY_EPSILON              = "epsilon"; +const char* const PROP_KEY_WF_PREDIVISION_RATIO = "wf_predivision_ratio"; +const char* const PROP_KEY_WF_MODE              = "wf_mode"; +const char* const PROP_KEY_WF_DECIMATION        = "wf_decimation"; + +// Edge property details +constexpr uint32_t HISTOGRAM_PORT = 0; +constexpr uint32_t WATERFALL_PORT = 1; + + +class fosphor_block_control_impl : public fosphor_block_control +{ +public: +    RFNOC_BLOCK_CONSTRUCTOR(fosphor_block_control) +    { +        // reset the core upon block construction +        this->regs().poke32(REG_CLEAR_ADDR, RESET_CORE_MASK); +        _register_props(); +    } + +    void set_enable_histogram(const bool enable_histogram) +    { +        set_property<bool>(PROP_KEY_ENABLE_HISTOGRAM, enable_histogram); +    } + +    bool get_enable_histogram() const +    { +        return _prop_enable_histogram.get(); +    } + +    void set_enable_waterfall(const bool enable_waterfall) +    { +        set_property<bool>(PROP_KEY_ENABLE_WATERFALL, enable_waterfall); +    } + +    bool get_enable_waterfall() const +    { +        return _prop_enable_waterfall.get(); +    } + +    void clear_history() +    { +        set_property<bool>(PROP_KEY_CLEAR_HISTORY, true); +    } + +    void set_enable_dither(const bool enable_dither) +    { +        set_property<bool>(PROP_KEY_ENABLE_DITHER, enable_dither); +    } + +    bool get_enable_dither() const +    { +        return _prop_enable_dither.get(); +    } + +    void set_enable_noise(const bool enable_noise) +    { +        set_property<bool>(PROP_KEY_ENABLE_NOISE, enable_noise); +    } + +    bool get_enable_noise() const +    { +        return _prop_enable_noise.get(); +    } + +    void set_histogram_decimation(const uint16_t decimation) +    { +        set_property<int>(PROP_KEY_HIST_DECIMATION, decimation); +    } + +    uint16_t get_histogram_decimation() const +    { +        return _prop_hist_decimation.get(); +    } + +    void set_histogram_offset(const uint16_t offset) +    { +        set_property<int>(PROP_KEY_OFFSET, offset); +    } + +    uint16_t get_histogram_offset() const +    { +        return _prop_offset.get(); +    } + +    void set_histogram_scale(const uint16_t scale) +    { +        set_property<int>(PROP_KEY_SCALE, scale); +    } + +    uint16_t get_histogram_scale() const +    { +        return _prop_scale.get(); +    } + +    void set_histogram_rise_rate(const uint16_t rise_rate) +    { +        set_property<int>(PROP_KEY_RISE_TIME, rise_rate); +    } + +    uint16_t get_histogram_rise_rate() const +    { +        return _prop_trise.get(); +    } + +    void set_histogram_decay_rate(const uint16_t decay_rate) +    { +        set_property<int>(PROP_KEY_DECAY_TIME, decay_rate); +    } + +    uint16_t get_histogram_decay_rate() const +    { +        return _prop_tdecay.get(); +    } + +    void set_spectrum_alpha(const uint16_t alpha) +    { +        set_property<int>(PROP_KEY_ALPHA, alpha); +    } + +    uint16_t get_spectrum_alpha() const +    { +        return _prop_alpha.get(); +    } + +    void set_spectrum_max_hold_decay(const uint16_t epsilon) +    { +        set_property<int>(PROP_KEY_EPSILON, epsilon); +    } + +    uint16_t get_spectrum_max_hold_decay() const +    { +        return _prop_epsilon.get(); +    } + +    void set_waterfall_predivision( +        const fosphor_waterfall_predivision_ratio waterfall_predivision) +    { +        set_property<int>( +            PROP_KEY_WF_PREDIVISION_RATIO, static_cast<int>(waterfall_predivision)); +    } + +    fosphor_waterfall_predivision_ratio get_waterfall_predivision() const +    { +        return static_cast<fosphor_waterfall_predivision_ratio>( +            _prop_wf_prediv_ratio.get()); +    } + +    void set_waterfall_mode(const fosphor_waterfall_mode waterfall_mode) +    { +        set_property<int>(PROP_KEY_WF_MODE, static_cast<int>(waterfall_mode)); +    } + +    fosphor_waterfall_mode get_waterfall_mode() const +    { +        return static_cast<fosphor_waterfall_mode>(_prop_wf_mode.get()); +    } + +    void set_waterfall_decimation(const uint16_t waterfall_decimation) +    { +        set_property<int>(PROP_KEY_WF_DECIMATION, waterfall_decimation); +    } + +    uint16_t get_waterfall_decimation() const +    { +        return _prop_wf_decim.get(); +    } + +    /************************************************************************** +     * Initialization +     *************************************************************************/ +private: +    void _register_props() +    { +        // register user properties +        register_property(&_prop_enable_histogram, [this]() { _program_enables(); }); +        register_property(&_prop_enable_waterfall, [this]() { _program_enables(); }); +        register_property(&_prop_clear_history, +            [this]() { this->regs().poke32(REG_CLEAR_ADDR, RESET_HISTORY_MASK); }); +        register_property( +            &_prop_enable_dither, [this]() { _program_randomness_enables(); }); +        register_property( +            &_prop_enable_noise, [this]() { _program_randomness_enables(); }); +        register_property(&_prop_hist_decimation, [this]() { +            int decim = _prop_hist_decimation.get(); +            if (decim < 2 || decim > 1024) { +                throw uhd::value_error( +                    "Histogram decimation value must be in [2, 1024]"); +            } +            this->regs().poke32(REG_DECIM_ADDR, uint32_t(decim - 2)); +        }); +        register_property(&_prop_offset, [this]() { +            int offset = _prop_offset.get(); +            if (offset < 0 || offset > 65535) { +                throw uhd::value_error("Offset value must be in [0, 65535]"); +            } +            this->regs().poke32(REG_OFFSET_ADDR, uint32_t(offset)); +        }); +        register_property(&_prop_scale, [this]() { +            int scale = _prop_scale.get(); +            if (scale < 0 || scale > 65535) { +                throw uhd::value_error("Scale value must be in [0, 65535]"); +            } +            this->regs().poke32(REG_SCALE_ADDR, uint32_t(scale)); +        }); +        register_property(&_prop_trise, [this]() { +            int trise = _prop_trise.get(); +            if (trise < 0 || trise > 65535) { +                throw uhd::value_error("Rise rate value must be in [0, 65535]"); +            } +            this->regs().poke32(REG_TRISE_ADDR, uint32_t(trise)); +        }); +        register_property(&_prop_tdecay, [this]() { +            int tdecay = _prop_tdecay.get(); +            if (tdecay < 0 || tdecay > 65535) { +                throw uhd::value_error("Decay rate value must be in [0, 65535]"); +            } +            this->regs().poke32(REG_TDECAY_ADDR, uint32_t(tdecay)); +        }); +        register_property(&_prop_alpha, [this]() { +            int alpha = _prop_alpha.get(); +            if (alpha < 0 || alpha > 65535) { +                throw uhd::value_error("Alpha value must be in [0, 65535]"); +            } +            this->regs().poke32(REG_ALPHA_ADDR, uint32_t(alpha)); +        }); +        register_property(&_prop_epsilon, [this]() { +            int epsilon = _prop_epsilon.get(); +            if (epsilon < 0 || epsilon > 65535) { +                throw uhd::value_error("Max hold decay rate must be in [0, 65535]"); +            } +            this->regs().poke32(REG_EPSILON_ADDR, uint32_t(epsilon)); +        }); +        register_property(&_prop_wf_prediv_ratio, [this]() { +            int prediv_ratio = _prop_wf_prediv_ratio.get(); +            if (prediv_ratio +                    < static_cast<int>(fosphor_waterfall_predivision_ratio::RATIO_1_1) +                || prediv_ratio > static_cast<int>( +                                      fosphor_waterfall_predivision_ratio::RATIO_1_256)) { +                throw uhd::value_error( +                    "Waterfall predivision ratio value must be in [0, 3]"); +            } +            _program_waterfall_mode(); +        }); +        register_property(&_prop_wf_mode, [this]() { +            int wf_mode = _prop_wf_mode.get(); +            if (wf_mode < static_cast<int>(fosphor_waterfall_mode::MAX_HOLD) +                || wf_mode > static_cast<int>(fosphor_waterfall_mode::AVERAGE)) { +                throw uhd::value_error("Waterfall mode value must be 0 or 1"); +            } +            _program_waterfall_mode(); +        }); +        register_property(&_prop_wf_decim, [this]() { +            int wf_decim = _prop_wf_decim.get(); +            if (wf_decim < 2 || wf_decim > 257) { +                throw uhd::value_error( +                    "Waterfall decimation value must be in [2, 257]"); +            } +            this->regs().poke32(REG_WF_DECIM_ADDR, uint32_t(wf_decim - 2)); +        }); + +        // register edge properties +        register_property(&_prop_type_in); +        register_property(&_prop_type_out_histogram); +        register_property(&_prop_type_out_wf); + +        // add resolvers for type +        add_property_resolver({&_prop_type_in}, {&_prop_type_in}, [this]() { +            _prop_type_in.set(IO_TYPE_SC16); +        }); +        add_property_resolver({&_prop_type_out_histogram}, +            {&_prop_type_out_histogram}, +            [this]() { _prop_type_out_histogram.set(IO_TYPE_U8); }); +        add_property_resolver({&_prop_type_out_wf}, {&_prop_type_out_wf}, [this]() { +            _prop_type_out_wf.set(IO_TYPE_U8); +        }); +    } + +    void _program_enables() +    { +        uint32_t reg_value = this->regs().peek32(REG_ENABLE_ADDR) +                             & ~(HISTOGRAM_ENABLE_MASK | WATERFALL_ENABLE_MASK); +        uint32_t histogram_enable_bit = +            (_prop_enable_histogram.get()) ? HISTOGRAM_ENABLE_MASK : 0; +        uint32_t waterfall_enable_bit = +            (_prop_enable_waterfall.get()) ? WATERFALL_ENABLE_MASK : 0; +        this->regs().poke32( +            REG_ENABLE_ADDR, reg_value | histogram_enable_bit | waterfall_enable_bit); +    } + +    void _program_randomness_enables() +    { +        uint32_t reg_value = this->regs().peek32(REG_RANDOM_ADDR) +                             & ~(DITHER_ENABLE_MASK | NOISE_ENABLE_MASK); +        uint32_t dither_enable_bit = (_prop_enable_dither.get()) ? DITHER_ENABLE_MASK : 0; +        uint32_t noise_enable_bit  = (_prop_enable_noise.get()) ? NOISE_ENABLE_MASK : 0; +        this->regs().poke32( +            REG_RANDOM_ADDR, reg_value | dither_enable_bit | noise_enable_bit); +    } + +    void _program_waterfall_mode() +    { +        uint32_t reg_value = this->regs().peek32(REG_WF_CTRL_ADDR) +                             & ~(WATERFALL_MODE_MASK | PREDIV_RATIO_MASK); +        int prediv_ratio      = _prop_wf_prediv_ratio.get(); +        int wf_mode           = _prop_wf_mode.get(); +        uint32_t wf_mode_bits = (wf_mode << WATERFALL_MODE_BIT) | prediv_ratio; +        this->regs().poke32(REG_WF_CTRL_ADDR, reg_value | wf_mode_bits); +    } + +    /************************************************************************** +     * Attributes +     *************************************************************************/ +    property_t<std::string> _prop_type_in = property_t<std::string>{ +        PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::INPUT_EDGE}}; +    property_t<std::string> _prop_type_out_histogram = property_t<std::string>{ +        PROP_KEY_TYPE, IO_TYPE_U8, {res_source_info::OUTPUT_EDGE, HISTOGRAM_PORT}}; +    property_t<std::string> _prop_type_out_wf = property_t<std::string>{ +        PROP_KEY_TYPE, IO_TYPE_U8, {res_source_info::OUTPUT_EDGE, WATERFALL_PORT}}; + +    property_t<bool> _prop_enable_histogram = +        property_t<bool>{PROP_KEY_ENABLE_HISTOGRAM, true, {res_source_info::USER}}; +    property_t<bool> _prop_enable_waterfall = +        property_t<bool>{PROP_KEY_ENABLE_WATERFALL, true, {res_source_info::USER}}; +    property_t<bool> _prop_clear_history = +        property_t<bool>{PROP_KEY_CLEAR_HISTORY, false, {res_source_info::USER}}; +    property_t<bool> _prop_enable_dither = +        property_t<bool>{PROP_KEY_ENABLE_DITHER, true, {res_source_info::USER}}; +    property_t<bool> _prop_enable_noise = +        property_t<bool>{PROP_KEY_ENABLE_NOISE, true, {res_source_info::USER}}; +    property_t<int> _prop_hist_decimation = +        property_t<int>{PROP_KEY_HIST_DECIMATION, 2, {res_source_info::USER}}; +    property_t<int> _prop_offset = +        property_t<int>{PROP_KEY_OFFSET, 0, {res_source_info::USER}}; +    property_t<int> _prop_scale = +        property_t<int>{PROP_KEY_SCALE, 256, {res_source_info::USER}}; +    property_t<int> _prop_trise = +        property_t<int>{PROP_KEY_RISE_TIME, 4096, {res_source_info::USER}}; +    property_t<int> _prop_tdecay = +        property_t<int>{PROP_KEY_DECAY_TIME, 16384, {res_source_info::USER}}; +    property_t<int> _prop_alpha = +        property_t<int>{PROP_KEY_ALPHA, 65280, {res_source_info::USER}}; +    property_t<int> _prop_epsilon = +        property_t<int>{PROP_KEY_EPSILON, 1, {res_source_info::USER}}; +    property_t<int> _prop_wf_prediv_ratio = property_t<int>{PROP_KEY_WF_PREDIVISION_RATIO, +        static_cast<int>(fosphor_waterfall_predivision_ratio::RATIO_1_1), +        {res_source_info::USER}}; +    property_t<int> _prop_wf_mode         = property_t<int>{PROP_KEY_WF_MODE, +        static_cast<int>(fosphor_waterfall_mode::MAX_HOLD), +        {res_source_info::USER}}; +    property_t<int> _prop_wf_decim = +        property_t<int>{PROP_KEY_WF_DECIMATION, 8, {res_source_info::USER}}; +}; + +UHD_RFNOC_BLOCK_REGISTER_DIRECT( +    fosphor_block_control, FOSPHOR_BLOCK, "Fosphor", CLOCK_KEY_GRAPH, "bus_clk") | 
