diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt
index ab1f4ef9a53ea89a3e3b4c1eebfce6cd76dee255..cd500415b647c169f6783d716d007a5bd76362b0 100644
--- a/grc/CMakeLists.txt
+++ b/grc/CMakeLists.txt
@@ -7,5 +7,7 @@
 #
 
 install(FILES
-    elen90089_corr_est_cc.block.yml DESTINATION share/gnuradio/grc/blocks
+    elen90089_corr_est_cc.block.yml
+    elen90089_moe_symbol_sync_cc.block.yml
+    DESTINATION share/gnuradio/grc/blocks
 )
diff --git a/grc/elen90089_moe_symbol_sync_cc.block.yml b/grc/elen90089_moe_symbol_sync_cc.block.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3d4f239eca6967e945dfeaf1adc091ee1ce769e6
--- /dev/null
+++ b/grc/elen90089_moe_symbol_sync_cc.block.yml
@@ -0,0 +1,31 @@
+id: elen90089_moe_symbol_sync_cc
+label: CDC MOE Symbol Sync
+category: '[elen90089]'
+
+templates:
+  imports: import elen90089
+  make: elen90089.moe_symbol_sync_cc(${sps}, ${nsymbols})
+
+parameters:
+- id: sps
+  label: Samples per symbol
+  dtype: int
+- id: nsymbols
+  label: Num symbol
+  dtype: int
+
+inputs:
+- label: in
+  domain: stream
+  dtype: complex
+
+outputs:
+- label: out0
+  domain: stream
+  dtype: complex
+- label: out1
+  domain: stream
+  dtype: int
+  optional: True
+
+file_format: 1
diff --git a/include/elen90089/CMakeLists.txt b/include/elen90089/CMakeLists.txt
index 1091fdada7bfb1c50398c98ebbfbee26a719471b..7ad10f5f6c1f20a95cda5262f0e84db3e3ec0866 100644
--- a/include/elen90089/CMakeLists.txt
+++ b/include/elen90089/CMakeLists.txt
@@ -12,5 +12,6 @@
 install(FILES
     api.h
     corr_est_cc.h
+    moe_symbol_sync_cc.h
     DESTINATION include/elen90089
 )
diff --git a/include/elen90089/moe_symbol_sync_cc.h b/include/elen90089/moe_symbol_sync_cc.h
new file mode 100644
index 0000000000000000000000000000000000000000..1fb5768bc76daa5ef7f9c0313e4fdd6560d3a6f2
--- /dev/null
+++ b/include/elen90089/moe_symbol_sync_cc.h
@@ -0,0 +1,50 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2022 University of Melbourne.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_ELEN90089_MOE_SYMBOL_SYNC_CC_H
+#define INCLUDED_ELEN90089_MOE_SYMBOL_SYNC_CC_H
+
+#include <elen90089/api.h>
+#include <gnuradio/sync_decimator.h>
+
+namespace gr {
+namespace elen90089 {
+
+/*!
+ * \brief Maximum output energy (MOE) symbol synchronization.
+ * \ingroup elen90089
+ *
+ * \details
+ * Input:
+ * \li Complex stream of symbols oversampled by factor of sps
+ *
+ * Output:
+ * \li Downsampled 
+ * \li Optional second output stream of ints
+ */
+class ELEN90089_API moe_symbol_sync_cc : virtual public gr::sync_decimator
+{
+public:
+    typedef std::shared_ptr<moe_symbol_sync_cc> sptr;
+
+    /*!
+     * \brief Make a block that selects \p sps samples per symbol period.
+     * 
+     * \param sps           Samples per symbol
+     * \param nsymbols      Symbols in averaging period
+     */
+    static sptr make(int sps,
+                     int nsymbols);
+
+    virtual int nsymbols() const = 0;
+    virtual void set_nsymbols(int nsymbols) = 0;
+};
+
+} // namespace elen90089
+} // namespace gr
+
+#endif /* INCLUDED_ELEN90089_MOE_SYMBOL_SYNC_CC_H */
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 4c6376a2a8f3d35df68aadcb8cee041ca7c9cc5d..f05d33b12649a0da5d038c2d595bb3478072f802 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -13,6 +13,7 @@ include(GrPlatform) #define LIB_SUFFIX
 
 list(APPEND elen90089_sources
     corr_est_cc_impl.cc
+    moe_symbol_sync_cc_impl.cc
 )
 
 set(elen90089_sources "${elen90089_sources}" PARENT_SCOPE)
