diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt
index 63f095b0a6c8946419de11c6622b68f7605df3eb..2cb52e997313825379a3758515b466f0f08d692d 100644
--- a/grc/CMakeLists.txt
+++ b/grc/CMakeLists.txt
@@ -10,5 +10,6 @@ install(FILES
     cdc_preamble_detect_cc.block.yml
     cdc_phy_tx.block.yml
     cdc_phy_rx.block.yml
-    cdc_insert_burst_c.block.yml DESTINATION share/gnuradio/grc/blocks
+    cdc_insert_burst_c.block.yml
+    cdc_dsa_pu_scenario_cc.block.yml DESTINATION share/gnuradio/grc/blocks
 )
diff --git a/grc/cdc_dsa_pu_scenario_cc.block.yml b/grc/cdc_dsa_pu_scenario_cc.block.yml
new file mode 100644
index 0000000000000000000000000000000000000000..94b5c22efcb74cf4695c09d9fd1f1540f2bda1b3
--- /dev/null
+++ b/grc/cdc_dsa_pu_scenario_cc.block.yml
@@ -0,0 +1,54 @@
+id: cdc_dsa_pu_scenario_cc
+label: 'CDC DSA PU Scenario'
+category: '[CDC]'
+
+templates:
+  imports: from gnuradio import cdc
+  make: cdc.dsa_pu_scenario_cc(${samp_rate}, ${duration_ms}, ${random}, ${seed}, ${scenario})
+  callbacks:
+  - set_scenario(${scenario})
+  - set_random(${random})
+  - set_duration_ms(${duration_ms})
+
+parameters:
+- id: num_channels
+  label: Num Channels
+  default: '1'
+  dtype: int
+- id: samp_rate
+  label: Samp rate
+  dtype: int
+  default: 'samp_rate'
+- id: duration_ms
+  label: Duration (ms)
+  dtype: float
+  default: 50.0
+- id: random
+  label: Randomize Scenario
+  dtype: raw
+  default: 'False'
+- id: seed
+  label: Seed
+  dtype: int
+  default: 1
+- id: scenario
+  label: Scenario
+  dtype: int
+  default: 0
+
+inputs:
+- label: in
+  domain: stream
+  dtype: complex
+  multiplicity: ${num_channels}
+
+outputs:
+- label: out
+  domain: stream
+  dtype: complex
+  multiplicity: ${num_channels}
+- label: mode
+  domain: message
+  optional: True
+
+file_format: 1
diff --git a/include/gnuradio/cdc/CMakeLists.txt b/include/gnuradio/cdc/CMakeLists.txt
index 09498fcc6592f3460af778b4616b54bbb5642519..91025a06f98fa23e2f0adeabeaa95dcf8916de8b 100644
--- a/include/gnuradio/cdc/CMakeLists.txt
+++ b/include/gnuradio/cdc/CMakeLists.txt
@@ -13,5 +13,6 @@ install(FILES
     api.h
     tag_tx_burst_cc.h
     preamble_detect_cc.h
-    insert_burst_c.h DESTINATION include/gnuradio/cdc
+    insert_burst_c.h
+    dsa_pu_scenario_cc.h DESTINATION include/gnuradio/cdc
 )
