diff options
| author | Ryan Marlow <ryan.marlow@ettus.com> | 2018-01-26 14:07:50 -0500 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2018-03-22 17:48:30 -0700 | 
| commit | d588005fd87dd2594adb29dbbdcf948bbb0ab0c1 (patch) | |
| tree | 7307df1b7a62996c68f7d5bca430359218e9e3da | |
| parent | 93617aa25cc0ec62cbdbf9cdb41ec11fcc29f2b3 (diff) | |
| download | uhd-d588005fd87dd2594adb29dbbdcf948bbb0ab0c1.tar.gz uhd-d588005fd87dd2594adb29dbbdcf948bbb0ab0c1.tar.bz2 uhd-d588005fd87dd2594adb29dbbdcf948bbb0ab0c1.zip  | |
DDC/DUC: switch CORDIC -> DDS for all relevant variable names
- Bump compat number for DDC/DUC to 2.0
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/ddc.xml | 4 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/ddc_single.xml | 4 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/duc.xml | 2 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/duc_single.xml | 2 | ||||
| -rw-r--r-- | host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp | 1 | ||||
| -rw-r--r-- | host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp | 1 | ||||
| -rw-r--r-- | host/lib/rfnoc/ddc_block_ctrl_impl.cpp | 22 | ||||
| -rw-r--r-- | host/lib/rfnoc/duc_block_ctrl_impl.cpp | 16 | ||||
| -rw-r--r-- | host/lib/usrp/cores/rx_dsp_core_3000.cpp | 14 | ||||
| -rw-r--r-- | host/lib/usrp/cores/tx_dsp_core_3000.cpp | 10 | ||||
| -rw-r--r-- | images/manifest.txt | 12 | 
11 files changed, 42 insertions, 46 deletions
diff --git a/host/include/uhd/rfnoc/blocks/ddc.xml b/host/include/uhd/rfnoc/blocks/ddc.xml index 13b9414bd..43e325c0e 100644 --- a/host/include/uhd/rfnoc/blocks/ddc.xml +++ b/host/include/uhd/rfnoc/blocks/ddc.xml @@ -25,8 +25,8 @@      </setreg>      <!-- DDC block registers -->      <setreg> -      <!-- CORDIC phase increment word --> -      <name>CORDIC_FREQ</name> +      <!-- DDS phase increment word --> +      <name>DDS_FREQ</name>        <address>132</address>      </setreg>      <setreg> diff --git a/host/include/uhd/rfnoc/blocks/ddc_single.xml b/host/include/uhd/rfnoc/blocks/ddc_single.xml index 581487388..1843adb5b 100644 --- a/host/include/uhd/rfnoc/blocks/ddc_single.xml +++ b/host/include/uhd/rfnoc/blocks/ddc_single.xml @@ -25,8 +25,8 @@      </setreg>      <!-- DDC block registers -->      <setreg> -      <!-- CORDIC phase increment word --> -      <name>CORDIC_FREQ</name> +      <!-- DDS phase increment word --> +      <name>DDS_FREQ</name>        <address>132</address>      </setreg>      <setreg> diff --git a/host/include/uhd/rfnoc/blocks/duc.xml b/host/include/uhd/rfnoc/blocks/duc.xml index 9be54da78..ea83942da 100644 --- a/host/include/uhd/rfnoc/blocks/duc.xml +++ b/host/include/uhd/rfnoc/blocks/duc.xml @@ -29,7 +29,7 @@        <address>131</address>      </setreg>      <setreg> -      <name>CORDIC_FREQ</name> +      <name>DDS_FREQ</name>        <address>132</address>      </setreg>      <setreg> diff --git a/host/include/uhd/rfnoc/blocks/duc_single.xml b/host/include/uhd/rfnoc/blocks/duc_single.xml index 39038eeee..235788989 100644 --- a/host/include/uhd/rfnoc/blocks/duc_single.xml +++ b/host/include/uhd/rfnoc/blocks/duc_single.xml @@ -29,7 +29,7 @@        <address>131</address>      </setreg>      <setreg> -      <name>CORDIC_FREQ</name> +      <name>DDS_FREQ</name>        <address>132</address>      </setreg>      <setreg> diff --git a/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp index 0fba8ed65..8ea4531a1 100644 --- a/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp +++ b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp @@ -22,6 +22,7 @@  class rx_dsp_core_3000 : boost::noncopyable{  public:      static const double DEFAULT_CORDIC_FREQ; +    static const double DEFAULT_DDS_FREQ;      static const double DEFAULT_RATE;      typedef boost::shared_ptr<rx_dsp_core_3000> sptr; diff --git a/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp index 3eb53da0d..d63f6a609 100644 --- a/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp +++ b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp @@ -19,6 +19,7 @@  class tx_dsp_core_3000 : boost::noncopyable{  public:      static const double DEFAULT_CORDIC_FREQ; +    static const double DEFAULT_DDS_FREQ;      static const double DEFAULT_RATE;      typedef boost::shared_ptr<tx_dsp_core_3000> sptr; diff --git a/host/lib/rfnoc/ddc_block_ctrl_impl.cpp b/host/lib/rfnoc/ddc_block_ctrl_impl.cpp index 2919c163b..9247708ef 100644 --- a/host/lib/rfnoc/ddc_block_ctrl_impl.cpp +++ b/host/lib/rfnoc/ddc_block_ctrl_impl.cpp @@ -197,7 +197,7 @@ public:      }  private: -    static constexpr size_t MAJOR_COMP = 1; +    static constexpr size_t MAJOR_COMP = 2;      static constexpr size_t MINOR_COMP = 0;      static constexpr size_t RB_REG_COMPAT_NUM = 0;      static constexpr size_t RB_REG_NUM_HALFBANDS = 1; @@ -207,18 +207,18 @@ private:      const size_t _num_halfbands;      const size_t _cic_max_decim; -    //! Set the CORDIC frequency shift the signal to \p requested_freq +    //! Set the DDS frequency shift the signal to \p requested_freq      double set_freq(const double requested_freq, const size_t chan)      {          const double input_rate = get_arg<double>("input_rate");          double actual_freq;          int32_t freq_word;          get_freq_and_freq_word(requested_freq, input_rate, actual_freq, freq_word); -        sr_write("CORDIC_FREQ", uint32_t(freq_word), chan); +        sr_write("DDS_FREQ", uint32_t(freq_word), chan);          return actual_freq;      } -    //! Return a range of valid frequencies the CORDIC can tune to +    //! Return a range of valid frequencies the DDS can tune to      uhd::meta_range_t get_freq_range(void)      {          const double input_rate = get_arg<double>("input_rate"); @@ -286,19 +286,17 @@ private:          // Calculate algorithmic gain of CIC for a given decimation.          // For Ettus CIC R=decim, M=1, N=4. Gain = (R * M) ^ N          const double rate_pow = std::pow(double(decim & 0xff), 4); -        // Calculate compensation gain values for algorithmic gain of CORDIC and CIC taking into account +        // Calculate compensation gain values for algorithmic gain of DDS and CIC taking into account          // gain compensation blocks already hardcoded in place in DDC (that provide simple 1/2^n gain compensation). -        // CORDIC algorithmic gain limits asymptotically around 1.647 after many iterations. -        static const double CORDIC_GAIN = 1.648; +        static const double DDS_GAIN = 2.0;          //          // The polar rotation of [I,Q] = [1,1] by Pi/8 also yields max magnitude of SQRT(2) (~1.4142) however -        // input to the CORDIC thats outside the unit circle can only be sourced from a saturated RF frontend. +        // input to the DDS thats outside the unit circle can only be sourced from a saturated RF frontend.          // To provide additional dynamic range head room accordingly using scale factor applied at egress from DDC would          // cost us small signal performance, thus we do no provide compensation gain for a saturated front end and allow          // the signal to clip in the H/W as needed. If we wished to avoid the signal clipping in these circumstances then adjust code to read: -        // _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(CORDIC_GAIN*rate_pow*1.415);          const double scaling_adjustment = -            std::pow(2, uhd::math::ceil_log2(rate_pow))/(CORDIC_GAIN*rate_pow); +            std::pow(2, uhd::math::ceil_log2(rate_pow))/(DDS_GAIN*rate_pow);          update_scalar(scaling_adjustment, chan);          return input_rate/decim_rate;      } @@ -312,7 +310,7 @@ private:          set_arg<double>("output_rate", desired_output_rate, chan);      } -    // Calculate compensation gain values for algorithmic gain of CORDIC and CIC taking into account +    // Calculate compensation gain values for algorithmic gain of DDS and CIC taking into account      // gain compensation blocks already hardcoded in place in DDC (that provide simple 1/2^n gain compensation).      // Further more factor in OTW format which adds further gain factor to weight output samples correctly.      void update_scalar(const double scalar, const size_t chan) @@ -324,7 +322,7 @@ private:              target_scalar / actual_scalar / double(1 << 15) // Rounding error, normalized to 1.0              * get_arg<double>("fullscale"); // Scaling requested by host          set_arg<double>("scalar_correction", scalar_correction, chan); -        // Write DDC with scaling correction for CIC and CORDIC that maximizes dynamic range in 32/16/12/8bits. +        // Write DDC with scaling correction for CIC and DDS that maximizes dynamic range in 32/16/12/8bits.          sr_write("SCALE_IQ", actual_scalar, chan);      } diff --git a/host/lib/rfnoc/duc_block_ctrl_impl.cpp b/host/lib/rfnoc/duc_block_ctrl_impl.cpp index 68109ec26..7f22ca903 100644 --- a/host/lib/rfnoc/duc_block_ctrl_impl.cpp +++ b/host/lib/rfnoc/duc_block_ctrl_impl.cpp @@ -182,7 +182,7 @@ public:  private: -    static constexpr size_t MAJOR_COMP = 1; +    static constexpr size_t MAJOR_COMP = 2;      static constexpr size_t MINOR_COMP = 0;      static constexpr size_t RB_REG_COMPAT_NUM = 0;      static constexpr size_t RB_REG_NUM_HALFBANDS = 1; @@ -192,19 +192,18 @@ private:      const size_t _num_halfbands;      const size_t _cic_max_interp; -    //! Set the CORDIC frequency shift the signal to \p requested_freq +    //! Set the DDS frequency shift the signal to \p requested_freq      double set_freq(const double requested_freq, const size_t chan)      {          const double output_rate = get_arg<double>("output_rate");          double actual_freq;          int32_t freq_word;          get_freq_and_freq_word(requested_freq, output_rate, actual_freq, freq_word); -        // Xilinx CORDIC uses a different format for the phase increment, hence the divide-by-four: -        sr_write("CORDIC_FREQ", uint32_t(freq_word/4), chan); +        sr_write("DDS_FREQ", uint32_t(freq_word), chan);          return actual_freq;      } -    //! Return a range of valid frequencies the CORDIC can tune to +    //! Return a range of valid frequencies the DDS can tune to      uhd::meta_range_t get_freq_range(void)      {          const double output_rate = get_arg<double>("output_rate"); @@ -263,10 +262,7 @@ private:          // For Ettus CIC R=interp, M=1, N=4. Gain = (R * M) ^ (N - 1)          const int CIC_N = 4;          const double rate_pow = std::pow(double(interp & 0xff), CIC_N - 1); - -        // Experimentally determined value to scale the output to [-1, 1] -        // This must also encompass the CORDIC gain -        static const double CONSTANT_GAIN = 1.1644; +        const double CONSTANT_GAIN = 1.0;          const double scaling_adjustment =              std::pow(2, uhd::math::ceil_log2(rate_pow))/(CONSTANT_GAIN*rate_pow); @@ -283,7 +279,7 @@ private:          set_arg<double>("input_rate", desired_input_rate, chan);      } -    // Calculate compensation gain values for algorithmic gain of CORDIC and CIC taking into account +    // Calculate compensation gain values for algorithmic gain of DDS and CIC taking into account      // gain compensation blocks already hardcoded in place in DUC (that provide simple 1/2^n gain compensation).      // Further more factor in OTW format which adds further gain factor to weight output samples correctly.      void update_scalar(const double scalar, const size_t chan) diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 27fc760af..c7d3c25c8 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -37,6 +37,7 @@ template <class T> T ceil_log2(T num){  using namespace uhd;  const double rx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double rx_dsp_core_3000::DEFAULT_DDS_FREQ = 0.0;  const double rx_dsp_core_3000::DEFAULT_RATE = 1e6;  rx_dsp_core_3000::~rx_dsp_core_3000(void){ @@ -191,24 +192,23 @@ public:          // Caclulate algorithmic gain of CIC for a given decimation.          // For Ettus CIC R=decim, M=1, N=4. Gain = (R * M) ^ N          const double rate_pow = std::pow(double(decim & 0xff), 4); -        // Calculate compensation gain values for algorithmic gain of CORDIC and CIC taking into account +        // Calculate compensation gain values for algorithmic gain of and CIC taking into account          // gain compensation blocks already hardcoded in place in DDC (that provide simple 1/2^n gain compensation). -        // CORDIC algorithmic gain limits asymptotically around 1.647 after many iterations.          //          // The polar rotation of [I,Q] = [1,1] by Pi/8 also yields max magnitude of SQRT(2) (~1.4142) however -        // input to the CORDIC thats outside the unit circle can only be sourced from a saturated RF frontend. +        // input to the DDS thats outside the unit circle can only be sourced from a saturated RF frontend.          // To provide additional dynamic range head room accordingly using scale factor applied at egress from DDC would          // cost us small signal performance, thus we do no provide compensation gain for a saturated front end and allow          // the signal to clip in the H/W as needed. If we wished to avoid the signal clipping in these circumstances then adjust code to read:          // _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.648*rate_pow*1.415); -        _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.648*rate_pow); +        _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(2.0*rate_pow);          this->update_scalar();          return _tick_rate/decim_rate;      } -    // Calculate compensation gain values for algorithmic gain of CORDIC and CIC taking into account +    // Calculate compensation gain values for algorithmic gain of DDS and CIC taking into account      // gain compensation blocks already hardcoded in place in DDC (that provide simple 1/2^n gain compensation).      // Further more factor in OTW format which adds further gain factor to weight output samples correctly.      void update_scalar(void){ @@ -216,7 +216,7 @@ public:          const int32_t actual_scalar = boost::math::iround(target_scalar);          // Calculate the error introduced by using integer representation for the scalar, can be corrected in host later.          _fxpt_scalar_correction = target_scalar/actual_scalar; -        // Write DDC with scaling correction for CIC and CORDIC that maximizes dynamic range in 32/16/12/8bits. +        // Write DDC with scaling correction for CIC and DDS that maximizes dynamic range in 32/16/12/8bits.          _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar);      } @@ -277,7 +277,7 @@ public:              .set_coercer(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1))          ;          subtree->create<double>("freq/value") -            .set(DEFAULT_CORDIC_FREQ) +            .set(DEFAULT_DDS_FREQ)              .set_coercer(boost::bind(&rx_dsp_core_3000::set_freq, this, _1))          ;          subtree->create<meta_range_t>("freq/range") diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp index b04a20497..cc4d23393 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -28,6 +28,7 @@ template <class T> T ceil_log2(T num){  using namespace uhd;  const double tx_dsp_core_3000::DEFAULT_CORDIC_FREQ = 0.0; +const double tx_dsp_core_3000::DEFAULT_DDS_FREQ = 0.0;  const double tx_dsp_core_3000::DEFAULT_RATE = 1e6;  tx_dsp_core_3000::~tx_dsp_core_3000(void){ @@ -103,16 +104,15 @@ public:          // Caclulate algorithmic gain of CIC for a given interpolation          // For Ettus CIC R=decim, M=1, N=3. Gain = (R * M) ^ N          const double rate_pow = std::pow(double(interp & 0xff), 3); -        // Calculate compensation gain values for algorithmic gain of CORDIC and CIC taking into account +        // Calculate compensation gain values for algorithmic gain of DDS and CIC taking into account          // gain compensation blocks already hardcoded in place in DDC (that provide simple 1/2^n gain compensation). -        // CORDIC algorithmic gain limits asymptotically around 1.647 after many iterations. -        _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.648*rate_pow); +        _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(rate_pow);          this->update_scalar();          return _tick_rate/interp_rate;      } -  // Calculate compensation gain values for algorithmic gain of CORDIC and CIC taking into account +  // Calculate compensation gain values for algorithmic gain of DDS and CIC taking into account    // gain compensation blocks already hardcoded in place in DDC (that provide simple 1/2^n gain compensation).    // Further more factor in OTW format which adds further gain factor to weight output samples correctly.      void update_scalar(void){ @@ -177,7 +177,7 @@ public:              .set_coercer(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1))          ;          subtree->create<double>("freq/value") -            .set(DEFAULT_CORDIC_FREQ) +            .set(DEFAULT_DDS_FREQ)              .set_coercer(boost::bind(&tx_dsp_core_3000::set_freq, this, _1))          ;          subtree->create<meta_range_t>("freq/range") diff --git a/images/manifest.txt b/images/manifest.txt index d3478ac3d..122b2c3e5 100644 --- a/images/manifest.txt +++ b/images/manifest.txt @@ -1,8 +1,8 @@  # UHD Image Manifest File  # Target    hash    url     SHA256  # X300-Series -x3xx_x310_fpga_default          fpga-1791847        x3xx/fpga-1791847/x3xx_x310_fpga_default-g1791847.zip                   b18622e48f8a7e762c07ec90a563a5925f6098fe9a905fe1689c246696678142 -x3xx_x300_fpga_default          fpga-1791847        x3xx/fpga-1791847/x3xx_x300_fpga_default-g1791847.zip                   2e184533f90abe17ce931848c8d2ca628b497399ac9bdd069f685ba9ce50aa3c +x3xx_x310_fpga_default          fpga-4bb66b3        x3xx/fpga-4bb66b3/x3xx_x310_fpga_default-g4bb66b3.zip                   ded331d6e5261589e32148ac1f21096e9fd17716e5527e998beb893d282691b4 +x3xx_x300_fpga_default          fpga-4bb66b3        x3xx/fpga-4bb66b3/x3xx_x300_fpga_default-g4bb66b3.zip                   e4d311f791e6ca5c4818b0f83b0fe5ee9ee3c593dd3044d5c28968a4f5c4e3f6  # Example daughterboard targets (none currently exist)  #x3xx_twinrx_cpld_default   example_target  #dboard_ubx_cpld_default    example_target @@ -11,9 +11,9 @@ x3xx_x300_fpga_default          fpga-1791847        x3xx/fpga-1791847/x3xx_x300_  e3xx_e310_fpga_default          fpga-1c568e6        e3xx/fpga-1c568e6/e3xx_e310_fpga_default-g1c568e6.zip                   2957b4bdefe6885644ef2bc7b0e3845d13a7fe45f5a0933a2adaffd24c1b6802  # N300-Series -n3xx_n310_fpga_default          fpga-fad351d        n3xx/fpga-fad351d/n3xx_n310_fpga_default-gfad351d.zip                   de65ef6521ed20c57fbe75c86afc0dc80531d77b0c88a89cec742bf67363b9be -n3xx_n300_fpga_default          fpga-fad351d        n3xx/fpga-fad351d/n3xx_n300_fpga_default-gfad351d.zip                   0fb5aebee11f9cbb99bb6c09f50099c5eba807acab46a3a23e8f65b1ef4e06c8 -n3xx_n310_fpga_aurora           fpga-6bea23d        n3xx/fpga-6bea23d/n3xx_n310_fpga_aurora-g6bea23d.zip                    62a12a2c85526f759c96a1eb7db226e715cdd83b9c277d29f037ae00c72bf7fa +n3xx_n310_fpga_default          fpga-4bb66b3        n3xx/fpga-4bb66b3/n3xx_n310_fpga_default-g4bb66b3.zip                   1b6fc983b7589351b6e6a935dcde70e77fca75cb3f629f43f5bef876b134e7f5 +n3xx_n300_fpga_default          fpga-4bb66b3        n3xx/fpga-4bb66b3/n3xx_n300_fpga_default-g4bb66b3.zip                   25d5d49da4174d6d7647d4b23e1bef1df9a2eb1e26d328f48fc73a9557a25a82 +n3xx_n310_fpga_aurora           fpga-4bb66b3        n3xx/fpga-4bb66b3/n3xx_n310_fpga_aurora-g4bb66b3.zip                    f93181aa29ebeee29b44afc1cb83202ba655a9dab2d1aad7a8e28e794746749b  #n3xx_n310_cpld_default         fpga-6bea23d        n3xx/fpga-6bea23d/n3xx_n310_cpld_default-g6bea23d.zip                   0  # N3XX Mykonos firmware  #n3xx_n310_fw_default           fpga-6bea23d        n3xx/fpga-6bea23d/n3xx_n310_fw_default-g6bea23d.zip                     0 @@ -38,7 +38,7 @@ usrp2_n200_fpga_default         fpga-6bea23d        usrp2/fpga-6bea23d/usrp2_n20  usrp2_n200_fw_default           fpga-6bea23d        usrp2/fpga-6bea23d/usrp2_n200_fw_default-g6bea23d.zip                   3eee2a6195caafe814912167fccf2dfc369f706446f8ecee36e97d2c0830116f  usrp2_n210_fpga_default         fpga-6bea23d        usrp2/fpga-6bea23d/usrp2_n210_fpga_default-g6bea23d.zip                 5ce68ac539ee6eeb7d04fb3127c1fabcaff442a8edfaaa2f3746590f9df909bd  usrp2_n210_fw_default           fpga-6bea23d        usrp2/fpga-6bea23d/usrp2_n210_fw_default-g6bea23d.zip                   3646fcd3fc974d18c621cb10dfe97c4dad6d282036dc63b7379995dfad95fb98 -n230_n230_fpga_default          fpga-1c568e6        n230/fpga-1c568e6/n230_n230_fpga_default-g1c568e6.zip                   8cf8b5318aa797d180b8a09b2dc39f25418a5952c11ec65687ceb5542a3a59cb +n230_n230_fpga_default          fpga-4bb66b3        n230/fpga-4bb66b3/n230_n230_fpga_default-g4bb66b3.zip                   02ba098d797832906b8cb2e75f10dba5b2dc21cc84ec99e1f159a72e0b89eefa  # USRP1 Devices  usrp1_usrp1_fpga_default        fpga-6bea23d        usrp1/fpga-6bea23d/usrp1_usrp1_fpga_default-g6bea23d.zip                03bf72868c900dd0853bf48e2ede91058d579829b0e70c021e51b0e282d1d5be  | 