diff --git a/lib/moe_symbol_sync_cc_impl.cc b/lib/moe_symbol_sync_cc_impl.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8cbea76602ae4d77e6773871190f20df73ba8b33
--- /dev/null
+++ b/lib/moe_symbol_sync_cc_impl.cc
@@ -0,0 +1,78 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2022 University of Melbourne.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <gnuradio/io_signature.h>
+#include "moe_symbol_sync_cc_impl.h"
+
+namespace gr {
+namespace elen90089 {
+
+moe_symbol_sync_cc::sptr moe_symbol_sync_cc::make(int sps,
+                                                  int nsymbols)
+{
+    return gnuradio::make_block_sptr<moe_symbol_sync_cc_impl>(sps, nsymbols);
+}
+
+moe_symbol_sync_cc_impl::moe_symbol_sync_cc_impl(int sps,
+                                                 int nsymbols)
+    : gr::sync_decimator("moe_symbol_sync_cc",
+                         gr::io_signature::make(1, 1, sizeof(gr_complex)),
+                         gr::io_signature::make2(1, 2, sizeof(gr_complex), sizeof(int)),
+                         sps),
+      d_nsymbols(nsymbols),
+      d_index(0),
+      d_mag_sq(sps, 0.0),
+      d_sum(sps, 0.0)
+{}
+
+moe_symbol_sync_cc_impl::~moe_symbol_sync_cc_impl() { }
+
+int moe_symbol_sync_cc_impl::nsymbols() const {
+    return d_nsymbols;
+}
+
+void moe_symbol_sync_cc_impl::set_nsymbols(int nsymbols) {
+    d_nsymbols = nsymbols;
+}
+
+int moe_symbol_sync_cc_impl::work(int noutput_items,
+                                  gr_vector_const_void_star &input_items,
+                                  gr_vector_void_star &output_items)
+{
+    auto in = static_cast<const gr_complex*>(input_items[0]);
+    auto out0 = static_cast<gr_complex*>(output_items[0]);
+
+    int *out1 = nullptr;
+    if (output_items.size() > 1)
+        out1 = static_cast<int*>(output_items[1]);
+
+    int sps = decimation();
+    auto mag_sq = d_mag_sq.data();
+    auto sum = d_sum.data();
+
+    int isymb = 0;
+    while(isymb < noutput_items) {
+        out0[isymb] = in[d_index];
+        if(out1 != nullptr)
+            out1[isymb] = static_cast<int>(d_index);
+
+        volk_32fc_magnitude_squared_32f(mag_sq, in, sps);
+        volk_32f_x2_add_32f(sum, sum, mag_sq, sps);
+
+        if (++isymb % d_nsymbols == 0) {
+            volk_32f_index_max_32u(&d_index, sum, sps);
+            std::fill(d_sum.begin(), d_sum.end(), 0.0);
+        }
+
+        in += sps; // increment input one symbol period
+    }
+
+    return noutput_items;
+}
+
+} /* namespace elen90089 */
+} /* namespace gr */
diff --git a/lib/moe_symbol_sync_cc_impl.h b/lib/moe_symbol_sync_cc_impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..b7785dfd6b760b1e04aac35e4f83eda13020600a
--- /dev/null
+++ b/lib/moe_symbol_sync_cc_impl.h
@@ -0,0 +1,42 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2022 University of Melbourne.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_ELEN90089_MOE_SYMBOL_SYNC_CC_IMPL_H
+#define INCLUDED_ELEN90089_MOE_SYMBOL_SYNC_CC_IMPL_H
+
+#include <elen90089/moe_symbol_sync_cc.h>
+#include <volk/volk_alloc.hh>
+
+namespace gr {
+namespace elen90089 {
+
+class moe_symbol_sync_cc_impl : public moe_symbol_sync_cc
+{
+private:
+    int d_nsymbols;
+    uint32_t d_index;
+    volk::vector<float> d_mag_sq;
+    volk::vector<float> d_sum;
+
+public:
+    moe_symbol_sync_cc_impl(int sps,
+                            int nsymbols);
+
+    ~moe_symbol_sync_cc_impl();
+
+    int nsymbols() const override;
+    void set_nsymbols(int nsymbols) override;
+
+    int work(int noutput_items,
+             gr_vector_const_void_star &input_items,
+             gr_vector_void_star &output_items);
+};
+
+} // namespace elen90089
+} // namespace gr
+
+#endif /* INCLUDED_ELEN90089_MOE_SYMBOL_SYNC_CC_IMPL_H */
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 9e056f9275e2e42d439e4bdcbb4e028d293b1b48..6c242766ff2cb44608eeda67c542ef988be61004 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -39,3 +39,5 @@ add_custom_target(
   COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}
           ${CMAKE_BINARY_DIR}/test_modules/elen90089/
 )