diff --git a/include/gnuradio/cdc/dsa_pu_scenario_cc.h b/include/gnuradio/cdc/dsa_pu_scenario_cc.h
new file mode 100644
index 0000000000000000000000000000000000000000..8805ac44fdf7affbf9f0c12a35c55fd87ad48228
--- /dev/null
+++ b/include/gnuradio/cdc/dsa_pu_scenario_cc.h
@@ -0,0 +1,69 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2023 University of Melbourne.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_CDC_DSA_PU_SCENARIO_CC_H
+#define INCLUDED_CDC_DSA_PU_SCENARIO_CC_H
+
+#include <gnuradio/cdc/api.h>
+#include <gnuradio/sync_block.h>
+
+namespace gr {
+namespace cdc {
+
+/*!
+ * \brief Implements primary user scenario control for CDC dynamic spectrum
+          access project.
+ * \ingroup cdc
+ *
+ */
+class CDC_API dsa_pu_scenario_cc : virtual public gr::sync_block
+{
+public:
+    typedef std::shared_ptr<dsa_pu_scenario_cc> sptr;
+
+    /*!
+    * \brief DSA PU Scenario Constructor
+    *
+    * \param samp_rate      Sample rate of incoming data streams
+    * \param duration_ms    Duration (in ms) of scenario before randomly
+    * \param random         Randomly select primary user scenario
+    * \param seed           Seed used in random scenario selection.
+    * \param scenario       Initial primary user scenario - bitmap of active
+    *                       channels, e.g., for 4 PU channels SCENARIO = 0b1010
+    *                       indicates channels 2 and 4 are active.
+    *                       selecting new scenario.
+    */
+    static sptr make(int samp_rate,
+                     float duration_ms,
+                     bool random,
+                     int seed = 0,
+                     int scenario = 0);
+    
+    //! Set primary user scenario
+    virtual void set_scenario(int scenario) = 0;
+
+    //! Get current primary user scenario
+    virtual int get_scenario(void) = 0;
+
+    //! Set random selection of primary user scenarios
+    virtual void set_random(bool random) = 0;
+
+    //! Get random selection of primary user scenarios
+    virtual bool get_random(void) = 0;
+    
+    //! Set duration (in ms) of random primary user scenarios
+    virtual void set_duration_ms(float duration_ms) = 0;
+
+    //! Get duration (in ms) of random primary user scenarios
+    virtual float get_duration_ms(void) = 0;
+
+};
+
+} // namespace cdc
+} // namespace gr
+
+#endif /* INCLUDED_CDC_DSA_PU_SCENARIO_CC_H */
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 9e28825fd259fd5d92832b89f61843e13c417630..0066e1d356f52a930796a404a695362df4d75b76 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -13,7 +13,8 @@ include(GrPlatform) #define LIB_SUFFIX
 list(APPEND cdc_sources
     tag_tx_burst_cc_impl.cc
     preamble_detect_cc_impl.cc
-    insert_burst_c_impl.cc )
+    insert_burst_c_impl.cc
+    dsa_pu_scenario_cc_impl.cc )
 
 set(cdc_sources "${cdc_sources}" PARENT_SCOPE)
 if(NOT cdc_sources)