+GR_ADD_TEST(qa_corr_est_cc ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_corr_est_cc.py)
+GR_ADD_TEST(qa_moe_symbol_sync_cc ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_moe_symbol_sync_cc.py)
diff --git a/python/bindings/CMakeLists.txt b/python/bindings/CMakeLists.txt
index 3c360f53dc91f633be59d0ff3af5286ab08d87f6..9ed43664351ad1cc647674b233ebd94d58006b7e 100644
--- a/python/bindings/CMakeLists.txt
+++ b/python/bindings/CMakeLists.txt
@@ -29,7 +29,9 @@ include(GrPybind)
 ########################################################################
 
 list(APPEND elen90089_python_files
-    corr_est_cc_python.cc python_bindings.cc)
+    corr_est_cc_python.cc
+    moe_symbol_sync_cc_python.cc
+    python_bindings.cc)
 
 GR_PYBIND_MAKE_OOT(elen90089
    ../..
diff --git a/python/bindings/docstrings/moe_symbol_sync_cc_pydoc_template.h b/python/bindings/docstrings/moe_symbol_sync_cc_pydoc_template.h
new file mode 100644
index 0000000000000000000000000000000000000000..c318afbcdd57184b1d80b09fc15f67675b3d7fec
--- /dev/null
+++ b/python/bindings/docstrings/moe_symbol_sync_cc_pydoc_template.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 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,elen90089, __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_elen90089_moe_symbol_sync_cc = R"doc()doc";
+
+
+ static const char *__doc_gr_elen90089_moe_symbol_sync_cc_moe_symbol_sync_cc_0 = R"doc()doc";
+
+
+ static const char *__doc_gr_elen90089_moe_symbol_sync_cc_moe_symbol_sync_cc_1 = R"doc()doc";
+
+
+ static const char *__doc_gr_elen90089_moe_symbol_sync_cc_make = R"doc()doc";
+
+
+ static const char *__doc_gr_elen90089_moe_symbol_sync_cc_nsymbols = R"doc()doc";
+
+
+ static const char *__doc_gr_elen90089_moe_symbol_sync_cc_set_nsymbols = R"doc()doc";
+
+  
diff --git a/python/bindings/moe_symbol_sync_cc_python.cc b/python/bindings/moe_symbol_sync_cc_python.cc
new file mode 100644
index 0000000000000000000000000000000000000000..31a6bb3e1545b4aefbb5b72c923f12ccd4f40a93
--- /dev/null
+++ b/python/bindings/moe_symbol_sync_cc_python.cc
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 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(moe_symbol_sync_cc.h)                                        */
+/* BINDTOOL_HEADER_FILE_HASH(9795612b9fef9511155dcc9ea55123c1)                     */
+/***********************************************************************************/
+
+#include <pybind11/complex.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+namespace py = pybind11;
+
+#include <elen90089/moe_symbol_sync_cc.h>
+// pydoc.h is automatically generated in the build directory
+#include <moe_symbol_sync_cc_pydoc.h>
+
+void bind_moe_symbol_sync_cc(py::module& m)
+{
+
+    using moe_symbol_sync_cc    = ::gr::elen90089::moe_symbol_sync_cc;
+
+
+    py::class_<moe_symbol_sync_cc, gr::sync_decimator,
+        std::shared_ptr<moe_symbol_sync_cc>>(m, "moe_symbol_sync_cc", D(moe_symbol_sync_cc))
+
+        .def(py::init(&moe_symbol_sync_cc::make),
+           py::arg("sps"),
+           py::arg("nsymbols"),
+           D(moe_symbol_sync_cc,make)
+        )
+        
+
+
+
+
+        
+        .def("nsymbols",&moe_symbol_sync_cc::nsymbols,       
+            D(moe_symbol_sync_cc,nsymbols)
+        )
+
+
+        
+        .def("set_nsymbols",&moe_symbol_sync_cc::set_nsymbols,       
+            py::arg("nsymbols"),
+            D(moe_symbol_sync_cc,set_nsymbols)
+        )
+
+        ;
+
+
+
+
+}
+
+
+
+
+
+
+
+
diff --git a/python/bindings/python_bindings.cc b/python/bindings/python_bindings.cc
index f3152253fbc8e008943fb9c9d006597da61a4cce..fff3f49b3ae58c0bf1446a5b9bad7880c0d35c6b 100644
--- a/python/bindings/python_bindings.cc
+++ b/python/bindings/python_bindings.cc
@@ -22,6 +22,7 @@ namespace py = pybind11;
 /**************************************/
 // BINDING_FUNCTION_PROTOTYPES(
     void bind_corr_est_cc(py::module& m);
+    void bind_moe_symbol_sync_cc(py::module& m);
 // ) END BINDING_FUNCTION_PROTOTYPES
 
 
@@ -51,5 +52,6 @@ PYBIND11_MODULE(elen90089_python, m)
     /**************************************/
     // BINDING_FUNCTION_CALLS(
     bind_corr_est_cc(m);
+    bind_moe_symbol_sync_cc(m);
     // ) END BINDING_FUNCTION_CALLS
 }