diff --git a/lib/dsa_pu_scenario_cc_impl.cc b/lib/dsa_pu_scenario_cc_impl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c8e8ae51555cafc56b53d0cb2bb743c88073b5c1
--- /dev/null
+++ b/lib/dsa_pu_scenario_cc_impl.cc
@@ -0,0 +1,94 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2023 University of Melbourne.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <gnuradio/io_signature.h>
+#include "dsa_pu_scenario_cc_impl.h"
+
+namespace gr {
+namespace cdc {
+
+dsa_pu_scenario_cc::sptr
+dsa_pu_scenario_cc::make(int samp_rate,
+                         float duration_ms,
+                         bool random,
+                         int seed,
+                         int scenario)
+{
+    return gnuradio::make_block_sptr<dsa_pu_scenario_cc_impl>(
+        samp_rate, duration_ms, random, seed, scenario);
+}
+
+dsa_pu_scenario_cc_impl::dsa_pu_scenario_cc_impl(int samp_rate,
+                                                 float duration_ms,
+                                                 bool random,
+                                                 int seed,
+                                                 int scenario)
+    : gr::sync_block("dsa_pu_scenario_cc",
+          gr::io_signature::make(1, -1, sizeof(gr_complex)),
+          gr::io_signature::make(1, -1, sizeof(gr_complex))),
+      d_samp_rate(samp_rate),
+      d_duration_ms(duration_ms),
+      d_engine(seed),
+      d_random(random),
+      d_scenario(scenario)
+{
+    message_port_register_out(pmt::mp("mode"));
+}
+
+dsa_pu_scenario_cc_impl::~dsa_pu_scenario_cc_impl()
+{
+    if (d_uniform) delete d_uniform;
+}
+
+void
+dsa_pu_scenario_cc_impl::update_scenario(int n_chan)
+{
+    if (!d_active.size())
+    {
+        d_active.resize(n_chan);
+        int n_scenarios = (0x1 << n_chan);
+        d_uniform = new std::uniform_int_distribution<int>(0, n_scenarios - 1);
+    }
+
+    if (d_samp_cnt <= 0)
+    {
+        d_samp_cnt = int(d_duration_ms * d_samp_rate / 1000.0);
+        if (d_random)
+            d_scenario = (*d_uniform)(d_engine);
+        message_port_pub(pmt::mp("mode"), pmt::mp(d_scenario));
+    }
+
+    for (int i_chan=0; i_chan < n_chan; i_chan++)
+        d_active[i_chan] = d_scenario & (0x1 << i_chan);
+}
+
+int
+dsa_pu_scenario_cc_impl::work(int noutput_items,
+                              gr_vector_const_void_star &input_items,
+                              gr_vector_void_star &output_items)
+{
+    int n_chan = output_items.size();
+
+    update_scenario(n_chan);
+
+    int n_samps = (d_samp_cnt < noutput_items) ? d_samp_cnt : noutput_items;
+    for (int i_chan = 0; i_chan < n_chan; i_chan++)
+    {
+        gr_complex *in = (gr_complex *)input_items[i_chan];
+        gr_complex *out = (gr_complex *)output_items[i_chan];
+        if (d_active[i_chan])
+            memcpy(out, in, n_samps * sizeof(gr_complex));
+        else
+            memset(out, 0x0, n_samps * sizeof(gr_complex));
+    }
+    d_samp_cnt -= n_samps;
+
+    return n_samps;
+}
+
+} /* namespace cdc */
+} /* namespace gr */
diff --git a/lib/dsa_pu_scenario_cc_impl.h b/lib/dsa_pu_scenario_cc_impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..8dcd25aad3908e60e8f84c2dc488484eb2ef1605
--- /dev/null
+++ b/lib/dsa_pu_scenario_cc_impl.h
@@ -0,0 +1,62 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2023 University of Melbourne.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_CDC_DSA_PU_SCENARIO_CC_IMPL_H
+#define INCLUDED_CDC_DSA_PU_SCENARIO_CC_IMPL_H
+
+#include <gnuradio/cdc/dsa_pu_scenario_cc.h>
+#include <random>
+
+namespace gr {
+namespace cdc {
+
+class dsa_pu_scenario_cc_impl : public dsa_pu_scenario_cc
+{
+private:
+    int d_samp_rate;
+    float d_duration_ms;
+    bool d_random;
+    int d_scenario;
+
+    std::default_random_engine d_engine;
+    std::uniform_int_distribution<int>* d_uniform = nullptr;
+    std::vector<bool> d_active;
+    int d_samp_cnt = 0;
+
+    void update_scenario(int n_chan);
+
+public:
+    dsa_pu_scenario_cc_impl(int samp_rate,
+                            float duration_ms,
+                            bool random,
+                            int seed,
+                            int scenario);
+
+    ~dsa_pu_scenario_cc_impl();
+
+    void set_scenario(int scenario) { d_scenario = scenario; };
+    
+    int get_scenario(void) { return d_scenario; };
+    
+    void set_random(bool random) { d_random = random; };
+    
+    bool get_random(void) { return random; };
+    
+    void set_duration_ms(float duration_ms) { d_duration_ms = duration_ms; };
+    
+    float get_duration_ms(void) { return d_duration_ms; };
+
+    int work(int noutput_items,
+             gr_vector_const_void_star &input_items,
+             gr_vector_void_star &output_items);
+
+};
+
+} // namespace cdc
+} // namespace gr
+
+#endif /* INCLUDED_CDC_DSA_PU_SCENARIO_CC_IMPL_H */
diff --git a/python/cdc/bindings/CMakeLists.txt b/python/cdc/bindings/CMakeLists.txt
index 2e33c2239bc862793cbcd77ad96bdb885203d445..a955da9cde1118b906c2b700ba85fe85df10d797 100644
--- a/python/cdc/bindings/CMakeLists.txt
+++ b/python/cdc/bindings/CMakeLists.txt
@@ -30,7 +30,8 @@ include(GrPybind)
 list(APPEND cdc_python_files
     tag_tx_burst_cc_python.cc
     preamble_detect_cc_python.cc
-    insert_burst_c_python.cc python_bindings.cc)
+    insert_burst_c_python.cc
+    dsa_pu_scenario_cc_python.cc python_bindings.cc)
 
 GR_PYBIND_MAKE_OOT(cdc
    ../../..
diff --git a/python/cdc/bindings/docstrings/dsa_pu_scenario_cc_pydoc_template.h b/python/cdc/bindings/docstrings/dsa_pu_scenario_cc_pydoc_template.h
new file mode 100644
index 0000000000000000000000000000000000000000..5dad6ffef2da01e8c112d2ed9a58a3577b8df7ad
--- /dev/null
+++ b/python/cdc/bindings/docstrings/dsa_pu_scenario_cc_pydoc_template.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2023 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+#include "pydoc_macros.h"
+#define D(...) DOC(gr,cdc, __VA_ARGS__ )
+/*
+  This file contains placeholders for docstrings for the Python bindings.
+  Do not edit! These were automatically extracted during the binding process
+  and will be overwritten during the build process
+ */
+
+
+ 
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_dsa_pu_scenario_cc_0 = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_dsa_pu_scenario_cc_1 = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_make = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_set_scenario = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_get_scenario = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_set_random = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_get_random = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_set_duration_ms = R"doc()doc";
+
+
+ static const char *__doc_gr_cdc_dsa_pu_scenario_cc_get_duration_ms = R"doc()doc";
+
+  
diff --git a/python/cdc/bindings/dsa_pu_scenario_cc_python.cc b/python/cdc/bindings/dsa_pu_scenario_cc_python.cc
new file mode 100644
index 0000000000000000000000000000000000000000..bb3de020625e08874a358e583523966a46149efa
--- /dev/null
+++ b/python/cdc/bindings/dsa_pu_scenario_cc_python.cc
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2023 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+/***********************************************************************************/
+/* This file is automatically generated using bindtool and can be manually edited  */
+/* The following lines can be configured to regenerate this file during cmake      */
+/* If manual edits are made, the following tags should be modified accordingly.    */
+/* BINDTOOL_GEN_AUTOMATIC(0)                                                       */
+/* BINDTOOL_USE_PYGCCXML(0)                                                        */
+/* BINDTOOL_HEADER_FILE(dsa_pu_scenario_cc.h)                                        */
+/* BINDTOOL_HEADER_FILE_HASH(219f58bccfee90b520aae47f5b3325c2)                     */
+/***********************************************************************************/
+
+#include <pybind11/complex.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+namespace py = pybind11;
+
+#include <gnuradio/cdc/dsa_pu_scenario_cc.h>
+// pydoc.h is automatically generated in the build directory
+#include <dsa_pu_scenario_cc_pydoc.h>
+
+void bind_dsa_pu_scenario_cc(py::module& m)
+{
+
+    using dsa_pu_scenario_cc    = ::gr::cdc::dsa_pu_scenario_cc;
+
+
+    py::class_<dsa_pu_scenario_cc, gr::sync_block, gr::block, gr::basic_block,
+        std::shared_ptr<dsa_pu_scenario_cc>>(m, "dsa_pu_scenario_cc", D(dsa_pu_scenario_cc))
+
+        .def(py::init(&dsa_pu_scenario_cc::make),
+           py::arg("samp_rate"),
+           py::arg("duration_ms"),
+           py::arg("random"),
+           py::arg("seed") = 0,
+           py::arg("scenario") = 0,
+           D(dsa_pu_scenario_cc,make)
+        )
+        
+
+
+
+
+        
+        .def("set_scenario",&dsa_pu_scenario_cc::set_scenario,       
+            py::arg("scenario"),
+            D(dsa_pu_scenario_cc,set_scenario)
+        )
+
+
+        
+        .def("get_scenario",&dsa_pu_scenario_cc::get_scenario,       
+            D(dsa_pu_scenario_cc,get_scenario)
+        )
+
+
+        
+        .def("set_random",&dsa_pu_scenario_cc::set_random,       
+            py::arg("random"),
+            D(dsa_pu_scenario_cc,set_random)
+        )
+
+
+        
+        .def("get_random",&dsa_pu_scenario_cc::get_random,       
+            D(dsa_pu_scenario_cc,get_random)
+        )
+
+
+        
+        .def("set_duration_ms",&dsa_pu_scenario_cc::set_duration_ms,       
+            py::arg("duration_ms"),
+            D(dsa_pu_scenario_cc,set_duration_ms)
+        )
+
+
+        
+        .def("get_duration_ms",&dsa_pu_scenario_cc::get_duration_ms,       
+            D(dsa_pu_scenario_cc,get_duration_ms)
+        )
+
+        ;
+
+
+
+
+}
+
+
+
+
+
+
+
+
diff --git a/python/cdc/bindings/python_bindings.cc b/python/cdc/bindings/python_bindings.cc
index d11cd00ddf619a9fcbe904299916a1cc97ec4dfc..9f463592a128d75d3cf6e20c6b95d8a144917e29 100644
--- a/python/cdc/bindings/python_bindings.cc
+++ b/python/cdc/bindings/python_bindings.cc
@@ -24,6 +24,7 @@ namespace py = pybind11;
     void bind_tag_tx_burst_cc(py::module& m);
     void bind_preamble_detect_cc(py::module& m);
     void bind_insert_burst_c(py::module& m);
+    void bind_dsa_pu_scenario_cc(py::module& m);
 // ) END BINDING_FUNCTION_PROTOTYPES
 
 
@@ -55,5 +56,6 @@ PYBIND11_MODULE(cdc_python, m)
     bind_tag_tx_burst_cc(m);
     bind_preamble_detect_cc(m);
     bind_insert_burst_c(m);
+    bind_dsa_pu_scenario_cc(m);
     // ) END BINDING_FUNCTION_CALLS
 }
\ No newline at end of file