\ No newline at end of file
diff --git a/python/qa_corr_est_cc.py b/python/qa_corr_est_cc.py
old mode 100644
new mode 100755
index d9bfa1fdef74197705265c988acddc5604ad8560..26c90685124dcc4a91471d8078494ef16ad6479c
--- a/python/qa_corr_est_cc.py
+++ b/python/qa_corr_est_cc.py
@@ -1,8 +1,23 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2022 University of Melbourne.
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
 from gnuradio import gr, gr_unittest
 from gnuradio import blocks
 import pmt
-import elen90089
 import numpy as np
+try:
+    from elen90089 import corr_est_cc
+except ImportError:
+    import os
+    import sys
+    dirname, filename = os.path.split(os.path.abspath(__file__))
+    sys.path.append(os.path.join(dirname, "bindings"))
+    from elen90089 import corr_est_cc
 
 
 class qa_corr_est_cc(gr_unittest.TestCase):
@@ -13,8 +28,8 @@ class qa_corr_est_cc(gr_unittest.TestCase):
     def tearDown(self):
         self.tb = None
 
-    def test_001(self):
-        """Test correlation result"""
+    def test_001_sequence_detection(self):
+        """Test basic sequence detection of correlator."""
         pad = (0,) * 16
         code = (1, 0, 1, 1, 1, 0, 1, 1)
         code_sym = [(2.0*x - 1.0) for x in code] 
@@ -22,7 +37,7 @@ class qa_corr_est_cc(gr_unittest.TestCase):
         src_data = [(2.0*x - 1.0) for x in src_bits]
         # build flowgraph
         src = blocks.vector_source_c(src_data)
-        cor = elen90089.corr_est_cc(code_sym, threshold=0.5)
+        cor = corr_est_cc(code_sym, threshold=0.5)
         dst = blocks.tag_debug(gr.sizeof_gr_complex, "", "corr_start")
         dst.set_display(False)
         self.tb.connect(src, cor, dst)
@@ -35,7 +50,7 @@ class qa_corr_est_cc(gr_unittest.TestCase):
         corr_mag = pmt.to_python(result_data[0].value)
         self.assertAlmostEqual(corr_mag, 1.0, places=6)
 
-    def test_002(self):
+    def test_002_frequency_estimate(self):
         """Test coarse frequency offset estimate."""
         phase_inc = 2*np.pi*0.02 # freq offset
         pad = (0,) * 16
@@ -46,7 +61,7 @@ class qa_corr_est_cc(gr_unittest.TestCase):
         # build flowgraph
         src = blocks.vector_source_c(src_data)
         rot = blocks.rotator_cc(phase_inc)
-        cor = elen90089.corr_est_cc(code_sym, threshold=0.5)
+        cor = corr_est_cc(code_sym, threshold=0.5)
         dst = blocks.tag_debug(gr.sizeof_gr_complex, "", "freq_est")
         dst.set_display(False)
         self.tb.connect(src, rot, cor, dst)
diff --git a/python/qa_moe_symbol_sync_cc.py b/python/qa_moe_symbol_sync_cc.py
new file mode 100755
index 0000000000000000000000000000000000000000..4c203347f4f64693dda680cc636c1dc961e5be08
--- /dev/null
+++ b/python/qa_moe_symbol_sync_cc.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2022 University of Melbourne.
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from gnuradio import gr, gr_unittest
+from gnuradio import blocks
+try:
+    from elen90089 import moe_symbol_sync_cc
+except ImportError:
+    import os
+    import sys
+    dirname, filename = os.path.split(os.path.abspath(__file__))
+    sys.path.append(os.path.join(dirname, "bindings"))
+    from elen90089 import moe_symbol_sync_cc
+
+class qa_moe_symbol_sync_cc(gr_unittest.TestCase):
+
+    def setUp(self):
+        self.tb = gr.top_block()
+
+    def tearDown(self):
+        self.tb = None
+
+    def test_001_moe_symbol_sync_cc(self):
+        sps, nsymbols = 4, 2
+        src_data = (1, 2, 3, 1,) * 4
+        expected_result = ((1+0j), (1+0j), (3+0j), (3+0j))
+        src = blocks.vector_source_c(src_data)
+        moe = moe_symbol_sync_cc(sps, nsymbols)
+        dst = blocks.vector_sink_c()
+        
+        self.tb.connect(src, moe, dst)
+        self.tb.run()
+        
+        actual_result = dst.data()
+        #print('expected_result', expected_result)
+        #print('actual_result', actual_result)
+        self.assertEqual(len(actual_result), len(src_data)//sps)
+        self.assertFloatTuplesAlmostEqual(expected_result, actual_result)
+
+if __name__ == '__main__':
+    gr_unittest.run(qa_moe_symbol_sync_cc)