diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/AUTHORS ns-3.41/AUTHORS
--- ns-3.40/AUTHORS	2023-09-27 13:30:37.522838800 -0700
+++ ns-3.41/AUTHORS	2024-02-09 14:44:07.717788700 -0800
@@ -27,10 +27,12 @@
 Pedro Bellotti (pedro177@gmail.com)
 Mehdi Benamor (mehdi.benamor@telecom-bretagne.eu)
 Apoorva Bhargava (apoorvabhargava13@gmail.com)
-Avakash Bhat(avakash261@gmail.com)
+Avakash Bhat (avakash261@gmail.com)
+Kavya Bhat (kavyabhat@gmail.com)
 Raj Bhattacharjea (raj.b@gatech.edu)
 Mathew Bielejeski (bielejeski1@llnl.gov)
 Timo Bingmann (timo.bingmann@student.kit.edu)
+Doug Blough (doug.blough@gatech.edu)
 Julien Boite (juboite@gmail.com)
 Biljana Bojovic (bbojovic@cttc.es)
 Elena Borovkova (borokovaes@iitp.ru)
@@ -42,6 +44,7 @@
 Jonathan Brugge (j.d.brugge@student.utwente.nl)
 Junling Bu (linlinjavaer@gmail.com)
 Elena Buchatskaia (borovkovaes@iitp.ru)
+Alessio Bugetti (alessiobugetti98@gmail.com)
 Nuno Cardoso (nunopcardoso@gmail.com)
 Gustavo Carneiro (gjc@inescporto.pt, gjcarneiro@gmail.com)
 Scott Carpenter (scarpen@ncsu.edu)
@@ -160,6 +163,7 @@
 Alexander Krotov (ilabdsf@yandex.ru)
 Deepak Kumaraswamy (deepakkavoor99@gmail.com)
 Mathieu Lacage (mathieu.lacage@inria.fr)
+Andrea Lacava (thecave003@gmail.com)
 Sandra Lagen (sandra.lagen@cttc.es)
 Emmanuelle Laprise (emmmanuelle.laprise@bluekazoo.ca)
 Harsh Lara (harshapplefan@gmail.com)
@@ -181,6 +185,7 @@
 Antti Makela (zarhan@cc.hut.fi)
 Francesco Malandrino (francesco.malandrino@gmail.com)
 Rubén Martínez (rmartinez@deic.uab.cat)
+Chaz Maschman
 Ryan Mast (mast9@llnl.gov)
 Fabian Mauchle (f1mauchl@hsr.ch)
 Mauriyin (haoyin@uw.edu)
@@ -188,6 +193,7 @@
 Jonathan McCrohan (jmccroha@tcd.ie)
 Andrew McGregor (andrewmcgr@gmail.com)
 Miralem Mehic (miralemmehic@gmail.com)
+Levente Mészáros (levy@omnetpp.org)
 Vedran Miletić (rivanvx@gmail.com)
 Saswat Mishra (clicksaswat@gmail.com)
 Jens Mittag (jens.mittag@kit.edu)
@@ -242,6 +248,7 @@
 Anna Poon (poon2@llnl.gov)
 Gauri Prasad
 Vikas Pushkar (vikaskupushkar@gmail.com)
+Martin Quinson (mquinson@debian.org)
 Philipp Raich (philipp.raich@tuwien.ac.at)
 Alberto Gallegos Ramonet (alramonet@is.tokushima-u.ac.jp)
 Manoj Kumar Rana (manoj24.rana@gmail.com)
@@ -327,6 +334,7 @@
 He Wu (mdzz@u.washington.edu)
 Xiuchao Wu (xiuchao@amazon.com)
 Aron Wussler (aron@wussler.it)
+Heran Yang (heran55@126.com)
 Yoshihiko Yazawa (yoshiyaz@gmail.com)
 Menglei Zhang (menglei@nyu.edu)
 Theodore Zhang (harryzwh@gmail.com)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/bindings/python/ns__init__.py ns-3.41/bindings/python/ns__init__.py
--- ns-3.40/bindings/python/ns__init__.py	2023-09-27 13:30:37.526838800 -0700
+++ ns-3.41/bindings/python/ns__init__.py	2024-02-09 14:44:07.721789000 -0800
@@ -1,10 +1,10 @@
 import builtins
-from functools import lru_cache
 import glob
 import os.path
+import re
 import sys
 import sysconfig
-import re
+from functools import lru_cache
 
 DEFAULT_INCLUDE_DIR = sysconfig.get_config_var("INCLUDEDIR")
 DEFAULT_LIB_DIR = sysconfig.get_config_var("LIBDIR")
@@ -14,7 +14,7 @@
     # Get the absolute path to this file
     path_to_this_init_file = os.path.dirname(os.path.abspath(__file__))
     path_to_lock = path_to_this_init_file
-    lock_file = (".lock-ns3_%s_build" % sys.platform)
+    lock_file = ".lock-ns3_%s_build" % sys.platform
 
     # Move upwards until we reach the directory with the ns3 script
     prev_path = None
@@ -32,15 +32,17 @@
     return path_to_lock
 
 
-SYSTEM_LIBRARY_DIRECTORIES = (DEFAULT_LIB_DIR,
-                              os.path.dirname(DEFAULT_LIB_DIR),
-                              "/usr/lib64",
-                              "/usr/lib"
-                              )
-DYNAMIC_LIBRARY_EXTENSIONS = {"linux": "so",
-                              "win32": "dll",
-                              "darwin": "dylib"
-                              }
+SYSTEM_LIBRARY_DIRECTORIES = (
+    DEFAULT_LIB_DIR,
+    os.path.dirname(DEFAULT_LIB_DIR),
+    "/usr/lib64",
+    "/usr/lib",
+)
+DYNAMIC_LIBRARY_EXTENSIONS = {
+    "linux": "so",
+    "win32": "dll",
+    "darwin": "dylib",
+}
 LIBRARY_EXTENSION = DYNAMIC_LIBRARY_EXTENSIONS[sys.platform]
 
 
@@ -76,8 +78,9 @@
     library_search_paths += [os.path.dirname(library_search_paths[-1])]
 
     # Filter unique search paths and those that are not part of system directories
-    library_search_paths = list(filter(lambda x: x not in SYSTEM_LIBRARY_DIRECTORIES,
-                                       set(library_search_paths)))
+    library_search_paths = list(
+        filter(lambda x: x not in SYSTEM_LIBRARY_DIRECTORIES, set(library_search_paths))
+    )
 
     # Search for the core library in the search paths
     libraries = []
@@ -88,7 +91,9 @@
     # Search system library directories (too slow for recursive search)
     for search_path in SYSTEM_LIBRARY_DIRECTORIES:
         if os.path.exists(search_path):
-            libraries += glob.glob("%s/**/*.%s*" % (search_path, LIBRARY_EXTENSION), recursive=False)
+            libraries += glob.glob(
+                "%s/**/*.%s*" % (search_path, LIBRARY_EXTENSION), recursive=False
+            )
             libraries += glob.glob("%s/*.%s*" % (search_path, LIBRARY_EXTENSION), recursive=False)
 
     del search_path, library_search_paths
@@ -106,7 +111,7 @@
         library_map[library_infix].add(library)
 
     # Replace sets with lists
-    for (key, values) in library_map.items():
+    for key, values in library_map.items():
         library_map[key] = list(values)
     return library_map
 
@@ -128,7 +133,7 @@
     "libxml2": ["HAVE_LIBXML2"],
     "libsqlite3": ["HAVE_SQLITE3"],
     "openflow": ["NS3_OPENFLOW", "ENABLE_OPENFLOW"],
-    "click": ["NS3_CLICK"]
+    "click": ["NS3_CLICK"],
 }
 
 
@@ -137,11 +142,11 @@
     defines = ""
     if len(has_defines):
         for define in LIBRARY_AND_DEFINES[has_defines[0]]:
-            defines += (f"""
+            defines += f"""
                     #ifndef {define}
                     #define {define} 1
                     #endif
-                """)
+                """
     return defines
 
 
@@ -156,10 +161,11 @@
     # First discover which 3rd-party libraries are used by the current module
     try:
         with open(os.path.abspath(library_path), "rb") as f:
-            linked_libs = re.findall(b"\x00(lib.*?.%b)" % LIBRARY_EXTENSION.encode("utf-8"), f.read())
+            linked_libs = re.findall(
+                b"\x00(lib.*?.%b)" % LIBRARY_EXTENSION.encode("utf-8"), f.read()
+            )
     except Exception as e:
-        print("Failed to extract libraries used by {library} with exception:{exception}"
-              .format(library=library_path, exception=e))
+        print(f"Failed to extract libraries used by {library_path} with exception:{e}")
         exit(-1)
     return library_path, lib, list(map(lambda x: x.decode("utf-8"), linked_libs))
 
@@ -182,8 +188,8 @@
         # Raise error in case the library can't be found
         if len(linked_library_path) == 0:
             raise Exception(
-                "Failed to find {library}. Make sure its library directory is in LD_LIBRARY_PATH.".format(
-                    library=linked_library))
+                f"Failed to find {linked_library}. Make sure its library directory is in LD_LIBRARY_PATH."
+            )
 
         # Get path with the shortest length
         linked_library_path = sorted(linked_library_path, key=lambda x: len(x))[0]
@@ -201,7 +207,9 @@
             linked_libs_include_dirs.add(system_include_dir)
 
             # If system_include_dir/library_name exists, we add it too
-            linked_library_name = linked_library.replace("lib", "").replace("." + LIBRARY_EXTENSION, "")
+            linked_library_name = linked_library.replace("lib", "").replace(
+                "." + LIBRARY_EXTENSION, ""
+            )
             if os.path.exists(os.path.join(system_include_dir, linked_library_name)):
                 linked_libs_include_dirs.add(os.path.join(system_include_dir, linked_library_name))
 
@@ -232,19 +240,26 @@
     suffix = "-" + values["BUILD_PROFILE"] if values["BUILD_PROFILE"] != "release" else ""
     modules = [module.replace("ns3-", "") for module in values["NS3_ENABLED_MODULES"]]
     prefix = values["out_dir"]
-    libraries = {os.path.splitext(os.path.basename(x))[0]: x for x in os.listdir(os.path.join(prefix, "lib"))}
+    libraries = {
+        os.path.splitext(os.path.basename(x))[0]: x for x in os.listdir(os.path.join(prefix, "lib"))
+    }
     version = values["VERSION"]
 
     # Filter out test libraries and incorrect versions
-    def filter_in_matching_ns3_libraries(libraries_to_filter: dict,
-                                         modules_to_filter: list,
-                                         version: str,
-                                         suffix: str) -> dict:
+    def filter_in_matching_ns3_libraries(
+        libraries_to_filter: dict,
+        modules_to_filter: list,
+        version: str,
+        suffix: str,
+    ) -> dict:
         suffix = [suffix[1:]] if len(suffix) > 1 else []
         filtered_in_modules = []
         for module in modules_to_filter:
-            filtered_in_modules += list(filter(lambda x: "-".join([version, module, *suffix]) in x,
-                                               libraries_to_filter.keys()))
+            filtered_in_modules += list(
+                filter(
+                    lambda x: "-".join([version, module, *suffix]) in x, libraries_to_filter.keys()
+                )
+            )
         for library in list(libraries_to_filter.keys()):
             if library not in filtered_in_modules:
                 libraries_to_filter.pop(library)
@@ -255,15 +270,11 @@
     # When we have the lock file, we assemble the correct library names
     libraries_to_load = []
     for module in modules:
-        library_name = "libns{version}-{module}{suffix}".format(
-            version=version,
-            module=module,
-            suffix=suffix
-        )
+        library_name = f"libns{version}-{module}{suffix}"
         if library_name not in libraries:
-            raise Exception("Missing library %s\n" % library_name,
-                            "Build all modules with './ns3 build'"
-                            )
+            raise Exception(
+                f"Missing library {library_name}\n", "Build all modules with './ns3 build'"
+            )
         libraries_to_load.append(libraries[library_name])
     return prefix, libraries_to_load, version
 
@@ -282,7 +293,14 @@
         components.pop(0)
 
     # Drop build profile suffix and test libraries
-    if components[-1] in ["debug", "default", "optimized", "release", "relwithdebinfo", "minsizerel"]:
+    if components[-1] in [
+        "debug",
+        "default",
+        "optimized",
+        "release",
+        "relwithdebinfo",
+        "minsizerel",
+    ]:
         components.pop(-1)
     return "-".join(components)
 
@@ -337,10 +355,14 @@
         # Filter out older ns-3 libraries
         for module in list(modules_to_filter):
             # Filter duplicates of modules, while excluding test libraries
-            conflicting_libraries = list(filter(lambda x: module == filter_module_name(x), libraries_to_filter))
+            conflicting_libraries = list(
+                filter(lambda x: module == filter_module_name(x), libraries_to_filter)
+            )
 
             # Extract versions from conflicting libraries
-            conflicting_libraries_versions = list(map(lambda x: extract_version(x, module), conflicting_libraries))
+            conflicting_libraries_versions = list(
+                map(lambda x: extract_version(x, module), conflicting_libraries)
+            )
 
             # Get the newest version found for that library
             newest_version = get_newest_version(conflicting_libraries_versions)
@@ -351,8 +373,9 @@
             else:
                 newest_version_found = get_newest_version([newest_version, newest_version_found])
                 if newest_version != newest_version_found:
-                    raise Exception("Incompatible versions of the ns-3 module '%s' were found: %s != %s."
-                                    % (module, newest_version, newest_version_found))
+                    raise Exception(
+                        f"Incompatible versions of the ns-3 module '{module}' were found: {newest_version} != {newest_version_found}."
+                    )
 
             for conflicting_library in list(conflicting_libraries):
                 if "-".join([newest_version, module]) not in conflicting_library:
@@ -360,8 +383,9 @@
                     conflicting_libraries.remove(conflicting_library)
 
             if len(conflicting_libraries) > 1:
-                raise Exception("There are multiple build profiles for module '%s'.\nDelete one to continue: %s"
-                                % (module, ", ".join(conflicting_libraries)))
+                raise Exception(
+                    f"There are multiple build profiles for module '{module}'.\nDelete one to continue: {', '.join(conflicting_libraries)}"
+                )
 
         return libraries_to_filter, newest_version_found
 
@@ -387,7 +411,9 @@
         libraries = list(map(lambda x: os.path.basename(x), libraries))
         for ns3_library in libraries:
             _, _, linked_libraries = extract_linked_libraries(ns3_library, prefix)
-            linked_libraries = list(filter(lambda x: "libns3" in x and ns3_library not in x, linked_libraries))
+            linked_libraries = list(
+                filter(lambda x: "libns3" in x and ns3_library not in x, linked_libraries)
+            )
             linked_libraries = list(map(lambda x: os.path.basename(x), linked_libraries))
             module_dependencies[os.path.basename(ns3_library)] = linked_libraries
 
@@ -404,19 +430,27 @@
                 modules.append(pending_module)
             return modules
 
-        def dependency_order(module_dependencies, pending_modules, current_modules, step_number=0, steps={}):
+        def dependency_order(
+            module_dependencies, pending_modules, current_modules, step_number=0, steps={}
+        ):
             if len(pending_modules) == 0:
                 return steps
             if step_number not in steps:
                 steps[step_number] = []
-            for module in modules_that_can_be_loaded(module_dependencies, pending_modules, current_modules):
+            for module in modules_that_can_be_loaded(
+                module_dependencies, pending_modules, current_modules
+            ):
                 steps[step_number].append(module)
                 pending_modules.remove(module)
                 current_modules.append(module)
-            return dependency_order(module_dependencies, pending_modules, current_modules, step_number + 1, steps)
+            return dependency_order(
+                module_dependencies, pending_modules, current_modules, step_number + 1, steps
+            )
 
         sorted_libraries = []
-        for step in dependency_order(module_dependencies, list(module_dependencies.keys()), [], 0).values():
+        for step in dependency_order(
+            module_dependencies, list(module_dependencies.keys()), [], 0
+        ).values():
             sorted_libraries.extend(step)
         return sorted_libraries
 
@@ -441,7 +475,8 @@
 
     # Register Ptr<> as a smart pointer
     import libcppyy
-    libcppyy.AddSmartPtrType('Ptr')
+
+    libcppyy.AddSmartPtrType("Ptr")
 
     # Import ns-3 libraries
     for variant in ["lib", "lib64"]:
@@ -479,7 +514,8 @@
         setattr(cppyy.gbl.ns3, module.replace("-", "_"), cppyy.gbl.ns3)
 
     # Set up a few tricks
-    cppyy.cppdef("""
+    cppyy.cppdef(
+        """
         using namespace ns3;
         bool Time_ge(Time& a, Time& b){ return a >= b;}
         bool Time_eq(Time& a, Time& b){ return a == b;}
@@ -487,7 +523,8 @@
         bool Time_le(Time& a, Time& b){ return a <= b;}
         bool Time_gt(Time& a, Time& b){ return a > b;}
         bool Time_lt(Time& a, Time& b){ return a < b;}
-    """)
+    """
+    )
     cppyy.gbl.ns3.Time.__ge__ = cppyy.gbl.Time_ge
     cppyy.gbl.ns3.Time.__eq__ = cppyy.gbl.Time_eq
     cppyy.gbl.ns3.Time.__ne__ = cppyy.gbl.Time_ne
@@ -508,7 +545,8 @@
 
     cppyy.gbl.ns3.Node.__del__ = Node_del
 
-    cppyy.cppdef("""
+    cppyy.cppdef(
+        """
         using namespace ns3;
         std::tuple<bool, TypeId> LookupByNameFailSafe(std::string name)
         {
@@ -516,44 +554,49 @@
             bool ok = TypeId::LookupByNameFailSafe(name, &id);
             return std::make_tuple(ok, id);
         }
-    """)
+    """
+    )
     setattr(cppyy.gbl.ns3, "LookupByNameFailSafe", cppyy.gbl.LookupByNameFailSafe)
 
     def CreateObject(className):
         try:
             try:
-                func = "CreateObject%s" % re.sub('[<|>]', '_', className)
+                func = "CreateObject%s" % re.sub("[<|>]", "_", className)
                 return getattr(cppyy.gbl, func)()
             except AttributeError:
                 pass
             try:
-                func = "Create%s" % re.sub('[<|>]', '_', className)
+                func = "Create%s" % re.sub("[<|>]", "_", className)
                 return getattr(cppyy.gbl, func)()
             except AttributeError:
                 pass
             raise AttributeError
         except AttributeError:
             try:
-                func = "CreateObject%s" % re.sub('[<|>]', '_', className)
-                cppyy.cppdef("""
+                func = "CreateObject%s" % re.sub("[<|>]", "_", className)
+                cppyy.cppdef(
+                    """
                             using namespace ns3;
                             Ptr<%s> %s(){
                                 Ptr<%s> object = CreateObject<%s>();
                                 return object;
                             }
-                            """ % (className, func, className, className)
-                             )
+                            """
+                    % (className, func, className, className)
+                )
             except Exception as e:
                 try:
-                    func = "Create%s" % re.sub('[<|>]', '_', className)
-                    cppyy.cppdef("""
+                    func = "Create%s" % re.sub("[<|>]", "_", className)
+                    cppyy.cppdef(
+                        """
                                 using namespace ns3;
                                 %s %s(){
                                     %s object = %s();
                                     return object;
                                 }
-                                """ % (className, func, className, className)
-                                 )
+                                """
+                        % (className, func, className, className)
+                    )
                 except Exception as e:
                     exit(-1)
         return getattr(cppyy.gbl, func)()
@@ -582,10 +625,12 @@
                {
                     return parentPtr->GetObject<%s>();
                }
-            """ % (aggregatedType, aggregatedType, aggregatedType, aggregatedType)
+            """
+            % (aggregatedType, aggregatedType, aggregatedType, aggregatedType)
+        )
+        return cppyy.gbl.getAggregatedObject(
+            parentObject, aggregatedObject if aggregatedIsClass else aggregatedObject.__class__
         )
-        return cppyy.gbl.getAggregatedObject(parentObject,
-                                             aggregatedObject if aggregatedIsClass else aggregatedObject.__class__)
 
     setattr(cppyy.gbl.ns3, "GetObject", GetObject)
     return cppyy.gbl.ns3
@@ -593,4 +638,4 @@
 
 # Load all modules and make them available via a built-in
 ns = load_modules()  # can be imported via 'from ns import ns'
-builtins.__dict__['ns'] = ns  # or be made widely available with 'from ns import *'
+builtins.__dict__["ns"] = ns  # or be made widely available with 'from ns import *'
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/3rd-party/colored-messages.cmake ns-3.41/build-support/3rd-party/colored-messages.cmake
--- ns-3.40/build-support/3rd-party/colored-messages.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/3rd-party/colored-messages.cmake	2024-02-09 14:44:07.721789000 -0800
@@ -24,21 +24,21 @@
 # cmake-format: on
 
 # Set empty values for colors
-set(ColourReset)
-set(Red)
-set(Green)
-set(Yellow)
-set(Blue)
-set(Magenta)
-set(Cyan)
-set(White)
-set(BoldRed)
-set(BoldGreen)
-set(BoldYellow)
-set(BoldBlue)
-set(BoldMagenta)
-set(BoldCyan)
-set(BoldWhite)
+set(ColourReset "")
+set(Red "")
+set(Green "")
+set(Yellow "")
+set(Blue "")
+set(Magenta "")
+set(Cyan "")
+set(White "")
+set(BoldRed "")
+set(BoldGreen "")
+set(BoldYellow "")
+set(BoldBlue "")
+set(BoldMagenta "")
+set(BoldCyan "")
+set(BoldWhite "")
 
 # Custom message types fallback when not colorized
 set(HIGHLIGHTED_STATUS STATUS)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/3rd-party/find-program-hints.cmake ns-3.41/build-support/3rd-party/find-program-hints.cmake
--- ns-3.40/build-support/3rd-party/find-program-hints.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/3rd-party/find-program-hints.cmake	2024-02-09 14:44:07.721789000 -0800
@@ -1,3 +1,4 @@
+disable_cmake_warnings()
 set(3RD_PARTY_FIND_PROGRAM_HINTS
     # find_program HINTS for Doxygen
     # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake
@@ -18,3 +19,4 @@
     /Applications/Graphviz.app/Contents/MacOS
     /Applications/Utilities/Graphviz.app/Contents/MacOS
 )
+enable_cmake_warnings()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/cmake-format-modules.yaml ns-3.41/build-support/cmake-format-modules.yaml
--- ns-3.40/build-support/cmake-format-modules.yaml	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/cmake-format-modules.yaml	2024-02-09 14:44:07.721789000 -0800
@@ -44,6 +44,7 @@
             DEPENDENCY_NAME : '1'
             HEADER_NAME : '1'
             LIBRARY_NAME : '1'
+            OUTPUT_VARIABLE : '1'
             HEADER_NAMES : '*'
             LIBRARY_NAMES : '*'
             PATH_SUFFIXES : '*'
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/cmake-format.yaml ns-3.41/build-support/cmake-format.yaml
--- ns-3.40/build-support/cmake-format.yaml	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/cmake-format.yaml	2024-02-09 14:44:07.721789000 -0800
@@ -44,10 +44,18 @@
             DEPENDENCY_NAME : '1'
             HEADER_NAME : '1'
             LIBRARY_NAME : '1'
+            OUTPUT_VARIABLE : '1'
             HEADER_NAMES : '*'
             LIBRARY_NAMES : '*'
             PATH_SUFFIXES : '*'
             SEARCH_PATHS : '*'
+    check_deps:
+        pargs:
+            nargs: 1
+        kwargs:
+            CMAKE_PACKAGES : '*'
+            EXECUTABLES : '*'
+            PYTHON_PACKAGES : '*'
 
 format:
   tab_size: 2
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/compiler-workarounds/ostream-operator-nullptr.h ns-3.41/build-support/compiler-workarounds/ostream-operator-nullptr.h
--- ns-3.40/build-support/compiler-workarounds/ostream-operator-nullptr.h	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/compiler-workarounds/ostream-operator-nullptr.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,16 +0,0 @@
-#ifndef OSTREAM_OPERATOR_NULLPTR_H
-#define OSTREAM_OPERATOR_NULLPTR_H
-
-#include <cstddef>
-#include <iostream>
-
-namespace std
-{
-inline std::ostream&
-operator<<(std::ostream& os, std::nullptr_t)
-{
-    return os << "nullptr"; // whatever you want nullptr to show up as in the console
-}
-} // namespace std
-
-#endif // OSTREAM_OPERATOR_NULLPTR_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-check-dependencies.cmake ns-3.41/build-support/custom-modules/ns3-check-dependencies.cmake
--- ns-3.40/build-support/custom-modules/ns3-check-dependencies.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-check-dependencies.cmake	2024-02-09 14:44:07.721789000 -0800
@@ -0,0 +1,59 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# function used to search for package and program dependencies than store list
+# of missing dependencies in the list whose name is stored in missing_deps
+function(check_deps missing_deps)
+  set(multiValueArgs CMAKE_PACKAGES EXECUTABLES PYTHON_PACKAGES)
+  cmake_parse_arguments("DEPS" "" "" "${multiValueArgs}" ${ARGN})
+
+  set(local_missing_deps)
+  # Search for package dependencies
+  foreach(package ${DEPS_CMAKE_PACKAGES})
+    find_package(${package})
+    if(NOT ${${package}_FOUND})
+      list(APPEND local_missing_deps ${package})
+    endif()
+  endforeach()
+
+  # And for program dependencies
+  foreach(program ${DEPS_EXECUTABLES})
+    # CMake likes to cache find_* to speed things up, so we can't reuse names
+    # here or it won't check other dependencies
+    string(TOUPPER ${program} upper_${program})
+    mark_as_advanced(${upper_${program}})
+    find_program(
+      ${upper_${program}} ${program} HINTS ${3RD_PARTY_FIND_PROGRAM_HINTS}
+    )
+    if("${${upper_${program}}}" STREQUAL "${upper_${program}}-NOTFOUND")
+      list(APPEND local_missing_deps ${program})
+    endif()
+  endforeach()
+
+  foreach(package ${DEPS_PYTHON_PACKAGES})
+    execute_process(
+      COMMAND ${Python3_EXECUTABLE} -c "import ${package}"
+      RESULT_VARIABLE return_code OUTPUT_QUIET ERROR_QUIET
+    )
+    if(NOT (${return_code} EQUAL 0))
+      list(APPEND local_missing_deps ${package})
+    endif()
+  endforeach()
+
+  # Store list of missing dependencies in the parent scope
+  set(${missing_deps} ${local_missing_deps} PARENT_SCOPE)
+endfunction()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-cmake-package.cmake ns-3.41/build-support/custom-modules/ns3-cmake-package.cmake
--- ns-3.40/build-support/custom-modules/ns3-cmake-package.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/custom-modules/ns3-cmake-package.cmake	2024-02-09 14:44:07.721789000 -0800
@@ -15,6 +15,19 @@
 #
 # Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
 
+# Set RPATH not too need LD_LIBRARY_PATH after installing. Add the lib64 variant
+# to support all platforms, such as alma linux, used to build the manylinux pip
+# wheel.
+set(CMAKE_INSTALL_RPATH
+    "${CMAKE_INSTALL_PREFIX}/lib:$ORIGIN/:$ORIGIN/../lib:${CMAKE_INSTALL_PREFIX}/lib64:$ORIGIN/:$ORIGIN/../lib64"
+)
+
+# cmake-format: off
+# You are a wizard, Harry!
+# source: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling
+# cmake-format: on
+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
+
 function(build_required_and_libs_lists module_name visibility libraries
          all_ns3_libraries
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-compiler-and-linker-support.cmake ns-3.41/build-support/custom-modules/ns3-compiler-and-linker-support.cmake
--- ns-3.40/build-support/custom-modules/ns3-compiler-and-linker-support.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-compiler-and-linker-support.cmake	2024-02-09 14:44:07.721789000 -0800
@@ -0,0 +1,128 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# Compiler-specific version checks and flag/feature setting should be done here
+
+# Identify compiler and check version
+set(below_minimum_msg "compiler is below the minimum required version")
+set(CLANG FALSE)
+if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang")
+  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${AppleClang_MinVersion})
+    message(
+      FATAL_ERROR
+        "Apple Clang ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${AppleClang_MinVersion}"
+    )
+  endif()
+  set(CLANG TRUE)
+endif()
+
+if((NOT CLANG) AND ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
+  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${Clang_MinVersion})
+    message(
+      FATAL_ERROR
+        "Clang ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${Clang_MinVersion}"
+    )
+  endif()
+  set(CLANG TRUE)
+endif()
+
+if(CLANG)
+  if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}")
+    add_definitions(-fcolor-diagnostics) # colorize clang++ output
+  endif()
+endif()
+
+set(GCC FALSE)
+if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
+  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${GNU_MinVersion})
+    message(
+      FATAL_ERROR
+        "GNU ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${GNU_MinVersion}"
+    )
+  endif()
+  if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.2.0"))
+    # PCH causes weird errors on certain versions of GCC when C++20 is enabled
+    # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106799
+    set(NS3_PRECOMPILE_HEADERS OFF
+        CACHE BOOL "Precompile module headers to speed up compilation" FORCE
+    )
+  endif()
+  if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12.1.0")
+     AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.3.2")
+  )
+    # Disable warnings-as-errors for certain versions of GCC when C++20 is
+    # enabled https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105545
+    add_compile_options(-Wno-restrict)
+  endif()
+  set(GCC TRUE)
+  add_definitions(-fno-semantic-interposition)
+  if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}")
+    add_definitions(-fdiagnostics-color=always) # colorize g++ output
+  endif()
+endif()
+unset(below_minimum_msg)
+
+set(LIB_AS_NEEDED_PRE "")
+set(LIB_AS_NEEDED_POST "")
+set(STATIC_LINK_FLAGS -static -static-libstdc++ -static-libgcc)
+if(${GCC} AND NOT APPLE)
+  # using GCC
+  set(LIB_AS_NEEDED_PRE -Wl,--no-as-needed)
+  set(LIB_AS_NEEDED_POST -Wl,--as-needed)
+  set(LIB_AS_NEEDED_PRE_STATIC -Wl,--whole-archive,-Bstatic)
+  set(LIB_AS_NEEDED_POST_STATIC -Wl,--no-whole-archive)
+  set(LIB_AS_NEEDED_POST_STATIC_DYN -Wl,-Bdynamic,--no-whole-archive)
+endif()
+
+if(${CLANG} AND APPLE)
+  # using Clang set(LIB_AS_NEEDED_PRE -all_load)
+  set(LIB_AS_NEEDED_POST "")
+  set(LIB_AS_NEEDED_PRE_STATIC -Wl,-all_load)
+  set(STATIC_LINK_FLAGS "")
+endif()
+
+if(${NS3_FAST_LINKERS})
+  # Search for faster linkers mold and lld, and use them if available
+  mark_as_advanced(MOLD LLD)
+  find_program(MOLD mold)
+  find_program(LLD ld.lld)
+
+  # USING_FAST_LINKER will be defined if a fast linker is being used and its
+  # content will correspond to the fast linker name
+
+  # Mold support was added in GCC 12.1.0
+  if(NOT USING_FAST_LINKER
+     AND NOT (${MOLD} STREQUAL "MOLD-NOTFOUND")
+     AND LINUX
+     AND ${GCC}
+     AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)
+  )
+    set(USING_FAST_LINKER MOLD)
+    add_link_options("-fuse-ld=mold")
+  endif()
+
+  if(NOT USING_FAST_LINKER AND NOT (${LLD} STREQUAL "LLD-NOTFOUND")
+     AND (${GCC} OR ${CLANG})
+  )
+    set(USING_FAST_LINKER LLD)
+    add_link_options("-fuse-ld=lld")
+    if(WIN32)
+      # Clear unsupported linker flags on Windows
+      set(LIB_AS_NEEDED_PRE "")
+    endif()
+  endif()
+endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-compiler-workarounds.cmake ns-3.41/build-support/custom-modules/ns3-compiler-workarounds.cmake
--- ns-3.40/build-support/custom-modules/ns3-compiler-workarounds.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/custom-modules/ns3-compiler-workarounds.cmake	2024-02-09 14:44:07.721789000 -0800
@@ -17,39 +17,7 @@
 
 include(CheckCXXSourceCompiles)
 
-# Clang 6, 7 and 8 shipped with incomplete C++17 features and do not handle
-# ostream& operator<<(ostream& os, nullptr_t ptr)
-# https://gitlab.com/nsnam/ns-3-dev/-/issues/730
-check_cxx_source_compiles(
-  "
-    #include <iostream>
-    #include <cstddef>
-    inline std::ostream& operator << (std::ostream& os, std::nullptr_t ptr)
-    {
-      return os << \"nullptr\"; //whatever you want nullptr to show up as in the console
-    }
-    int main()
-    {
-        std::ostream os(NULL);
-        os << std::nullptr_t();
-        return 0;
-    }
-    "
-  MISSING_OSTREAM_NULLPTR_OPERATOR
-)
-
-if(${MISSING_OSTREAM_NULLPTR_OPERATOR})
-  message(
-    ${HIGHLIGHTED_STATUS}
-    "Using compiler workaround: compiling in \"ostream& operator<<(ostream&, nullptr_t)\""
-  )
-  add_definitions(
-    -include
-    ${CMAKE_CURRENT_SOURCE_DIR}/build-support/compiler-workarounds/ostream-operator-nullptr.h
-  )
-endif()
-
-# Some compilers (e.g. GCC < 9.1 and Clang < 9) do not link
+# Some compilers (e.g. GCC < 9.1) do not link
 # std::filesystem/std::experimental::filesystem by default. If the sample
 # program can be linked, it means it is indeed linked by default. Otherwise, we
 # link it manually. https://en.cppreference.com/w/cpp/filesystem
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-executables.cmake ns-3.41/build-support/custom-modules/ns3-executables.cmake
--- ns-3.40/build-support/custom-modules/ns3-executables.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-executables.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,162 @@
+function(set_runtime_outputdirectory target_name output_directory target_prefix)
+  # Prevent duplicate '/' in EXECUTABLE_DIRECTORY_PATH, since it gets translated
+  # to doubled underlines and will cause the ns3 script to fail
+  string(REPLACE "//" "/" output_directory "${output_directory}")
+
+  set(ns3-exec-outputname ns${NS3_VER}-${target_name}${build_profile_suffix})
+  set(ns3-execs "${output_directory}${ns3-exec-outputname};${ns3-execs}"
+      CACHE INTERNAL "list of c++ executables"
+  )
+  set(ns3-execs-clean "${target_prefix}${target_name};${ns3-execs-clean}"
+      CACHE INTERNAL
+            "list of c++ executables without version prefix and build suffix"
+  )
+
+  set_target_properties(
+    ${target_prefix}${target_name}
+    PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_directory}
+               RUNTIME_OUTPUT_NAME ${ns3-exec-outputname}
+  )
+  if(${XCODE})
+    # Is that so hard not to break people's CI, AAPL?? Why would you output the
+    # targets to a Debug/Release subfolder? Why?
+    foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
+      string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
+      set_target_properties(
+        ${target_prefix}${target_name}
+        PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output_directory}
+                   RUNTIME_OUTPUT_NAME_${OUTPUTCONFIG} ${ns3-exec-outputname}
+      )
+    endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES)
+  endif()
+
+  if(${ENABLE_TESTS})
+    add_dependencies(all-test-targets ${target_prefix}${target_name})
+    # Create a CTest entry for each executable
+    if(WIN32)
+      # Windows require this workaround to make sure the DLL files are located
+      add_test(
+        NAME ctest-${target_prefix}${target_name}
+        COMMAND
+          ${CMAKE_COMMAND} -E env
+          "PATH=$ENV{PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY};${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
+          ${ns3-exec-outputname}
+        WORKING_DIRECTORY ${output_directory}
+      )
+    else()
+      add_test(NAME ctest-${target_prefix}${target_name}
+               COMMAND ${ns3-exec-outputname}
+               WORKING_DIRECTORY ${output_directory}
+      )
+    endif()
+  endif()
+
+  if(${NS3_CLANG_TIMETRACE})
+    add_dependencies(timeTraceReport ${target_prefix}${target_name})
+  endif()
+endfunction(set_runtime_outputdirectory)
+
+function(get_scratch_prefix prefix)
+  # /path/to/ns-3-dev/scratch/nested-subdir
+  set(temp ${CMAKE_CURRENT_SOURCE_DIR})
+  # remove /path/to/ns-3-dev/ to get scratch/nested-subdir
+  string(REPLACE "${PROJECT_SOURCE_DIR}/" "" temp "${temp}")
+  # replace path separators with underlines
+  string(REPLACE "/" "_" temp "${temp}")
+  # save the prefix value to the passed variable
+  set(${prefix} ${temp}_ PARENT_SCOPE)
+endfunction()
+
+function(build_exec)
+  # Argument parsing
+  set(options IGNORE_PCH STANDALONE)
+  set(oneValueArgs EXECNAME EXECNAME_PREFIX EXECUTABLE_DIRECTORY_PATH
+                   INSTALL_DIRECTORY_PATH
+  )
+  set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK DEFINITIONS)
+  cmake_parse_arguments(
+    "BEXEC" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
+  )
+
+  # Resolve nested scratch prefixes without user intervention
+  string(REPLACE "${PROJECT_SOURCE_DIR}" "" relative_path
+                 "${CMAKE_CURRENT_SOURCE_DIR}"
+  )
+  if("${relative_path}" MATCHES "scratch" AND "${BEXEC_EXECNAME_PREFIX}"
+                                              STREQUAL ""
+  )
+    get_scratch_prefix(BEXEC_EXECNAME_PREFIX)
+  endif()
+
+  add_executable(
+    ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} "${BEXEC_SOURCE_FILES}"
+  )
+
+  target_compile_definitions(
+    ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} PUBLIC ${BEXEC_DEFINITIONS}
+  )
+
+  if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${BEXEC_IGNORE_PCH}))
+    target_precompile_headers(
+      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} REUSE_FROM stdlib_pch_exec
+    )
+  endif()
+
+  if(${NS3_STATIC} AND (NOT BEXEC_STANDALONE))
+    target_link_libraries(
+      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE_STATIC}
+      ${lib-ns3-static}
+    )
+  elseif(${NS3_MONOLIB} AND (NOT BEXEC_STANDALONE))
+    target_link_libraries(
+      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE}
+      ${lib-ns3-monolib} ${LIB_AS_NEEDED_POST}
+    )
+  else()
+    target_link_libraries(
+      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE}
+      "${BEXEC_LIBRARIES_TO_LINK}" ${LIB_AS_NEEDED_POST}
+    )
+  endif()
+
+  set_runtime_outputdirectory(
+    "${BEXEC_EXECNAME}" "${BEXEC_EXECUTABLE_DIRECTORY_PATH}/"
+    "${BEXEC_EXECNAME_PREFIX}"
+  )
+
+  if(BEXEC_INSTALL_DIRECTORY_PATH)
+    install(TARGETS ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
+            EXPORT ns3ExportTargets
+            RUNTIME DESTINATION ${BEXEC_INSTALL_DIRECTORY_PATH}
+    )
+    get_property(
+      filename TARGET ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
+      PROPERTY RUNTIME_OUTPUT_NAME
+    )
+    add_custom_target(
+      uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
+      COMMAND
+        rm ${CMAKE_INSTALL_PREFIX}/${BEXEC_INSTALL_DIRECTORY_PATH}/${filename}
+    )
+    add_dependencies(
+      uninstall uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
+    )
+  endif()
+endfunction(build_exec)
+
+function(scan_python_examples path)
+  # Skip python examples search in case the bindings are disabled
+  if(NOT ${ENABLE_PYTHON_BINDINGS})
+    return()
+  endif()
+
+  # Search for python examples
+  file(GLOB_RECURSE python_examples ${path}/*.py)
+  foreach(python_example ${python_examples})
+    if(NOT (${python_example} MATCHES "examples-to-run"))
+      set(ns3-execs-py "${python_example};${ns3-execs-py}"
+          CACHE INTERNAL "list of python scripts"
+      )
+    endif()
+  endforeach()
+endfunction()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake ns-3.41/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake
--- ns-3.40/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -2,13 +2,11 @@
 
 ExternalProject_Add(
   brite_dep
-  URL https://code.nsnam.org/BRITE/archive/30338f4f63b9.zip
-  URL_HASH MD5=b36ecf8f6b5f2cfae936ba1f1bfcff5c
+  GIT_REPOSITORY https://gitlab.com/nsnam/BRITE.git
+  GIT_TAG 29c301e828d2a4f303b3d0c69360c987b02d0745
   PREFIX brite_dep
   BUILD_IN_SOURCE TRUE
-  CONFIGURE_COMMAND make
-  BUILD_COMMAND make
-  INSTALL_COMMAND make install PREFIX=${CMAKE_OUTPUT_DIRECTORY}
+  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_OUTPUT_DIRECTORY}
 )
 
 ExternalProject_Add(
@@ -18,21 +16,40 @@
   PREFIX click_dep
   BUILD_IN_SOURCE TRUE
   UPDATE_DISCONNECTED TRUE
-  CONFIGURE_COMMAND ./configure --disable-linuxmodule --enable-nsclick
-                    --enable-wifi --prefix ${CMAKE_OUTPUT_DIRECTORY}
+  CONFIGURE_COMMAND
+    ./configure --disable-linuxmodule --enable-nsclick --enable-wifi --prefix
+    ${CMAKE_OUTPUT_DIRECTORY} --libdir ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
   BUILD_COMMAND make -j${NumThreads}
   INSTALL_COMMAND make install
 )
 
 ExternalProject_Add(
   openflow_dep
-  URL https://code.nsnam.org/openflow/archive/d45e7d184151.zip
-  URL_HASH MD5=a068cdaec5523586921b2f1f81f10916
+  GIT_REPOSITORY https://gitlab.com/nsnam/openflow.git
+  GIT_TAG 4869d4f6900342440af02ea93a3d8040c8316e5f
   PREFIX openflow_dep
   BUILD_IN_SOURCE TRUE
-  CONFIGURE_COMMAND ./waf configure --prefix ${CMAKE_OUTPUT_DIRECTORY}
-  BUILD_COMMAND ./waf build
-  INSTALL_COMMAND ./waf install
+  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${CMAKE_OUTPUT_DIRECTORY}
+)
+
+find_file(
+  BOOST_STATIC_ASSERT
+  NAMES static_assert.hpp
+  PATH_SUFFIXES boost
+  HINTS /usr/local
+)
+
+if(${BOOST_STATIC_ASSERT} STREQUAL "BOOST_STATIC_ASSERT-NOTFOUND")
+  message(FATAL_ERROR "Boost static assert is required by openflow")
+endif()
+
+get_filename_component(boost_dir ${BOOST_STATIC_ASSERT} DIRECTORY)
+
+install(
+  DIRECTORY ${boost_dir}
+  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+  USE_SOURCE_PERMISSIONS
+  PATTERN "boost/*"
 )
 
 install(
@@ -56,11 +73,12 @@
   USE_SOURCE_PERMISSIONS
   PATTERN "boost/*"
 )
+
 install(
-  DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/lib/
+  DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/${libdir}/
   DESTINATION ${CMAKE_INSTALL_LIBDIR}
   USE_SOURCE_PERMISSIONS
-  PATTERN "lib/*"
+  PATTERN "${libdir}/*"
 )
 
 macro(add_dependency_to_optional_modules_dependencies)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-filter-modules.cmake ns-3.41/build-support/custom-modules/ns3-filter-modules.cmake
--- ns-3.40/build-support/custom-modules/ns3-filter-modules.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-filter-modules.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,233 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# This is where recursive module dependency checking is implemented for
+# filtering enabled and disabled modules
+
+function(filter_modules modules_to_filter all_modules_list filter_in)
+  set(new_modules_to_build)
+  # We are receiving variable names as arguments, so we have to "dereference"
+  # them first That is why we have the duplicated ${${}}
+  foreach(module ${${all_modules_list}})
+    if(${filter_in} (${module} IN_LIST ${modules_to_filter}))
+      list(APPEND new_modules_to_build ${module})
+    endif()
+  endforeach()
+  set(${all_modules_list} ${new_modules_to_build} PARENT_SCOPE)
+endfunction()
+
+function(resolve_dependencies module_name dependencies contrib_dependencies)
+  # Create cache variables to hold dependencies list and visited
+  set(dependency_visited "" CACHE INTERNAL "")
+  set(contrib_dependency_visited "" CACHE INTERNAL "")
+  recursive_dependency(${module_name})
+  if(${module_name} IN_LIST dependency_visited)
+    set(temp ${dependency_visited})
+    list(REMOVE_AT temp 0)
+    set(${dependencies} ${temp} PARENT_SCOPE)
+    set(${contrib_dependencies} ${contrib_dependency_visited} PARENT_SCOPE)
+  else()
+    set(temp ${contrib_dependency_visited})
+    list(REMOVE_AT temp 0)
+    set(${dependencies} ${dependency_visited} PARENT_SCOPE)
+    set(${contrib_dependencies} ${temp} PARENT_SCOPE)
+  endif()
+  unset(dependency_visited CACHE)
+  unset(contrib_dependency_visited CACHE)
+endfunction()
+
+function(filter_libraries cmakelists_contents libraries)
+  string(REGEX MATCHALL "{lib[^}]*[^obj]}" matches "${cmakelists_content}")
+  list(REMOVE_ITEM matches "{libraries_to_link}")
+  string(REPLACE "{lib\${name" "" matches "${matches}") # special case for
+  # src/test
+  string(REPLACE "{lib" "" matches "${matches}")
+  string(REPLACE "}" "" matches "${matches}")
+  set(${libraries} ${matches} PARENT_SCOPE)
+endfunction()
+
+function(recursive_dependency module_name)
+  # Read module CMakeLists.txt and search for ns-3 modules
+  set(src_cmakelist ${PROJECT_SOURCE_DIR}/src/${module_name}/CMakeLists.txt)
+  set(contrib_cmakelist
+      ${PROJECT_SOURCE_DIR}/contrib/${module_name}/CMakeLists.txt
+  )
+  set(contrib FALSE)
+  if(EXISTS ${src_cmakelist})
+    file(READ ${src_cmakelist} cmakelists_content)
+  elseif(EXISTS ${contrib_cmakelist})
+    file(READ ${contrib_cmakelist} cmakelists_content)
+    set(contrib TRUE)
+  else()
+    set(cmakelists_content "")
+    message(${HIGHLIGHTED_STATUS}
+            "The CMakeLists.txt file for module ${module_name} was not found."
+    )
+  endif()
+
+  filter_libraries("${cmakelists_content}" matches)
+
+  # Add this visited module dependencies to the dependencies list
+  if(contrib)
+    set(contrib_dependency_visited
+        "${contrib_dependency_visited};${module_name}" CACHE INTERNAL ""
+    )
+    set(examples_cmakelists ${contrib_cmakelist})
+  else()
+    set(dependency_visited "${dependency_visited};${module_name}" CACHE INTERNAL
+                                                                        ""
+    )
+    set(examples_cmakelists ${src_cmakelist})
+  endif()
+
+  # cmake-format: off
+  # Scan dependencies required by this module examples
+  set(example_matches)
+  #if(${ENABLE_EXAMPLES})
+  #  string(REPLACE "${module_name}" "${module_name}/examples" examples_cmakelists ${examples_cmakelists})
+  #  if(EXISTS ${examples_cmakelists})
+  #    file(READ ${examples_cmakelists} cmakelists_content)
+  #    filter_libraries(${cmakelists_content} example_matches)
+  #  endif()
+  #endif()
+  # cmake-format: on
+
+  # For each dependency, call this same function
+  set(matches "${matches};${example_matches}")
+  foreach(match ${matches})
+    if(NOT ((${match} IN_LIST dependency_visited)
+            OR (${match} IN_LIST contrib_dependency_visited))
+    )
+      recursive_dependency(${match})
+    endif()
+  endforeach()
+endfunction()
+
+macro(
+  filter_enabled_and_disabled_modules
+  libs_to_build
+  contrib_libs_to_build
+  NS3_ENABLED_MODULES
+  NS3_DISABLED_MODULES
+  ns3rc_enabled_modules
+  ns3rc_disabled_modules
+)
+  mark_as_advanced(ns3-all-enabled-modules)
+
+  # Before filtering, we set a variable with all scanned modules in the src
+  # directory
+  set(scanned_modules ${${libs_to_build}})
+
+  # Ensure enabled and disable modules lists are using semicolons
+  string(REPLACE "," ";" ${NS3_ENABLED_MODULES} "${${NS3_ENABLED_MODULES}}")
+  string(REPLACE "," ";" ${NS3_DISABLED_MODULES} "${${NS3_DISABLED_MODULES}}")
+
+  # Now that scanning modules finished, we can remove the disabled modules or
+  # replace the modules list with the ones in the enabled list
+  if(${NS3_ENABLED_MODULES} OR ${ns3rc_enabled_modules})
+    # List of enabled modules passed by the command line overwrites list read
+    # from ns3rc
+    if(${NS3_ENABLED_MODULES})
+      set(ns3rc_enabled_modules ${${NS3_ENABLED_MODULES}})
+    endif()
+
+    # Filter enabled modules
+    filter_modules(ns3rc_enabled_modules libs_to_build "")
+    filter_modules(ns3rc_enabled_modules contrib_libs_to_build "")
+
+    # Use recursion to automatically determine dependencies required by the
+    # manually enabled modules
+    foreach(lib ${${contrib_libs_to_build}})
+      resolve_dependencies(${lib} dependencies contrib_dependencies)
+      list(APPEND ${contrib_libs_to_build} "${contrib_dependencies}")
+      list(APPEND ${libs_to_build} "${dependencies}")
+      unset(dependencies)
+      unset(contrib_dependencies)
+    endforeach()
+
+    foreach(lib ${${libs_to_build}})
+      resolve_dependencies(${lib} dependencies contrib_dependencies)
+      list(APPEND ${libs_to_build} "${dependencies}")
+      unset(dependencies)
+      unset(contrib_dependencies)
+    endforeach()
+  endif()
+
+  if(${NS3_DISABLED_MODULES} OR ${ns3rc_disabled_modules})
+    # List of disabled modules passed by the command line overwrites list read
+    # from ns3rc
+
+    if(${NS3_DISABLED_MODULES})
+      set(ns3rc_disabled_modules ${${NS3_DISABLED_MODULES}})
+    endif()
+
+    set(all_libs ${${libs_to_build}};${${contrib_libs_to_build}})
+
+    # We then use the recursive dependency finding to get all dependencies of
+    # all modules
+    foreach(lib ${all_libs})
+      resolve_dependencies(${lib} dependencies contrib_dependencies)
+      set(${lib}_dependencies "${dependencies};${contrib_dependencies}")
+      unset(dependencies)
+      unset(contrib_dependencies)
+    endforeach()
+
+    # Now we can begin removing libraries that require disabled dependencies
+    set(disabled_libs "${${ns3rc_disabled_modules}}")
+    foreach(libo ${all_libs})
+      foreach(lib ${all_libs})
+        foreach(disabled_lib ${disabled_libs})
+          if(${lib} STREQUAL ${disabled_lib})
+            continue()
+          endif()
+          if(${disabled_lib} IN_LIST ${lib}_dependencies)
+            list(APPEND disabled_libs ${lib})
+            break() # proceed to the next lib in all_libs
+          endif()
+        endforeach()
+      endforeach()
+    endforeach()
+
+    # Clean dependencies lists
+    foreach(lib ${all_libs})
+      unset(${lib}_dependencies)
+    endforeach()
+
+    # We can filter out disabled modules
+    filter_modules(disabled_libs libs_to_build "NOT")
+    filter_modules(disabled_libs contrib_libs_to_build "NOT")
+
+    if(core IN_LIST ${libs_to_build})
+      list(APPEND ${libs_to_build} test) # include test module
+    endif()
+  endif()
+
+  # Older CMake versions require this workaround for empty lists
+  if(NOT ${contrib_libs_to_build})
+    set(${contrib_libs_to_build} "")
+  endif()
+
+  # Filter out any eventual duplicates
+  list(REMOVE_DUPLICATES ${libs_to_build})
+  list(REMOVE_DUPLICATES ${contrib_libs_to_build})
+
+  # Export list with all enabled modules (used to separate ns libraries from
+  # non-ns libraries in ns3_module_macros)
+  set(ns3-all-enabled-modules "${${libs_to_build}};${${contrib_libs_to_build}}"
+      CACHE INTERNAL "list with all enabled modules"
+  )
+endmacro()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-find-external-library.cmake ns-3.41/build-support/custom-modules/ns3-find-external-library.cmake
--- ns-3.40/build-support/custom-modules/ns3-find-external-library.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-find-external-library.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,339 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# This is our own variant of find_package, for CMake and non-CMake based
+# projects
+function(log_find_searched_paths)
+  # Parse arguments
+  set(options)
+  set(oneValueArgs TARGET_TYPE TARGET_NAME SEARCH_RESULT SEARCH_SYSTEM_PREFIX)
+  set(multiValueArgs SEARCH_PATHS SEARCH_SUFFIXES)
+  cmake_parse_arguments(
+    "LOGFIND" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
+  )
+
+  # Get searched paths and add cmake_system_prefix_path if not explicitly marked
+  # not to include it
+  set(tsearch_paths ${LOGFIND_SEARCH_PATHS})
+  if("${LOGFIND_SEARCH_SYSTEM_PREFIX}" STREQUAL "")
+    list(APPEND tsearch_paths "${CMAKE_SYSTEM_PREFIX_PATH}")
+  endif()
+
+  set(log_find
+      "Looking for ${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} in:\n"
+  )
+  # For each search path and suffix combination, print a line
+  foreach(tsearch_path ${tsearch_paths})
+    foreach(suffix ${LOGFIND_SEARCH_SUFFIXES})
+      string(APPEND log_find
+             "\t${tsearch_path}${suffix}/${LOGFIND_TARGET_NAME}\n"
+      )
+    endforeach()
+  endforeach()
+
+  # Add a final line saying if the file was found and where, or if it was not
+  # found
+  if("${${LOGFIND_SEARCH_RESULT}}" STREQUAL "${LOGFIND_SEARCH_RESULT}-NOTFOUND")
+    string(APPEND log_find
+           "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was not found\n"
+    )
+  else()
+    string(
+      APPEND
+      log_find
+      "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was found in ${${LOGFIND_SEARCH_RESULT}}\n"
+    )
+  endif()
+
+  # Replace duplicate path separators
+  string(REPLACE "//" "/" log_find "${log_find}")
+
+  # Print find debug message similar to the one produced by
+  # CMAKE_FIND_DEBUG_MODE=true in CMake >= 3.17
+  message(STATUS ${log_find})
+endfunction()
+
+function(find_external_library)
+  # Parse arguments
+  set(options QUIET)
+  set(oneValueArgs DEPENDENCY_NAME HEADER_NAME LIBRARY_NAME OUTPUT_VARIABLE)
+  set(multiValueArgs HEADER_NAMES LIBRARY_NAMES PATH_SUFFIXES SEARCH_PATHS)
+  cmake_parse_arguments(
+    "FIND_LIB" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
+  )
+
+  # Set the external package/dependency name
+  set(name ${FIND_LIB_DEPENDENCY_NAME})
+
+  # We process individual and list of headers and libraries by transforming them
+  # into lists
+  set(library_names "${FIND_LIB_LIBRARY_NAME};${FIND_LIB_LIBRARY_NAMES}")
+  set(header_names "${FIND_LIB_HEADER_NAME};${FIND_LIB_HEADER_NAMES}")
+
+  # Just changing the parsed argument name back to something shorter
+  set(search_paths ${FIND_LIB_SEARCH_PATHS})
+  set(path_suffixes "${FIND_LIB_PATH_SUFFIXES}")
+
+  set(not_found_libraries)
+  set(library_dirs)
+  set(libraries)
+
+  # Include parent directories in the search paths to handle Bake cases
+  get_filename_component(parent_project_dir ${PROJECT_SOURCE_DIR} DIRECTORY)
+  get_filename_component(
+    grandparent_project_dir ${parent_project_dir} DIRECTORY
+  )
+  set(project_parent_dirs ${parent_project_dir} ${grandparent_project_dir})
+
+  # Paths and suffixes where libraries will be searched on
+  disable_cmake_warnings()
+  set(library_search_paths
+      ${search_paths}
+      ${project_parent_dirs}
+      ${CMAKE_OUTPUT_DIRECTORY} # Search for libraries in ns-3-dev/build
+      ${CMAKE_INSTALL_PREFIX} # Search for libraries in the install directory
+      # (e.g. /usr/)
+      $ENV{LD_LIBRARY_PATH} # Search for libraries in LD_LIBRARY_PATH
+      # directories
+      $ENV{PATH} # Search for libraries in PATH directories
+  )
+  enable_cmake_warnings()
+  # cmake-format: off
+    #
+    # Split : separated entries from environment variables
+    # by replacing separators with ;
+    #
+    # cmake-format: on
+  string(REPLACE ":" ";" library_search_paths "${library_search_paths}")
+
+  set(suffixes /build /lib /build/lib / /bin ${path_suffixes})
+
+  # For each of the library names in LIBRARY_NAMES or LIBRARY_NAME
+  foreach(library ${library_names})
+    # We mark this value is advanced not to pollute the configuration with
+    # ccmake with the cache variables used internally
+    mark_as_advanced(${name}_library_internal_${library})
+
+    # We search for the library named ${library} and store the results in
+    # ${name}_library_internal_${library}
+    find_library(
+      ${name}_library_internal_${library} ${library}
+      HINTS ${library_search_paths} PATH_SUFFIXES ${suffixes}
+    )
+    # cmake-format: off
+        # Note: the PATH_SUFFIXES above apply to *ALL* PATHS and HINTS Which
+        # translates to CMake searching on standard library directories
+        # CMAKE_SYSTEM_PREFIX_PATH, user-settable CMAKE_PREFIX_PATH or
+        # CMAKE_LIBRARY_PATH and the directories listed above
+        #
+        # e.g.  from Ubuntu 22.04 CMAKE_SYSTEM_PREFIX_PATH =
+        # /usr/local;/usr;/;/usr/local;/usr/X11R6;/usr/pkg;/opt
+        #
+        # Searched directories without suffixes
+        #
+        # ${CMAKE_SYSTEM_PREFIX_PATH}[0] = /usr/local/
+        # ${CMAKE_SYSTEM_PREFIX_PATH}[1] = /usr
+        # ${CMAKE_SYSTEM_PREFIX_PATH}[2] = /
+        # ...
+        # ${CMAKE_SYSTEM_PREFIX_PATH}[6] = /opt
+        # ${LD_LIBRARY_PATH}[0]
+        # ...
+        # ${LD_LIBRARY_PATH}[m]
+        # ${PATH}[0]
+        # ...
+        # ${PATH}[m]
+        #
+        # Searched directories with suffixes include all of the directories above
+        # plus all suffixes
+        # PATH_SUFFIXES /build /lib /build/lib / /bin # ${path_suffixes}
+        #
+        # /usr/local/build
+        # /usr/local/lib
+        # /usr/local/build/lib
+        # /usr/local/bin
+        # ...
+        #
+        # cmake-format: on
+    # Or enable NS3_VERBOSE to print the searched paths
+
+    # Print tested paths to the searched library and if it was found
+    if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0"))
+      log_find_searched_paths(
+        TARGET_TYPE
+        Library
+        TARGET_NAME
+        ${library}
+        SEARCH_RESULT
+        ${name}_library_internal_${library}
+        SEARCH_PATHS
+        ${library_search_paths}
+        SEARCH_SUFFIXES
+        ${suffixes}
+      )
+    endif()
+
+    # After searching the library, the internal variable should have either the
+    # absolute path to the library or the name of the variable appended with
+    # -NOTFOUND
+    if("${${name}_library_internal_${library}}" STREQUAL
+       "${name}_library_internal_${library}-NOTFOUND"
+    )
+      # We keep track of libraries that were not found
+      list(APPEND not_found_libraries ${library})
+    else()
+      # We get the name of the parent directory of the library and append the
+      # library to a list of found libraries
+      get_filename_component(
+        ${name}_library_dir_internal ${${name}_library_internal_${library}}
+        DIRECTORY
+      ) # e.g. lib/openflow.(so|dll|dylib|a) -> lib
+      list(APPEND library_dirs ${${name}_library_dir_internal})
+      list(APPEND libraries ${${name}_library_internal_${library}})
+    endif()
+  endforeach()
+
+  # For each library that was found (e.g. /usr/lib/pthread.so), get their parent
+  # directory (/usr/lib) and its parent (/usr)
+  set(parent_dirs)
+  foreach(libdir ${library_dirs})
+    get_filename_component(parent_libdir ${libdir} DIRECTORY)
+    get_filename_component(parent_parent_libdir ${parent_libdir} DIRECTORY)
+    list(APPEND parent_dirs ${libdir} ${parent_libdir} ${parent_parent_libdir})
+  endforeach()
+
+  set(header_search_paths
+      ${search_paths}
+      ${parent_dirs}
+      ${project_parent_dirs}
+      ${CMAKE_OUTPUT_DIRECTORY} # Search for headers in
+      # ns-3-dev/build
+      ${CMAKE_INSTALL_PREFIX} # Search for headers in the install
+  )
+
+  set(not_found_headers)
+  set(include_dirs)
+  foreach(header ${header_names})
+    # The same way with libraries, we mark the internal variable as advanced not
+    # to pollute ccmake configuration with variables used internally
+    mark_as_advanced(${name}_header_internal_${header})
+    set(suffixes
+        /build
+        /include
+        /build/include
+        /build/include/${name}
+        /include/${name}
+        /${name}
+        /
+        ${path_suffixes}
+    )
+
+    # cmake-format: off
+        # Here we search for the header file named ${header} and store the result in
+        # ${name}_header_internal_${header}
+        #
+        # The same way we did with libraries, here we search on
+        # CMAKE_SYSTEM_PREFIX_PATH, along with user-settable ${search_paths}, the
+        # parent directories from the libraries, CMAKE_OUTPUT_DIRECTORY and
+        # CMAKE_INSTALL_PREFIX
+        #
+        # And again, for each of them, for every suffix listed /usr/local/build
+        # /usr/local/include
+        # /usr/local/build/include
+        # /usr/local/build/include/${name}
+        # /usr/local/include/${name}
+        # ...
+        #
+        # cmake-format: on
+    # Or enable NS3_VERBOSE to get the searched paths printed while configuring
+
+    find_file(${name}_header_internal_${header} ${header}
+              HINTS ${header_search_paths} # directory (e.g. /usr/)
+              PATH_SUFFIXES ${suffixes}
+    )
+
+    # Print tested paths to the searched header and if it was found
+    if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0"))
+      log_find_searched_paths(
+        TARGET_TYPE
+        Header
+        TARGET_NAME
+        ${header}
+        SEARCH_RESULT
+        ${name}_header_internal_${header}
+        SEARCH_PATHS
+        ${header_search_paths}
+        SEARCH_SUFFIXES
+        ${suffixes}
+      )
+    endif()
+
+    # If the header file was not found, append to the not-found list
+    if("${${name}_header_internal_${header}}" STREQUAL
+       "${name}_header_internal_${header}-NOTFOUND"
+    )
+      list(APPEND not_found_headers ${header})
+    else()
+      # If the header file was found, get their directories and the parent of
+      # their directories to add as include directories
+      get_filename_component(
+        header_include_dir ${${name}_header_internal_${header}} DIRECTORY
+      ) # e.g. include/click/ (simclick.h) -> #include <simclick.h> should work
+      get_filename_component(
+        header_include_dir2 ${header_include_dir} DIRECTORY
+      ) # e.g. include/(click) -> #include <click/simclick.h> should work
+      list(APPEND include_dirs ${header_include_dir} ${header_include_dir2})
+    endif()
+  endforeach()
+
+  # Remove duplicate include directories
+  if(include_dirs)
+    list(REMOVE_DUPLICATES include_dirs)
+  endif()
+
+  # If we find both library and header, we export their values
+  if((NOT not_found_libraries) AND (NOT not_found_headers))
+    set(${name}_INCLUDE_DIRS "${include_dirs}" PARENT_SCOPE)
+    set(${name}_LIBRARIES "${libraries}" PARENT_SCOPE)
+    set(${name}_FOUND TRUE PARENT_SCOPE)
+    if(NOT ${FIND_LIB_QUIET})
+      message(STATUS "find_external_library: ${name} was found.")
+    endif()
+  else()
+    set(${name}_INCLUDE_DIRS PARENT_SCOPE)
+    set(${name}_LIBRARIES PARENT_SCOPE)
+    set(${name}_FOUND FALSE PARENT_SCOPE)
+
+    set(missing_components
+        "Missing headers: \"${not_found_headers}\" and missing libraries: \"${not_found_libraries}\""
+    )
+
+    # Propagate missing components to the specified variable
+    if(DEFINED FIND_LIB_OUTPUT_VARIABLE)
+      mark_as_advanced(${FIND_LIB_OUTPUT_VARIABLE})
+      set(${FIND_LIB_OUTPUT_VARIABLE} "${missing_components}" CACHE INTERNAL "")
+      return()
+    endif()
+
+    # If no variable is specified, log it instead
+    if(NOT ${FIND_LIB_QUIET})
+      message(
+        ${HIGHLIGHTED_STATUS}
+        "find_external_library: ${name} was not found. ${missing_components}."
+      )
+    endif()
+  endif()
+endfunction()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-hidden-settings.cmake ns-3.41/build-support/custom-modules/ns3-hidden-settings.cmake
--- ns-3.40/build-support/custom-modules/ns3-hidden-settings.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-hidden-settings.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,49 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# Set INT128 as the default option for INT64X64 and register alternative
+# implementations
+set(NS3_INT64X64 "INT128" CACHE STRING "Int64x64 implementation")
+set_property(CACHE NS3_INT64X64 PROPERTY STRINGS INT128 CAIRO DOUBLE)
+
+# Purposefully hidden options:
+
+# For ease of use, export all libraries and include directories to ns-3 module
+# consumers by default
+option(NS3_REEXPORT_THIRD_PARTY_LIBRARIES "Export all third-party libraries
+and include directories to ns-3 module consumers" ON
+)
+
+# Since we can't really do that safely from the CMake side
+option(NS3_ENABLE_SUDO
+       "Set executables ownership to root and enable the SUID flag" OFF
+)
+
+# A flag that controls some aspects related to pip packaging
+option(NS3_PIP_PACKAGING "Control aspects related to pip wheel packaging" OFF)
+
+# fPIC (position-independent code) and fPIE (position-independent executable)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+# Do not create a file-level dependency with shared libraries reducing
+# unnecessary relinking
+set(CMAKE_LINK_DEPENDS_NO_SHARED TRUE)
+
+# Honor CMAKE_CXX_STANDARD in check_cxx_source_compiles
+# https://cmake.org/cmake/help/latest/policy/CMP0067.html
+cmake_policy(SET CMP0066 NEW)
+cmake_policy(SET CMP0067 NEW)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-lock.cmake ns-3.41/build-support/custom-modules/ns3-lock.cmake
--- ns-3.40/build-support/custom-modules/ns3-lock.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/custom-modules/ns3-lock.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -76,7 +76,6 @@
   string(REPLACE ";" "', '" PATH_LIST "${PATH_LIST}")
   string(APPEND lock_contents "NS3_MODULE_PATH = ['${PATH_LIST}']\n")
 
-  cache_cmake_flag(ENABLE_REALTIME "ENABLE_REAL_TIME" lock_contents)
   cache_cmake_flag(ENABLE_EXAMPLES "ENABLE_EXAMPLES" lock_contents)
   cache_cmake_flag(ENABLE_TESTS "ENABLE_TESTS" lock_contents)
   cache_cmake_flag(NS3_OPENFLOW "ENABLE_OPENFLOW" lock_contents)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-module-macros.cmake ns-3.41/build-support/custom-modules/ns3-module-macros.cmake
--- ns-3.40/build-support/custom-modules/ns3-module-macros.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/custom-modules/ns3-module-macros.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -15,6 +15,80 @@
 #
 # Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
 
+add_custom_target(copy_all_headers)
+function(copy_headers_before_building_lib libname outputdir headers visibility)
+  foreach(header ${headers})
+    # Copy header to output directory on changes -> too darn slow
+    # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${header} ${outputdir}/
+    # COPYONLY)
+
+    get_filename_component(
+      header_name ${CMAKE_CURRENT_SOURCE_DIR}/${header} NAME
+    )
+
+    # If output directory does not exist, create it
+    if(NOT (EXISTS ${outputdir}))
+      file(MAKE_DIRECTORY ${outputdir})
+    endif()
+
+    # If header already exists, skip symlinking/stub header creation
+    if(EXISTS ${outputdir}/${header_name})
+      continue()
+    endif()
+
+    # Create a stub header in the output directory, including the real header
+    # inside their respective module
+    get_filename_component(
+      ABSOLUTE_HEADER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${header}" ABSOLUTE
+    )
+    file(WRITE ${outputdir}/${header_name}
+         "#include \"${ABSOLUTE_HEADER_PATH}\"\n"
+    )
+  endforeach()
+endfunction(copy_headers_before_building_lib)
+
+function(remove_lib_prefix prefixed_library library)
+  # Check if there is a lib prefix
+  string(FIND "${prefixed_library}" "lib" lib_pos)
+
+  # If there is a lib prefix, try to remove it
+  if(${lib_pos} EQUAL 0)
+    # Check if we still have something remaining after removing the "lib" prefix
+    string(LENGTH ${prefixed_library} len)
+    if(${len} LESS 4)
+      message(FATAL_ERROR "Invalid library name: ${prefixed_library}")
+    endif()
+
+    # Remove lib prefix from module name (e.g. libcore -> core)
+    string(SUBSTRING "${prefixed_library}" 3 -1 unprefixed_library)
+  else()
+    set(unprefixed_library ${prefixed_library})
+  endif()
+
+  # Save the unprefixed library name to the parent scope
+  set(${library} ${unprefixed_library} PARENT_SCOPE)
+endfunction()
+
+function(check_for_missing_libraries output_variable_name libraries)
+  set(missing_dependencies)
+  foreach(lib ${libraries})
+    # skip check for ns-3 modules if its a path to a library
+    if(EXISTS ${lib})
+      continue()
+    endif()
+
+    # check if the example depends on disabled modules
+    remove_lib_prefix("${lib}" lib)
+
+    # Check if the module exists in the ns-3 modules list or if it is a
+    # 3rd-party library
+    if(NOT (${lib} IN_LIST ns3-all-enabled-modules))
+      list(APPEND missing_dependencies ${lib})
+    endif()
+  endforeach()
+  set(${output_variable_name} ${missing_dependencies} PARENT_SCOPE)
+endfunction()
+
 # cmake-format: off
 #
 # This macro processes a ns-3 module
@@ -94,6 +168,7 @@
   add_library(ns3::${lib${BLIB_LIBNAME}} ALIAS ${lib${BLIB_LIBNAME}})
 
   # Associate public headers with library for installation purposes
+  set(config_headers)
   if("${BLIB_LIBNAME}" STREQUAL "core")
     set(config_headers ${CMAKE_HEADER_OUTPUT_DIRECTORY}/config-store-config.h
                        ${CMAKE_HEADER_OUTPUT_DIRECTORY}/core-config.h
@@ -104,9 +179,7 @@
       )
     endif()
 
-    if((NOT FILESYSTEM_LIBRARY_IS_LINKED) OR (${GCC} AND ${GCC8}))
-      # The GCC8 alternative is necessary since when installed alongside newer
-      # releases, the incorrect shared library can end up being linked.
+    if(NOT FILESYSTEM_LIBRARY_IS_LINKED)
       list(APPEND BLIB_LIBRARIES_TO_LINK -lstdc++fs)
     endif()
 
@@ -225,17 +298,9 @@
   )
 
   if(NOT ${XCODE})
-    # Since linking libraries to object libraries in not allowed in older CMake
-    # releases, we need to import each of their include directories. Otherwise,
-    # include directories won't be properly propagated
-    set(temp)
-    foreach(target ${ns_libraries_to_link})
-      list(APPEND temp
-           "$<TARGET_PROPERTY:${target},INTERFACE_INCLUDE_DIRECTORIES>"
-      )
-    endforeach()
-    target_include_directories(${lib${BLIB_LIBNAME}}-obj PRIVATE ${temp})
-    unset(temp)
+    target_link_libraries(
+      ${lib${BLIB_LIBNAME}}-obj PRIVATE ${ns_libraries_to_link}
+    )
   endif()
 
   # set output name of library
@@ -468,8 +533,9 @@
 
   if((NOT missing_dependencies) AND ${filtered_in})
     # Convert boolean into text to forward argument
+    set(IGNORE_PCH)
     if(${BLIB_EXAMPLE_IGNORE_PCH})
-      set(IGNORE_PCH IGNORE_PCH)
+      set(IGNORE_PCH "IGNORE_PCH")
     endif()
     # Create executable with sources and headers
     # cmake-format: off
@@ -479,7 +545,7 @@
       HEADER_FILES ${BLIB_EXAMPLE_HEADER_FILES}
       LIBRARIES_TO_LINK
         ${lib${BLIB_LIBNAME}} ${BLIB_EXAMPLE_LIBRARIES_TO_LINK}
-        ${optional_visualizer_lib}
+        ${ns3-optional-visualizer-lib}
       EXECUTABLE_DIRECTORY_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${FOLDER}/
       ${IGNORE_PCH}
     )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-ns3rc-parser.cmake ns-3.41/build-support/custom-modules/ns3-ns3rc-parser.cmake
--- ns-3.40/build-support/custom-modules/ns3-ns3rc-parser.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-ns3rc-parser.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,112 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# Parse .ns3rc
+macro(parse_ns3rc enabled_modules disabled_modules examples_enabled
+      tests_enabled
+)
+  # Try to find .ns3rc
+  disable_cmake_warnings()
+  find_file(NS3RC .ns3rc PATHS /etc $ENV{HOME} $ENV{USERPROFILE}
+                               ${PROJECT_SOURCE_DIR} NO_CACHE
+  )
+  enable_cmake_warnings()
+
+  # Set variables with default values (all modules, no examples nor tests)
+  set(${enabled_modules} "")
+  set(${disabled_modules} "")
+  set(${examples_enabled} "FALSE")
+  set(${tests_enabled} "FALSE")
+
+  if(NOT (${NS3RC} STREQUAL "NS3RC-NOTFOUND"))
+    message(${HIGHLIGHTED_STATUS}
+            "Configuration file .ns3rc being used : ${NS3RC}"
+    )
+    file(READ ${NS3RC} ns3rc_contents)
+    # Check if ns3rc file is CMake or Python based and act accordingly
+    if(ns3rc_contents MATCHES "ns3rc_*")
+      include(${NS3RC})
+    else()
+      parse_python_ns3rc(
+        "${ns3rc_contents}" ${enabled_modules} ${examples_enabled}
+        ${tests_enabled} ${NS3RC}
+      )
+    endif()
+  endif()
+endmacro(parse_ns3rc)
+
+function(parse_python_ns3rc ns3rc_contents enabled_modules examples_enabled
+         tests_enabled ns3rc_location
+)
+  # Save .ns3rc backup
+  file(WRITE ${ns3rc_location}.backup ${ns3rc_contents})
+
+  # Match modules_enabled list
+  if(ns3rc_contents MATCHES "modules_enabled.*\\[(.*).*\\]")
+    set(${enabled_modules} ${CMAKE_MATCH_1})
+    if(${enabled_modules} MATCHES "all_modules")
+      # If all modules, just clean the filter and all modules will get built by
+      # default
+      set(${enabled_modules})
+    else()
+      # If modules are listed, remove quotes and replace commas with semicolons
+      # transforming a string into a cmake list
+      string(REPLACE "," ";" ${enabled_modules} "${${enabled_modules}}")
+      string(REPLACE "'" "" ${enabled_modules} "${${enabled_modules}}")
+      string(REPLACE "\"" "" ${enabled_modules} "${${enabled_modules}}")
+      string(REPLACE " " "" ${enabled_modules} "${${enabled_modules}}")
+      string(REPLACE "\n" ";" ${enabled_modules} "${${enabled_modules}}")
+      list(SORT ${enabled_modules})
+
+      # Remove possibly empty entry
+      list(REMOVE_ITEM ${enabled_modules} "")
+      foreach(element ${${enabled_modules}})
+        # Inspect each element for comments
+        if(${element} MATCHES "#.*")
+          list(REMOVE_ITEM ${enabled_modules} ${element})
+        endif()
+      endforeach()
+    endif()
+  endif()
+
+  string(REPLACE "True" "ON" ns3rc_contents ${ns3rc_contents})
+  string(REPLACE "False" "OFF" ns3rc_contents ${ns3rc_contents})
+
+  # Match examples_enabled flag
+  if(ns3rc_contents MATCHES "examples_enabled = (ON|OFF)")
+    set(${examples_enabled} ${CMAKE_MATCH_1})
+  endif()
+
+  # Match tests_enabled flag
+  if(ns3rc_contents MATCHES "tests_enabled = (ON|OFF)")
+    set(${tests_enabled} ${CMAKE_MATCH_1})
+  endif()
+
+  # Save variables to parent scope
+  set(${enabled_modules} "${${enabled_modules}}" PARENT_SCOPE)
+  set(${examples_enabled} "${${examples_enabled}}" PARENT_SCOPE)
+  set(${tests_enabled} "${${tests_enabled}}" PARENT_SCOPE)
+
+  # Save updated .ns3rc file
+  message(
+    ${HIGHLIGHTED_STATUS}
+    "The python-based .ns3rc file format is deprecated and was updated to the CMake format"
+  )
+  configure_file(
+    ${PROJECT_SOURCE_DIR}/build-support/.ns3rc-template ${ns3rc_location} @ONLY
+  )
+endfunction(parse_python_ns3rc)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-output-directory.cmake ns-3.41/build-support/custom-modules/ns3-output-directory.cmake
--- ns-3.40/build-support/custom-modules/ns3-output-directory.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-output-directory.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,82 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# This is where we define where executables, libraries and headers will end up
+
+if("${NS3_OUTPUT_DIRECTORY}" STREQUAL "")
+  message(STATUS "Using default output directory ${PROJECT_SOURCE_DIR}/build")
+  set(CMAKE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build) # default output
+  # folder
+else()
+  # Check if NS3_OUTPUT_DIRECTORY is a relative path
+  set(absolute_ns3_output_directory "${NS3_OUTPUT_DIRECTORY}")
+  if(NOT IS_ABSOLUTE ${NS3_OUTPUT_DIRECTORY})
+    set(absolute_ns3_output_directory
+        "${PROJECT_SOURCE_DIR}/${NS3_OUTPUT_DIRECTORY}"
+    )
+  endif()
+
+  # Transform backward slash into forward slash Not the best way to do it since
+  # \ is a scape thing and can be used before whitespaces
+  string(REPLACE "\\" "/" absolute_ns3_output_directory
+                 "${absolute_ns3_output_directory}"
+  )
+
+  if(NOT (EXISTS ${absolute_ns3_output_directory}))
+    message(
+      STATUS
+        "User-defined output directory \"${NS3_OUTPUT_DIRECTORY}\" doesn't exist. Trying to create it"
+    )
+    file(MAKE_DIRECTORY ${absolute_ns3_output_directory})
+    if(NOT (EXISTS ${absolute_ns3_output_directory}))
+      message(
+        FATAL_ERROR
+          "User-defined output directory \"${absolute_ns3_output_directory}\" could not be created. "
+          "Try changing the value of NS3_OUTPUT_DIRECTORY"
+      )
+    endif()
+
+    # If this directory is not inside the ns-3-dev folder, alert users tests may
+    # break
+    if(NOT ("${absolute_ns3_output_directory}" MATCHES "${PROJECT_SOURCE_DIR}"))
+      message(
+        WARNING
+          "User-defined output directory \"${absolute_ns3_output_directory}\" is outside "
+          " of the ns-3 directory ${PROJECT_SOURCE_DIR}, which will break some tests"
+      )
+    endif()
+  endif()
+  message(
+    STATUS
+      "User-defined output directory \"${absolute_ns3_output_directory}\" will be used"
+  )
+  set(CMAKE_OUTPUT_DIRECTORY ${absolute_ns3_output_directory})
+endif()
+
+set(libdir "lib")
+if(${CMAKE_INSTALL_LIBDIR} MATCHES "lib64")
+  set(libdir "lib64")
+endif()
+
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/${libdir})
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/${libdir})
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY})
+set(CMAKE_HEADER_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/include/ns3)
+set(THIRD_PARTY_DIRECTORY ${PROJECT_SOURCE_DIR}/3rd-party)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+link_directories(${CMAKE_OUTPUT_DIRECTORY}/${libdir})
+file(MAKE_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY})
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-platform-support.cmake ns-3.41/build-support/custom-modules/ns3-platform-support.cmake
--- ns-3.40/build-support/custom-modules/ns3-platform-support.cmake	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/custom-modules/ns3-platform-support.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,81 @@
+# Copyright (c) 2023 Universidade de Brasília
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License version 2 as published by the Free
+# Software Foundation;
+#
+# 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; if not, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Author: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+
+# This is where we check for platform specifics
+
+# WSLv1 doesn't support tap features
+if(EXISTS "/proc/version")
+  file(READ "/proc/version" CMAKE_LINUX_DISTRO)
+  string(FIND "${CMAKE_LINUX_DISTRO}" "Microsoft" res)
+  if(res EQUAL -1)
+    set(WSLv1 False)
+  else()
+    set(WSLv1 True)
+  endif()
+endif()
+
+# Set Linux flag if on Linux
+if(UNIX AND NOT APPLE)
+  set(LINUX TRUE)
+  add_definitions(-D__LINUX__)
+endif()
+
+if(APPLE)
+  add_definitions(-D__APPLE__)
+  # cmake-format: off
+    # Configure find_program to search for AppBundles only if programs are not found in PATH.
+    # This prevents Doxywizard from being launched when the Doxygen.app is installed.
+    # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake
+    # cmake-format: on
+  set(CMAKE_FIND_APPBUNDLE "LAST")
+endif()
+
+if(WIN32)
+  set(NS3_PRECOMPILE_HEADERS OFF
+      CACHE BOOL "Precompile module headers to speed up compilation" FORCE
+  )
+
+  # For whatever reason getting M_PI and other math.h definitions from cmath
+  # requires this definition
+  # https://docs.microsoft.com/en-us/cpp/c-runtime-library/math-constants?view=vs-2019
+  add_definitions(/D_USE_MATH_DEFINES)
+endif()
+
+set(cat_command cat)
+
+if(CMAKE_XCODE_BUILD_SYSTEM)
+  set(XCODE True)
+else()
+  set(XCODE False)
+endif()
+
+if(${XCODE})
+  # Is that so hard not to break people's CI, AAPL? Why would you output the
+  # targets to a Debug/Release subfolder? Why?
+  foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
+    string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
+    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
+        ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
+    )
+    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
+        ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
+    )
+    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
+        ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
+    )
+  endforeach()
+endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/custom-modules/ns3-vcpkg-hunter.cmake ns-3.41/build-support/custom-modules/ns3-vcpkg-hunter.cmake
--- ns-3.40/build-support/custom-modules/ns3-vcpkg-hunter.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/custom-modules/ns3-vcpkg-hunter.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -73,7 +73,7 @@
     )
   endif()
 
-  if(${MSVC})
+  if(DEFINED MSVC)
     message(FATAL_ERROR "vcpkg: Visual Studio is unsupported")
   else()
     # Check if required packages are installed (unzip curl tar)
@@ -134,10 +134,12 @@
     execute_process(COMMAND chmod +x ${VCPKG_DIR}/${VCPKG_EXEC})
   endif()
 
+  disable_cmake_warnings()
   set(CMAKE_PREFIX_PATH
       "${VCPKG_DIR}/installed/${VCPKG_TRIPLET}/;${CMAKE_PREFIX_PATH}"
       PARENT_SCOPE
   )
+  enable_cmake_warnings()
 
   # Install packages in manifest mode
   if(EXISTS ${VCPKG_MANIFEST})
@@ -195,4 +197,6 @@
 
 if(${NS3_VCPKG})
   setup_vcpkg()
+  include_directories(${VCPKG_DIR}/installed/${VCPKG_TRIPLET}/include)
+  link_directories(${VCPKG_DIR}/installed/${VCPKG_TRIPLET}/lib)
 endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/macros-and-definitions.cmake ns-3.41/build-support/macros-and-definitions.cmake
--- ns-3.40/build-support/macros-and-definitions.cmake	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/macros-and-definitions.cmake	2024-02-09 14:44:07.725788800 -0800
@@ -18,296 +18,57 @@
 # Export compile time variable setting the directory to the NS3 root folder
 add_definitions(-DPROJECT_SOURCE_PATH="${PROJECT_SOURCE_DIR}")
 
-# Set INT128 as the default option for INT64X64 and register alternative
-# implementations
-set(NS3_INT64X64 "INT128" CACHE STRING "Int64x64 implementation")
-set_property(CACHE NS3_INT64X64 PROPERTY STRINGS INT128 CAIRO DOUBLE)
-
-# Purposefully hidden options:
-
-# for ease of use, export all libraries and include directories to ns-3 module
-# consumers by default
-option(NS3_REEXPORT_THIRD_PARTY_LIBRARIES "Export all third-party libraries
-and include directories to ns-3 module consumers" ON
+# Add custom and 3rd-part cmake modules to the path
+list(APPEND CMAKE_MODULE_PATH
+     "${PROJECT_SOURCE_DIR}/build-support/custom-modules"
 )
+list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/3rd-party")
 
-# since we can't really do that safely from the CMake side
-option(NS3_ENABLE_SUDO
-       "Set executables ownership to root and enable the SUID flag" OFF
-)
+macro(disable_cmake_warnings)
+  set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "" FORCE)
+endmacro()
+
+macro(enable_cmake_warnings)
+  set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 0 CACHE BOOL "" FORCE)
+endmacro()
 
-# a flag that controls some aspects related to pip packaging
-option(NS3_PIP_PACKAGING "Control aspects related to pip wheel packaging" OFF)
+# Set options that are not really meant to be changed
+include(ns3-hidden-settings)
 
 # Replace default CMake messages (logging) with custom colored messages as early
 # as possible
-include(${PROJECT_SOURCE_DIR}/build-support/3rd-party/colored-messages.cmake)
-
-# WSLv1 doesn't support tap features
-if(EXISTS "/proc/version")
-  file(READ "/proc/version" CMAKE_LINUX_DISTRO)
-  string(FIND "${CMAKE_LINUX_DISTRO}" "Microsoft" res)
-  if(res EQUAL -1)
-    set(WSLv1 False)
-  else()
-    set(WSLv1 True)
-  endif()
-endif()
-
-# Set Linux flag if on Linux
-if(UNIX AND NOT APPLE)
-  set(LINUX TRUE)
-  add_definitions(-D__LINUX__)
-endif()
-
-if(APPLE)
-  add_definitions(-D__APPLE__)
-  # cmake-format: off
-  # Configure find_program to search for AppBundles only if programs are not found in PATH.
-  # This prevents Doxywizard from being launched when the Doxygen.app is installed.
-  # https://gitlab.kitware.com/cmake/cmake/-/blob/master/Modules/FindDoxygen.cmake
-  # cmake-format: on
-  set(CMAKE_FIND_APPBUNDLE "LAST")
-endif()
-
-if(WIN32)
-  set(NS3_PRECOMPILE_HEADERS OFF
-      CACHE BOOL "Precompile module headers to speed up compilation" FORCE
-  )
-
-  # For whatever reason getting M_PI and other math.h definitions from cmath
-  # requires this definition
-  # https://docs.microsoft.com/en-us/cpp/c-runtime-library/math-constants?view=vs-2019
-  add_definitions(/D_USE_MATH_DEFINES)
-endif()
-
-set(cat_command cat)
-
-if(CMAKE_XCODE_BUILD_SYSTEM)
-  set(XCODE True)
-else()
-  set(XCODE False)
-endif()
-
-# Check the number of threads
-include(ProcessorCount)
-ProcessorCount(NumThreads)
-
-# Output folders
-if("${NS3_OUTPUT_DIRECTORY}" STREQUAL "")
-  message(STATUS "Using default output directory ${PROJECT_SOURCE_DIR}/build")
-  set(CMAKE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build) # default output
-                                                          # folder
-else()
-  # Check if NS3_OUTPUT_DIRECTORY is a relative path
-  set(absolute_ns3_output_directory "${NS3_OUTPUT_DIRECTORY}")
-  if(NOT IS_ABSOLUTE ${NS3_OUTPUT_DIRECTORY})
-    set(absolute_ns3_output_directory
-        "${PROJECT_SOURCE_DIR}/${NS3_OUTPUT_DIRECTORY}"
-    )
-  endif()
-
-  # Transform backward slash into forward slash Not the best way to do it since
-  # \ is a scape thing and can be used before whitespaces
-  string(REPLACE "\\" "/" absolute_ns3_output_directory
-                 "${absolute_ns3_output_directory}"
-  )
-
-  if(NOT (EXISTS ${absolute_ns3_output_directory}))
-    message(
-      STATUS
-        "User-defined output directory \"${NS3_OUTPUT_DIRECTORY}\" doesn't exist. Trying to create it"
-    )
-    file(MAKE_DIRECTORY ${absolute_ns3_output_directory})
-    if(NOT (EXISTS ${absolute_ns3_output_directory}))
-      message(
-        FATAL_ERROR
-          "User-defined output directory \"${absolute_ns3_output_directory}\" could not be created. "
-          "Try changing the value of NS3_OUTPUT_DIRECTORY"
-      )
-    endif()
-
-    # If this directory is not inside the ns-3-dev folder, alert users tests may
-    # break
-    if(NOT ("${absolute_ns3_output_directory}" MATCHES "${PROJECT_SOURCE_DIR}"))
-      message(
-        WARNING
-          "User-defined output directory \"${absolute_ns3_output_directory}\" is outside "
-          " of the ns-3 directory ${PROJECT_SOURCE_DIR}, which will break some tests"
-      )
-    endif()
-  endif()
-  message(
-    STATUS
-      "User-defined output directory \"${absolute_ns3_output_directory}\" will be used"
-  )
-  set(CMAKE_OUTPUT_DIRECTORY ${absolute_ns3_output_directory})
-endif()
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/lib)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/lib)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY})
-set(CMAKE_HEADER_OUTPUT_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY}/include/ns3)
-set(THIRD_PARTY_DIRECTORY ${PROJECT_SOURCE_DIR}/3rd-party)
-set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib)
-file(MAKE_DIRECTORY ${CMAKE_OUTPUT_DIRECTORY})
+include(colored-messages)
 
-# Get installation folder default values for each platform and include package
-# configuration macro
+# Get installation folder default values
 include(GNUInstallDirs)
-include(build-support/custom-modules/ns3-cmake-package.cmake)
-
-# Set RPATH not too need LD_LIBRARY_PATH after installing
-set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib:$ORIGIN/:$ORIGIN/../lib")
 
-# Add the 64 suffix to the library path when manually requested with the
-# -DNS3_USE_LIB64=ON flag. May be necessary depending on the target platform.
-# This is used to properly build the manylinux pip wheel.
-if(${NS3_USE_LIB64})
-  link_directories(${CMAKE_OUTPUT_DIRECTORY}/lib64)
-  set(CMAKE_INSTALL_RPATH
-      "${CMAKE_INSTALL_RPATH}:${CMAKE_INSTALL_PREFIX}/lib64:$ORIGIN/:$ORIGIN/../lib64"
-  )
-endif()
+# Define output directories
+include(ns3-output-directory)
 
-# cmake-format: off
-# You are a wizard, Harry!
-# source: https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling
-# cmake-format: on
-set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
-
-if(${XCODE})
-  # Is that so hard not to break people's CI, AAPL? Why would you output the
-  # targets to a Debug/Release subfolder? Why?
-  foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
-    string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
-    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
-        ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
-    )
-    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
-        ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
-    )
-    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG}
-        ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
-    )
-  endforeach()
-endif()
+# Configure packaging related settings
+include(ns3-cmake-package)
 
-# fPIC (position-independent code) and fPIE (position-independent executable)
-set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+# Windows, Linux and Mac related checks
+include(ns3-platform-support)
 
-# do not create a file-level dependency with shared libraries reducing
-# unnecessary relinking
-set(CMAKE_LINK_DEPENDS_NO_SHARED TRUE)
-
-# Identify compiler and check version
-set(below_minimum_msg "compiler is below the minimum required version")
-set(CLANG FALSE)
-if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang")
-  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${AppleClang_MinVersion})
-    message(
-      FATAL_ERROR
-        "Apple Clang ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${AppleClang_MinVersion}"
-    )
-  endif()
-  set(CLANG TRUE)
-endif()
+# Perform compiler and linker checks
+include(ns3-compiler-and-linker-support)
 
-if((NOT CLANG) AND ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
-  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${Clang_MinVersion})
-    message(
-      FATAL_ERROR
-        "Clang ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${Clang_MinVersion}"
-    )
-  endif()
-  set(CLANG TRUE)
-endif()
+# Include CMake file with common find_program HINTS
+include(find-program-hints)
 
-if(CLANG)
-  if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}")
-    add_definitions(-fcolor-diagnostics) # colorize clang++ output
-  endif()
-endif()
+# Custom find_package alternative
+include(ns3-find-external-library)
 
-set(GCC FALSE)
-set(GCC8 FALSE)
-if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
-  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${GNU_MinVersion})
-    message(
-      FATAL_ERROR
-        "GNU ${CMAKE_CXX_COMPILER_VERSION} ${below_minimum_msg} ${GNU_MinVersion}"
-    )
-  endif()
-  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0")
-    # This block is used to identify if GCC8 is being used. In this case, we
-    # want to explicitly link stdc++fs, which is done in
-    # ns3-module-macros.cmake.
-    set(GCC8 TRUE)
-  endif()
-  set(GCC TRUE)
-  add_definitions(-fno-semantic-interposition)
-  if(${NS3_COLORED_OUTPUT} OR "$ENV{CLICOLOR}")
-    add_definitions(-fdiagnostics-color=always) # colorize g++ output
-  endif()
-endif()
-unset(below_minimum_msg)
+# Custom check_deps to search for packages, executables and python dependencies
+include(ns3-check-dependencies)
 
 # Set compiler options and get command to force unused function linkage (useful
 # for libraries)
-set(CXX_UNSUPPORTED_STANDARDS 98 11 14)
-set(CMAKE_CXX_STANDARD_MINIMUM 17)
+set(CXX_UNSUPPORTED_STANDARDS 98 11 14 17)
+set(CMAKE_CXX_STANDARD_MINIMUM 20)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_CXX_EXTENSIONS OFF)
-set(LIB_AS_NEEDED_PRE)
-set(LIB_AS_NEEDED_POST)
-set(STATIC_LINK_FLAGS -static -static-libstdc++ -static-libgcc)
-if(${GCC} AND NOT APPLE)
-  # using GCC
-  set(LIB_AS_NEEDED_PRE -Wl,--no-as-needed)
-  set(LIB_AS_NEEDED_POST -Wl,--as-needed)
-  set(LIB_AS_NEEDED_PRE_STATIC -Wl,--whole-archive,-Bstatic)
-  set(LIB_AS_NEEDED_POST_STATIC -Wl,--no-whole-archive)
-  set(LIB_AS_NEEDED_POST_STATIC_DYN -Wl,-Bdynamic,--no-whole-archive)
-endif()
-
-if(${CLANG} AND APPLE)
-  # using Clang set(LIB_AS_NEEDED_PRE -all_load)
-  set(LIB_AS_NEEDED_POST)
-  set(LIB_AS_NEEDED_PRE_STATIC -Wl,-all_load)
-  set(STATIC_LINK_FLAGS)
-endif()
-
-if(${NS3_FAST_LINKERS})
-  # Search for faster linkers mold and lld, and use them if available
-  mark_as_advanced(MOLD LLD)
-  find_program(MOLD mold)
-  find_program(LLD ld.lld)
-
-  # USING_FAST_LINKER will be defined if a fast linker is being used and its
-  # content will correspond to the fast linker name
-
-  # Mold support was added in GCC 12.1.0
-  if(NOT USING_FAST_LINKER
-     AND NOT (${MOLD} STREQUAL "MOLD-NOTFOUND")
-     AND LINUX
-     AND ${GCC}
-     AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)
-  )
-    set(USING_FAST_LINKER MOLD)
-    add_link_options("-fuse-ld=mold")
-  endif()
-
-  if(NOT USING_FAST_LINKER AND NOT (${LLD} STREQUAL "LLD-NOTFOUND")
-     AND (${GCC} OR ${CLANG})
-  )
-    set(USING_FAST_LINKER LLD)
-    add_link_options("-fuse-ld=lld")
-    if(WIN32)
-      # Clear unsupported linker flags on Windows
-      set(LIB_AS_NEEDED_PRE)
-    endif()
-  endif()
-endif()
 
 # Include CMake files used for compiler checks
 include(CheckIncludeFile) # Used to check a single C header at a time
@@ -315,6 +76,10 @@
 include(CheckIncludeFiles) # Used to check multiple headers at once
 include(CheckFunctionExists)
 
+# Check the number of threads
+include(ProcessorCount)
+ProcessorCount(NumThreads)
+
 macro(SUBDIRLIST result curdir)
   file(GLOB children RELATIVE ${curdir} ${curdir}/*)
   set(dirlist "")
@@ -334,16 +99,25 @@
   # clear cache variables
   unset(build_profile CACHE)
   unset(build_profile_suffix CACHE)
-  unset(lib-ns3-static-objs CACHE)
-  unset(ns3-contrib-libs CACHE)
-  unset(ns3-example-folders CACHE)
-  unset(ns3-execs CACHE)
-  unset(ns3-execs-clean CACHE)
-  unset(ns3-execs-py CACHE)
-  unset(ns3-external-libs CACHE)
-  unset(ns3-headers-to-module-map CACHE)
-  unset(ns3-libs CACHE)
-  unset(ns3-libs-tests CACHE)
+  set(lib-ns3-static-objs
+      ""
+      CACHE
+        INTERNAL
+        "list of object files from module used by NS3_STATIC and NS3_MONOLIB"
+  )
+  set(ns3-contrib-libs "" CACHE INTERNAL "list of processed contrib modules")
+  set(ns3-example-folders "" CACHE INTERNAL "list of example folders")
+  set(ns3-execs "" CACHE INTERNAL "list of c++ executables")
+  set(ns3-execs-clean "" CACHE INTERNAL "list of c++ executables")
+  set(ns3-execs-py "" CACHE INTERNAL "list of python scripts")
+  set(ns3-external-libs ""
+      CACHE INTERNAL
+            "list of non-ns libraries to link to NS3_STATIC and NS3_MONOLIB"
+  )
+  set(ns3-libs "" CACHE INTERNAL "list of processed upstream modules")
+  set(ns3-libs-tests "" CACHE INTERNAL "list of test libraries")
+  set(ns3-optional-visualizer-lib "" CACHE INTERNAL "visualizer library name")
+
   mark_as_advanced(
     build_profile
     build_profile_suffix
@@ -354,45 +128,12 @@
     ns3-execs-clean
     ns3-execs-py
     ns3-external-libs
-    ns3-headers-to-module-map
     ns3-libs
     ns3-libs-tests
+    ns3-optional-visualizer-lib
   )
 endmacro()
 
-# Include CMake file with common find_program HINTS
-include(build-support/3rd-party/find-program-hints.cmake)
-
-# function used to search for package and program dependencies than store list
-# of missing dependencies in the list whose name is stored in missing_deps
-function(check_deps package_deps program_deps missing_deps)
-  set(local_missing_deps)
-  # Search for package dependencies
-  foreach(package ${package_deps})
-    find_package(${package})
-    if(NOT ${${package}_FOUND})
-      list(APPEND local_missing_deps ${package})
-    endif()
-  endforeach()
-
-  # And for program dependencies
-  foreach(program ${program_deps})
-    # CMake likes to cache find_* to speed things up, so we can't reuse names
-    # here or it won't check other dependencies
-    string(TOUPPER ${program} upper_${program})
-    mark_as_advanced(${upper_${program}})
-    find_program(
-      ${upper_${program}} ${program} HINTS ${3RD_PARTY_FIND_PROGRAM_HINTS}
-    )
-    if("${${upper_${program}}}" STREQUAL "${upper_${program}}-NOTFOUND")
-      list(APPEND local_missing_deps ${program})
-    endif()
-  endforeach()
-
-  # Store list of missing dependencies in the parent scope
-  set(${missing_deps} ${local_missing_deps} PARENT_SCOPE)
-endfunction()
-
 # process all options passed in main cmakeLists
 macro(process_options)
   clear_global_cached_variables()
@@ -466,7 +207,7 @@
 
   # Set warning level and warning as errors
   if(${NS3_WARNINGS})
-    if(MSVC)
+    if(DEFINED MSVC)
       add_compile_options(/W3) # /W4 = -Wall + -Wextra
       if(${NS3_WARNINGS_AS_ERRORS})
         add_compile_options(/WX)
@@ -479,7 +220,7 @@
     endif()
   endif()
 
-  include(build-support/custom-modules/ns3-versioning.cmake)
+  include(ns3-versioning)
   set(ENABLE_BUILD_VERSION False)
   configure_embedded_version()
 
@@ -515,18 +256,6 @@
     if("${CLANG_TIDY}" STREQUAL "CLANG_TIDY-NOTFOUND")
       message(FATAL_ERROR "Clang-tidy was not found")
     else()
-      if((${CMAKE_VERSION} VERSION_LESS "3.12.0") AND ${NS3_CCACHE}
-         AND (NOT ("${CCACHE}" STREQUAL "CCACHE-NOTFOUND"))
-      )
-        # CMake <3.12 puts CMAKE_CXX_COMPILER_LAUNCHER in the incorrect place
-        # and CCache ends up being unable to cache anything if calling
-        # clang-tidy https://gitlab.kitware.com/cmake/cmake/-/issues/18266
-        message(
-          FATAL_ERROR
-            "The current CMake ${CMAKE_VERSION} won't ccache objects correctly when running with clang-tidy."
-            "Update CMake to at least version 3.12, or disable either ccache or clang-tidy to continue."
-        )
-      endif()
       set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY}")
     endif()
   else()
@@ -596,12 +325,23 @@
         ${PROJECT_SOURCE_DIR}/build-support/cmake-format-modules.yaml -i
         ${MODULES_CMAKE_FILES}
     )
+    add_custom_target(
+      cmake-format-check
+      COMMAND
+        ${CMAKE_FORMAT_PROGRAM} -c
+        ${PROJECT_SOURCE_DIR}/build-support/cmake-format.yaml --check
+        ${INTERNAL_CMAKE_FILES}
+      COMMAND
+        ${CMAKE_FORMAT_PROGRAM} -c
+        ${PROJECT_SOURCE_DIR}/build-support/cmake-format-modules.yaml --check
+        ${MODULES_CMAKE_FILES}
+    )
     unset(MODULES_CMAKE_FILES)
     unset(INTERNAL_CMAKE_FILES)
   endif()
 
   # If the user has not set a CXX standard version, assume the minimum
-  if(NOT "${CMAKE_CXX_STANDARD}")
+  if(NOT (DEFINED CMAKE_CXX_STANDARD))
     set(CMAKE_CXX_STANDARD ${CMAKE_CXX_STANDARD_MINIMUM})
   endif()
 
@@ -615,14 +355,9 @@
     )
   endif()
 
-  # Honor CMAKE_CXX_STANDARD in check_cxx_source_compiles
-  # https://cmake.org/cmake/help/latest/policy/CMP0067.html
-  cmake_policy(SET CMP0066 NEW)
-  cmake_policy(SET CMP0067 NEW)
-
   # After setting the correct CXX version, we can proceed to check for compiler
   # workarounds
-  include(build-support/custom-modules/ns3-compiler-workarounds.cmake)
+  include(ns3-compiler-workarounds)
 
   if(${NS3_DES_METRICS})
     add_definitions(-DENABLE_DES_METRICS)
@@ -729,12 +464,6 @@
   # Set common include folder (./build/include, where we find ns3/core-module.h)
   include_directories(${CMAKE_OUTPUT_DIRECTORY}/include)
 
-  # find required dependencies
-  list(APPEND CMAKE_MODULE_PATH
-       "${PROJECT_SOURCE_DIR}/build-support/custom-modules"
-  )
-  list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/build-support/3rd-party")
-
   # Include our package managers
   # cmake-format: off
   # Starting with a custom cmake file that provides a Hunter-like interface to vcpkg
@@ -785,21 +514,24 @@
     # find_package(SQLite3 QUIET) # unsupported in CMake 3.10 We emulate the
     # behavior of find_package below
     find_external_library(
-      DEPENDENCY_NAME SQLite3 HEADER_NAME sqlite3.h LIBRARY_NAME sqlite3
+      DEPENDENCY_NAME SQLite3
+      HEADER_NAME sqlite3.h
+      LIBRARY_NAME sqlite3
+      OUTPUT_VARIABLE "ENABLE_SQLITE_REASON"
     )
 
     if(${SQLite3_FOUND})
       set(ENABLE_SQLITE True)
       add_definitions(-DHAVE_SQLITE3)
       include_directories(${SQLite3_INCLUDE_DIRS})
-    else()
-      message(${HIGHLIGHTED_STATUS} "SQLite was not found")
     endif()
   endif()
 
   set(ENABLE_EIGEN False)
   if(${NS3_EIGEN})
+    disable_cmake_warnings()
     find_package(Eigen3 QUIET)
+    enable_cmake_warnings()
 
     if(${EIGEN3_FOUND})
       set(ENABLE_EIGEN True)
@@ -807,34 +539,32 @@
       add_definitions(-DEIGEN_MPL2_ONLY)
       include_directories(${EIGEN3_INCLUDE_DIR})
     else()
-      message(${HIGHLIGHTED_STATUS} "Eigen was not found")
+      set(ENABLE_EIGEN_REASON "Eigen was not found")
     endif()
   endif()
 
   # GTK3 Don't search for it if you don't have it installed, as it take an
   # insane amount of time
+  set(GTK3_FOUND FALSE)
   if(${NS3_GTK3})
+    disable_cmake_warnings()
     find_package(HarfBuzz QUIET)
+    enable_cmake_warnings()
     if(NOT ${HarfBuzz_FOUND})
-      message(${HIGHLIGHTED_STATUS}
-              "Harfbuzz is required by GTK3 and was not found."
-      )
+      set(GTK3_FOUND_REASON "Harfbuzz is required by GTK3 and was not found")
     else()
-      set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "")
+      disable_cmake_warnings()
       find_package(GTK3 QUIET)
-      unset(CMAKE_SUPPRESS_DEVELOPER_WARNINGS CACHE)
+      enable_cmake_warnings()
       if(NOT ${GTK3_FOUND})
-        message(${HIGHLIGHTED_STATUS}
-                "GTK3 was not found. Continuing without it."
-        )
+        set(GTK3_FOUND_REASON "GTK3 was not found")
       else()
         if(${GTK3_VERSION} VERSION_LESS 3.22)
           set(GTK3_FOUND FALSE)
-          message(${HIGHLIGHTED_STATUS}
-                  "GTK3 found with incompatible version ${GTK3_VERSION}"
+          set(GTK3_FOUND_REASON
+              "GTK3 found with incompatible version ${GTK3_VERSION}"
           )
         else()
-          message(STATUS "GTK3 was found.")
           include_directories(${GTK3_INCLUDE_DIRS} ${HarfBuzz_INCLUDE_DIRS})
         endif()
       endif()
@@ -858,11 +588,8 @@
   else()
     find_package(LibXml2 QUIET)
     if(NOT ${LIBXML2_FOUND})
-      message(${HIGHLIGHTED_STATUS}
-              "LibXML2 was not found. Continuing without it."
-      )
+      set(LIBXML2_FOUND_REASON "LibXML2 was not found")
     else()
-      message(STATUS "LibXML2 was found.")
       add_definitions(-DHAVE_LIBXML2)
       include_directories(${LIBXML2_INCLUDE_DIR})
     endif()
@@ -878,31 +605,12 @@
   set(Python3_EXECUTABLE)
   set(Python3_FOUND FALSE)
   set(Python3_INCLUDE_DIRS)
+  set(Python3_Interpreter_FOUND FALSE)
   if(${NS3_PYTHON_BINDINGS})
-    if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0")
-      find_package(Python3 COMPONENTS Interpreter Development)
-    else()
-      # cmake-format: off
-      set(Python_ADDITIONAL_VERSIONS 3.6 3.7 3.8 3.9 3.10 3.11)
-      # cmake-format: on
-      find_package(PythonInterp)
-      find_package(PythonLibs)
-
-      # Move deprecated results into the FindPython3 resulting variables
-      set(Python3_Interpreter_FOUND ${PYTHONINTERP_FOUND})
-      set(Python3_Development_FOUND ${PYTHONLIBS_FOUND})
-      if(${PYTHONINTERP_FOUND})
-        set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
-        set(Python3_FOUND TRUE)
-      endif()
-      if(${PYTHONLIBS_FOUND})
-        set(Python3_LIBRARIES ${PYTHON_LIBRARIES})
-        set(Python3_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
-      endif()
-    endif()
+    find_package(Python3 COMPONENTS Interpreter Development)
   else()
     # If Python was not set yet, use the version found by check_deps
-    check_deps("" "python3" python3_deps)
+    check_deps(python3_deps EXECUTABLES python3)
     if(python3_deps)
       message(FATAL_ERROR "Python3 was not found")
     else()
@@ -964,7 +672,7 @@
           "macOS silicon detected -- see issue 930"
       )
     else()
-      check_python_packages("cppyy" missing_packages)
+      check_deps(missing_packages PYTHON_PACKAGES cppyy)
       if(missing_packages)
         message(
           ${HIGHLIGHTED_STATUS}
@@ -1072,9 +780,6 @@
     else()
       set(ENABLE_VISUALIZER_REASON "Python Bindings are disabled")
     endif()
-    if(ENABLE_VISUALIZER_REASON)
-      message(${HIGHLIGHTED_STATUS} "Visualizer: ${ENABLE_VISUALIZER_REASON}")
-    endif()
   endif()
 
   if(${NS3_COVERAGE} AND (NOT ${ENABLE_TESTS} OR NOT ${ENABLE_EXAMPLES}))
@@ -1097,7 +802,7 @@
       DEPENDS all-test-targets
     )
     if(${ENABLE_EXAMPLES})
-      include(build-support/custom-modules/ns3-coverage.cmake)
+      include(ns3-coverage)
     endif()
   endif()
 
@@ -1127,10 +832,11 @@
     set(CMAKE_REQUIRED_INCLUDES ${Boost_INCLUDE_DIRS})
   endif()
 
+  set(GSL_FOUND FALSE)
   if(${NS3_GSL})
     find_package(GSL QUIET)
     if(NOT ${GSL_FOUND})
-      message(${HIGHLIGHTED_STATUS} "GSL was not found. Continuing without it.")
+      set(GSL_FOUND_REASON "GSL was not found")
     else()
       message(STATUS "GSL was found.")
       add_definitions(-DHAVE_GSL)
@@ -1142,7 +848,7 @@
 
   # First we check for doxygen dependencies
   mark_as_advanced(DOXYGEN)
-  check_deps("" "doxygen;dot;dia;python3" doxygen_docs_missing_deps)
+  check_deps(doxygen_docs_missing_deps EXECUTABLES doxygen dot dia python3)
   if(doxygen_docs_missing_deps)
     message(
       ${HIGHLIGHTED_STATUS}
@@ -1258,8 +964,8 @@
   # Check deps accepts a list of packages, list of programs and name of the
   # return variable
   check_deps(
-    "Sphinx" "epstopdf;pdflatex;latexmk;convert;dvipng"
-    sphinx_docs_missing_deps
+    sphinx_docs_missing_deps CMAKE_PACKAGES Sphinx
+    EXECUTABLES epstopdf pdflatex latexmk convert dvipng
   )
   if(sphinx_docs_missing_deps)
     message(
@@ -1435,6 +1141,7 @@
   if(APPLE OR WSLv1 OR WIN32)
     set(ENABLE_TAP OFF)
     set(ENABLE_EMU OFF)
+    set(ENABLE_FDNETDEV FALSE)
     list(REMOVE_ITEM libs_to_build fd-net-device)
     message(
       STATUS
@@ -1471,9 +1178,10 @@
     set(lib${libname}-obj ${targetname}-obj CACHE INTERNAL "")
   endforeach()
 
-  unset(optional_visualizer_lib)
   if(${ENABLE_VISUALIZER} AND (visualizer IN_LIST libs_to_build))
-    set(optional_visualizer_lib ${libvisualizer})
+    set(ns3-optional-visualizer-lib "${libvisualizer}"
+        CACHE INTERNAL "visualizer library name"
+    )
   endif()
 
   set(PRECOMPILE_HEADERS_ENABLED OFF)
@@ -1600,254 +1308,40 @@
   endif()
 
   if(${NS3_FETCH_OPTIONAL_COMPONENTS})
-    include(
-      build-support/custom-modules/ns3-fetch-optional-modules-dependencies.cmake
-    )
+    include(ns3-fetch-optional-modules-dependencies)
   endif()
 endmacro()
 
-function(set_runtime_outputdirectory target_name output_directory target_prefix)
-  # Prevent duplicate '/' in EXECUTABLE_DIRECTORY_PATH, since it gets translated
-  # to doubled underlines and will cause the ns3 script to fail
-  string(REPLACE "//" "/" output_directory "${output_directory}")
-
-  set(ns3-exec-outputname ns${NS3_VER}-${target_name}${build_profile_suffix})
-  set(ns3-execs "${output_directory}${ns3-exec-outputname};${ns3-execs}"
-      CACHE INTERNAL "list of c++ executables"
-  )
-  set(ns3-execs-clean "${target_prefix}${target_name};${ns3-execs-clean}"
-      CACHE INTERNAL
-            "list of c++ executables without version prefix and build suffix"
-  )
-
-  set_target_properties(
-    ${target_prefix}${target_name}
-    PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${output_directory}
-               RUNTIME_OUTPUT_NAME ${ns3-exec-outputname}
-  )
-  if(${XCODE})
-    # Is that so hard not to break people's CI, AAPL?? Why would you output the
-    # targets to a Debug/Release subfolder? Why?
-    foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
-      string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
-      set_target_properties(
-        ${target_prefix}${target_name}
-        PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${output_directory}
-                   RUNTIME_OUTPUT_NAME_${OUTPUTCONFIG} ${ns3-exec-outputname}
-      )
-    endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES)
-  endif()
-
-  if(${ENABLE_TESTS})
-    add_dependencies(all-test-targets ${target_prefix}${target_name})
-    # Create a CTest entry for each executable
-    if(WIN32)
-      # Windows require this workaround to make sure the DLL files are located
-      add_test(
-        NAME ctest-${target_prefix}${target_name}
-        COMMAND
-          ${CMAKE_COMMAND} -E env
-          "PATH=$ENV{PATH};${CMAKE_RUNTIME_OUTPUT_DIRECTORY};${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
-          ${ns3-exec-outputname}
-        WORKING_DIRECTORY ${output_directory}
-      )
-    else()
-      add_test(NAME ctest-${target_prefix}${target_name}
-               COMMAND ${ns3-exec-outputname}
-               WORKING_DIRECTORY ${output_directory}
-      )
-    endif()
-  endif()
-
-  if(${NS3_CLANG_TIMETRACE})
-    add_dependencies(timeTraceReport ${target_prefix}${target_name})
-  endif()
-endfunction(set_runtime_outputdirectory)
-
-function(get_scratch_prefix prefix)
-  # /path/to/ns-3-dev/scratch/nested-subdir
-  set(temp ${CMAKE_CURRENT_SOURCE_DIR})
-  # remove /path/to/ns-3-dev/ to get scratch/nested-subdir
-  string(REPLACE "${PROJECT_SOURCE_DIR}/" "" temp "${temp}")
-  # replace path separators with underlines
-  string(REPLACE "/" "_" temp "${temp}")
-  # save the prefix value to the passed variable
-  set(${prefix} ${temp}_ PARENT_SCOPE)
-endfunction()
-
-function(build_exec)
-  # Argument parsing
-  set(options IGNORE_PCH STANDALONE)
-  set(oneValueArgs EXECNAME EXECNAME_PREFIX EXECUTABLE_DIRECTORY_PATH
-                   INSTALL_DIRECTORY_PATH
-  )
-  set(multiValueArgs SOURCE_FILES HEADER_FILES LIBRARIES_TO_LINK DEFINITIONS)
-  cmake_parse_arguments(
-    "BEXEC" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
-  )
-
-  # Resolve nested scratch prefixes without user intervention
-  string(REPLACE "${PROJECT_SOURCE_DIR}" "" relative_path
-                 "${CMAKE_CURRENT_SOURCE_DIR}"
-  )
-  if("${relative_path}" MATCHES "scratch" AND "${BEXEC_EXECNAME_PREFIX}"
-                                              STREQUAL ""
-  )
-    get_scratch_prefix(BEXEC_EXECNAME_PREFIX)
-  endif()
-
-  add_executable(
-    ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} "${BEXEC_SOURCE_FILES}"
-  )
-
-  target_compile_definitions(
-    ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} PUBLIC ${BEXEC_DEFINITIONS}
-  )
-
-  if(${PRECOMPILE_HEADERS_ENABLED} AND (NOT ${BEXEC_IGNORE_PCH}))
-    target_precompile_headers(
-      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} REUSE_FROM stdlib_pch_exec
-    )
-  endif()
-
-  if(${NS3_STATIC} AND (NOT BEXEC_STANDALONE))
-    target_link_libraries(
-      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE_STATIC}
-      ${lib-ns3-static}
-    )
-  elseif(${NS3_MONOLIB} AND (NOT BEXEC_STANDALONE))
-    target_link_libraries(
-      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE}
-      ${lib-ns3-monolib} ${LIB_AS_NEEDED_POST}
-    )
-  else()
-    target_link_libraries(
-      ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME} ${LIB_AS_NEEDED_PRE}
-      "${BEXEC_LIBRARIES_TO_LINK}" ${LIB_AS_NEEDED_POST}
-    )
-  endif()
-
-  set_runtime_outputdirectory(
-    "${BEXEC_EXECNAME}" "${BEXEC_EXECUTABLE_DIRECTORY_PATH}/"
-    "${BEXEC_EXECNAME_PREFIX}"
-  )
-
-  if(BEXEC_INSTALL_DIRECTORY_PATH)
-    install(TARGETS ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
-            EXPORT ns3ExportTargets
-            RUNTIME DESTINATION ${BEXEC_INSTALL_DIRECTORY_PATH}
-    )
-    get_property(
-      filename TARGET ${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
-      PROPERTY RUNTIME_OUTPUT_NAME
-    )
-    add_custom_target(
-      uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
-      COMMAND
-        rm ${CMAKE_INSTALL_PREFIX}/${BEXEC_INSTALL_DIRECTORY_PATH}/${filename}
-    )
-    add_dependencies(
-      uninstall uninstall_${BEXEC_EXECNAME_PREFIX}${BEXEC_EXECNAME}
-    )
-  endif()
-endfunction(build_exec)
-
-function(scan_python_examples path)
-  # Skip python examples search in case the bindings are disabled
-  if(NOT ${ENABLE_PYTHON_BINDINGS})
-    return()
-  endif()
-
-  # Search for python examples
-  file(GLOB_RECURSE python_examples ${path}/*.py)
-  foreach(python_example ${python_examples})
-    if(NOT (${python_example} MATCHES "examples-to-run"))
-      set(ns3-execs-py "${python_example};${ns3-execs-py}"
-          CACHE INTERNAL "list of python scripts"
-      )
-    endif()
-  endforeach()
-endfunction()
-
-add_custom_target(copy_all_headers)
-function(copy_headers_before_building_lib libname outputdir headers visibility)
-  foreach(header ${headers})
-    # Copy header to output directory on changes -> too darn slow
-    # configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${header} ${outputdir}/
-    # COPYONLY)
-
-    get_filename_component(
-      header_name ${CMAKE_CURRENT_SOURCE_DIR}/${header} NAME
-    )
-
-    # If output directory does not exist, create it
-    if(NOT (EXISTS ${outputdir}))
-      file(MAKE_DIRECTORY ${outputdir})
-    endif()
-
-    # If header already exists, skip symlinking/stub header creation
-    if(EXISTS ${outputdir}/${header_name})
+function(get_target_includes target output)
+  set(include_directories)
+  get_target_property(include_dirs ${target} INCLUDE_DIRECTORIES)
+  list(REMOVE_DUPLICATES include_dirs)
+  foreach(include_dir ${include_dirs})
+    if(include_dir MATCHES "<")
+      # Skip CMake build and install interface includes
       continue()
+    else()
+      # Append the include directory to a list
+      set(include_directories ${include_directories} -I${include_dir})
     endif()
-
-    # Create a stub header in the output directory, including the real header
-    # inside their respective module
-    get_filename_component(
-      ABSOLUTE_HEADER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${header}" ABSOLUTE
-    )
-    file(WRITE ${outputdir}/${header_name}
-         "#include \"${ABSOLUTE_HEADER_PATH}\"\n"
-    )
   endforeach()
-endfunction(copy_headers_before_building_lib)
-
-function(remove_lib_prefix prefixed_library library)
-  # Check if there is a lib prefix
-  string(FIND "${prefixed_library}" "lib" lib_pos)
-
-  # If there is a lib prefix, try to remove it
-  if(${lib_pos} EQUAL 0)
-    # Check if we still have something remaining after removing the "lib" prefix
-    string(LENGTH ${prefixed_library} len)
-    if(${len} LESS 4)
-      message(FATAL_ERROR "Invalid library name: ${prefixed_library}")
-    endif()
-
-    # Remove lib prefix from module name (e.g. libcore -> core)
-    string(SUBSTRING "${prefixed_library}" 3 -1 unprefixed_library)
-  else()
-    set(unprefixed_library ${prefixed_library})
-  endif()
-
-  # Save the unprefixed library name to the parent scope
-  set(${library} ${unprefixed_library} PARENT_SCOPE)
+  set(${output} ${include_directories} PARENT_SCOPE)
 endfunction()
 
-function(check_for_missing_libraries output_variable_name libraries)
-  set(missing_dependencies)
-  foreach(lib ${libraries})
-    # skip check for ns-3 modules if its a path to a library
-    if(EXISTS ${lib})
-      continue()
-    endif()
-
-    # check if the example depends on disabled modules
-    remove_lib_prefix("${lib}" lib)
-
-    # Check if the module exists in the ns-3 modules list or if it is a
-    # 3rd-party library
-    if(NOT (${lib} IN_LIST ns3-all-enabled-modules))
-      list(APPEND missing_dependencies ${lib})
-    endif()
-  endforeach()
-  set(${output_variable_name} ${missing_dependencies} PARENT_SCOPE)
-endfunction()
+# Macros related to the definition of executables
+include(ns3-executables)
 
 # Import macros used for modules and define specialized versions for src modules
-include(build-support/custom-modules/ns3-module-macros.cmake)
+include(ns3-module-macros)
 
 # Contrib modules counterparts of macros above
-include(build-support/custom-modules/ns3-contributions.cmake)
+include(ns3-contributions)
+
+# Macros for enabled/disabled module filtering
+include(ns3-filter-modules)
+
+# .ns3rc configuration file parsers
+include(ns3-ns3rc-parser)
 
 # Macro to build examples in ns-3-dev/examples/
 macro(build_example)
@@ -1875,670 +1369,41 @@
 
   if((NOT missing_dependencies) AND ${filtered_in})
     # Convert boolean into text to forward argument
+    set(IGNORE_PCH)
     if(${EXAMPLE_IGNORE_PCH})
-      set(IGNORE_PCH IGNORE_PCH)
-    endif()
-    # Create example library with sources and headers
-    # cmake-format: off
-    build_exec(
-      EXECNAME ${EXAMPLE_NAME}
-      SOURCE_FILES ${EXAMPLE_SOURCE_FILES}
-      HEADER_FILES ${EXAMPLE_HEADER_FILES}
-      LIBRARIES_TO_LINK ${EXAMPLE_LIBRARIES_TO_LINK} ${optional_visualizer_lib}
-      EXECUTABLE_DIRECTORY_PATH
-        ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/
-      ${IGNORE_PCH}
-    )
-    # cmake-format: on
-  endif()
-endmacro()
-
-function(filter_modules modules_to_filter all_modules_list filter_in)
-  set(new_modules_to_build)
-  # We are receiving variable names as arguments, so we have to "dereference"
-  # them first That is why we have the duplicated ${${}}
-  foreach(module ${${all_modules_list}})
-    if(${filter_in} (${module} IN_LIST ${modules_to_filter}))
-      list(APPEND new_modules_to_build ${module})
-    endif()
-  endforeach()
-  set(${all_modules_list} ${new_modules_to_build} PARENT_SCOPE)
-endfunction()
-
-function(resolve_dependencies module_name dependencies contrib_dependencies)
-  # Create cache variables to hold dependencies list and visited
-  set(dependency_visited "" CACHE INTERNAL "")
-  set(contrib_dependency_visited "" CACHE INTERNAL "")
-  recursive_dependency(${module_name})
-  if(${module_name} IN_LIST dependency_visited)
-    set(temp ${dependency_visited})
-    list(REMOVE_AT temp 0)
-    set(${dependencies} ${temp} PARENT_SCOPE)
-    set(${contrib_dependencies} ${contrib_dependency_visited} PARENT_SCOPE)
-  else()
-    set(temp ${contrib_dependency_visited})
-    list(REMOVE_AT temp 0)
-    set(${dependencies} ${dependency_visited} PARENT_SCOPE)
-    set(${contrib_dependencies} ${temp} PARENT_SCOPE)
-  endif()
-  unset(dependency_visited CACHE)
-  unset(contrib_dependency_visited CACHE)
-endfunction()
-
-function(filter_libraries cmakelists_contents libraries)
-  string(REGEX MATCHALL "{lib[^}]*[^obj]}" matches "${cmakelists_content}")
-  list(REMOVE_ITEM matches "{libraries_to_link}")
-  string(REPLACE "{lib\${name" "" matches "${matches}") # special case for
-                                                        # src/test
-  string(REPLACE "{lib" "" matches "${matches}")
-  string(REPLACE "}" "" matches "${matches}")
-  set(${libraries} ${matches} PARENT_SCOPE)
-endfunction()
-
-function(recursive_dependency module_name)
-  # Read module CMakeLists.txt and search for ns-3 modules
-  set(src_cmakelist ${PROJECT_SOURCE_DIR}/src/${module_name}/CMakeLists.txt)
-  set(contrib_cmakelist
-      ${PROJECT_SOURCE_DIR}/contrib/${module_name}/CMakeLists.txt
-  )
-  set(contrib FALSE)
-  if(EXISTS ${src_cmakelist})
-    file(READ ${src_cmakelist} cmakelists_content)
-  elseif(EXISTS ${contrib_cmakelist})
-    file(READ ${contrib_cmakelist} cmakelists_content)
-    set(contrib TRUE)
-  else()
-    set(cmakelists_content "")
-    message(${HIGHLIGHTED_STATUS}
-            "The CMakeLists.txt file for module ${module_name} was not found."
-    )
-  endif()
-
-  filter_libraries("${cmakelists_content}" matches)
-
-  # Add this visited module dependencies to the dependencies list
-  if(contrib)
-    set(contrib_dependency_visited
-        "${contrib_dependency_visited};${module_name}" CACHE INTERNAL ""
-    )
-    set(examples_cmakelists ${contrib_cmakelist})
-  else()
-    set(dependency_visited "${dependency_visited};${module_name}" CACHE INTERNAL
-                                                                        ""
-    )
-    set(examples_cmakelists ${src_cmakelist})
-  endif()
-
-  # cmake-format: off
-  # Scan dependencies required by this module examples
-  #if(${ENABLE_EXAMPLES})
-  #  string(REPLACE "${module_name}" "${module_name}/examples" examples_cmakelists ${examples_cmakelists})
-  #  if(EXISTS ${examples_cmakelists})
-  #    file(READ ${examples_cmakelists} cmakelists_content)
-  #    filter_libraries(${cmakelists_content} example_matches)
-  #  endif()
-  #endif()
-  # cmake-format: on
-
-  # For each dependency, call this same function
-  set(matches "${matches};${example_matches}")
-  foreach(match ${matches})
-    if(NOT ((${match} IN_LIST dependency_visited)
-            OR (${match} IN_LIST contrib_dependency_visited))
-    )
-      recursive_dependency(${match})
+      set(IGNORE_PCH "IGNORE_PCH")
     endif()
-  endforeach()
-endfunction()
-
-macro(
-  filter_enabled_and_disabled_modules
-  libs_to_build
-  contrib_libs_to_build
-  NS3_ENABLED_MODULES
-  NS3_DISABLED_MODULES
-  ns3rc_enabled_modules
-  ns3rc_disabled_modules
-)
-  mark_as_advanced(ns3-all-enabled-modules)
 
-  # Before filtering, we set a variable with all scanned modules in the src
-  # directory
-  set(scanned_modules ${${libs_to_build}})
-
-  # Ensure enabled and disable modules lists are using semicolons
-  string(REPLACE "," ";" ${NS3_ENABLED_MODULES} "${${NS3_ENABLED_MODULES}}")
-  string(REPLACE "," ";" ${NS3_DISABLED_MODULES} "${${NS3_DISABLED_MODULES}}")
-
-  # Now that scanning modules finished, we can remove the disabled modules or
-  # replace the modules list with the ones in the enabled list
-  if(${NS3_ENABLED_MODULES} OR ${ns3rc_enabled_modules})
-    # List of enabled modules passed by the command line overwrites list read
-    # from ns3rc
-    if(${NS3_ENABLED_MODULES})
-      set(ns3rc_enabled_modules ${${NS3_ENABLED_MODULES}})
-    endif()
-
-    # Filter enabled modules
-    filter_modules(ns3rc_enabled_modules libs_to_build "")
-    filter_modules(ns3rc_enabled_modules contrib_libs_to_build "")
-
-    # Use recursion to automatically determine dependencies required by the
-    # manually enabled modules
-    foreach(lib ${${contrib_libs_to_build}})
-      resolve_dependencies(${lib} dependencies contrib_dependencies)
-      list(APPEND ${contrib_libs_to_build} "${contrib_dependencies}")
-      list(APPEND ${libs_to_build} "${dependencies}")
-      unset(dependencies)
-      unset(contrib_dependencies)
-    endforeach()
-
-    foreach(lib ${${libs_to_build}})
-      resolve_dependencies(${lib} dependencies contrib_dependencies)
-      list(APPEND ${libs_to_build} "${dependencies}")
-      unset(dependencies)
-      unset(contrib_dependencies)
-    endforeach()
-  endif()
-
-  if(${NS3_DISABLED_MODULES} OR ${ns3rc_disabled_modules})
-    # List of disabled modules passed by the command line overwrites list read
-    # from ns3rc
-
-    if(${NS3_DISABLED_MODULES})
-      set(ns3rc_disabled_modules ${${NS3_DISABLED_MODULES}})
-    endif()
-
-    set(all_libs ${${libs_to_build}};${${contrib_libs_to_build}})
-
-    # We then use the recursive dependency finding to get all dependencies of
-    # all modules
-    foreach(lib ${all_libs})
-      resolve_dependencies(${lib} dependencies contrib_dependencies)
-      set(${lib}_dependencies "${dependencies};${contrib_dependencies}")
-      unset(dependencies)
-      unset(contrib_dependencies)
-    endforeach()
-
-    # Now we can begin removing libraries that require disabled dependencies
-    set(disabled_libs "${${ns3rc_disabled_modules}}")
-    foreach(libo ${all_libs})
-      foreach(lib ${all_libs})
-        foreach(disabled_lib ${disabled_libs})
-          if(${lib} STREQUAL ${disabled_lib})
-            continue()
-          endif()
-          if(${disabled_lib} IN_LIST ${lib}_dependencies)
-            list(APPEND disabled_libs ${lib})
-            break() # proceed to the next lib in all_libs
-          endif()
-        endforeach()
-      endforeach()
-    endforeach()
-
-    # Clean dependencies lists
-    foreach(lib ${all_libs})
-      unset(${lib}_dependencies)
-    endforeach()
-
-    # We can filter out disabled modules
-    filter_modules(disabled_libs libs_to_build "NOT")
-    filter_modules(disabled_libs contrib_libs_to_build "NOT")
-
-    if(core IN_LIST ${libs_to_build})
-      list(APPEND ${libs_to_build} test) # include test module
-    endif()
-  endif()
-
-  # Older CMake versions require this workaround for empty lists
-  if(NOT ${contrib_libs_to_build})
-    set(${contrib_libs_to_build} "")
-  endif()
-
-  # Filter out any eventual duplicates
-  list(REMOVE_DUPLICATES ${libs_to_build})
-  list(REMOVE_DUPLICATES ${contrib_libs_to_build})
-
-  # Export list with all enabled modules (used to separate ns libraries from
-  # non-ns libraries in ns3_module_macros)
-  set(ns3-all-enabled-modules "${${libs_to_build}};${${contrib_libs_to_build}}"
-      CACHE INTERNAL "list with all enabled modules"
-  )
-endmacro()
-
-# Parse .ns3rc
-macro(parse_ns3rc enabled_modules disabled_modules examples_enabled
-      tests_enabled
-)
-  # Try to find .ns3rc
-  find_file(NS3RC .ns3rc PATHS /etc $ENV{HOME} $ENV{USERPROFILE}
-                               ${PROJECT_SOURCE_DIR} NO_CACHE
-  )
-
-  # Set variables with default values (all modules, no examples nor tests)
-  set(${enabled_modules} "")
-  set(${disabled_modules} "")
-  set(${examples_enabled} "FALSE")
-  set(${tests_enabled} "FALSE")
-
-  if(NOT (${NS3RC} STREQUAL "NS3RC-NOTFOUND"))
-    message(${HIGHLIGHTED_STATUS}
-            "Configuration file .ns3rc being used : ${NS3RC}"
+    set(current_directory ${CMAKE_CURRENT_SOURCE_DIR})
+    string(REPLACE "${PROJECT_SOURCE_DIR}" "" current_directory
+                   "${current_directory}"
     )
-    file(READ ${NS3RC} ns3rc_contents)
-    # Check if ns3rc file is CMake or Python based and act accordingly
-    if(ns3rc_contents MATCHES "ns3rc_*")
-      include(${NS3RC})
-    else()
-      parse_python_ns3rc(
-        "${ns3rc_contents}" ${enabled_modules} ${examples_enabled}
-        ${tests_enabled} ${NS3RC}
+    if("${current_directory}" MATCHES ".*/(src|contrib)/.*")
+      message(
+        FATAL_ERROR
+          "build_example() macro is meant for ns-3-dev/examples, and not for modules. Use build_lib_example() instead."
       )
     endif()
-  endif()
-endmacro(parse_ns3rc)
-
-function(parse_python_ns3rc ns3rc_contents enabled_modules examples_enabled
-         tests_enabled ns3rc_location
-)
-  # Save .ns3rc backup
-  file(WRITE ${ns3rc_location}.backup ${ns3rc_contents})
-
-  # Match modules_enabled list
-  if(ns3rc_contents MATCHES "modules_enabled.*\\[(.*).*\\]")
-    set(${enabled_modules} ${CMAKE_MATCH_1})
-    if(${enabled_modules} MATCHES "all_modules")
-      # If all modules, just clean the filter and all modules will get built by
-      # default
-      set(${enabled_modules})
-    else()
-      # If modules are listed, remove quotes and replace commas with semicolons
-      # transforming a string into a cmake list
-      string(REPLACE "," ";" ${enabled_modules} "${${enabled_modules}}")
-      string(REPLACE "'" "" ${enabled_modules} "${${enabled_modules}}")
-      string(REPLACE "\"" "" ${enabled_modules} "${${enabled_modules}}")
-      string(REPLACE " " "" ${enabled_modules} "${${enabled_modules}}")
-      string(REPLACE "\n" ";" ${enabled_modules} "${${enabled_modules}}")
-      list(SORT ${enabled_modules})
-
-      # Remove possibly empty entry
-      list(REMOVE_ITEM ${enabled_modules} "")
-      foreach(element ${${enabled_modules}})
-        # Inspect each element for comments
-        if(${element} MATCHES "#.*")
-          list(REMOVE_ITEM ${enabled_modules} ${element})
-        endif()
-      endforeach()
-    endif()
-  endif()
-
-  string(REPLACE "True" "ON" ns3rc_contents ${ns3rc_contents})
-  string(REPLACE "False" "OFF" ns3rc_contents ${ns3rc_contents})
-
-  # Match examples_enabled flag
-  if(ns3rc_contents MATCHES "examples_enabled = (ON|OFF)")
-    set(${examples_enabled} ${CMAKE_MATCH_1})
-  endif()
-
-  # Match tests_enabled flag
-  if(ns3rc_contents MATCHES "tests_enabled = (ON|OFF)")
-    set(${tests_enabled} ${CMAKE_MATCH_1})
-  endif()
-
-  # Save variables to parent scope
-  set(${enabled_modules} "${${enabled_modules}}" PARENT_SCOPE)
-  set(${examples_enabled} "${${examples_enabled}}" PARENT_SCOPE)
-  set(${tests_enabled} "${${tests_enabled}}" PARENT_SCOPE)
-
-  # Save updated .ns3rc file
-  message(
-    ${HIGHLIGHTED_STATUS}
-    "The python-based .ns3rc file format is deprecated and was updated to the CMake format"
-  )
-  configure_file(
-    ${PROJECT_SOURCE_DIR}/build-support/.ns3rc-template ${ns3rc_location} @ONLY
-  )
-endfunction(parse_python_ns3rc)
 
-function(log_find_searched_paths)
-  # Parse arguments
-  set(options)
-  set(oneValueArgs TARGET_TYPE TARGET_NAME SEARCH_RESULT SEARCH_SYSTEM_PREFIX)
-  set(multiValueArgs SEARCH_PATHS SEARCH_SUFFIXES)
-  cmake_parse_arguments(
-    "LOGFIND" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
-  )
-
-  # Get searched paths and add cmake_system_prefix_path if not explicitly marked
-  # not to include it
-  set(tsearch_paths ${LOGFIND_SEARCH_PATHS})
-  if("${LOGFIND_SEARCH_SYSTEM_PREFIX}" STREQUAL "")
-    list(APPEND tsearch_paths "${CMAKE_SYSTEM_PREFIX_PATH}")
-  endif()
-
-  set(log_find
-      "Looking for ${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} in:\n"
-  )
-  # For each search path and suffix combination, print a line
-  foreach(tsearch_path ${tsearch_paths})
-    foreach(suffix ${LOGFIND_SEARCH_SUFFIXES})
-      string(APPEND log_find
-             "\t${tsearch_path}${suffix}/${LOGFIND_TARGET_NAME}\n"
-      )
-    endforeach()
-  endforeach()
+    get_filename_component(examplefolder ${CMAKE_CURRENT_SOURCE_DIR} NAME)
 
-  # Add a final line saying if the file was found and where, or if it was not
-  # found
-  if("${${LOGFIND_SEARCH_RESULT}}" STREQUAL "${LOGFIND_SEARCH_RESULT}-NOTFOUND")
-    string(APPEND log_find
-           "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was not found\n"
-    )
-  else()
-    string(
-      APPEND
-      log_find
-      "\n\t${LOGFIND_TARGET_TYPE} ${LOGFIND_TARGET_NAME} was found in ${${LOGFIND_SEARCH_RESULT}}\n"
-    )
-  endif()
-
-  # Replace duplicate path separators
-  string(REPLACE "//" "/" log_find "${log_find}")
-
-  # Print find debug message similar to the one produced by
-  # CMAKE_FIND_DEBUG_MODE=true in CMake >= 3.17
-  message(STATUS ${log_find})
-endfunction()
-
-function(find_external_library)
-  # Parse arguments
-  set(options QUIET)
-  set(oneValueArgs DEPENDENCY_NAME HEADER_NAME LIBRARY_NAME)
-  set(multiValueArgs HEADER_NAMES LIBRARY_NAMES PATH_SUFFIXES SEARCH_PATHS)
-  cmake_parse_arguments(
-    "FIND_LIB" "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}
-  )
-
-  # Set the external package/dependency name
-  set(name ${FIND_LIB_DEPENDENCY_NAME})
-
-  # We process individual and list of headers and libraries by transforming them
-  # into lists
-  set(library_names "${FIND_LIB_LIBRARY_NAME};${FIND_LIB_LIBRARY_NAMES}")
-  set(header_names "${FIND_LIB_HEADER_NAME};${FIND_LIB_HEADER_NAMES}")
-
-  # Just changing the parsed argument name back to something shorter
-  set(search_paths ${FIND_LIB_SEARCH_PATHS})
-  set(path_suffixes "${FIND_LIB_PATH_SUFFIXES}")
-
-  set(not_found_libraries)
-  set(library_dirs)
-  set(libraries)
-
-  # Include parent directories in the search paths to handle Bake cases
-  get_filename_component(parent_project_dir ${PROJECT_SOURCE_DIR} DIRECTORY)
-  get_filename_component(
-    grandparent_project_dir ${parent_project_dir} DIRECTORY
-  )
-  set(project_parent_dirs ${parent_project_dir} ${grandparent_project_dir})
-
-  # Paths and suffixes where libraries will be searched on
-  set(library_search_paths
-      ${search_paths}
-      ${project_parent_dirs}
-      ${CMAKE_OUTPUT_DIRECTORY} # Search for libraries in ns-3-dev/build
-      ${CMAKE_INSTALL_PREFIX} # Search for libraries in the install directory
-                              # (e.g. /usr/)
-      $ENV{LD_LIBRARY_PATH} # Search for libraries in LD_LIBRARY_PATH
-                            # directories
-      $ENV{PATH} # Search for libraries in PATH directories
-  )
-  # cmake-format: off
-  #
-  # Split : separated entries from environment variables
-  # by replacing separators with ;
-  #
-  # cmake-format: on
-  string(REPLACE ":" ";" library_search_paths "${library_search_paths}")
-
-  set(suffixes /build /lib /build/lib / /bin ${path_suffixes})
-
-  # For each of the library names in LIBRARY_NAMES or LIBRARY_NAME
-  foreach(library ${library_names})
-    # We mark this value is advanced not to pollute the configuration with
-    # ccmake with the cache variables used internally
-    mark_as_advanced(${name}_library_internal_${library})
-
-    # We search for the library named ${library} and store the results in
-    # ${name}_library_internal_${library}
-    find_library(
-      ${name}_library_internal_${library} ${library}
-      HINTS ${library_search_paths} PATH_SUFFIXES ${suffixes}
-    )
+    # Create example library with sources and headers
     # cmake-format: off
-    # Note: the PATH_SUFFIXES above apply to *ALL* PATHS and HINTS Which
-    # translates to CMake searching on standard library directories
-    # CMAKE_SYSTEM_PREFIX_PATH, user-settable CMAKE_PREFIX_PATH or
-    # CMAKE_LIBRARY_PATH and the directories listed above
-    #
-    # e.g.  from Ubuntu 22.04 CMAKE_SYSTEM_PREFIX_PATH =
-    # /usr/local;/usr;/;/usr/local;/usr/X11R6;/usr/pkg;/opt
-    #
-    # Searched directories without suffixes
-    #
-    # ${CMAKE_SYSTEM_PREFIX_PATH}[0] = /usr/local/
-    # ${CMAKE_SYSTEM_PREFIX_PATH}[1] = /usr
-    # ${CMAKE_SYSTEM_PREFIX_PATH}[2] = /
-    # ...
-    # ${CMAKE_SYSTEM_PREFIX_PATH}[6] = /opt
-    # ${LD_LIBRARY_PATH}[0]
-    # ...
-    # ${LD_LIBRARY_PATH}[m]
-    # ${PATH}[0]
-    # ...
-    # ${PATH}[m]
-    #
-    # Searched directories with suffixes include all of the directories above
-    # plus all suffixes
-    # PATH_SUFFIXES /build /lib /build/lib / /bin # ${path_suffixes}
-    #
-    # /usr/local/build
-    # /usr/local/lib
-    # /usr/local/build/lib
-    # /usr/local/bin
-    # ...
-    #
-    # cmake-format: on
-    # Or enable NS3_VERBOSE to print the searched paths
-
-    # Print tested paths to the searched library and if it was found
-    if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0"))
-      log_find_searched_paths(
-        TARGET_TYPE
-        Library
-        TARGET_NAME
-        ${library}
-        SEARCH_RESULT
-        ${name}_library_internal_${library}
-        SEARCH_PATHS
-        ${library_search_paths}
-        SEARCH_SUFFIXES
-        ${suffixes}
-      )
-    endif()
-
-    # After searching the library, the internal variable should have either the
-    # absolute path to the library or the name of the variable appended with
-    # -NOTFOUND
-    if("${${name}_library_internal_${library}}" STREQUAL
-       "${name}_library_internal_${library}-NOTFOUND"
-    )
-      # We keep track of libraries that were not found
-      list(APPEND not_found_libraries ${library})
-    else()
-      # We get the name of the parent directory of the library and append the
-      # library to a list of found libraries
-      get_filename_component(
-        ${name}_library_dir_internal ${${name}_library_internal_${library}}
-        DIRECTORY
-      ) # e.g. lib/openflow.(so|dll|dylib|a) -> lib
-      list(APPEND library_dirs ${${name}_library_dir_internal})
-      list(APPEND libraries ${${name}_library_internal_${library}})
-    endif()
-  endforeach()
-
-  # For each library that was found (e.g. /usr/lib/pthread.so), get their parent
-  # directory (/usr/lib) and its parent (/usr)
-  set(parent_dirs)
-  foreach(libdir ${library_dirs})
-    get_filename_component(parent_libdir ${libdir} DIRECTORY)
-    get_filename_component(parent_parent_libdir ${parent_libdir} DIRECTORY)
-    list(APPEND parent_dirs ${libdir} ${parent_libdir} ${parent_parent_libdir})
-  endforeach()
-
-  set(header_search_paths
-      ${search_paths}
-      ${parent_dirs}
-      ${project_parent_dirs}
-      ${CMAKE_OUTPUT_DIRECTORY} # Search for headers in
-      # ns-3-dev/build
-      ${CMAKE_INSTALL_PREFIX} # Search for headers in the install
-  )
-
-  set(not_found_headers)
-  set(include_dirs)
-  foreach(header ${header_names})
-    # The same way with libraries, we mark the internal variable as advanced not
-    # to pollute ccmake configuration with variables used internally
-    mark_as_advanced(${name}_header_internal_${header})
-    set(suffixes
-        /build
-        /include
-        /build/include
-        /build/include/${name}
-        /include/${name}
-        /${name}
-        /
-        ${path_suffixes}
+    build_exec(
+            EXECNAME ${EXAMPLE_NAME}
+            SOURCE_FILES ${EXAMPLE_SOURCE_FILES}
+            HEADER_FILES ${EXAMPLE_HEADER_FILES}
+            LIBRARIES_TO_LINK ${EXAMPLE_LIBRARIES_TO_LINK} ${ns3-optional-visualizer-lib}
+            EXECUTABLE_DIRECTORY_PATH
+            ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/
+            ${IGNORE_PCH}
     )
-
-    # cmake-format: off
-    # Here we search for the header file named ${header} and store the result in
-    # ${name}_header_internal_${header}
-    #
-    # The same way we did with libraries, here we search on
-    # CMAKE_SYSTEM_PREFIX_PATH, along with user-settable ${search_paths}, the
-    # parent directories from the libraries, CMAKE_OUTPUT_DIRECTORY and
-    # CMAKE_INSTALL_PREFIX
-    #
-    # And again, for each of them, for every suffix listed /usr/local/build
-    # /usr/local/include
-    # /usr/local/build/include
-    # /usr/local/build/include/${name}
-    # /usr/local/include/${name}
-    # ...
-    #
     # cmake-format: on
-    # Or enable NS3_VERBOSE to get the searched paths printed while configuring
-
-    find_file(${name}_header_internal_${header} ${header}
-              HINTS ${header_search_paths} # directory (e.g. /usr/)
-                    ${header_skip_system_prefix} PATH_SUFFIXES ${suffixes}
-    )
-
-    # Print tested paths to the searched header and if it was found
-    if(${NS3_VERBOSE} AND (${CMAKE_VERSION} VERSION_LESS "3.17.0"))
-      log_find_searched_paths(
-        TARGET_TYPE
-        Header
-        TARGET_NAME
-        ${header}
-        SEARCH_RESULT
-        ${name}_header_internal_${header}
-        SEARCH_PATHS
-        ${header_search_paths}
-        SEARCH_SUFFIXES
-        ${suffixes}
-        SEARCH_SYSTEM_PREFIX
-        ${header_skip_system_prefix}
-      )
-    endif()
-
-    # If the header file was not found, append to the not-found list
-    if("${${name}_header_internal_${header}}" STREQUAL
-       "${name}_header_internal_${header}-NOTFOUND"
-    )
-      list(APPEND not_found_headers ${header})
-    else()
-      # If the header file was found, get their directories and the parent of
-      # their directories to add as include directories
-      get_filename_component(
-        header_include_dir ${${name}_header_internal_${header}} DIRECTORY
-      ) # e.g. include/click/ (simclick.h) -> #include <simclick.h> should work
-      get_filename_component(
-        header_include_dir2 ${header_include_dir} DIRECTORY
-      ) # e.g. include/(click) -> #include <click/simclick.h> should work
-      list(APPEND include_dirs ${header_include_dir} ${header_include_dir2})
-    endif()
-  endforeach()
-
-  # Remove duplicate include directories
-  if(include_dirs)
-    list(REMOVE_DUPLICATES include_dirs)
   endif()
+endmacro()
 
-  # If we find both library and header, we export their values
-  if((NOT not_found_libraries) AND (NOT not_found_headers))
-    set(${name}_INCLUDE_DIRS "${include_dirs}" PARENT_SCOPE)
-    set(${name}_LIBRARIES "${libraries}" PARENT_SCOPE)
-    set(${name}_HEADER ${${name}_header_internal} PARENT_SCOPE)
-    set(${name}_FOUND TRUE PARENT_SCOPE)
-    if(NOT ${FIND_LIB_QUIET})
-      message(STATUS "find_external_library: ${name} was found.")
-    endif()
-  else()
-    set(${name}_INCLUDE_DIRS PARENT_SCOPE)
-    set(${name}_LIBRARIES PARENT_SCOPE)
-    set(${name}_HEADER PARENT_SCOPE)
-    set(${name}_FOUND FALSE PARENT_SCOPE)
-    if(NOT ${FIND_LIB_QUIET})
-      message(
-        ${HIGHLIGHTED_STATUS}
-        "find_external_library: ${name} was not found. Missing headers: \"${not_found_headers}\" and missing libraries: \"${not_found_libraries}\"."
-      )
-    endif()
-  endif()
-endfunction()
-
-function(get_target_includes target output)
-  set(include_directories)
-  get_target_property(include_dirs ${target} INCLUDE_DIRECTORIES)
-  list(REMOVE_DUPLICATES include_dirs)
-  foreach(include_dir ${include_dirs})
-    if(include_dir MATCHES "<")
-      # Skip CMake build and install interface includes
-      continue()
-    else()
-      # Append the include directory to a list
-      set(include_directories ${include_directories} -I${include_dir})
-    endif()
-  endforeach()
-  set(${output} ${include_directories} PARENT_SCOPE)
-endfunction()
-
-function(check_python_packages packages missing_packages)
-  set(missing)
-  foreach(package ${packages})
-    execute_process(
-      COMMAND ${Python3_EXECUTABLE} -c "import ${package}"
-      RESULT_VARIABLE return_code OUTPUT_QUIET ERROR_QUIET
-    )
-    if(NOT (${return_code} EQUAL 0))
-      list(APPEND missing ${package})
-    endif()
-  endforeach()
-  set(${missing_packages} "${missing}" PARENT_SCOPE)
-endfunction()
+# Macros to write the lock file
+include(ns3-lock)
 
-include(build-support/custom-modules/ns3-lock.cmake)
-include(build-support/custom-modules/ns3-configtable.cmake)
+# Macros to build the config table file
+include(ns3-configtable)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/pip-wheel/auditwheel-exclude-list.py ns-3.41/build-support/pip-wheel/auditwheel-exclude-list.py
--- ns-3.40/build-support/pip-wheel/auditwheel-exclude-list.py	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/pip-wheel/auditwheel-exclude-list.py	2024-02-09 14:44:07.725788800 -0800
@@ -8,4 +8,4 @@
         continue
     for lib in os.listdir(lib_dir):
         if "libns3" in lib:
-            print(f"--exclude {lib}", end=' ')
+            print(f"--exclude {lib}", end=" ")
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/pip-wheel/ns/__init__.py ns-3.41/build-support/pip-wheel/ns/__init__.py
--- ns-3.40/build-support/pip-wheel/ns/__init__.py	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/pip-wheel/ns/__init__.py	2024-02-09 14:44:07.725788800 -0800
@@ -4,7 +4,8 @@
 
 try:
     import ns3.ns
-    sys.modules['ns'] = ns3.ns
+
+    sys.modules["ns"] = ns3.ns
 except ModuleNotFoundError as e:
     print("Install the ns3 package with pip install ns3.", file=sys.stderr)
     exit(-1)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/pip-wheel/visualizer/__init__.py ns-3.41/build-support/pip-wheel/visualizer/__init__.py
--- ns-3.40/build-support/pip-wheel/visualizer/__init__.py	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/pip-wheel/visualizer/__init__.py	2024-02-09 14:44:07.725788800 -0800
@@ -8,4 +8,4 @@
     print("Install the ns3 package with pip install ns3.", file=sys.stderr)
     exit(-1)
 
-from ns3.visualizer import start, register_plugin, set_bounds, add_initialization_hook
+from ns3.visualizer import add_initialization_hook, register_plugin, set_bounds, start
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt ns-3.41/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt
--- ns-3.40/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
@@ -1,8 +0,0 @@
-build_lib(
-        LIBNAME test-src-dependant-on-contrib
-        SOURCE_FILES src-source.cc
-        HEADER_FILES src-header.h
-        LIBRARIES_TO_LINK ${libcore}
-                          ${libtest-contrib-dependency}
-)
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt ns-3.41/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt
--- ns-3.40/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
@@ -1,6 +0,0 @@
-build_lib_example(
-        NAME source-example
-        SOURCE_FILES source-example.cc
-        LIBRARIES_TO_LINK
-        ${libtest-src-dependant-on-contrib}
-)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc ns-3.41/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc
--- ns-3.40/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,8 +0,0 @@
-#include <ns3/src-header.h>
-
-int
-main()
-{
-    testPrint2();
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependant-on-contrib/src-header.h ns-3.41/build-support/test-files/test-src-dependant-on-contrib/src-header.h
--- ns-3.40/build-support/test-files/test-src-dependant-on-contrib/src-header.h	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/test-files/test-src-dependant-on-contrib/src-header.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,6 +0,0 @@
-#ifndef NS3_SRC_SOURCE2_H
-#define NS3_SRC_SOURCE2_H
-
-void testPrint2();
-
-#endif // NS3_SOURCE_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependant-on-contrib/src-source.cc ns-3.41/build-support/test-files/test-src-dependant-on-contrib/src-source.cc
--- ns-3.40/build-support/test-files/test-src-dependant-on-contrib/src-source.cc	2023-09-27 13:30:37.530838700 -0700
+++ ns-3.41/build-support/test-files/test-src-dependant-on-contrib/src-source.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,7 +0,0 @@
-#include <ns3/contrib-header.h>
-
-void
-testPrint2()
-{
-    testPrint();
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependent-on-contrib/CMakeLists.txt ns-3.41/build-support/test-files/test-src-dependent-on-contrib/CMakeLists.txt
--- ns-3.40/build-support/test-files/test-src-dependent-on-contrib/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/test-files/test-src-dependent-on-contrib/CMakeLists.txt	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,8 @@
+build_lib(
+        LIBNAME test-src-dependent-on-contrib
+        SOURCE_FILES src-source.cc
+        HEADER_FILES src-header.h
+        LIBRARIES_TO_LINK ${libcore}
+                          ${libtest-contrib-dependency}
+)
+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependent-on-contrib/examples/CMakeLists.txt ns-3.41/build-support/test-files/test-src-dependent-on-contrib/examples/CMakeLists.txt
--- ns-3.40/build-support/test-files/test-src-dependent-on-contrib/examples/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/test-files/test-src-dependent-on-contrib/examples/CMakeLists.txt	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,6 @@
+build_lib_example(
+        NAME source-example
+        SOURCE_FILES source-example.cc
+        LIBRARIES_TO_LINK
+        ${libtest-src-dependent-on-contrib}
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependent-on-contrib/examples/source-example.cc ns-3.41/build-support/test-files/test-src-dependent-on-contrib/examples/source-example.cc
--- ns-3.40/build-support/test-files/test-src-dependent-on-contrib/examples/source-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/test-files/test-src-dependent-on-contrib/examples/source-example.cc	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,8 @@
+#include <ns3/src-header.h>
+
+int
+main()
+{
+    testPrint2();
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependent-on-contrib/src-header.h ns-3.41/build-support/test-files/test-src-dependent-on-contrib/src-header.h
--- ns-3.40/build-support/test-files/test-src-dependent-on-contrib/src-header.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/test-files/test-src-dependent-on-contrib/src-header.h	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,6 @@
+#ifndef NS3_SRC_SOURCE2_H
+#define NS3_SRC_SOURCE2_H
+
+void testPrint2();
+
+#endif // NS3_SOURCE_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/build-support/test-files/test-src-dependent-on-contrib/src-source.cc ns-3.41/build-support/test-files/test-src-dependent-on-contrib/src-source.cc
--- ns-3.40/build-support/test-files/test-src-dependent-on-contrib/src-source.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/build-support/test-files/test-src-dependent-on-contrib/src-source.cc	2024-02-09 14:44:07.725788800 -0800
@@ -0,0 +1,7 @@
+#include <ns3/contrib-header.h>
+
+void
+testPrint2()
+{
+    testPrint();
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/CHANGES.md ns-3.41/CHANGES.md
--- ns-3.40/CHANGES.md	2023-09-27 13:30:37.526838800 -0700
+++ ns-3.41/CHANGES.md	2024-02-09 14:44:07.721789000 -0800
@@ -13,6 +13,66 @@
 
 This file is a best-effort approach to solving this issue; we will do our best but can guarantee that there will be things that fall through the cracks, unfortunately. If you, as a user, can suggest improvements to this file based on your experience, please contribute a patch or drop us a note on ns-developers mailing list.
 
+Changes from ns-3.40 to ns-3.41
+-------------------------------
+
+### New API
+
+* (core) Added `BernoulliRandomVariable` class implementing the bernoulli random variable, and `BinomialRandomVariable` class implementing the binomial random variable.
+* (core) Added wrapper around `UniformRandomVariable` to meet the C++11 requirements of UniformRandomBitGenerator.
+* (core) Added method to `GetStopEvent()` from `Simulator`
+* (internet) It is now possible to set the TOS field for IPv4 ICMP Echo Requests/Responses, via a new `Tos` attribute.
+* (spectrum) `SpectrumSignalParameters` is extended to include two new members called: `spectrumChannelMatrix` and `precodingMatrix` which are the key information needed to support MIMO simulations.
+* (spectrum) `SpectrumChannel::AssignStreams()` is added, to allow random variable stream assignment for all propagation loss and delay models added to the channel.
+* (wifi) Added new attribute `ChannelAccessManager:GenerateBackoffIfTxopWithoutTx` to invoke the backoff procedure when an AC gains the right to start a TXOP but it does not transmit any frame, provided that the queue is not actually empty. No transmission may occur,e.g., due to constraints associated with EMLSR operations. This possibility is specified by the current draft revision of the IEEE 802.11 standard.
+
+### Changes to existing API
+
+* (antenna) `UniformPlannarArray` has new attributes `NumVerticalPorts`, `NumHorizontalPorts`, and `IsDualPolarized`.
+* (antenna) `GetNumberOfElements` is renamed to `GetNumElems` for the sake of simplifying the long lines of code that use complex mathematical expressions.
+* (core) The EnumValue class now also supports enum class in addition to plain enums.  As a result of this change, attributes that wrap enums must update the syntax for the `MakeEnumAccessor` method call. Where you once were able to write, for example (from attribute-test-suite.cc):
+
+  ```cpp
+  MakeEnumAccessor(&AttributeObjectTest::m_enum),
+  ```
+
+  you must now write it with a template parameter such as:
+
+  ```cpp
+  MakeEnumAccessor<Test_e>(&AttributeObjectTest::m_enum),
+  ```
+* (internet) Deprecated `Ipv4::WeakEsModel` and `Ipv4::GetWeakEsModel()`, `Ipv4::SetWeakEsModel(bool)` methods. Moved `Ipv6L3Protocol::StrongEndSystemModel` to `Ipv6::StrongEndSystemModel` and added `Ipv4::StrongEndSystemModel` with corresponding `GetStrongEndSystemModel()` and `SetStrongEndSystemModel(bool)` methods to improve end system model configuration options.
+* (lr-wpan) Change the CapabilityField parameter in `LrWpanMac::MlmeAssociateRequest` and `LrWpanMac::MlmeAssociateIndication` to a standard bitmap.
+* (lr-wpan) Change the MAC SuperframeField usage to a standard bitmap, this change impact parameters in the `BeaconPayloadHeader`.
+* (lr-wpan) Create a new abstract class that defines the form of any Lr-wpan MAC layers (`LrWpanMacBase`).
+* (lr-wpan) Add the capability to see the enum values of the MAC transition states in log prints for easier debugging.
+* (lr-wpan) Group MAC status enumerations into a single `LrWpanMacStatus` enumeration in `lr-wpan-mac-base.h.`
+* The spelling of the following files, classes, functions, constants, defines and enumerated values was corrected; this will affect existing users who were using them with the misspelling.
+  * (lte) Struct member `fdbetsFlowPerf_t::lastTtiBytesTrasmitted` in file `fdbet-ff-mac-scheduler.h` was renamed `fdbetsFlowPerf_t::lastTtiBytesTransmitted`.
+  * (lte) Struct member `tdbetsFlowPerf_t::lastTtiBytesTrasmitted` in file `tdbet-ff-mac-scheduler.h` was renamed `fdbetsFlowPerf_t::lastTtiBytesTransmitted`.
+  * (lte) Struct member `pfsFlowPerf_t::lastTtiBytesTrasmitted` in file `pf-ff-mac-scheduler.h` was renamed `fdbetsFlowPerf_t::lastTtiBytesTransmitted`.
+* (sixlowpan) Remove `ForceEtherType` and `EtherType` attributes, and use RFC 7973 EtherType for interfaces supporting an EtherType.
+* (spectrum) `PhasedArraySpectrumPropagationLossModel::CalcRxPowerSpectralDensity` return type is changed from `Ptr<SpectrumValue>` to `Ptr<SpectrumSignalParameters>` to support MIMO, because when multiple transmit and receive antenna ports are present, it is not enough to have a single PSD (represented by `Ptr<SpectrumValue>`) but also the 3D channel matrix is needed per receive and transmit antenna port. Notice that `CalcRxPowerSpectralDensity` is typically called from within `MultiModelSpectrumChannel`, but if some external ns-3 module is calling directly this function, it can still access to its original return value through `Ptr<SpectrumSignalParameters>` which contains `Ptr<SpectrumValue>`.
+* (wifi) The `HeConfiguration::MpduBufferSize` attribute is now obsolete. Use the `WifiMac::MpduBufferSize` attribute instead.
+* (wifi) The `LinkSetupCanceled` trace source of `StaWifiMac` has been obsoleted because disassociation does not occur at link level for non-AP MLDs.
+* (wifi) The default value for `WifiRemoteStationManager::RtsCtsThreshold` has been increased from 65535 to 4692480.
+* (wifi) `SpectrumWifiHelper::SpectrumChannelSwitched()` is now static
+
+### Changes to build system
+
+* In preparation to enable C++20, the following actions have been taken due to compiler issues:
+  * Precompiled headers have been disabled in GCC versions >= 12.2.
+  * The `restrict` warning has been disabled in GCC versions 12.1-12.3.1.
+* Raised minimum CMake version to 3.13.
+* Raised minimum C++ version to C++20.
+* Added guard rails for scratch targets missing or containing more than one `main` function.
+
+### Changed behavior
+* (sixlowpan) Now uses RFC 7973 Ethertype by default
+* (spectrum) SpectrumChannel objects and the loss/delay models attached are now automatically initialized (Object::Initialize) at time zero
+* (tcp) TCP Cubic (the default congestion control in ns-3) now supports TCP-friendliness by default (see RFC 9438 Section 4.3), making the congestion window growth somewhat more aggressive.  This follows the default Linux behavior.
+* (wifi) Increase the duration of the timer started when waiting for an ADDBA_RESPONSE from 1ms to 5ms to better account for the time required by the recipient to access the medium and complete the frame exchange (which may involve protection with (MU-)RTS/CTS).
+
 Changes from ns-3.39 to ns-3.40
 -------------------------------
 
@@ -48,7 +108,7 @@
 
 ### New API
 
-* (lr-wpan) Added support for orphan scans. Orphan scans can now be performed using the existing `LrWpanMac::MlmeScanRequest`; This orphan scan use the added orphan notification commands and coordinator realigment commands. Usage is shown in added `lr-wpan-orphan-scan.cc` example and in the `TestOrphanScan` included in `lr-wpan-mac-test.cc`.
+* (lr-wpan) Added support for orphan scans. Orphan scans can now be performed using the existing `LrWpanMac::MlmeScanRequest`; This orphan scan use the added orphan notification commands and coordinator realignment commands. Usage is shown in added `lr-wpan-orphan-scan.cc` example and in the `TestOrphanScan` included in `lr-wpan-mac-test.cc`.
 * (network) Added `Mac64Address::ConvertToInt`. Converts a Mac64Address object to a uint64_t.
 * (network) Added `Mac16Address::ConvertToInt`. Converts a Mac16Address object to a uint16_t.
 * (network) Added `Mac16Address::Mac16Address(uint16t addr)` and `Mac16Address::Mac64Address(uint64t addr)` constructors.
@@ -360,6 +420,7 @@
 * The **wifi BCC AWGN error rate tables** have been aligned with the ones provided by MATLAB and users may note a few dB difference when using BCC at high SNR and high MCS.
 * **`ThreeGppChannelModel` has been fixed**: cluster and sub-cluster angles could have been generated with inclination angles outside the inclination range `[0, pi]`, and have now been constrained to the correct range.
 * The **LTE RLC Acknowledged Mode (AM) transmit buffer** is now limited by default to a size of (`1024 * 10`) bytes. Configuration of unlimited behavior can still be made by passing the value of zero to the new attribute `MaxTxBufferSize`.
+* A **non-AP MLD loses association** when receiving no beacon an any link link for an interval of duration equal to the maximum number of missed beacons times the interval between two consecutive Beacon frames.
 
 Changes from ns-3.32 to ns-3.33
 -------------------------------
@@ -489,7 +550,7 @@
 * (as reported above) previously the `Config::Connect` and `Config::Set` families of functions would fail silently if the attribute or trace source didn't exist on the path given (typically due to spelling errors). Now those functions will throw a fatal error. If you need the old behavior use the new `...FailSafe ()` variants.
 * Attempting to deserialize an enum name which wasn't registered with `MakeEnumChecker` now causes a fatal error, rather failing silently. (This can be triggered by setting an enum Attribute from a StringValue.)
 * As a result of the above API changes in `MobilityBuildingInfo` and `BuildingsHelper` classes, a building aware pathloss models, e.g., `HybridBuildingsPropagationLossModel` is now able to accurately compute the pathloss for a node moving in and out of buildings in a simulation. See [issue 80](https://gitlab.com/nsnam/ns-3-dev/issues/80) for discussion.
-* The implementation of the **Wi-Fi channel access** functions has been improved to make them more conformant to the IEEE 802.11-2016 standard. Concerning the DCF, the backoff procedure is no longer invoked when a packet is queued for transmission and the medium has not been idle for a DIFS, but it is invoked if the medium is busy or does not remain idle for a DIFS after the packet has been queued. Concerning the EDCAF, tranmissions are now correctly aligned at slot boundaries.
+* The implementation of the **Wi-Fi channel access** functions has been improved to make them more conformant to the IEEE 802.11-2016 standard. Concerning the DCF, the backoff procedure is no longer invoked when a packet is queued for transmission and the medium has not been idle for a DIFS, but it is invoked if the medium is busy or does not remain idle for a DIFS after the packet has been queued. Concerning the EDCAF, transmissions are now correctly aligned at slot boundaries.
 * Various wifi physical layer behavior around channel occupancy calculation, phy state calculation, and handling different channel widths has been updated.
 
 Changes from ns-3.29 to ns-3.30
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/.clang-format ns-3.41/.clang-format
--- ns-3.40/.clang-format	2023-09-27 13:30:37.522838800 -0700
+++ ns-3.41/.clang-format	2024-02-09 14:44:07.717788700 -0800
@@ -26,4 +26,4 @@
 PointerAlignment: Left
 QualifierAlignment: Left
 SeparateDefinitionBlocks: Always
-Standard: "c++17"
+Standard: "c++20"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/.clang-tidy ns-3.41/.clang-tidy
--- ns-3.40/.clang-tidy	2023-09-27 13:30:37.522838800 -0700
+++ ns-3.41/.clang-tidy	2024-02-09 14:44:07.717788700 -0800
@@ -47,7 +47,6 @@
   readability-static-accessed-through-instance,
   readability-static-definition-in-anonymous-namespace,
   readability-string-compare,
-  readability-uppercase-literal-suffix,
 
 CheckOptions:
   - key: performance-unnecessary-copy-initialization.AllowedTypes
@@ -55,6 +54,9 @@
   - key: readability-simplify-boolean-expr.SimplifyDeMorgan
     value: false
 
+ExtraArgsBefore:
+  - -Wno-unknown-warning-option
+
 FormatStyle: "file"
 HeaderFilterRegex: ".*(ns|NS).*/(contrib|examples|src|scratch|utils)/*/.*h"
 UseColor: true
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/CMakeLists.txt ns-3.41/CMakeLists.txt
--- ns-3.40/CMakeLists.txt	2023-09-27 13:30:37.526838800 -0700
+++ ns-3.41/CMakeLists.txt	2024-02-09 14:44:07.721789000 -0800
@@ -1,7 +1,7 @@
 # ##############################################################################
 # Required CMake version            #
 # ##############################################################################
-cmake_minimum_required(VERSION 3.10..3.10)
+cmake_minimum_required(VERSION 3.13..3.13)
 
 # ##############################################################################
 # Project name                        #
@@ -12,8 +12,8 @@
 
 # minimum compiler versions
 set(AppleClang_MinVersion 11.0.0)
-set(Clang_MinVersion 6.0.0)
-set(GNU_MinVersion 8.0.0)
+set(Clang_MinVersion 10.0.0)
+set(GNU_MinVersion 9.0.0)
 
 # common options
 option(NS3_ASSERT "Enable assert on failure" OFF)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/contributing/pickle-to-xml.py ns-3.41/doc/contributing/pickle-to-xml.py
--- ns-3.40/doc/contributing/pickle-to-xml.py	2023-09-27 13:30:37.534838700 -0700
+++ ns-3.41/doc/contributing/pickle-to-xml.py	2024-02-09 14:44:07.725788800 -0800
@@ -7,35 +7,41 @@
 # ...
 # </pages>
 
-import pickle
-import os
 import codecs
+import os
+import pickle
+
 
 def dump_pickles(out, dirname, filename, path):
-    with open(os.path.join(dirname, filename), 'r', encoding='utf-8') as f:
+    with open(os.path.join(dirname, filename), "r", encoding="utf-8") as f:
         data = pickle.load(f)
-    with codecs.open(data['current_page_name'] + '.frag', mode='w', encoding='utf-8') as fragment_file:
-        fragment_file.write(data['body'])
+    with codecs.open(
+        data["current_page_name"] + ".frag", mode="w", encoding="utf-8"
+    ) as fragment_file:
+        fragment_file.write(data["body"])
 
     out.write('  <page url="%s">\n' % path)
-    out.write('    <fragment>%s.frag</fragment>\n' % data['current_page_name'])
-    if data['prev'] is not None:
-        out.write('    <prev url="%s">%s</prev>\n' %
-                  (os.path.normpath(os.path.join(path, data['prev']['link'])),
-                   data['prev']['title']))
-    if data['next'] is not None:
-        out.write('    <next url="%s">%s</next>\n' %
-                  (os.path.normpath(os.path.join(path, data['next']['link'])),
-                   data['next']['title']))
-    out.write('  </page>\n')
-
-    if data['next'] is not None:
-        next_path = os.path.normpath(os.path.join(path, data['next']['link']))
-        next_filename = os.path.basename(next_path) + '.fpickle'
+    out.write("    <fragment>%s.frag</fragment>\n" % data["current_page_name"])
+    if data["prev"] is not None:
+        out.write(
+            '    <prev url="%s">%s</prev>\n'
+            % (os.path.normpath(os.path.join(path, data["prev"]["link"])), data["prev"]["title"])
+        )
+    if data["next"] is not None:
+        out.write(
+            '    <next url="%s">%s</next>\n'
+            % (os.path.normpath(os.path.join(path, data["next"]["link"])), data["next"]["title"])
+        )
+    out.write("  </page>\n")
+
+    if data["next"] is not None:
+        next_path = os.path.normpath(os.path.join(path, data["next"]["link"]))
+        next_filename = os.path.basename(next_path) + ".fpickle"
         dump_pickles(out, dirname, next_filename, next_path)
 
+
 import sys
 
-sys.stdout.write('<pages>\n')
-dump_pickles(sys.stdout, os.path.dirname(sys.argv[1]), os.path.basename(sys.argv[1]), '/')
-sys.stdout.write('</pages>')
+sys.stdout.write("<pages>\n")
+dump_pickles(sys.stdout, os.path.dirname(sys.argv[1]), os.path.basename(sys.argv[1]), "/")
+sys.stdout.write("</pages>")
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/contributing/source/coding-style.rst ns-3.41/doc/contributing/source/coding-style.rst
--- ns-3.40/doc/contributing/source/coding-style.rst	2023-09-27 13:30:37.534838700 -0700
+++ ns-3.41/doc/contributing/source/coding-style.rst	2024-02-09 14:44:07.725788800 -0800
@@ -38,6 +38,7 @@
 The following list contains the set of clang-format versions that are verified
 to produce consistent output among themselves.
 
+* clang-format-17
 * clang-format-16
 * clang-format-15
 * clang-format-14
@@ -114,8 +115,8 @@
 To facilitate checking and fixing source code files according to the |ns3| coding style,
 |ns3| maintains the ``check-style-clang-format.py`` Python script (located in ``utils/``).
 This script is a wrapper to clang-format and provides useful options to check and fix
-source code files. Additionally, it checks and fixes trailing whitespace and tabs in text
-files.
+source code files. Additionally, it performs other manual checks and fixes in text files
+(described below).
 
 We recommend running this script over your newly introduced C++ files prior to submission
 as a Merge Request.
@@ -160,6 +161,9 @@
   # Specific directory or file
   /path/to/utils/check-style-clang-format.py --fix absolute_or_relative/path/to/directory_or_file
 
+  # Modified files
+  git diff --name-only | xargs ./utils/check-style-clang-format.py --fix
+
 Clang-tidy
 **********
 
@@ -1155,44 +1159,44 @@
 
 For instance, the following code:
 
-  .. sourcecode:: cpp
+.. sourcecode:: cpp
 
-    bool IsPositive(int n)
-    {
-        if (n > 0)
-        {
-            return true;
-        }
-        else
-        {
-            return false;
-        }
-    }
+  bool IsPositive(int n)
+  {
+      if (n > 0)
+      {
+          return true;
+      }
+      else
+      {
+          return false;
+      }
+  }
 
-    void ProcessNumber(int n)
-    {
-        if (IsPositive(n) == true)
-        {
-            ...
-        }
-    }
+  void ProcessNumber(int n)
+  {
+      if (IsPositive(n) == true)
+      {
+          ...
+      }
+  }
 
 can be rewritten as:
 
-  .. sourcecode:: cpp
+.. sourcecode:: cpp
 
-    bool IsPositive(int n)
-    {
-        return n > 0;
-    }
+  bool IsPositive(int n)
+  {
+      return n > 0;
+  }
 
-    void ProcessNumber(int n)
-    {
-        if (IsPositive(n))
-        {
-            ...
-        }
-    }
+  void ProcessNumber(int n)
+  {
+      if (IsPositive(n))
+      {
+          ...
+      }
+  }
 
 Smart pointer boolean comparisons
 =================================
@@ -1349,6 +1353,32 @@
 features than this, please email the developers list. We will move this
 language support forward as our minimally supported compiler moves forward.
 
+Guidelines for using maps
+=========================
+
+Maps (associative containers) are used heavily in ns-3 models to store
+key/value pairs.  The C++ standard, over time, has added various methods to
+insert elements to maps, and the ns-3 codebase has made use of most or all
+of these constructs.  For the sake of uniformity and readability, the
+following guidelines are recommended for any new code.
+
+Prefer the use of ``std::map`` to ``std::unordered_map`` unless there is
+a measurable performance advantage.  Use ``std::unordered_map`` only for
+use cases in which the map does not need to be iterated or the iteration
+order does not affect the results of the operation (because different
+implementations of the hash function may lead to different iteration orders
+on different systems).
+
+Keep in mind that C++ now allows several methods to insert values into
+maps, and the behavior can be different when a value already exists for
+a key.  If the intended behavior is that the insertion should not overwrite
+an existing value for the key, ``try_emplace()`` can be a good choice.  If
+the intention is to allow the overwriting of a key/value pair,
+``insert_or_assign()`` can be a good choice.  Both of the above methods
+provide return values that can be checked-- in the case of ``try_emplace()``,
+whether the insertion succeeded or did not occur, and in the case of
+``insert_or_assign()``, whether an insertion or assignment occurred.
+
 Miscellaneous items
 ===================
 
@@ -1519,3 +1549,108 @@
 
     // Avoid repeating the type name "MyClass" in std::less<>
     std::map<MyClass, int, std::less<MyClass>> myMap;
+
+
+CMake file formatting
+*********************
+
+The ``CMakeLists.txt`` and other ``*.cmake`` files follow the formatting rules defined in
+``build-support/cmake-format.yaml`` and ``build-support/cmake-format-modules.yaml``.
+
+The first set of rules applies to CMake files in all directories that are not modules,
+while the second one applies to files within modules.
+
+.. _cmake-format: https://cmake-format.readthedocs.io/en/latest/cmake-format.html
+
+Those rules are enforced via the `cmake-format`_ tool, that can be installed via Pip.
+
+.. sourcecode:: console
+
+    pip install cmake-format pyyaml
+
+After installing cmake-format, it can be called to fix the formatting of a CMake file
+with the following command:
+
+.. sourcecode:: console
+
+    cmake-format -c ./build-support/cmake-format.yaml CMakeLists.txt
+
+To check the formatting, add the `--check` option to the command, before specifying
+the list of CMake files.
+
+Instead of calling this command for every single CMake file, it is recommended to use
+the ``ns3`` script to run the custom targets that do that automatically.
+
+.. sourcecode:: console
+
+    # Check CMake formatting
+    ./ns3 build cmake-format-check
+
+    # Check and fix CMake formatting
+    ./ns3 build cmake-format
+
+Custom functions and macros need to be explicitly configured in the ``cmake-format.yaml`` files,
+otherwise their formatting will be broken.
+
+Python file formatting
+**********************
+
+.. _Black: https://black.readthedocs.io/en/stable/index.html
+.. _Isort: https://pycqa.github.io/isort/index.html
+.. _Black current style: https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html
+
+Python format style and rule enforcement is based on the default settings for the `Black`_
+formatter tool and `Isort`_ import sorter tool. Black default format is detailed in `Black current style`_.
+
+The custom settings for both tools are set in the ``pyproject.toml`` file.
+
+These tools that can be installed via Pip, using the following command:
+
+.. sourcecode:: console
+
+    pip install black isort
+
+To check the formatting, add the `--check` option to the command:
+
+.. sourcecode:: console
+
+    black --check .
+    isort --check .
+
+To check and fix the formatting, run the commands as follows:
+
+.. sourcecode:: console
+
+    black .
+    isort .
+
+.. _MS Black formatter: https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter
+.. _MS Isort: https://marketplace.visualstudio.com/items?itemName=ms-python.isort
+
+For VS Code users, `MS Black formatter`_ and `MS Isort`_ extensions, which repackage
+Black and Isort for VS Code, can be installed to apply fixes regularly.
+To configure VS Code to automatically format code when saving, editing or pasting code,
+add the following configuration to ``.vscode/settings.json``:
+
+.. sourcecode:: json
+
+  {
+    "editor.formatOnPaste": true,
+    "editor.formatOnSave": true,
+    "editor.formatOnType": true,
+    "[python]": {
+      "editor.defaultFormatter": "ms-python.black-formatter",
+      "editor.codeActionsOnSave": {
+          "source.organizeImports": "explicit",
+      },
+    },
+    "black-formatter.args": [
+      "--config",
+      "pyproject.toml",
+    ],
+    "isort.check": true,
+    "isort.args": [
+      "--sp",
+      "pyproject.toml",
+    ],
+  }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/contributing/source/conf.py ns-3.41/doc/contributing/source/conf.py
--- ns-3.40/doc/contributing/source/conf.py	2023-09-27 13:30:55.802883400 -0700
+++ ns-3.41/doc/contributing/source/conf.py	2024-02-09 14:45:45.430484000 -0800
@@ -11,206 +11,203 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'ns-3'
-copyright = u'2015, ns-3 project'
+project = "ns-3"
+copyright = "2015, ns-3 project"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3.40'
+version = "ns-3.41"
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3.40'
+release = "ns-3.41"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'ns3_html_theme'
+html_theme = "ns3_html_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ['../..']
+html_theme_path = ["../.."]
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-html_title = 'Contributing'
+html_title = "Contributing"
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y %H:%M'
+html_last_updated_fmt = "%b %d, %Y %H:%M"
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'ns-3doc'
+htmlhelp_basename = "ns-3doc"
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'ns-3-contributing.tex', u'Contributing to ns-3',
-   u'ns-3 project', 'manual'),
+    ("index", "ns-3-contributing.tex", "Contributing to ns-3", "ns-3 project", "manual"),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-latex_logo = '../../ns3_html_theme/static/ns-3.png'
+latex_logo = "../../ns3_html_theme/static/ns-3.png"
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Additional stuff for the LaTeX preamble.
-latex_preamble = '\\usepackage{amssymb}'
+latex_preamble = "\\usepackage{amssymb}"
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output --------------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-contributing', u'Contributing to ns-3',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-contributing", "Contributing to ns-3", ["ns-3 project"], 1)]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/contributing/source/index.rst ns-3.41/doc/contributing/source/index.rst
--- ns-3.40/doc/contributing/source/index.rst	2023-09-27 13:30:37.534838700 -0700
+++ ns-3.41/doc/contributing/source/index.rst	2024-02-09 14:44:07.729788800 -0800
@@ -8,7 +8,7 @@
 
 * Several guides that are version controlled for each release (the
   `latest release <https://www.nsnam.org/documentation/latest/>`_) and
-  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+  `development tree <https://www.nsnam.org/documentation/development-tree/>`_:
 
   * Tutorial
   * Installation Guide
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/installation/source/conf.py ns-3.41/doc/installation/source/conf.py
--- ns-3.40/doc/installation/source/conf.py	2023-09-27 13:30:55.802883400 -0700
+++ ns-3.41/doc/installation/source/conf.py	2024-02-09 14:45:45.430484000 -0800
@@ -11,206 +11,203 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'ns-3'
-copyright = u'2018, ns-3 project'
+project = "ns-3"
+copyright = "2018, ns-3 project"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3.40'
+version = "ns-3.41"
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3.40'
+release = "ns-3.41"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'ns3_html_theme'
+html_theme = "ns3_html_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ['../..']
+html_theme_path = ["../.."]
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-html_title = 'Installation guide'
+html_title = "Installation guide"
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-html_last_updated_fmt = '%b %d, %Y %H:%M'
+html_last_updated_fmt = "%b %d, %Y %H:%M"
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'ns-3doc'
+htmlhelp_basename = "ns-3doc"
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'ns-3-installation.tex', u'ns-3 Installation Guide',
-   u'ns-3 project', 'manual'),
+    ("index", "ns-3-installation.tex", "ns-3 Installation Guide", "ns-3 project", "manual"),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-latex_logo = '../../ns3_html_theme/static/ns-3.png'
+latex_logo = "../../ns3_html_theme/static/ns-3.png"
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Additional stuff for the LaTeX preamble.
-latex_preamble = '\\usepackage{amssymb}'
+latex_preamble = "\\usepackage{amssymb}"
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output --------------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-installation', u'ns-3 Installation',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-installation", "ns-3 Installation", ["ns-3 project"], 1)]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/installation/source/index.rst ns-3.41/doc/installation/source/index.rst
--- ns-3.40/doc/installation/source/index.rst	2023-09-27 13:30:37.534838700 -0700
+++ ns-3.41/doc/installation/source/index.rst	2024-02-09 14:44:07.729788800 -0800
@@ -8,7 +8,7 @@
 
 * Several guides that are version controlled for each release (the
   `latest release <https://www.nsnam.org/documentation/latest/>`_) and
-  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+  `development tree <https://www.nsnam.org/documentation/development-tree/>`_:
 
   * Tutorial
   * Installation Guide *(this document)*
@@ -20,7 +20,7 @@
 * `ns-3 wiki <https://www.nsnam.org/wiki/Main_Page>`_
 
 This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_
-for `Sphinx <http://sphinx.pocoo.org/>`_ and is maintained in the ``doc/installation`` directory of ns-3's source code.  Source file column width is 100 columns.
+for `Sphinx <https://www.sphinx-doc.org/>`_ and is maintained in the ``doc/installation`` directory of ns-3's source code.  Source file column width is 100 columns.
 
 .. toctree::
    :maxdepth: 2
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/installation/source/macos.rst ns-3.41/doc/installation/source/macos.rst
--- ns-3.40/doc/installation/source/macos.rst	2023-09-27 13:30:37.534838700 -0700
+++ ns-3.41/doc/installation/source/macos.rst	2024-02-09 14:44:07.729788800 -0800
@@ -48,28 +48,28 @@
 works on recent versions of both ``clang++`` and ``g++``, so for macOS, there is no need
 to install ``g++``.
 
-  +--------------------+---------------------------------------------------------------------+
+  +--------------------+-------------------------------+-------------------------------------+
   | **ns-3 Version**   | **Homebrew packages**         | **MacPorts packages**               |
-  +====================+==================+==================================================+
+  +====================+===============================+=====================================+
   | 3.36 and later     | ``cmake ninja``               | ``cmake ninja``                     |
-  +--------------------+---------------------------------------------------------------------+
+  +--------------------+-------------------------------+-------------------------------------+
   | 3.35 and earlier   | None                          | None                                |
-  +--------------------+---------------------------------------------------------------------+
+  +--------------------+-------------------------------+-------------------------------------+
 
 Recommended
 ***********
 
-  +-----------------------------+------------------------------------------------------------+
+  +-----------------------------+------------------------+-----------------------------------+
   | **Feature**                 | **Homebrew packages**  | **MacPorts packages**             |
-  +=============================+============================================================+
+  +=============================+========================+===================================+
   | Compiler cache optimization | ``ccache``             | ``ccache``                        |
-  +-----------------------------+------------------------------------------------------------+
+  +-----------------------------+------------------------+-----------------------------------+
   | Code linting                | ``clang-format llvm``  |  clang-format included with       |
   |                             |                        | ``clang``, need to select         |
   |                             |                        | ``clang-XX llvm-XX`` versions     |
-  +-----------------------------+------------------------------------------------------------+
+  +-----------------------------+------------------------+-----------------------------------+
   | Debugging                   | None                   | ``gdb ddd`` (ddd requires gdb)    |
-  +-----------------------------+------------------------------------------------------------+
+  +-----------------------------+------------------------+-----------------------------------+
 
 .. note::
   The ``llvm`` Homebrew package provides ``clang-tidy``, but please note that the binary is
@@ -77,6 +77,11 @@
   ``$PATH`` variable.
 
 .. note::
+  Likewise, when using MacPorts, the ``clang-tidy`` and ``clang-format`` binaries will be
+  placed in ``/opt/local/libexec/llvm-XX/bin`` (where ``XX` is the installed version number
+  such as ``16``), so you will need to add this to your ``$PATH`` variable.
+
+.. note::
   For debugging, ``lldb`` is the default debugger for llvm.  Memory checkers such as
   Memory Graph exist for macOS, but the ns-3 team doesn't have experience with it as a
   substitution for ``valgrind`` (which is reported to not work on M1 Macs).
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/installation/source/quick-start.rst ns-3.41/doc/installation/source/quick-start.rst
--- ns-3.40/doc/installation/source/quick-start.rst	2023-09-27 13:30:37.534838700 -0700
+++ ns-3.41/doc/installation/source/quick-start.rst	2024-02-09 14:44:07.729788800 -0800
@@ -80,13 +80,13 @@
 
 There are two main options:
 
-1. Download a release tarball.  This will unpack to a directory such as ``ns-allinone-3.40``
+1. Download a release tarball.  This will unpack to a directory such as ``ns-allinone-3.41``
 containing |ns3| and some other programs.  Below is a command-line download using ``wget``,
 but a browser download will also work::
 
-  $ wget https://www.nsnam.org/releases/ns-allinone-3.40.tar.bz2
-  $ tar xfj ns-allinone-3.40.tar.bz2
-  $ cd ns-allinone-3.40/ns-3.40
+  $ wget https://www.nsnam.org/releases/ns-allinone-3.41.tar.bz2
+  $ tar xfj ns-allinone-3.41.tar.bz2
+  $ cd ns-allinone-3.41/ns-3.41
 
 2. Clone |ns3| from the Git repository.  The ``ns-3-allinone`` can be cloned, as well as
 ``ns-3-dev`` by itself.  Below, we illustrate the latter::
@@ -99,7 +99,7 @@
 the |ns3| ``master`` branch, which is a development branch.  All |ns3| releases are tagged
 in Git, so if you would then like to check out a past release, you can do so as follows::
 
-  $ git checkout -b ns-3.40-release ns-3.40
+  $ git checkout -b ns-3.41-release ns-3.41
 
 In this quick-start, we are omitting download and build instructions for optional |ns3| modules,
 the ``NetAnim`` animator, Python bindings, and ``NetSimulyzer``.  The
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/Makefile ns-3.41/doc/manual/Makefile
--- ns-3.40/doc/manual/Makefile	2023-09-27 13:30:37.534838700 -0700
+++ ns-3.41/doc/manual/Makefile	2024-02-09 14:44:07.729788800 -0800
@@ -77,6 +77,7 @@
 	figures/hotspot-setup.png \
 	figures/hotspot-top-down.png \
 	figures/perfetto-trace-cmake.png \
+	figures/time-consuming-event-handling.png \
 	figures/uprof-collect-callstack.png \
 	figures/uprof-profile-application.png \
 	figures/uprof-select-events.png \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/callbacks.rst ns-3.41/doc/manual/source/callbacks.rst
--- ns-3.40/doc/manual/source/callbacks.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/callbacks.rst	2024-02-09 14:44:07.777789000 -0800
@@ -511,8 +511,8 @@
 
   template <typename T, typename ARG, typename BOUND_ARG>
   class SpecificFunctor : public Functor
-   {
-   public:
+  {
+    public:
       SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
       {
         m_p = p;
@@ -524,11 +524,11 @@
       {
         (*m_p.*m_pmi)(m_boundArg, arg);
       }
-  private:
+    private:
       void (T::*m_pmi)(ARG arg);
       T* m_p;
       BOUND_ARG m_boundArg;
-   };
+  };
 
 You can see that when the specific functor is created, the bound argument is saved
 in the functor / callback object itself.  When the ``operator()`` is invoked with
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/conf.py ns-3.41/doc/manual/source/conf.py
--- ns-3.40/doc/manual/source/conf.py	2023-09-27 13:30:55.802883400 -0700
+++ ns-3.41/doc/manual/source/conf.py	2024-02-09 14:45:45.430484000 -0800
@@ -20,21 +20,22 @@
 # import sys
 # sys.path.insert(0, os.path.abspath('.'))
 
-import sys, os
+import os
+import sys
 
 # To change default code-block format in Latex to footnotesize (8pt)
 # Tip from https://stackoverflow.com/questions/9899283/how-do-you-change-the-code-example-font-size-in-latex-pdf-output-with-sphinx/9955928
 # Note:  sizes are \footnotesize (8pt), \small (9pt), and \normalsize (10pt).
 
-#from sphinx.highlighting import PygmentsBridge
-#from pygments.formatters.latex import LatexFormatter
+# from sphinx.highlighting import PygmentsBridge
+# from pygments.formatters.latex import LatexFormatter
 #
-#class CustomLatexFormatter(LatexFormatter):
+# class CustomLatexFormatter(LatexFormatter):
 #    def __init__(self, **options):
 #        super(CustomLatexFormatter, self).__init__(**options)
 #        self.verboptions = r"formatcom=\footnotesize"
 #
-#PygmentsBridge.latex_formatter = CustomLatexFormatter
+# PygmentsBridge.latex_formatter = CustomLatexFormatter
 
 # -- General configuration ------------------------------------------------
 
@@ -45,37 +46,37 @@
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix(es) of source filenames.
 # You can specify multiple suffix as a list of string:
 #
 # source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
 #
 # source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'ns-3 project'
-copyright = u'2006-2019'
-#author = u'test'
+project = "ns-3 project"
+copyright = "2006-2019, ns-3 project"
+# author = u'test'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.40'
+version = "ns-3.41"
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.40'
+release = "ns-3.41"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -96,7 +97,7 @@
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # These patterns also affect html_static_path and html_extra_path
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
@@ -118,7 +119,7 @@
 # show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
@@ -135,7 +136,7 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = 'ns3_html_theme'
+html_theme = "ns3_html_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -144,13 +145,13 @@
 # html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ['../..']
+html_theme_path = ["../.."]
 
 # The name for this set of Sphinx documents.
 # "<project> v<release> documentation" by default.
 #
 # html_title = 'est vtest'
-html_title = 'Manual'
+html_title = "Manual"
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
 #
@@ -170,7 +171,7 @@
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
 
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
@@ -182,7 +183,7 @@
 # bottom, using the given strftime format.
 # The empty string is equivalent to '%b %d, %Y'.
 #
-html_last_updated_fmt = '%b %d, %Y %H:%M'
+html_last_updated_fmt = "%b %d, %Y %H:%M"
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
@@ -250,7 +251,7 @@
 # html_search_scorer = 'scorer.js'
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'ns-3doc'
+htmlhelp_basename = "ns-3doc"
 
 # -- Options for LaTeX output ---------------------------------------------
 
@@ -258,11 +259,9 @@
     # The paper size ('letterpaper' or 'a4paper').
     #
     # 'papersize': 'letterpaper',
-
     # The font size ('10pt', '11pt' or '12pt').
     #
     # 'pointsize': '10pt',
-
     # Additional stuff for the LaTeX preamble.
     #
     # VerbatimBorderColor:  make the box around code samples blend into the background
@@ -275,10 +274,10 @@
     # See above to change the font size of verbatim code blocks
     #
     # 'preamble': '',
-    'preamble': u'''\\usepackage{amssymb}
+    "preamble": """\\usepackage{amssymb}
  \\definecolor{VerbatimBorderColor}{rgb}{1,1,1}
  \\renewcommand{\\sphinxcode}[1]{\\texttt{\\small{#1}}}
-'''
+"""
     # Latex figure (float) alignment
     #
     # 'figure_align': 'htbp',
@@ -288,14 +287,13 @@
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    ('index', 'ns-3-manual.tex', u'ns-3 Manual',
-     u'ns-3 project', 'manual'),
+    ("index", "ns-3-manual.tex", "ns-3 Manual", "ns-3 project", "manual"),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
 #
-latex_logo = '../../ns3_html_theme/static/ns-3.png'
+latex_logo = "../../ns3_html_theme/static/ns-3.png"
 
 # If true, show page references after internal links.
 #
@@ -324,10 +322,7 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-manual', u'ns-3 Manual',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-manual", "ns-3 Manual", ["ns-3 project"], 1)]
 
 # If true, show URL addresses after external links.
 #
@@ -335,11 +330,11 @@
 
 # -- Options for texinfo output ---------------------------------------
 
-#texinfo_documents = [
+# texinfo_documents = [
 #    (master_doc, 'test', u'test Documentation',
 #    author, 'test', 'One line description of project.',
 #     'Miscellaneous'),
-#]
+# ]
 
 # Documents to append as an appendix to all manuals.
 #
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/events.rst ns-3.41/doc/manual/source/events.rst
--- ns-3.40/doc/manual/source/events.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/events.rst	2024-02-09 14:44:07.777789000 -0800
@@ -36,7 +36,79 @@
 Event
 *****
 
-*To be completed*
+An event represents something that changes the simulation status, i.e.,
+between two events the simulation status does not change, and the event
+will likely change it (it could also not change anything).
+
+Note that another way to understand an event is to consider it as a delayed
+function call. With the due differences, a discrete event simulation is not
+much different from a "normal" program where the functions are not called
+immediately, but are marked with a "time", and the time is used to decide
+the order of the functions execution.
+
+The time, of course, is a simulated time, and is quite different from the
+"real" time. Depending on the simulation complexity the simulated time
+can advance faster or slower then the "real" time, but like a "real" time
+can only go forward.
+
+An example of an event is the reception of a packet, or the expiration
+of a timer.
+
+An event is represented by:
+
+* The time at which the event will happen
+* A pointer to the function that will "handle" the event,
+* The parameters of the function that will handle the event (if any),
+* Other internal structures.
+
+An event is scheduled through a call to ``Simulator::Schedule``, and once
+scheduled, it can be canceled or removed.
+Removal implies removal from the scheduler data structure, while cancel
+keeps them in the data structure but sets a boolean flag that suppresses
+calling the bound event function at the scheduled time.  When an event is
+scheduled by the Simulator, an ``EventId`` is returned.  The client may use
+this event ID to later cancel or remove the event; see the example program
+``src/core/examples/sample-simulator.{cc,py}`` for example usage.
+Cancelling an event is typically less computationally expensive than
+removing it, but cancelled events consumes more memory in the scheduler
+data structure, which might impact its performances.
+
+Events are stored by the simulator in a scheduler data
+structure.  Events are handled in increasing order of
+simulator time, and in the case of two events with the same
+scheduled time, the event with the lowest unique ID (a
+monotonically increasing counter) will be handled first.
+In other words tied events are handled in FIFO order.
+
+Note that concurrent events (events that happen at the very same time)
+are unlikely in a real system - not to say impossible. In |ns-3|
+concurrent events are common for a number of reasons, one of them
+being the time representation. While developing a model this must
+be carefully taken into account.
+
+During the event execution, the simulation time will not advance, i.e., each
+event is executed in zero time. This is a common assumption in
+discrete event simulations, and holds when the computational complexity of
+the operations executed in the event is negligible.
+When this assumption does not hold, it is necessary to schedule a second event
+to mimic the end of the computationally intensive task.
+
+As an example, suppose to have a device that receives a packet and has to
+perform a complex analysis on it (e.g., an image processing task). The
+sequence of events will be:
+
+* T(t) - Packet reception and processing, save the result somewhere, and
+  schedule an event in (t+d) marking the end of the data processing.
+* T(t+d) - Retrieve the data, and do other stuff based them.
+
+So, even if the data processing actually did return a result in the
+execution of the first event, the data is considered valid only after
+the second event.
+
+The image below can be useful to clarify the idea.
+
+.. image:: figures/time-consuming-event-handling.png
+
 
 Simulator
 *********
@@ -219,7 +291,7 @@
 
 .. sourcecode:: terminal
 
-  $ ./ns3 run "...  -–SimulatorImplementationType=ns3::DistributedSimulatorImpl"
+  $ ./ns3 run "...  --SimulatorImplementationType=ns3::DistributedSimulatorImpl"
 
 In addition to the basic simulator engines there is a general facility used
 to build "adapters" which provide small behavior modifications to one of
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/index.rst ns-3.41/doc/manual/source/index.rst
--- ns-3.40/doc/manual/source/index.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/index.rst	2024-02-09 14:44:07.777789000 -0800
@@ -8,7 +8,7 @@
 
 * Several guides that are version controlled for each release (the
   `latest release <https://www.nsnam.org/documentation/latest/>`_) and
-  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+  `development tree <https://www.nsnam.org/documentation/development-tree/>`_:
 
   * Tutorial
   * Installation Guide
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/new-models.rst ns-3.41/doc/manual/source/new-models.rst
--- ns-3.40/doc/manual/source/new-models.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/new-models.rst	2024-02-09 14:44:07.777789000 -0800
@@ -324,9 +324,8 @@
 Third, it is a good idea to implement constructors and destructors rather than
 to let the compiler generate them, and to make the destructor virtual. In C++,
 note also that copy assignment operator and copy constructors are auto-generated
-if they are not defined, so if you do not want those, you should implement those
-as private members. This aspect of C++ is discussed in Scott Meyers' Effective
-C++ book. item 45.
+if they are not defined, so if you do not want those, you should declare them
+as ``= delete``.
 
 Let's now look at some corresponding skeletal implementation code in the .cc
 file.::
@@ -385,6 +384,29 @@
 registration of the class into the system.  The :ref:`Object-model` chapter
 discusses this in more detail.
 
+Note: Template classes should both export the instantiated template and call
+``NS_OBJECT_TEMPLATE_CLASS_DEFINE (TemplateClass, TemplateArgument);``
+to prevent the same template from being instantiated more than a single
+time in different modules. This prevents errors such as
+``Trying to allocate twice the same uid: TemplateClass<TemplateArgument>``.
+
+An example for the ``CounterCalculator<uint32_t>``::
+
+    //.h file
+    namespace ns3
+    {
+        extern template class CounterCalculator<uint32_t>;
+    }
+    //.cc file
+    #include <ns3/.h file>
+    namespace ns3
+    {
+        NS_OBJECT_TEMPLATE_CLASS_DEFINE (CounterCalculator, uint32_t);
+    }
+
+More details can be found in `issue #761 <https://gitlab.com/nsnam/ns-3-dev/-/issues/761>`_.
+
+
 Including External Files
 ++++++++++++++++++++++++
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/new-modules.rst ns-3.41/doc/manual/source/new-modules.rst
--- ns-3.40/doc/manual/source/new-modules.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/new-modules.rst	2024-02-09 14:44:07.777789000 -0800
@@ -419,7 +419,7 @@
 
 .. sourcecode:: python
 
-    ("realtime-udp-echo.py", "ENABLE_REAL_TIME == False"),
+    ("brite-generic-example", "ENABLE_BRITE == True", "False"),
 
 
 Step 8 - Configure and Build
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/profiling.rst ns-3.41/doc/manual/source/profiling.rst
--- ns-3.40/doc/manual/source/profiling.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/profiling.rst	2024-02-09 14:44:07.777789000 -0800
@@ -467,7 +467,7 @@
 .. _Perf : https://perf.wiki.kernel.org/index.php/Tutorial
 .. _Hotspot : https://github.com/KDAB/hotspot
 .. _AMD uProf : https://www.amd.com/en/developer/uprof.html
-.. _Intel VTune : https://www.intel.com/content/www/us/en/develop/documentation/get-started-with-vtune/top.html
+.. _Intel VTune : https://www.intel.com/content/www/us/en/docs/vtune-profiler/get-started-guide/2023-1/overview.html
 .. _Windows Performance Toolkit : https://docs.microsoft.com/en-us/windows-hardware/test/wpt/
 .. _Sysprof : https://wiki.gnome.org/Apps/Sysprof
 .. _Oprofile : https://oprofile.sourceforge.io/faq/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/python.rst ns-3.41/doc/manual/source/python.rst
--- ns-3.40/doc/manual/source/python.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/python.rst	2024-02-09 14:44:07.777789000 -0800
@@ -632,7 +632,7 @@
 .. sourcecode:: yaml
 
       # Configure and build wheel
-      - $PYTHON setup.py bdist_wheel build_ext "-DNS3_USE_LIB64=TRUE"
+      - $PYTHON setup.py bdist_wheel build_ext
 
 At this point, we have a wheel that only works in the current system,
 since external libraries are not shipped.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/random-variables.rst ns-3.41/doc/manual/source/random-variables.rst
--- ns-3.40/doc/manual/source/random-variables.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/random-variables.rst	2024-02-09 14:44:07.777789000 -0800
@@ -261,6 +261,8 @@
 * class :cpp:class:`ZetaRandomVariable`
 * class :cpp:class:`DeterministicRandomVariable`
 * class :cpp:class:`EmpiricalRandomVariable`
+* class :cpp:class:`BinomialRandomVariable`
+* class :cpp:class:`BernoulliRandomVariable`
 
 Semantics of RandomVariableStream objects
 *****************************************
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/troubleshoot.rst ns-3.41/doc/manual/source/troubleshoot.rst
--- ns-3.40/doc/manual/source/troubleshoot.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/troubleshoot.rst	2024-02-09 14:44:07.781789300 -0800
@@ -30,8 +30,8 @@
 
 The error message says that the program terminated unsuccessfuly, but it is
 not clear from this information what might be wrong. To examine more
-closely, try running it under the `gdb debugger
-<https://access.redhat.com/documentation/en-us/red_hat_developer_toolset/9/html/user_guide/chap-gdb>`_:
+closely, try running it under the
+`gdb debugger <https://www.nsnam.org/wiki/HOWTO_use_gdb_to_debug_program_errors>`_:
 
 .. sourcecode:: bash
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/utilities.rst ns-3.41/doc/manual/source/utilities.rst
--- ns-3.40/doc/manual/source/utilities.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/utilities.rst	2024-02-09 14:44:07.781789300 -0800
@@ -94,10 +94,10 @@
 
     Program Options:
     --all:     use all schedulers [false]
-    --cal:     use CalendarSheduler [false]
+    --cal:     use CalendarScheduler [false]
     --calrev:  reverse ordering in the CalendarScheduler [false]
     --heap:    use HeapScheduler [false]
-    --list:    use ListSheduler [false]
+    --list:    use ListScheduler [false]
     --map:     use MapScheduler (default) [true]
     --pri:     use PriorityQueue [false]
     --debug:   enable debugging output [false]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/working-with-cmake.rst ns-3.41/doc/manual/source/working-with-cmake.rst
--- ns-3.40/doc/manual/source/working-with-cmake.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/working-with-cmake.rst	2024-02-09 14:44:07.781789300 -0800
@@ -73,13 +73,13 @@
   ~$ cd ns-3-dev
   ~/ns-3-dev$ ./ns3 configure --help
   usage: ns3 configure [-h] [-d {debug,release,optimized}] [-G G]
-                     [--cxx-standard CXX_STANDARD] [--enable-asserts]
-                     [--disable-asserts] [--enable-examples]
-                     [--disable-examples] [--enable-logs]
-                     [--disable-logs] [--enable-tests]
-                     [--disable-tests] [--enable-verbose]
-                     [--disable-verbose]
-                     ...
+                       [--cxx-standard CXX_STANDARD] [--enable-asserts]
+                       [--disable-asserts] [--enable-examples]
+                       [--disable-examples] [--enable-logs]
+                       [--disable-logs] [--enable-tests]
+                       [--disable-tests] [--enable-verbose]
+                       [--disable-verbose]
+                       ...
 
   positional arguments:
     configure
@@ -2269,7 +2269,7 @@
          EXECNAME ${EXAMPLE_NAME}
          SOURCE_FILES ${EXAMPLE_SOURCE_FILES}
          HEADER_FILES ${EXAMPLE_HEADER_FILES}
-         LIBRARIES_TO_LINK ${EXAMPLE_LIBRARIES_TO_LINK} ${optional_visualizer_lib}
+         LIBRARIES_TO_LINK ${EXAMPLE_LIBRARIES_TO_LINK} ${ns3-optional-visualizer-lib}
          EXECUTABLE_DIRECTORY_PATH
            ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/examples/${examplefolder}/
          ${IGNORE_PCH}
@@ -2741,8 +2741,8 @@
 If the list ``missing_dependencies`` is empty, we create the example. Otherwise, we skip it.
 The example can be linked to the current module (``${lib${BLIB_EXAMPLE_LIBNAME}}``) and
 other libraries to link (``${BLIB_EXAMPLE_LIBRARIES_TO_LINK}``) and optionally to the visualizer
-module (``${optional_visualizer_lib}``).
-If the visualizer module is not enabled, ``optional_visualizer_lib`` is empty.
+module (``${ns3-optional-visualizer-lib}``).
+If the visualizer module is not enabled, ``ns3-optional-visualizer-lib`` is empty.
 
 The example can also be linked to a single |ns3| shared library (``lib-ns3-monolib``) or
 a single |ns3| static library (``lib-ns3-static``), if either ``NS3_MONOLIB=ON`` or ``NS3_STATIC=ON``.
@@ -2776,7 +2776,7 @@
          HEADER_FILES ${BLIB_EXAMPLE_HEADER_FILES}
          LIBRARIES_TO_LINK
            ${lib${BLIB_EXAMPLE_LIBNAME}} ${BLIB_EXAMPLE_LIBRARIES_TO_LINK}
-           ${optional_visualizer_lib}
+           ${ns3-optional-visualizer-lib}
          EXECUTABLE_DIRECTORY_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${FOLDER}/
          ${IGNORE_PCH}
        )
@@ -2950,7 +2950,7 @@
 
   # This custom macro checks for dependencies CMake find_package and program
   # dependencies and return the missing dependencies in the third argument
-  check_deps("" "doxygen;dot;dia" doxygen_docs_missing_deps)
+  check_deps(doxygen_docs_missing_deps EXECUTABLES doxygen dot dia python3)
 
   # If the variable contains missing dependencies, we stop processing doxygen targets
   if(doxygen_docs_missing_deps)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/working-with-gitlab-ci-local.rst ns-3.41/doc/manual/source/working-with-gitlab-ci-local.rst
--- ns-3.40/doc/manual/source/working-with-gitlab-ci-local.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/working-with-gitlab-ci-local.rst	2024-02-09 14:44:07.781789300 -0800
@@ -167,3 +167,15 @@
 based on the job name.
 
 ``~/ns-3-dev/.gitlab-ci-local/artifacts/jobname``
+
+Note: some jobs may access the ``CI_DEFAULT_BRANCH`` environment variable,
+which is set by default to ``main`` instead of ``master``. To change that, we need
+to create a file ``~/.gitlab-ci-local/variables.yml`` containing the following:
+
+.. sourcecode:: YAML
+
+  global:
+    CI_DEFAULT_BRANCH: "master"
+
+In case you are using Docker with root, you need to create this file in
+``/root/.gitlab-ci-local/variables.yml``.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/manual/source/working-with-git.rst ns-3.41/doc/manual/source/working-with-git.rst
--- ns-3.40/doc/manual/source/working-with-git.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/manual/source/working-with-git.rst	2024-02-09 14:44:07.781789300 -0800
@@ -368,7 +368,7 @@
   $ cat VERSION
   3.34
 
-We next change the file conf.py in the contributing, tutorial, manual, and models directories
+We next change the file conf.py in the contributing, installation, tutorial, manual, and models directories
 to change the strings 'ns-3-dev' to ns-3.34.
 
 When you are done, the 'git diff --stat' command should show:
@@ -377,10 +377,11 @@
 
   VERSION                         | 2 +-
   doc/contributing/source/conf.py | 4 ++--
+  doc/installation/source/conf.py | 4 ++--
   doc/manual/source/conf.py       | 4 ++--
   doc/models/source/conf.py       | 4 ++--
   doc/tutorial/source/conf.py     | 4 ++--
-  5 files changed, 9 insertions(+), 9 deletions(-)
+  6 files changed, 11 insertions(+), 11 deletions(-)
 
 Make a commit of these files:
 
@@ -404,6 +405,8 @@
 
 Commit this change:
 
+::
+
   $ git commit -m"Update availability in RELEASE_NOTES.md" RELEASE_NOTES.md
 
 Finally, add a Git annotated tag:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/models/Makefile ns-3.41/doc/models/Makefile
--- ns-3.40/doc/models/Makefile	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/models/Makefile	2024-02-09 14:44:07.781789300 -0800
@@ -145,6 +145,8 @@
 	$(SRC)/wifi/doc/source/figures/ack-su-format.pdf \
 	$(SRC)/wifi/doc/source/figures/ack-su-format.png \
 	$(SRC)/wifi/doc/source/figures/assoc-manager.eps \
+	$(SRC)/wifi/doc/source/figures/emlsr-dl-txop.eps \
+	$(SRC)/wifi/doc/source/figures/emlsr-ul-txop.eps \
 	$(SRC)/wifi/doc/source/figures/mu-bar.pdf \
 	$(SRC)/wifi/doc/source/figures/mu-bar.png \
 	$(SRC)/wifi/doc/source/figures/aggr-mu-bar.pdf \
@@ -466,6 +468,8 @@
 	$(FIGURES)/energyFramework.eps \
 	$(FIGURES)/clear-channel.eps \
 	$(FIGURES)/assoc-manager.eps \
+	$(FIGURES)/emlsr-dl-txop.eps \
+	$(FIGURES)/emlsr-ul-txop.eps \
 	$(FIGURES)/nist-frame-success-rate.eps \
 	$(FIGURES)/nist-frame-success-rate-n.eps \
 	$(FIGURES)/nist-frame-success-rate-ac.eps \
@@ -545,6 +549,8 @@
 $(FIGURES)/nist-frame-success-rate-ac.pdf_width = 12cm
 $(FIGURES)/nist-frame-success-rate-ax.pdf_width = 12cm
 $(FIGURES)/assoc-manager.pdf_width = 12cm
+$(FIGURES)/emlsr-dl-txop.pdf_width = 12cm
+$(FIGURES)/emlsr-ul-txop.pdf_width = 12cm
 
 IMAGES_PNG = ${IMAGES_EPS:.eps=.png}
 IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/models/source/conf.py ns-3.41/doc/models/source/conf.py
--- ns-3.40/doc/models/source/conf.py	2023-09-27 13:30:55.802883400 -0700
+++ ns-3.41/doc/models/source/conf.py	2024-02-09 14:45:45.430484000 -0800
@@ -20,21 +20,22 @@
 # import sys
 # sys.path.insert(0, os.path.abspath('.'))
 
-import sys, os
+import os
+import sys
 
 # To change default code-block format in Latex to footnotesize (8pt)
 # Tip from https://stackoverflow.com/questions/9899283/how-do-you-change-the-code-example-font-size-in-latex-pdf-output-with-sphinx/9955928
 # Note:  sizes are \footnotesize (8pt), \small (9pt), and \normalsize (10pt).
 
-#from sphinx.highlighting import PygmentsBridge
-#from pygments.formatters.latex import LatexFormatter
+# from sphinx.highlighting import PygmentsBridge
+# from pygments.formatters.latex import LatexFormatter
 #
-#class CustomLatexFormatter(LatexFormatter):
+# class CustomLatexFormatter(LatexFormatter):
 #    def __init__(self, **options):
 #        super(CustomLatexFormatter, self).__init__(**options)
 #        self.verboptions = r"formatcom=\footnotesize"
 #
-#PygmentsBridge.latex_formatter = CustomLatexFormatter
+# PygmentsBridge.latex_formatter = CustomLatexFormatter
 
 # -- General configuration ------------------------------------------------
 
@@ -45,37 +46,37 @@
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix(es) of source filenames.
 # You can specify multiple suffix as a list of string:
 #
 # source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
 #
 # source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'ns-3 project'
-copyright = u'2006-2019'
-#author = u'test'
+project = "ns-3 project"
+copyright = "2006-2019, ns-3 project"
+# author = u'test'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.40'
+version = "ns-3.41"
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.40'
+release = "ns-3.41"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -96,7 +97,7 @@
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # These patterns also affect html_static_path and html_extra_path
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
@@ -118,7 +119,7 @@
 # show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
@@ -135,7 +136,7 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = 'ns3_html_theme'
+html_theme = "ns3_html_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -144,18 +145,18 @@
 # html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ['../..']
+html_theme_path = ["../.."]
 
 # The name for this set of Sphinx documents.
 # "<project> v<release> documentation" by default.
 #
 # html_title = 'est vtest'
-html_title = 'Model Library'
+html_title = "Model Library"
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
 #
 # html_short_title = None
-html_short_title = 'Models'
+html_short_title = "Models"
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
@@ -171,7 +172,7 @@
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
 
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
@@ -183,7 +184,7 @@
 # bottom, using the given strftime format.
 # The empty string is equivalent to '%b %d, %Y'.
 #
-html_last_updated_fmt = '%b %d, %Y %H:%M'
+html_last_updated_fmt = "%b %d, %Y %H:%M"
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
@@ -251,7 +252,7 @@
 # html_search_scorer = 'scorer.js'
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'ns-3doc'
+htmlhelp_basename = "ns-3doc"
 
 # -- Options for LaTeX output ---------------------------------------------
 
@@ -259,11 +260,9 @@
     # The paper size ('letterpaper' or 'a4paper').
     #
     # 'papersize': 'letterpaper',
-
     # The font size ('10pt', '11pt' or '12pt').
     #
     # 'pointsize': '10pt',
-
     # Additional stuff for the LaTeX preamble.
     #
     # VerbatimBorderColor:  make the box around code samples blend into the background
@@ -276,10 +275,10 @@
     # See above to change the font size of verbatim code blocks
     #
     # 'preamble': '',
-    'preamble': u'''\\usepackage{amssymb}
+    "preamble": """\\usepackage{amssymb}
  \\definecolor{VerbatimBorderColor}{rgb}{1,1,1}
  \\renewcommand{\\sphinxcode}[1]{\\texttt{\\small{#1}}}
-'''
+"""
     # Latex figure (float) alignment
     #
     # 'figure_align': 'htbp',
@@ -289,14 +288,13 @@
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    ('index', 'ns-3-model-library.tex', u'ns-3 Model Library',
-     u'ns-3 project', 'manual'),
+    ("index", "ns-3-model-library.tex", "ns-3 Model Library", "ns-3 project", "manual"),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
 #
-latex_logo = '../../ns3_html_theme/static/ns-3.png'
+latex_logo = "../../ns3_html_theme/static/ns-3.png"
 
 # If true, show page references after internal links.
 #
@@ -325,10 +323,7 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-model-library', u'ns-3 Model Library',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-model-library", "ns-3 Model Library", ["ns-3 project"], 1)]
 
 # If true, show URL addresses after external links.
 #
@@ -336,11 +331,11 @@
 
 # -- Options for texinfo output ---------------------------------------
 
-#texinfo_documents = [
+# texinfo_documents = [
 #    (master_doc, 'test', u'test Documentation',
 #    author, 'test', 'One line description of project.',
 #     'Miscellaneous'),
-#]
+# ]
 
 # Documents to append as an appendix to all manuals.
 #
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/models/source/index.rst ns-3.41/doc/models/source/index.rst
--- ns-3.40/doc/models/source/index.rst	2023-09-27 13:30:37.570839000 -0700
+++ ns-3.41/doc/models/source/index.rst	2024-02-09 14:44:07.781789300 -0800
@@ -8,7 +8,7 @@
 
 * Several guides that are version controlled for each release (the
   `latest release <https://www.nsnam.org/documentation/latest/>`_) and
-  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+  `development tree <https://www.nsnam.org/documentation/development-tree/>`_:
 
   * Tutorial
   * Installation Guide
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/ns3_html_theme/ns3_doxy_footer.html ns-3.41/doc/ns3_html_theme/ns3_doxy_footer.html
--- ns-3.40/doc/ns3_html_theme/ns3_doxy_footer.html	2023-09-27 13:30:37.574838900 -0700
+++ ns-3.41/doc/ns3_html_theme/ns3_doxy_footer.html	2024-02-09 14:44:07.781789300 -0800
@@ -9,14 +9,14 @@
     $navpath
     <li class="footer">$generatedby
     <a href="http://www.doxygen.org/index.html">
-    <img class="footer" src="$relpath$doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
+    <img class="footer" src="$relpath$doxygen.svg" alt="doxygen"/></a> $doxygenversion </li>
   </ul>
 </div>
 <!--END GENERATE_TREEVIEW-->
 <!--BEGIN !GENERATE_TREEVIEW-->
 <hr class="footer"/><address class="footer"><small>
 $generatedby &#160;<a href="http://www.doxygen.org/index.html">
-<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/>
+<img class="footer" src="$relpath$doxygen.svg" alt="doxygen"/>
 </a> $doxygenversion
 </small></address>
 <!--END !GENERATE_TREEVIEW-->
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/ns3_html_theme/static/ns3_stylesheet.css ns-3.41/doc/ns3_html_theme/static/ns3_stylesheet.css
--- ns-3.40/doc/ns3_html_theme/static/ns3_stylesheet.css	2023-09-27 13:30:37.574838900 -0700
+++ ns-3.41/doc/ns3_html_theme/static/ns3_stylesheet.css	2024-02-09 14:44:07.785789300 -0800
@@ -353,4 +353,16 @@
         background-color: #ceccca;
         padding: 3px;
     }
+
+    #ns3_home1 img {
+        background-color: transparent;
+    }
+
+    img.footer {
+        background-color: transparent;
+    }
+
+    #nav-sync img {
+        background-color: transparent;
+    }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/tutorial/pickle-to-xml.py ns-3.41/doc/tutorial/pickle-to-xml.py
--- ns-3.40/doc/tutorial/pickle-to-xml.py	2023-09-27 13:30:37.598839000 -0700
+++ ns-3.41/doc/tutorial/pickle-to-xml.py	2024-02-09 14:44:07.829789600 -0800
@@ -7,35 +7,41 @@
 # ...
 # </pages>
 
-import pickle
-import os
 import codecs
+import os
+import pickle
+
 
 def dump_pickles(out, dirname, filename, path):
-    with open(os.path.join(dirname, filename), 'r', encoding='utf-8') as f:
+    with open(os.path.join(dirname, filename), "r", encoding="utf-8") as f:
         data = pickle.load(f)
-    with codecs.open(data['current_page_name'] + '.frag', mode='w', encoding='utf-8') as fragment_file:
-        fragment_file.write(data['body'])
+    with codecs.open(
+        data["current_page_name"] + ".frag", mode="w", encoding="utf-8"
+    ) as fragment_file:
+        fragment_file.write(data["body"])
 
     out.write('  <page url="%s">\n' % path)
-    out.write('    <fragment>%s.frag</fragment>\n' % data['current_page_name'])
-    if data['prev'] is not None:
-        out.write('    <prev url="%s">%s</prev>\n' %
-                  (os.path.normpath(os.path.join(path, data['prev']['link'])),
-                   data['prev']['title']))
-    if data['next'] is not None:
-        out.write('    <next url="%s">%s</next>\n' %
-                  (os.path.normpath(os.path.join(path, data['next']['link'])),
-                   data['next']['title']))
-    out.write('  </page>\n')
-
-    if data['next'] is not None:
-        next_path = os.path.normpath(os.path.join(path, data['next']['link']))
-        next_filename = os.path.basename(next_path) + '.fpickle'
+    out.write("    <fragment>%s.frag</fragment>\n" % data["current_page_name"])
+    if data["prev"] is not None:
+        out.write(
+            '    <prev url="%s">%s</prev>\n'
+            % (os.path.normpath(os.path.join(path, data["prev"]["link"])), data["prev"]["title"])
+        )
+    if data["next"] is not None:
+        out.write(
+            '    <next url="%s">%s</next>\n'
+            % (os.path.normpath(os.path.join(path, data["next"]["link"])), data["next"]["title"])
+        )
+    out.write("  </page>\n")
+
+    if data["next"] is not None:
+        next_path = os.path.normpath(os.path.join(path, data["next"]["link"]))
+        next_filename = os.path.basename(next_path) + ".fpickle"
         dump_pickles(out, dirname, next_filename, next_path)
 
+
 import sys
 
-sys.stdout.write('<pages>\n')
-dump_pickles(sys.stdout, os.path.dirname(sys.argv[1]), os.path.basename(sys.argv[1]), '/')
-sys.stdout.write('</pages>')
+sys.stdout.write("<pages>\n")
+dump_pickles(sys.stdout, os.path.dirname(sys.argv[1]), os.path.basename(sys.argv[1]), "/")
+sys.stdout.write("</pages>")
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/tutorial/source/conf.py ns-3.41/doc/tutorial/source/conf.py
--- ns-3.40/doc/tutorial/source/conf.py	2023-09-27 13:30:55.802883400 -0700
+++ ns-3.41/doc/tutorial/source/conf.py	2024-02-09 14:45:45.430484000 -0800
@@ -24,15 +24,15 @@
 # Tip from https://stackoverflow.com/questions/9899283/how-do-you-change-the-code-example-font-size-in-latex-pdf-output-with-sphinx/9955928
 # Note:  sizes are \footnotesize (8pt), \small (9pt), and \normalsize (10pt).
 
-#from sphinx.highlighting import PygmentsBridge
-#from pygments.formatters.latex import LatexFormatter
+# from sphinx.highlighting import PygmentsBridge
+# from pygments.formatters.latex import LatexFormatter
 #
-#class CustomLatexFormatter(LatexFormatter):
+# class CustomLatexFormatter(LatexFormatter):
 #    def __init__(self, **options):
 #        super(CustomLatexFormatter, self).__init__(**options)
 #        self.verboptions = r"formatcom=\footnotesize"
 #
-#PygmentsBridge.latex_formatter = CustomLatexFormatter
+# PygmentsBridge.latex_formatter = CustomLatexFormatter
 
 # -- General configuration ------------------------------------------------
 
@@ -43,37 +43,37 @@
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix(es) of source filenames.
 # You can specify multiple suffix as a list of string:
 #
 # source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
 #
 # source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'ns-3 project'
-copyright = u'2006-2019'
-#author = u'test'
+project = "ns-3 project"
+copyright = "2006-2019, ns-3 project"
+# author = u'test'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.40'
+version = "ns-3.41"
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.40'
+release = "ns-3.41"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -94,7 +94,7 @@
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 # These patterns also affect html_static_path and html_extra_path
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
@@ -116,7 +116,7 @@
 # show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
@@ -133,7 +133,7 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = 'ns3_html_theme'
+html_theme = "ns3_html_theme"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -142,13 +142,13 @@
 # html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ['../..']
+html_theme_path = ["../.."]
 
 # The name for this set of Sphinx documents.
 # "<project> v<release> documentation" by default.
 #
 # html_title = 'est vtest'
-html_title = 'Tutorial'
+html_title = "Tutorial"
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
 #
@@ -168,7 +168,7 @@
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = ["_static"]
 
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
@@ -180,7 +180,7 @@
 # bottom, using the given strftime format.
 # The empty string is equivalent to '%b %d, %Y'.
 #
-html_last_updated_fmt = '%b %d, %Y %H:%M'
+html_last_updated_fmt = "%b %d, %Y %H:%M"
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
@@ -248,7 +248,7 @@
 # html_search_scorer = 'scorer.js'
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'ns-3doc'
+htmlhelp_basename = "ns-3doc"
 
 # -- Options for LaTeX output ---------------------------------------------
 
@@ -256,11 +256,9 @@
     # The paper size ('letterpaper' or 'a4paper').
     #
     # 'papersize': 'letterpaper',
-
     # The font size ('10pt', '11pt' or '12pt').
     #
     # 'pointsize': '10pt',
-
     # Additional stuff for the LaTeX preamble.
     #
     # VerbatimBorderColor:  make the box around code samples blend into the background
@@ -273,10 +271,10 @@
     # See above to change the font size of verbatim code blocks
     #
     # 'preamble': '',
-    'preamble': u'''\\usepackage{amssymb}
+    "preamble": """\\usepackage{amssymb}
  \\definecolor{VerbatimBorderColor}{rgb}{1,1,1}
  \\renewcommand{\\sphinxcode}[1]{\\texttt{\\small{#1}}}
-'''
+"""
     # Latex figure (float) alignment
     #
     # 'figure_align': 'htbp',
@@ -286,14 +284,13 @@
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    ('index', 'ns-3-tutorial.tex', u'ns-3 Tutorial',
-     u'ns-3 project', 'manual'),
+    ("index", "ns-3-tutorial.tex", "ns-3 Tutorial", "ns-3 project", "manual"),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
 #
-latex_logo = '../../ns3_html_theme/static/ns-3.png'
+latex_logo = "../../ns3_html_theme/static/ns-3.png"
 
 # If true, show page references after internal links.
 #
@@ -322,10 +319,7 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-tutorial', u'ns-3 Tutorial',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-tutorial", "ns-3 Tutorial", ["ns-3 project"], 1)]
 
 # If true, show URL addresses after external links.
 #
@@ -333,11 +327,11 @@
 
 # -- Options for texinfo output ---------------------------------------
 
-#texinfo_documents = [
+# texinfo_documents = [
 #    (master_doc, 'test', u'test Documentation',
 #    author, 'test', 'One line description of project.',
 #     'Miscellaneous'),
-#]
+# ]
 
 # Documents to append as an appendix to all manuals.
 #
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/tutorial/source/getting-started.rst ns-3.41/doc/tutorial/source/getting-started.rst
--- ns-3.40/doc/tutorial/source/getting-started.rst	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/doc/tutorial/source/getting-started.rst	2024-02-09 14:44:07.829789600 -0800
@@ -61,7 +61,7 @@
 for various systems, available at
 https://www.nsnam.org/docs/installation/html/index.html.
 
-As of the most recent |ns3| release (ns-3.40), the following tools
+As of the most recent |ns3| release (ns-3.41), the following tools
 are needed to get started with |ns3|:
 
 ============  ===========================================================
@@ -69,7 +69,7 @@
 ============  ===========================================================
 C++ compiler  ``clang++`` or ``g++`` (g++ version 9 or greater)
 Python        ``python3`` version >=3.6
-CMake         ``cmake`` version >=3.10
+CMake         ``cmake`` version >=3.13
 Build system  ``make``, ``ninja``, ``xcodebuild`` (XCode)
 Git           any recent version (to access |ns3| from `GitLab.com <https://gitlab.com/nsnam/ns-3-dev/>`_)
 tar           any recent version (to unpack an `ns-3 release <https://www.nsnam.org/releases/>`_)
@@ -116,21 +116,21 @@
   $ cd
   $ mkdir workspace
   $ cd workspace
-  $ wget https://www.nsnam.org/release/ns-allinone-3.40.tar.bz2
-  $ tar xjf ns-allinone-3.40.tar.bz2
+  $ wget https://www.nsnam.org/release/ns-allinone-3.41.tar.bz2
+  $ tar xjf ns-allinone-3.41.tar.bz2
 
 Notice the use above of the ``wget`` utility, which is a command-line
 tool to fetch objects from the web; if you do not have this installed,
 you can use a browser for this step.
 
 Following these steps, if you change into the directory
-``ns-allinone-3.40``, you should see a number of files and directories
+``ns-allinone-3.41``, you should see a number of files and directories
 
 .. sourcecode:: text
 
-  $ cd ns-allinone-3.40
+  $ cd ns-allinone-3.41
   $ ls
-  bake  build.py  constants.py  netanim-3.109 ns-3.40  README.md  util.py
+  bake  build.py  constants.py  netanim-3.109 ns-3.41  README.md  util.py
 
 You are now ready to build the base |ns3| distribution and may skip ahead
 to the section on building |ns3|.
@@ -180,7 +180,7 @@
 
 .. sourcecode:: console
 
-  $ python3 download.py -n ns-3.40
+  $ python3 download.py -n ns-3.41
 
 After this step, the additional repositories of |ns3|, bake, pybindgen,
 and netanim will be downloaded to the ``ns-3-allinone`` directory.
@@ -249,9 +249,9 @@
 
 There are a few configuration targets available:
 
-1.  ``ns-3.40``:  the code corresponding to the release
+1.  ``ns-3.41``:  the code corresponding to the release
 2.  ``ns-3-dev``:  a similar module but using the development code tree
-3.  ``ns-allinone-3.40``:  the module that includes other optional features
+3.  ``ns-allinone-3.41``:  the module that includes other optional features
     such as bake build system, netanim animator, and pybindgen
 4.  ``ns-3-allinone``:  similar to the released version of the allinone
     module, but for development code.
@@ -268,7 +268,7 @@
 `"ns-3 Releases"
 <https://www.nsnam.org/releases>`_
 web page and clicking on the latest release link.  We'll proceed in
-this tutorial example with ``ns-3.40``.
+this tutorial example with ``ns-3.41``.
 
 We are now going to use the bake tool to pull down the various pieces of
 |ns3| you will be using.  First, we'll say a word about running bake.
@@ -297,7 +297,7 @@
 
 .. sourcecode:: console
 
-  $ ./bake.py configure -e ns-allinone-3.40
+  $ ./bake.py configure -e ns-allinone-3.41
 
 Next, we'll ask bake to check whether we have enough tools to download
 various components.  Type:
@@ -347,7 +347,7 @@
   >> Downloading click-ns-3.37 - OK
   >> Downloading BRITE - OK
   >> Downloading openflow-dev - OK
-  >> Downloading ns-3.40 (target directory:ns-3.40) - OK
+  >> Downloading ns-3.41 (target directory:ns-3.41) - OK
 
 The above suggests that three sources have been downloaded.  Check the
 ``source`` directory now and type ``ls``; one should see:
@@ -356,7 +356,7 @@
 
   $ cd source
   $ ls
-  BRITE  click-ns-3.37  netanim-3.109  ns-3.40  openflow-dev
+  BRITE  click-ns-3.37  netanim-3.109  ns-3.41  openflow-dev
 
 You are now ready to build the |ns3| distribution.
 
@@ -386,7 +386,7 @@
 
 If you downloaded
 using a tarball you should have a directory called something like
-``ns-allinone-3.40`` under your ``~/workspace`` directory.
+``ns-allinone-3.41`` under your ``~/workspace`` directory.
 Type the following:
 
 .. sourcecode:: console
@@ -419,7 +419,7 @@
 .. sourcecode:: text
 
   >> Building netanim-3.109 - OK
-  >> Building ns-3.40 - OK
+  >> Building ns-3.41 - OK
 
 There may be failures to build all components, but the build will proceed
 anyway if the component is optional.
@@ -1146,6 +1146,65 @@
 
 .. figure:: ../figures/vscode/debugging.png
 
+Note: If you are running on Windows, you need to manually add your ns-3 library directory
+to the ``PATH`` environment variable. This can be accomplished in two ways.
+
+The first, is to set VSCode's ``settings.json`` file to include the following:
+
+.. sourcecode:: json
+
+  "cmake.environment": {
+        "PATH": "${env:PATH};${workspaceFolder}/build/lib"
+    }
+
+The second, a more permanent solution, with the following command:
+
+.. sourcecode:: terminal
+
+  > echo %PATH%
+  C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;
+  C:\Windows\System32\OpenSSH\;C:\Program Files\dotnet\;C:\Program Files\PuTTY\;C:\Program Files\VSCodium\bin;
+  C:\Program Files\Meld\;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\system32;C:\Windows;
+  C:\Windows\System32\Wbem;C:\Windows\System32\OpenSSH\;C:\Program Files\dotnet\;C:\Program Files\PuTTY\;
+  C:\Program Files\VSCodium\bin;C:\Program Files\Meld\;C:\Users\username\AppData\Local\Microsoft\WindowsApps;
+
+  > setx PATH "%PATH%;C:\path\to\ns-3-dev\build\lib"
+
+  SUCCESS: Specified value was saved.
+
+  > echo %PATH%
+  C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;
+  ...
+  C:\Program Files\VSCodium\bin;C:\Program Files\Meld\;C:\Users\username\AppData\Local\Microsoft\WindowsApps;
+  C:\tools\source\ns-3-dev\build\lib;
+
+If you do not setup your ``PATH`` environment variable, you may end up having problems to debug that
+look like the following:
+
+.. sourcecode:: console
+
+  =thread-group-added,id="i1"
+  GNU gdb (GDB) 14.1
+  Copyright (C) 2023 Free Software Foundation, Inc.
+  License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+  ...
+  ERROR: Unable to start debugging. GDB exited unexpectedly.
+  The program 'C:\tools\source\ns-3-dev\build\examples\wireless\ns3-dev-wifi-he-network-debug.exe' has exited with code 0 (0x00000000).
+
+  ERROR: During startup program exited with code 0xc0000135.
+
+Or
+
+.. sourcecode:: console
+
+  =thread-group-added,id="i1"
+  GNU gdb (GDB) 14.1
+  Copyright (C) 2023 Free Software Foundation, Inc.
+  License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+  ...
+  ERROR: Unable to start debugging. Unexpected GDB output from command "-exec-run". During startup program exited with code 0xc0000135.
+  The program 'C:\tools\source\ns-3-dev\build\examples\wireless\ns3-dev-wifi-he-network-debug.exe' has exited with code 0 (0x00000000).
+
 JetBrains CLion
 ===============
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/tutorial/source/index.rst ns-3.41/doc/tutorial/source/index.rst
--- ns-3.40/doc/tutorial/source/index.rst	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/doc/tutorial/source/index.rst	2024-02-09 14:44:07.829789600 -0800
@@ -8,7 +8,7 @@
 
 * Several guides that are version controlled for each release (the
   `latest release <https://www.nsnam.org/documentation/latest/>`_) and
-  `development tree <https://www.nsnam.org/ns-3-dev/documentation/>`_:
+  `development tree <https://www.nsnam.org/documentation/development-tree/>`_:
 
   * Tutorial *(this document)*
   * Installation Guide
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/tutorial/source/quick-start.rst ns-3.41/doc/tutorial/source/quick-start.rst
--- ns-3.40/doc/tutorial/source/quick-start.rst	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/doc/tutorial/source/quick-start.rst	2024-02-09 14:44:07.833789600 -0800
@@ -64,13 +64,13 @@
 
    ::
 
-    $ tar xjf ns-allinone-3.40.tar.bz2
+    $ tar xjf ns-allinone-3.41.tar.bz2
 
 3) Change into the |ns3| directory directly; e.g.
 
    ::
 
-    $ cd ns-allinone-3.40/ns-3.40
+    $ cd ns-allinone-3.41/ns-3.41
 
 The ns-allinone directory has some additional components but we are skipping
 over them here; one can work directly from the |ns3| source code directory.
@@ -92,12 +92,12 @@
 
   $ cd ns-3-dev
 
-If instead you want to try the most recent release (version 3.40 as of this
+If instead you want to try the most recent release (version 3.41 as of this
 writing), you can checkout a branch corresponding to that git tag:
 
 ::
 
-  $ git checkout -b ns-3.40-branch ns-3.40
+  $ git checkout -b ns-3.41-branch ns-3.41
 
 Building and testing ns-3
 *************************
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/doc/tutorial/source/tracing.rst ns-3.41/doc/tutorial/source/tracing.rst
--- ns-3.40/doc/tutorial/source/tracing.rst	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/doc/tutorial/source/tracing.rst	2024-02-09 14:44:07.833789600 -0800
@@ -558,7 +558,7 @@
 directly::
 
   Ptr<Object> theObject = wifiStaNodes.Get(nWifi - 1);
-  theObject->TraceConnectWithoutContext("CourseChange", MakeCallback(&CourseChange));
+  theObject->GetObject<MobilityModel>()->TraceConnectWithoutContext("CourseChange", MakeCallback(&CourseChange));
 
 In the ``third.cc`` example, we actually wanted an additional "context"
 to be delivered along with the Callback parameters (which will be
@@ -566,7 +566,7 @@
 code::
 
   Ptr<Object> theObject = wifiStaNodes.Get(nWifi - 1);
-  theObject->TraceConnect("CourseChange", MakeCallback(&CourseChange));
+  theObject->GetObject<MobilityModel>()->TraceConnect("CourseChange", MakeCallback(&CourseChange));
 
 It turns out that the internal code for
 ``Config::ConnectWithoutContext`` and ``Config::Connect`` actually
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/.editorconfig ns-3.41/.editorconfig
--- ns-3.40/.editorconfig	2023-09-27 13:30:37.522838800 -0700
+++ ns-3.41/.editorconfig	2024-02-09 14:44:07.717788700 -0800
@@ -10,10 +10,14 @@
 root = true
 
 [*]
-end_of_line = lf # Unix-style newlines
-charset = utf-8 # All files are in UTF-8
-insert_final_newline = true # All files end with a newline
-trim_trailing_whitespace = true # No trailing whitespaces
+# Unix-style newlines
+end_of_line = lf
+# All files are in UTF-8
+charset = utf-8
+# All files end with a newline
+insert_final_newline = true
+# No trailing whitespaces
+trim_trailing_whitespace = true
 
 [Makefile]
 indent_style = tab
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/channel-models/three-gpp-v2v-channel-example.cc ns-3.41/examples/channel-models/three-gpp-v2v-channel-example.cc
--- ns-3.40/examples/channel-models/three-gpp-v2v-channel-example.cc	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/examples/channel-models/three-gpp-v2v-channel-example.cc	2024-02-09 14:44:07.833789600 -0800
@@ -114,11 +114,12 @@
     *(params.txParams->psd) *= propagationGainLinear;
 
     // apply the fast fading and the beamforming gain
-    Ptr<SpectrumValue> rxPsd = m_spectrumLossModel->CalcRxPowerSpectralDensity(params.txParams,
-                                                                               params.txMob,
-                                                                               params.rxMob,
-                                                                               params.txAntenna,
-                                                                               params.rxAntenna);
+    auto rxParams = m_spectrumLossModel->CalcRxPowerSpectralDensity(params.txParams,
+                                                                    params.txMob,
+                                                                    params.rxMob,
+                                                                    params.txAntenna,
+                                                                    params.rxAntenna);
+    Ptr<SpectrumValue> rxPsd = rxParams->psd;
     NS_LOG_DEBUG("Average rx power " << 10 * log10(Sum(*rxPsd) * 180e3) << " dB");
 
     // create the noise psd
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/energy/energy-model-example.cc ns-3.41/examples/energy/energy-model-example.cc
--- ns-3.40/examples/energy/energy-model-example.cc	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/examples/energy/energy-model-example.cc	2024-02-09 14:44:07.833789600 -0800
@@ -17,7 +17,6 @@
  * Author: Sidharth Nabar <snabar@uw.edu>, He Wu <mdzz@u.washington.edu>
  */
 
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/energy-module.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/energy/energy-model-with-harvesting-example.cc ns-3.41/examples/energy/energy-model-with-harvesting-example.cc
--- ns-3.40/examples/energy/energy-model-with-harvesting-example.cc	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/examples/energy/energy-model-with-harvesting-example.cc	2024-02-09 14:44:07.833789600 -0800
@@ -46,7 +46,6 @@
  *
  */
 
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/energy-module.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/naming/object-names.cc ns-3.41/examples/naming/object-names.cc
--- ns-3.40/examples/naming/object-names.cc	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/examples/naming/object-names.cc	2024-02-09 14:44:07.833789600 -0800
@@ -217,7 +217,7 @@
     if (!outputValidated)
     {
         std::cerr << "Program internal checking failed; returning with error" << std::endl;
-        return (1);
+        return 1;
     }
 
     return 0;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/realtime/realtime-udp-echo.py ns-3.41/examples/realtime/realtime-udp-echo.py
--- ns-3.40/examples/realtime/realtime-udp-echo.py	2023-09-27 13:30:37.602839000 -0700
+++ ns-3.41/examples/realtime/realtime-udp-echo.py	2024-02-09 14:44:07.833789600 -0800
@@ -23,89 +23,101 @@
 # - DropTail queues
 # - Tracing of queues and packet receptions to file "udp-echo.tr"
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
+
 
 def main(argv):
-  #
-  # Allow the user to override any of the defaults and the above Bind() at
-  # run-time, via command-line arguments
-  #
-  cmd = ns.core.CommandLine()
-  cmd.Parse(argv)
-
-  #
-  # But since this is a realtime script, don't allow the user to mess with
-  # that.
-  #
-  ns.core.GlobalValue.Bind("SimulatorImplementationType", ns.core.StringValue("ns3::RealtimeSimulatorImpl"))
-
-  #
-  # Explicitly create the nodes required by the topology (shown above).
-  #
-  print ("Create nodes.")
-  n = ns.network.NodeContainer()
-  n.Create(4)
-
-  internet = ns.internet.InternetStackHelper()
-  internet.Install(n)
-
-  #
-  # Explicitly create the channels required by the topology (shown above).
-  #
-  print ("Create channels.")
-  csma = ns.csma.CsmaHelper()
-  csma.SetChannelAttribute("DataRate", ns.network.DataRateValue(ns.network.DataRate(5000000)))
-  csma.SetChannelAttribute("Delay", ns.core.TimeValue(ns.core.MilliSeconds(2)));
-  csma.SetDeviceAttribute("Mtu", ns.core.UintegerValue(1400))
-  d = csma.Install(n)
-
-  #
-  # We've got the "hardware" in place.  Now we need to add IP addresses.
-  #
-  print ("Assign IP Addresses.")
-  ipv4 = ns.internet.Ipv4AddressHelper()
-  ipv4.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
-  i = ipv4.Assign(d)
-
-  print ("Create Applications.")
-
-  #
-  # Create a UdpEchoServer application on node one.
-  #
-  port = 9  # well-known echo port number
-  server = ns.applications.UdpEchoServerHelper(port)
-  apps = server.Install(n.Get(1))
-  apps.Start(ns.core.Seconds(1.0))
-  apps.Stop(ns.core.Seconds(10.0))
-
-  #
-  # Create a UdpEchoClient application to send UDP datagrams from node zero to
-  # node one.
-  #
-  packetSize = 1024
-  maxPacketCount = 500
-  interPacketInterval = ns.core.Seconds(0.01)
-  client = ns.applications.UdpEchoClientHelper(i.GetAddress(1).ConvertTo(), port)
-  client.SetAttribute("MaxPackets", ns.core.UintegerValue(maxPacketCount))
-  client.SetAttribute("Interval", ns.core.TimeValue(interPacketInterval))
-  client.SetAttribute("PacketSize", ns.core.UintegerValue(packetSize))
-  apps = client.Install(n.Get(0))
-  apps.Start(ns.core.Seconds(2.0))
-  apps.Stop(ns.core.Seconds(10.0))
-
-  ascii = ns.network.AsciiTraceHelper()
-  csma.EnableAsciiAll(ascii.CreateFileStream("realtime-udp-echo.tr"))
-  csma.EnablePcapAll("realtime-udp-echo", False)
-
-  #
-  # Now, do the actual simulation.
-  #
-  print ("Run Simulation.")
-  ns.core.Simulator.Stop(ns.Seconds(10))
-  ns.core.Simulator.Run()
-  ns.core.Simulator.Destroy()
-  print ("Done.")
-
-if __name__ == '__main__':
-  import sys
-  main(sys.argv)
+    #
+    # Allow the user to override any of the defaults and the above Bind() at
+    # run-time, via command-line arguments
+    #
+    cmd = ns.core.CommandLine()
+    cmd.Parse(argv)
+
+    #
+    # But since this is a realtime script, don't allow the user to mess with
+    # that.
+    #
+    ns.core.GlobalValue.Bind(
+        "SimulatorImplementationType", ns.core.StringValue("ns3::RealtimeSimulatorImpl")
+    )
+
+    #
+    # Explicitly create the nodes required by the topology (shown above).
+    #
+    print("Create nodes.")
+    n = ns.network.NodeContainer()
+    n.Create(4)
+
+    internet = ns.internet.InternetStackHelper()
+    internet.Install(n)
+
+    #
+    # Explicitly create the channels required by the topology (shown above).
+    #
+    print("Create channels.")
+    csma = ns.csma.CsmaHelper()
+    csma.SetChannelAttribute("DataRate", ns.network.DataRateValue(ns.network.DataRate(5000000)))
+    csma.SetChannelAttribute("Delay", ns.core.TimeValue(ns.core.MilliSeconds(2)))
+    csma.SetDeviceAttribute("Mtu", ns.core.UintegerValue(1400))
+    d = csma.Install(n)
+
+    #
+    # We've got the "hardware" in place.  Now we need to add IP addresses.
+    #
+    print("Assign IP Addresses.")
+    ipv4 = ns.internet.Ipv4AddressHelper()
+    ipv4.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
+    i = ipv4.Assign(d)
+
+    print("Create Applications.")
+
+    #
+    # Create a UdpEchoServer application on node one.
+    #
+    port = 9  # well-known echo port number
+    server = ns.applications.UdpEchoServerHelper(port)
+    apps = server.Install(n.Get(1))
+    apps.Start(ns.core.Seconds(1.0))
+    apps.Stop(ns.core.Seconds(10.0))
+
+    #
+    # Create a UdpEchoClient application to send UDP datagrams from node zero to
+    # node one.
+    #
+    packetSize = 1024
+    maxPacketCount = 500
+    interPacketInterval = ns.core.Seconds(0.01)
+    client = ns.applications.UdpEchoClientHelper(i.GetAddress(1).ConvertTo(), port)
+    client.SetAttribute("MaxPackets", ns.core.UintegerValue(maxPacketCount))
+    client.SetAttribute("Interval", ns.core.TimeValue(interPacketInterval))
+    client.SetAttribute("PacketSize", ns.core.UintegerValue(packetSize))
+    apps = client.Install(n.Get(0))
+    apps.Start(ns.core.Seconds(2.0))
+    apps.Stop(ns.core.Seconds(10.0))
+
+    ascii = ns.network.AsciiTraceHelper()
+    csma.EnableAsciiAll(ascii.CreateFileStream("realtime-udp-echo.tr"))
+    csma.EnablePcapAll("realtime-udp-echo", False)
+
+    #
+    # Now, do the actual simulation.
+    #
+    print("Run Simulation.")
+    ns.core.Simulator.Stop(ns.Seconds(10))
+    ns.core.Simulator.Run()
+    ns.core.Simulator.Destroy()
+    print("Done.")
+
+
+if __name__ == "__main__":
+    import sys
+
+    main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/routing/simple-routing-ping6.py ns-3.41/examples/routing/simple-routing-ping6.py
--- ns-3.40/examples/routing/simple-routing-ping6.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/routing/simple-routing-ping6.py	2024-02-09 14:44:07.837789500 -0800
@@ -27,7 +27,14 @@
 #                router
 #
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 
 def main(argv):
@@ -73,7 +80,7 @@
     print("Application")
     packetSize = 1024
     maxPacketCount = 5
-    interPacketInterval = ns.Seconds(1.)
+    interPacketInterval = ns.Seconds(1.0)
     # ping = ns.PingHelper(i2.GetAddress(1, 1).ConvertTo())
     ping = ns.PingHelper(i2.GetAddress(1, 1).ConvertTo())
 
@@ -98,7 +105,7 @@
     ns.Simulator.Destroy()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     import sys
 
     main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/tcp/examples-to-run.py ns-3.41/examples/tcp/examples-to-run.py
--- ns-3.40/examples/tcp/examples-to-run.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/tcp/examples-to-run.py	2024-02-09 14:44:07.837789500 -0800
@@ -11,10 +11,26 @@
     ("tcp-large-transfer", "True", "True"),
     ("tcp-star-server", "True", "True"),
     ("tcp-variants-comparison", "True", "True"),
-    ("tcp-validation --firstTcpType=dctcp --linkRate=50Mbps --baseRtt=10ms --queueUseEcn=1 --stopTime=15s --validate=dctcp-10ms", "True", "True"),
-    ("tcp-validation --firstTcpType=dctcp --linkRate=50Mbps --baseRtt=80ms --queueUseEcn=1 --stopTime=40s --validate=dctcp-80ms", "True", "True"),
-    ("tcp-validation --firstTcpType=cubic --linkRate=50Mbps --baseRtt=50ms --queueUseEcn=0 --stopTime=20s --validate=cubic-50ms-no-ecn", "True", "True"),
-    ("tcp-validation --firstTcpType=cubic --linkRate=50Mbps --baseRtt=50ms --queueUseEcn=1 --stopTime=20s --validate=cubic-50ms-ecn", "True", "True"),
+    (
+        "tcp-validation --firstTcpType=dctcp --linkRate=50Mbps --baseRtt=10ms --queueUseEcn=1 --stopTime=15s --validate=dctcp-10ms",
+        "True",
+        "True",
+    ),
+    (
+        "tcp-validation --firstTcpType=dctcp --linkRate=50Mbps --baseRtt=80ms --queueUseEcn=1 --stopTime=40s --validate=dctcp-80ms",
+        "True",
+        "True",
+    ),
+    (
+        "tcp-validation --firstTcpType=cubic --linkRate=50Mbps --baseRtt=50ms --queueUseEcn=0 --stopTime=20s --validate=cubic-50ms-no-ecn",
+        "True",
+        "True",
+    ),
+    (
+        "tcp-validation --firstTcpType=cubic --linkRate=50Mbps --baseRtt=50ms --queueUseEcn=1 --stopTime=20s --validate=cubic-50ms-ecn",
+        "True",
+        "True",
+    ),
 ]
 
 # A list of Python examples to run in order to ensure that they remain
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/tcp/tcp-bbr-example.cc ns-3.41/examples/tcp/tcp-bbr-example.cc
--- ns-3.40/examples/tcp/tcp-bbr-example.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/tcp/tcp-bbr-example.cc	2024-02-09 14:44:07.837789500 -0800
@@ -60,9 +60,15 @@
 #include "ns3/point-to-point-module.h"
 #include "ns3/traffic-control-module.h"
 
+#include <filesystem>
+
 using namespace ns3;
+using namespace ns3::SystemPath;
 
 std::string dir;
+std::ofstream throughput;
+std::ofstream queueSize;
+
 uint32_t prev = 0;
 Time prevTime = Seconds(0);
 
@@ -71,15 +77,16 @@
 TraceThroughput(Ptr<FlowMonitor> monitor)
 {
     FlowMonitor::FlowStatsContainer stats = monitor->GetFlowStats();
-    auto itr = stats.begin();
-    Time curTime = Now();
-    std::ofstream thr(dir + "/throughput.dat", std::ios::out | std::ios::app);
-    thr << curTime << " "
-        << 8 * (itr->second.txBytes - prev) /
-               (1000 * 1000 * (curTime.GetSeconds() - prevTime.GetSeconds()))
-        << std::endl;
-    prevTime = curTime;
-    prev = itr->second.txBytes;
+    if (!stats.empty())
+    {
+        auto itr = stats.begin();
+        Time curTime = Now();
+        throughput << curTime << " "
+                   << 8 * (itr->second.txBytes - prev) / ((curTime - prevTime).ToDouble(Time::US))
+                   << std::endl;
+        prevTime = curTime;
+        prev = itr->second.txBytes;
+    }
     Simulator::Schedule(Seconds(0.2), &TraceThroughput, monitor);
 }
 
@@ -89,9 +96,7 @@
 {
     uint32_t qsize = qd->GetCurrentSize().GetValue();
     Simulator::Schedule(Seconds(0.2), &CheckQueueSize, qd);
-    std::ofstream q(dir + "/queueSize.dat", std::ios::out | std::ios::app);
-    q << Simulator::Now().GetSeconds() << " " << qsize << std::endl;
-    q.close();
+    queueSize << Simulator::Now().GetSeconds() << " " << qsize << std::endl;
 }
 
 // Trace congestion window
@@ -143,6 +148,10 @@
     queueDisc = std::string("ns3::") + queueDisc;
 
     Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue("ns3::" + tcpTypeId));
+
+    // The maximum send buffer size is set to 4194304 bytes (4MB) and the
+    // maximum receive buffer size is set to 6291456 bytes (6MB) in the Linux
+    // kernel. The same buffer sizes are used as default in this example.
     Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(4194304));
     Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(6291456));
     Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(10));
@@ -225,23 +234,20 @@
 
     // Create a new directory to store the output of the program
     dir = "bbr-results/" + currentTime + "/";
-    std::string dirToSave = "mkdir -p " + dir;
-    if (system(dirToSave.c_str()) == -1)
-    {
-        exit(1);
-    }
+    MakeDirectories(dir);
 
     // The plotting scripts are provided in the following repository, if needed:
     // https://github.com/mohittahiliani/BBR-Validation/
     //
     // Download 'PlotScripts' directory (which is inside ns-3 scripts directory)
     // from the link given above and place it in the ns-3 root directory.
-    // Uncomment the following three lines to generate plots for Congestion
-    // Window, sender side throughput and queue occupancy on the bottleneck link.
+    // Uncomment the following three lines to copy plot scripts for
+    // Congestion Window, sender side throughput and queue occupancy on the
+    // bottleneck link into the output directory.
     //
-    // system (("cp -R PlotScripts/gnuplotScriptCwnd " + dir).c_str ());
-    // system (("cp -R PlotScripts/gnuplotScriptThroughput " + dir).c_str ());
-    // system (("cp -R PlotScripts/gnuplotScriptQueueSize " + dir).c_str ());
+    // std::filesystem::copy("PlotScripts/gnuplotScriptCwnd", dir);
+    // std::filesystem::copy("PlotScripts/gnuplotScriptThroughput", dir);
+    // std::filesystem::copy("PlotScripts/gnuplotScriptQueueSize", dir);
 
     // Trace the queue occupancy on the second interface of R1
     tch.Uninstall(routers.Get(0)->GetDevice(1));
@@ -252,13 +258,17 @@
     // Generate PCAP traces if it is enabled
     if (enablePcap)
     {
-        if (system((dirToSave + "/pcap/").c_str()) == -1)
-        {
-            exit(1);
-        }
+        MakeDirectories(dir + "pcap/");
         bottleneckLink.EnablePcapAll(dir + "/pcap/bbr", true);
     }
 
+    // Open files for writing throughput traces and queue size
+    throughput.open(dir + "/throughput.dat", std::ios::out);
+    queueSize.open(dir + "/queueSize.dat", std::ios::out);
+
+    NS_ASSERT_MSG(throughput.is_open(), "Throughput file was not opened correctly");
+    NS_ASSERT_MSG(queueSize.is_open(), "Queue size file was not opened correctly");
+
     // Check for dropped packets using Flow Monitor
     FlowMonitorHelper flowmon;
     Ptr<FlowMonitor> monitor = flowmon.InstallAll();
@@ -268,5 +278,8 @@
     Simulator::Run();
     Simulator::Destroy();
 
+    throughput.close();
+    queueSize.close();
+
     return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/tcp/tcp-linux-reno.cc ns-3.41/examples/tcp/tcp-linux-reno.cc
--- ns-3.40/examples/tcp/tcp-linux-reno.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/tcp/tcp-linux-reno.cc	2024-02-09 14:44:07.837789500 -0800
@@ -52,6 +52,9 @@
 Time stopTime = Seconds(60);
 uint32_t segmentSize = 524;
 
+std::ofstream fPlotQueue;
+std::ofstream fPlotCwnd;
+
 // Function to check queue length of Router 1
 void
 CheckQueueSize(Ptr<QueueDisc> queue)
@@ -60,19 +63,14 @@
 
     // Check queue size every 1/100 of a second
     Simulator::Schedule(Seconds(0.001), &CheckQueueSize, queue);
-    std::ofstream fPlotQueue(std::stringstream(dir + "queue-size.dat").str(),
-                             std::ios::out | std::ios::app);
     fPlotQueue << Simulator::Now().GetSeconds() << " " << qSize << std::endl;
-    fPlotQueue.close();
 }
 
 // Function to trace change in cwnd at n0
 static void
 CwndChange(uint32_t oldCwnd, uint32_t newCwnd)
 {
-    std::ofstream fPlotQueue(dir + "cwndTraces/n0.dat", std::ios::out | std::ios::app);
-    fPlotQueue << Simulator::Now().GetSeconds() << " " << newCwnd / segmentSize << std::endl;
-    fPlotQueue.close();
+    fPlotCwnd << Simulator::Now().GetSeconds() << " " << newCwnd / segmentSize << std::endl;
 }
 
 // Function to calculate drops in a particular Queue
@@ -228,15 +226,11 @@
         retVal = system(dirToRemove.c_str());
         NS_ASSERT_MSG(retVal == 0, "Error in return value");
     }
-    std::string dirToSave = "mkdir -p " + dir;
-    retVal = system(dirToSave.c_str());
-    NS_ASSERT_MSG(retVal == 0, "Error in return value");
-    retVal = system((dirToSave + "/pcap/").c_str());
-    NS_ASSERT_MSG(retVal == 0, "Error in return value");
-    retVal = system((dirToSave + "/queueTraces/").c_str());
-    NS_ASSERT_MSG(retVal == 0, "Error in return value");
-    retVal = system((dirToSave + "/cwndTraces/").c_str());
-    NS_ASSERT_MSG(retVal == 0, "Error in return value");
+
+    SystemPath::MakeDirectories(dir);
+    SystemPath::MakeDirectories(dir + "/pcap/");
+    SystemPath::MakeDirectories(dir + "/queueTraces/");
+    SystemPath::MakeDirectories(dir + "/cwndTraces/");
 
     // Set default parameters for queue discipline
     Config::SetDefault(qdiscTypeId + "::MaxSize", QueueSizeValue(QueueSize("100p")));
@@ -251,6 +245,10 @@
     // Enable BQL
     tch.SetQueueLimits("ns3::DynamicQueueLimits");
 
+    // Open files for writing queue size and cwnd traces
+    fPlotQueue.open(dir + "queue-size.dat", std::ios::out);
+    fPlotCwnd.open(dir + "cwndTraces/n0.dat", std::ios::out);
+
     // Calls function to check queue size
     Simulator::ScheduleNow(&CheckQueueSize, qd.Get(0));
 
@@ -299,5 +297,8 @@
 
     Simulator::Destroy();
 
+    fPlotQueue.close();
+    fPlotCwnd.close();
+
     return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/tcp/tcp-validation.cc ns-3.41/examples/tcp/tcp-validation.cc
--- ns-3.40/examples/tcp/tcp-validation.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/tcp/tcp-validation.cc	2024-02-09 14:44:07.837789500 -0800
@@ -663,6 +663,9 @@
     Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(10));
     Config::SetDefault("ns3::TcpL4Protocol::RecoveryType",
                        TypeIdValue(TcpPrrRecovery::GetTypeId()));
+    // Validation criteria were written for TCP Cubic without Reno-friendly behavior, so disable it
+    // for these tests
+    Config::SetDefault("ns3::TcpCubic::TcpFriendliness", BooleanValue(false));
 
     ////////////////////////////////////////////////////////////
     // command-line argument parsing                          //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/tutorial/first.py ns-3.41/examples/tutorial/first.py
--- ns-3.40/examples/tutorial/first.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/tutorial/first.py	2024-02-09 14:44:07.841789700 -0800
@@ -1,19 +1,26 @@
-# /*
-#  * This program is free software; you can redistribute it and/or modify
-#  * it under the terms of the GNU General Public License version 2 as
-#  * published by the Free Software Foundation;
-#  *
-#  * 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; if not, write to the Free Software
-#  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#  */
-
-from ns import ns
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
+#
+# 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; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 # // Default Network Topology
 # //
@@ -38,8 +45,7 @@
 stack.Install(nodes)
 
 address = ns.internet.Ipv4AddressHelper()
-address.SetBase(ns.network.Ipv4Address("10.1.1.0"),
-                ns.network.Ipv4Mask("255.255.255.0"))
+address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
 
 interfaces = address.Assign(devices)
 
@@ -61,4 +67,3 @@
 
 ns.core.Simulator.Run()
 ns.core.Simulator.Destroy()
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/tutorial/second.py ns-3.41/examples/tutorial/second.py
--- ns-3.40/examples/tutorial/second.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/tutorial/second.py	2024-02-09 14:44:07.841789700 -0800
@@ -1,23 +1,30 @@
-# -*-  Mode: Python; -*-
-# /*
-#  * This program is free software; you can redistribute it and/or modify
-#  * it under the terms of the GNU General Public License version 2 as
-#  * published by the Free Software Foundation;
-#  *
-#  * 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; if not, write to the Free Software
-#  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#  *
-#  * Ported to Python by Mohit P. Tahiliani
-#  */
-
-from ns import ns
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
+#
+# 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; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# Ported to Python by Mohit P. Tahiliani
+#
+
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 import sys
+from ctypes import c_bool, c_int
 
 # // Default Network Topology
 # //
@@ -27,7 +34,7 @@
 # //                    ================
 # //                      LAN 10.1.2.0
 
-from ctypes import c_int, c_bool
+
 nCsma = c_int(3)
 verbose = c_bool(True)
 cmd = ns.CommandLine(__file__)
@@ -76,9 +83,11 @@
 serverApps.Start(ns.core.Seconds(1.0))
 serverApps.Stop(ns.core.Seconds(10.0))
 
-echoClient = ns.applications.UdpEchoClientHelper(csmaInterfaces.GetAddress(nCsma.value).ConvertTo(), 9)
+echoClient = ns.applications.UdpEchoClientHelper(
+    csmaInterfaces.GetAddress(nCsma.value).ConvertTo(), 9
+)
 echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
-echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
+echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0)))
 echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
 
 clientApps = echoClient.Install(p2pNodes.Get(0))
@@ -88,8 +97,7 @@
 ns.internet.Ipv4GlobalRoutingHelper.PopulateRoutingTables()
 
 pointToPoint.EnablePcapAll("second")
-csma.EnablePcap ("second", csmaDevices.Get (1), True)
+csma.EnablePcap("second", csmaDevices.Get(1), True)
 
 ns.core.Simulator.Run()
 ns.core.Simulator.Destroy()
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/tutorial/third.py ns-3.41/examples/tutorial/third.py
--- ns-3.40/examples/tutorial/third.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/tutorial/third.py	2024-02-09 14:44:07.841789700 -0800
@@ -1,23 +1,30 @@
-# -*-  Mode: Python; -*-
-# /*
-#  * This program is free software; you can redistribute it and/or modify
-#  * it under the terms of the GNU General Public License version 2 as
-#  * published by the Free Software Foundation;
-#  *
-#  * 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; if not, write to the Free Software
-#  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#  *
-#  * Ported to Python by Mohit P. Tahiliani
-#  */
-
-from ns import ns
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
+#
+# 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; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# Ported to Python by Mohit P. Tahiliani
+#
+
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 import sys
+from ctypes import c_bool, c_int
 
 # // Default Network Topology
 # //
@@ -30,7 +37,7 @@
 # //                                   ================
 # //                                     LAN 10.1.2.0
 
-from ctypes import c_bool, c_int
+
 nCsma = c_int(3)
 verbose = c_bool(True)
 nWifi = c_int(3)
@@ -83,22 +90,40 @@
 phy.SetChannel(channel.Create())
 
 mac = ns.wifi.WifiMacHelper()
-ssid = ns.wifi.Ssid ("ns-3-ssid")
+ssid = ns.wifi.Ssid("ns-3-ssid")
 
 wifi = ns.wifi.WifiHelper()
 
-mac.SetType ("ns3::StaWifiMac", "Ssid", ns.wifi.SsidValue(ssid), "ActiveProbing", ns.core.BooleanValue(False))
+mac.SetType(
+    "ns3::StaWifiMac", "Ssid", ns.wifi.SsidValue(ssid), "ActiveProbing", ns.core.BooleanValue(False)
+)
 staDevices = wifi.Install(phy, mac, wifiStaNodes)
 
-mac.SetType("ns3::ApWifiMac","Ssid", ns.wifi.SsidValue (ssid))
+mac.SetType("ns3::ApWifiMac", "Ssid", ns.wifi.SsidValue(ssid))
 apDevices = wifi.Install(phy, mac, wifiApNode)
 
 mobility = ns.mobility.MobilityHelper()
-mobility.SetPositionAllocator("ns3::GridPositionAllocator", "MinX", ns.core.DoubleValue(0.0),
-                              "MinY", ns.core.DoubleValue (0.0), "DeltaX", ns.core.DoubleValue(5.0), "DeltaY", ns.core.DoubleValue(10.0),
-                              "GridWidth", ns.core.UintegerValue(3), "LayoutType", ns.core.StringValue("RowFirst"))
-
-mobility.SetMobilityModel ("ns3::RandomWalk2dMobilityModel", "Bounds", ns.mobility.RectangleValue(ns.mobility.Rectangle (-50, 50, -50, 50)))
+mobility.SetPositionAllocator(
+    "ns3::GridPositionAllocator",
+    "MinX",
+    ns.core.DoubleValue(0.0),
+    "MinY",
+    ns.core.DoubleValue(0.0),
+    "DeltaX",
+    ns.core.DoubleValue(5.0),
+    "DeltaY",
+    ns.core.DoubleValue(10.0),
+    "GridWidth",
+    ns.core.UintegerValue(3),
+    "LayoutType",
+    ns.core.StringValue("RowFirst"),
+)
+
+mobility.SetMobilityModel(
+    "ns3::RandomWalk2dMobilityModel",
+    "Bounds",
+    ns.mobility.RectangleValue(ns.mobility.Rectangle(-50, 50, -50, 50)),
+)
 mobility.Install(wifiStaNodes)
 
 mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel")
@@ -126,12 +151,14 @@
 serverApps.Start(ns.core.Seconds(1.0))
 serverApps.Stop(ns.core.Seconds(10.0))
 
-echoClient = ns.applications.UdpEchoClientHelper(csmaInterfaces.GetAddress(nCsma.value).ConvertTo(), 9)
+echoClient = ns.applications.UdpEchoClientHelper(
+    csmaInterfaces.GetAddress(nCsma.value).ConvertTo(), 9
+)
 echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
-echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
+echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0)))
 echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
 
-clientApps = echoClient.Install(wifiStaNodes.Get (nWifi.value - 1))
+clientApps = echoClient.Install(wifiStaNodes.Get(nWifi.value - 1))
 clientApps.Start(ns.core.Seconds(2.0))
 clientApps.Stop(ns.core.Seconds(10.0))
 
@@ -141,10 +168,9 @@
 
 if tracing.value:
     phy.SetPcapDataLinkType(phy.DLT_IEEE802_11_RADIO)
-    pointToPoint.EnablePcapAll ("third")
-    phy.EnablePcap ("third", apDevices.Get (0))
-    csma.EnablePcap ("third", csmaDevices.Get (0), True)
+    pointToPoint.EnablePcapAll("third")
+    phy.EnablePcap("third", apDevices.Get(0))
+    csma.EnablePcap("third", csmaDevices.Get(0), True)
 
 ns.core.Simulator.Run()
 ns.core.Simulator.Destroy()
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/examples-to-run.py ns-3.41/examples/wireless/examples-to-run.py
--- ns-3.40/examples/wireless/examples-to-run.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/examples-to-run.py	2024-02-09 14:44:07.841789700 -0800
@@ -15,9 +15,9 @@
     ("wifi-multirate --totalTime=0.3s --rateManager=ns3::MinstrelWifiManager", "True", "False"),
     ("wifi-multirate --totalTime=0.3s --rateManager=ns3::OnoeWifiManager", "True", "False"),
     ("wifi-multirate --totalTime=0.3s --rateManager=ns3::RraaWifiManager", "True", "False"),
-    ("wifi-adhoc", "False", "True"), # Takes too long to run
-    ("wifi-ap --verbose=0", "True", "True"), # Don't let it spew to stdout
-    ("wifi-clear-channel-cmu", "False", "True"), # Requires specific hardware
+    ("wifi-adhoc", "False", "True"),  # Takes too long to run
+    ("wifi-ap --verbose=0", "True", "True"),  # Don't let it spew to stdout
+    ("wifi-clear-channel-cmu", "False", "True"),  # Requires specific hardware
     ("wifi-simple-adhoc", "True", "True"),
     ("wifi-simple-adhoc-grid", "True", "True"),
     ("wifi-simple-infra", "True", "True"),
@@ -26,12 +26,36 @@
     ("wifi-sleep", "True", "True"),
     ("wifi-blockack", "True", "True"),
     ("wifi-timing-attributes --simulationTime=1", "True", "True"),
-    ("wifi-power-adaptation-distance --manager=ns3::ParfWifiManager --outputFileName=parf --steps=5 --stepsSize=10", "True", "True"),
-    ("wifi-power-adaptation-distance --manager=ns3::AparfWifiManager --outputFileName=aparf --steps=5 --stepsSize=10", "True", "False"),
-    ("wifi-power-adaptation-distance --manager=ns3::RrpaaWifiManager --outputFileName=rrpaa --steps=5 --stepsSize=10", "True", "False"),
-    ("wifi-rate-adaptation-distance --standard=802.11a --staManager=ns3::MinstrelWifiManager --apManager=ns3::MinstrelWifiManager --outputFileName=minstrel --stepsSize=50 --stepsTime=0.1", "True", "False"),
-    ("wifi-rate-adaptation-distance --standard=802.11a --staManager=ns3::MinstrelWifiManager --apManager=ns3::MinstrelWifiManager --outputFileName=minstrel --stepsSize=50 --stepsTime=0.1 --STA1_x=-200", "True", "False"),
-    ("wifi-rate-adaptation-distance --staManager=ns3::MinstrelHtWifiManager --apManager=ns3::MinstrelHtWifiManager --outputFileName=minstrelHt --shortGuardInterval=true --channelWidth=40 --stepsSize=50 --stepsTime=0.1", "True", "False"),
+    (
+        "wifi-power-adaptation-distance --manager=ns3::ParfWifiManager --outputFileName=parf --steps=5 --stepsSize=10",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-power-adaptation-distance --manager=ns3::AparfWifiManager --outputFileName=aparf --steps=5 --stepsSize=10",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-power-adaptation-distance --manager=ns3::RrpaaWifiManager --outputFileName=rrpaa --steps=5 --stepsSize=10",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-rate-adaptation-distance --standard=802.11a --staManager=ns3::MinstrelWifiManager --apManager=ns3::MinstrelWifiManager --outputFileName=minstrel --stepsSize=50 --stepsTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-rate-adaptation-distance --standard=802.11a --staManager=ns3::MinstrelWifiManager --apManager=ns3::MinstrelWifiManager --outputFileName=minstrel --stepsSize=50 --stepsTime=0.1 --STA1_x=-200",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-rate-adaptation-distance --staManager=ns3::MinstrelHtWifiManager --apManager=ns3::MinstrelHtWifiManager --outputFileName=minstrelHt --shortGuardInterval=true --channelWidth=40 --stepsSize=50 --stepsTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-power-adaptation-interference --simuTime=5", "True", "False"),
     ("wifi-dsss-validation", "True", "True"),
     ("wifi-ofdm-validation", "True", "True"),
@@ -40,37 +64,145 @@
     ("wifi-ofdm-he-validation", "True", "True"),
     ("wifi-error-models-comparison", "True", "True"),
     ("wifi-80211n-mimo --simulationTime=0.1 --step=10", "True", "True"),
-    ("wifi-ht-network --simulationTime=0.2 --frequency=5 --useRts=0 --minExpectedThroughput=5 --maxExpectedThroughput=135", "True", "True"),
-    ("wifi-ht-network --simulationTime=0.2 --frequency=5 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=132", "True", "True"),
-    ("wifi-ht-network --simulationTime=0.2 --frequency=2.4 --useRts=0 --minExpectedThroughput=5 --maxExpectedThroughput=132", "True", "True"),
-    ("wifi-ht-network --simulationTime=0.2 --frequency=2.4 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=129", "True", "True"),
-    ("wifi-vht-network --simulationTime=0.2 --useRts=0  --minExpectedThroughput=5 --maxExpectedThroughput=583", "True", "True"),
-    ("wifi-vht-network --simulationTime=0.2 --useRts=1  --minExpectedThroughput=5 --maxExpectedThroughput=557", "True", "True"),
-    ("wifi-he-network --simulationTime=0.25 --frequency=5 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=844", "True", "True"),
-    ("wifi-he-network --simulationTime=0.3 --frequency=5 --useRts=0 --useExtendedBlockAck=1 --minExpectedThroughput=6 --maxExpectedThroughput=1033", "True", "True"),
-    ("wifi-he-network --simulationTime=0.3 --frequency=5 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=745", "True", "True"),
-    ("wifi-he-network --simulationTime=0.25 --frequency=2.4 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=238", "True", "True"),
-    ("wifi-he-network --simulationTime=0.3 --frequency=2.4 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=223", "True", "True"),
-    ("wifi-he-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=4 --minExpectedThroughput=20 --maxExpectedThroughput=212", "True", "True"),
-    ("wifi-he-network --simulationTime=0.3 --frequency=2.4 --udp=0 --downlink=1 --useRts=1 --nStations=5 --dlAckType=MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --minExpectedThroughput=27 --maxExpectedThroughput=50", "True", "True"),
-    ("wifi-he-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=0 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=6 --muSchedAccessReqInterval=50ms --minExpectedThroughput=31 --maxExpectedThroughput=290", "True", "True"),
-    ("wifi-he-network --simulationTime=0.3 --udp=1 --downlink=0 --useRts=1 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --muSchedAccessReqInterval=50ms --minExpectedThroughput=46 --maxExpectedThroughput=327", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=550", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=0 --useExtendedBlockAck=1 --frequency2=6 --minExpectedThroughput=12 --maxExpectedThroughput=550", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=547", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=0 --useExtendedBlockAck=1 --frequency2=5 --minExpectedThroughput=12 --maxExpectedThroughput=500", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=212", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.23 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=4 --frequency2=6  --minExpectedThroughput=35 --maxExpectedThroughput=280", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.25 --frequency=2.4 --udp=0 --downlink=1 --useRts=0 --nStations=5 --dlAckType=MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --frequency2=5 --useExtendedBlockAck=1 --minExpectedThroughput=40 --maxExpectedThroughput=100", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=1 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=6 --muSchedAccessReqInterval=50ms --frequency2=2.4 --minExpectedThroughput=50 --maxExpectedThroughput=140", "True", "True"),
-    ("wifi-eht-network --simulationTime=0.2 --udp=1 --downlink=0 --useRts=0 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --muSchedAccessReqInterval=50ms --frequency2=6 --minExpectedThroughput=70 --maxExpectedThroughput=715", "True", "True"),
-    ("wifi-simple-ht-hidden-stations --simulationTime=1 --enableRts=0 --nMpdus=32 --minExpectedThroughput=59 --maxExpectedThroughput=60", "True", "True"),
-    ("wifi-simple-ht-hidden-stations --simulationTime=1 --enableRts=1 --nMpdus=32 --minExpectedThroughput=57 --maxExpectedThroughput=58", "True", "True"),
+    (
+        "wifi-ht-network --simulationTime=0.2 --frequency=5 --useRts=0 --minExpectedThroughput=5 --maxExpectedThroughput=135",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-ht-network --simulationTime=0.2 --frequency=5 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=132",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-ht-network --simulationTime=0.2 --frequency=2.4 --useRts=0 --minExpectedThroughput=5 --maxExpectedThroughput=132",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-ht-network --simulationTime=0.2 --frequency=2.4 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=129",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-vht-network --simulationTime=0.2 --useRts=0  --minExpectedThroughput=5 --maxExpectedThroughput=583",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-vht-network --simulationTime=0.2 --useRts=1  --minExpectedThroughput=5 --maxExpectedThroughput=557",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.25 --frequency=5 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=844",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.3 --frequency=5 --useRts=0 --useExtendedBlockAck=1 --minExpectedThroughput=6 --maxExpectedThroughput=1033",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.3 --frequency=5 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=745",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.25 --frequency=2.4 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=238",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.3 --frequency=2.4 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=223",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=4 --minExpectedThroughput=20 --maxExpectedThroughput=212",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.3 --frequency=2.4 --udp=0 --downlink=1 --useRts=1 --nStations=5 --dlAckType=MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --minExpectedThroughput=27 --maxExpectedThroughput=50",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=0 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=6 --muSchedAccessReqInterval=50ms --minExpectedThroughput=31 --maxExpectedThroughput=290",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-he-network --simulationTime=0.3 --udp=1 --downlink=0 --useRts=1 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --muSchedAccessReqInterval=50ms --minExpectedThroughput=46 --maxExpectedThroughput=327",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=0 --minExpectedThroughput=6 --maxExpectedThroughput=550",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=0 --mpduBufferSize=1024 --frequency2=6 --minExpectedThroughput=12 --maxExpectedThroughput=550",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.1 --frequency=5 --useRts=1 --minExpectedThroughput=6 --maxExpectedThroughput=547",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=0 --mpduBufferSize=512 --frequency2=5 --minExpectedThroughput=12 --maxExpectedThroughput=500",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.1 --frequency=2.4 --useRts=1 --minExpectedThroughput=5 --maxExpectedThroughput=240",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.23 --udp=0 --downlink=1 --useRts=0 --nStations=4 --dlAckType=ACK-SU-FORMAT --enableUlOfdma=1 --enableBsrp=0 --mcs=5 --frequency2=6  --minExpectedThroughput=35 --maxExpectedThroughput=280",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.25 --frequency=2.4 --udp=0 --downlink=1 --useRts=0 --nStations=5 --dlAckType=MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mcs=5 --frequency2=5 --mpduBufferSize=1024 --minExpectedThroughput=50 --maxExpectedThroughput=120",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.3 --udp=0 --downlink=1 --useRts=1 --nStations=5 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=0 --mcs=6 --muSchedAccessReqInterval=50ms --frequency2=2.4 --minExpectedThroughput=50 --maxExpectedThroughput=140",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-eht-network --simulationTime=0.2 --udp=0 --downlink=0 --useRts=0 --nStations=4 --dlAckType=AGGR-MU-BAR --enableUlOfdma=1 --enableBsrp=1 --mpduBufferSize=1024 --mcs=4 --muSchedAccessReqInterval=45ms --frequency2=6 --minExpectedThroughput=50 --maxExpectedThroughput=415",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-simple-ht-hidden-stations --simulationTime=1 --enableRts=0 --nMpdus=32 --minExpectedThroughput=59 --maxExpectedThroughput=60",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-simple-ht-hidden-stations --simulationTime=1 --enableRts=1 --nMpdus=32 --minExpectedThroughput=57 --maxExpectedThroughput=58",
+        "True",
+        "True",
+    ),
     ("wifi-mixed-network --simulationTime=1", "True", "True"),
     ("wifi-aggregation --simulationTime=1 --verifyResults=1", "True", "True"),
     ("wifi-txop-aggregation --simulationTime=1 --verifyResults=1", "True", "True"),
     ("wifi-80211e-txop --simulationTime=1 --verifyResults=1", "True", "True"),
-    ("wifi-multi-tos --simulationTime=1 --nWifi=16 --useRts=1 --useShortGuardInterval=1", "True", "True"),
+    (
+        "wifi-multi-tos --simulationTime=1 --nWifi=16 --useRts=1 --useShortGuardInterval=1",
+        "True",
+        "True",
+    ),
     ("wifi-tcp", "True", "True"),
     ("wifi-hidden-terminal --wifiManager=Arf", "True", "True"),
     ("wifi-hidden-terminal --wifiManager=Aarf", "True", "True"),
@@ -81,14 +213,42 @@
     ("wifi-hidden-terminal --wifiManager=Cara", "True", "True"),
     ("wifi-hidden-terminal --wifiManager=Rraa", "True", "True"),
     ("wifi-hidden-terminal --wifiManager=Rrpaa", "True", "True"),
-    ("wifi-spectrum-per-example --distance=52 --index=3 --wifiType=ns3::SpectrumWifiPhy --simulationTime=1", "True", "True"),
-    ("wifi-spectrum-per-example --distance=24 --index=31 --wifiType=ns3::YansWifiPhy --simulationTime=1", "True", "False"),
-    ("wifi-spectrum-per-interference --distance=24 --index=31 --simulationTime=1 --waveformPower=0.1", "True", "True"),
+    (
+        "wifi-spectrum-per-example --distance=52 --index=3 --wifiType=ns3::SpectrumWifiPhy --simulationTime=1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-spectrum-per-example --distance=24 --index=31 --wifiType=ns3::YansWifiPhy --simulationTime=1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-spectrum-per-interference --distance=24 --index=31 --simulationTime=1 --waveformPower=0.1",
+        "True",
+        "True",
+    ),
     ("wifi-spectrum-saturation-example --simulationTime=1 --index=63", "True", "True"),
-    ("wifi-backward-compatibility --apVersion=80211a --staVersion=80211n_5GHZ --simulationTime=1", "True", "True"),
-    ("wifi-backward-compatibility --apVersion=80211a --staVersion=80211n_5GHZ --apRaa=Ideal --staRaa=Ideal --simulationTime=1", "True", "False"),
-    ("wifi-backward-compatibility --apVersion=80211a --staVersion=80211ac --simulationTime=1", "True", "False"),
-    ("wifi-backward-compatibility --apVersion=80211a --staVersion=80211ac --apRaa=Ideal --staRaa=Ideal --simulationTime=1", "True", "False"),
+    (
+        "wifi-backward-compatibility --apVersion=80211a --staVersion=80211n_5GHZ --simulationTime=1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-backward-compatibility --apVersion=80211a --staVersion=80211n_5GHZ --apRaa=Ideal --staRaa=Ideal --simulationTime=1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-backward-compatibility --apVersion=80211a --staVersion=80211ac --simulationTime=1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-backward-compatibility --apVersion=80211a --staVersion=80211ac --apRaa=Ideal --staRaa=Ideal --simulationTime=1",
+        "True",
+        "False",
+    ),
 ]
 
 # A list of Python examples to run in order to ensure that they remain
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/mixed-wired-wireless.py ns-3.41/examples/wireless/mixed-wired-wireless.py
--- ns-3.40/examples/wireless/mixed-wired-wireless.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/mixed-wired-wireless.py	2024-02-09 14:44:07.841789700 -0800
@@ -51,7 +51,14 @@
 #           +----------------+                       +----------------+
 #
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 # #
 # #  This function will be used below as a trace sink
@@ -63,12 +70,14 @@
 #   std.cout << "CourseChange " << path << " x=" << position.x << ", y=" << position.y << ", z=" << position.z << std.endl;
 # }
 
+
 def main(argv):
     #
     #  First, we initialize a few local variables that control some
     #  simulation parameters.
     #
-    from ctypes import c_int, c_double
+    from ctypes import c_double, c_int
+
     backboneNodes = c_int(10)
     infraNodes = c_int(2)
     lanNodes = c_int(2)
@@ -99,8 +108,8 @@
     #
     cmd.Parse(argv)
 
-    if (stopTime.value < 10):
-        print ("Use a simulation stop time >= 10 seconds")
+    if stopTime.value < 10:
+        print("Use a simulation stop time >= 10 seconds")
         exit(1)
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # /
     #                                                                        #
@@ -121,8 +130,9 @@
     wifi = ns.wifi.WifiHelper()
     mac = ns.wifi.WifiMacHelper()
     mac.SetType("ns3::AdhocWifiMac")
-    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                  "DataMode", ns.core.StringValue("OfdmRate54Mbps"))
+    wifi.SetRemoteStationManager(
+        "ns3::ConstantRateWifiManager", "DataMode", ns.core.StringValue("OfdmRate54Mbps")
+    )
     wifiPhy = ns.wifi.YansWifiPhyHelper()
     wifiPhy.SetPcapDataLinkType(wifiPhy.DLT_IEEE802_11_RADIO)
     wifiChannel = ns.wifi.YansWifiChannelHelper.Default()
@@ -131,11 +141,12 @@
     #
     #  Add the IPv4 protocol stack to the nodes in our container
     #
-    print ("Enabling OLSR routing on all backbone nodes")
+    print("Enabling OLSR routing on all backbone nodes")
     internet = ns.internet.InternetStackHelper()
     olsr = ns.olsr.OlsrHelper()
-    internet.SetRoutingHelper(olsr); # has effect on the next Install ()
-    internet.Install(backbone);
+    internet.SetRoutingHelper(olsr)
+    # has effect on the next Install ()
+    internet.Install(backbone)
     # re-initialize for non-olsr routing.
     # internet.Reset()
     #
@@ -151,17 +162,30 @@
     #  each of the nodes we just finished building.
     #
     mobility = ns.mobility.MobilityHelper()
-    mobility.SetPositionAllocator("ns3::GridPositionAllocator",
-                                  "MinX", ns.core.DoubleValue(20.0),
-                                  "MinY", ns.core.DoubleValue(20.0),
-                                  "DeltaX", ns.core.DoubleValue(20.0),
-                                  "DeltaY", ns.core.DoubleValue(20.0),
-                                  "GridWidth", ns.core.UintegerValue(5),
-                                  "LayoutType", ns.core.StringValue("RowFirst"))
-    mobility.SetMobilityModel("ns3::RandomDirection2dMobilityModel",
-                               "Bounds", ns.mobility.RectangleValue(ns.mobility.Rectangle(-500, 500, -500, 500)),
-                               "Speed", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=2]"),
-                               "Pause", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=0.2]"))
+    mobility.SetPositionAllocator(
+        "ns3::GridPositionAllocator",
+        "MinX",
+        ns.core.DoubleValue(20.0),
+        "MinY",
+        ns.core.DoubleValue(20.0),
+        "DeltaX",
+        ns.core.DoubleValue(20.0),
+        "DeltaY",
+        ns.core.DoubleValue(20.0),
+        "GridWidth",
+        ns.core.UintegerValue(5),
+        "LayoutType",
+        ns.core.StringValue("RowFirst"),
+    )
+    mobility.SetMobilityModel(
+        "ns3::RandomDirection2dMobilityModel",
+        "Bounds",
+        ns.mobility.RectangleValue(ns.mobility.Rectangle(-500, 500, -500, 500)),
+        "Speed",
+        ns.core.StringValue("ns3::ConstantRandomVariable[Constant=2]"),
+        "Pause",
+        ns.core.StringValue("ns3::ConstantRandomVariable[Constant=0.2]"),
+    )
     mobility.Install(backbone)
 
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # /
@@ -175,7 +199,7 @@
     ipAddrs.SetBase(ns.network.Ipv4Address("172.16.0.0"), ns.network.Ipv4Mask("255.255.255.0"))
 
     for i in range(backboneNodes.value):
-        print ("Configuring local area network for backbone node ", i)
+        print("Configuring local area network for backbone node ", i)
         #
         #  Create a container to manage the nodes of the LAN.  We need
         #  two containers here; one with all of the new nodes, and one
@@ -214,12 +238,12 @@
         mobilityLan = ns.mobility.MobilityHelper()
         positionAlloc = ns.mobility.ListPositionAllocator()
         for j in range(newLanNodes.GetN()):
-            positionAlloc.Add(ns.core.Vector(0.0, (j*10 + 10), 0.0))
+            positionAlloc.Add(ns.core.Vector(0.0, (j * 10 + 10), 0.0))
 
         mobilityLan.SetPositionAllocator(positionAlloc)
         mobilityLan.PushReferenceMobilityModel(backbone.Get(i))
         mobilityLan.SetMobilityModel("ns3::ConstantPositionMobilityModel")
-        mobilityLan.Install(newLanNodes);
+        mobilityLan.Install(newLanNodes)
 
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # /
     #                                                                        #
@@ -232,7 +256,7 @@
     ipAddrs.SetBase(ns.network.Ipv4Address("10.0.0.0"), ns.network.Ipv4Mask("255.255.255.0"))
     tempRef = []  # list of references to be held to prevent garbage collection
     for i in range(backboneNodes.value):
-        print ("Configuring wireless network for backbone node ", i)
+        print("Configuring wireless network for backbone node ", i)
         #
         #  Create a container to manage the nodes of the LAN.  We need
         #  two containers here; one with all of the new nodes, and one
@@ -245,18 +269,16 @@
         #
         #  Create another ad hoc network and devices
         #
-        ssid = ns.wifi.Ssid('wifi-infra' + str(i))
+        ssid = ns.wifi.Ssid("wifi-infra" + str(i))
         wifiInfra = ns.wifi.WifiHelper()
         wifiPhy.SetChannel(wifiChannel.Create())
-        macInfra = ns.wifi.WifiMacHelper();
-        macInfra.SetType("ns3::StaWifiMac",
-                         "Ssid", ns.wifi.SsidValue(ssid))
+        macInfra = ns.wifi.WifiMacHelper()
+        macInfra.SetType("ns3::StaWifiMac", "Ssid", ns.wifi.SsidValue(ssid))
 
         # setup stas
         staDevices = wifiInfra.Install(wifiPhy, macInfra, stas)
         # setup ap.
-        macInfra.SetType("ns3::ApWifiMac",
-                         "Ssid", ns.wifi.SsidValue(ssid))
+        macInfra.SetType("ns3::ApWifiMac", "Ssid", ns.wifi.SsidValue(ssid))
         apDevices = wifiInfra.Install(wifiPhy, macInfra, backbone.Get(i))
         # Collect all of these new devices
         infraDevices = ns.network.NetDeviceContainer(apDevices, staDevices)
@@ -291,10 +313,15 @@
 
         mobility.PushReferenceMobilityModel(backbone.Get(i))
         mobility.SetPositionAllocator(subnetAlloc)
-        mobility.SetMobilityModel("ns3::RandomDirection2dMobilityModel",
-                                  "Bounds", ns.mobility.RectangleValue(ns.mobility.Rectangle(-10, 10, -10, 10)),
-                                  "Speed", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=3]"),
-                                  "Pause", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=0.4]"))
+        mobility.SetMobilityModel(
+            "ns3::RandomDirection2dMobilityModel",
+            "Bounds",
+            ns.mobility.RectangleValue(ns.mobility.Rectangle(-10, 10, -10, 10)),
+            "Speed",
+            ns.core.StringValue("ns3::ConstantRandomVariable[Constant=3]"),
+            "Pause",
+            ns.core.StringValue("ns3::ConstantRandomVariable[Constant=0.4]"),
+        )
         mobility.Install(stas)
 
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # /
@@ -305,18 +332,25 @@
 
     #  Create the OnOff application to send UDP datagrams of size
     #  210 bytes at a rate of 448 Kb/s, between two nodes
-    print ("Create Applications.")
-    port = 9   #  Discard port(RFC 863)
+    print("Create Applications.")
+    port = 9  #  Discard port(RFC 863)
 
     appSource = ns.network.NodeList.GetNode(backboneNodes.value)
-    lastNodeIndex = backboneNodes.value + backboneNodes.value*(lanNodes.value - 1) + backboneNodes.value*(infraNodes.value - 1) - 1
+    lastNodeIndex = (
+        backboneNodes.value
+        + backboneNodes.value * (lanNodes.value - 1)
+        + backboneNodes.value * (infraNodes.value - 1)
+        - 1
+    )
     appSink = ns.network.NodeList.GetNode(lastNodeIndex)
 
-    ns.cppyy.cppdef("""
+    ns.cppyy.cppdef(
+        """
         Ipv4Address getIpv4AddressFromNode(Ptr<Node> node){
         return node->GetObject<Ipv4>()->GetAddress(1,0).GetLocal();
         }
-    """)
+    """
+    )
     # Let's fetch the IP address of the last node, which is on Ipv4Interface 1
     remoteAddr = ns.cppyy.gbl.getIpv4AddressFromNode(appSink)
     socketAddr = ns.network.InetSocketAddress(remoteAddr, port)
@@ -326,8 +360,12 @@
     apps.Stop(ns.core.Seconds(stopTime.value - 1))
 
     #  Create a packet sink to receive these packets
-    sink = ns.applications.PacketSinkHelper("ns3::UdpSocketFactory",
-                                ns.network.InetSocketAddress(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), port)).ConvertTo())
+    sink = ns.applications.PacketSinkHelper(
+        "ns3::UdpSocketFactory",
+        ns.network.InetSocketAddress(
+            ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), port)
+        ).ConvertTo(),
+    )
     sinkContainer = ns.network.NodeContainer(appSink)
     apps = sink.Install(sinkContainer)
     apps.Start(ns.core.Seconds(3))
@@ -338,16 +376,16 @@
     #                                                                        #
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # /
 
-    print ("Configure Tracing.")
+    print("Configure Tracing.")
     csma = ns.csma.CsmaHelper()
     #
     #  Let's set up some ns-2-like ascii traces, using another helper class
     #
-    ascii = ns.network.AsciiTraceHelper();
-    stream = ascii.CreateFileStream("mixed-wireless.tr");
-    wifiPhy.EnableAsciiAll(stream);
-    csma.EnableAsciiAll(stream);
-    internet.EnableAsciiIpv4All(stream);
+    ascii = ns.network.AsciiTraceHelper()
+    stream = ascii.CreateFileStream("mixed-wireless.tr")
+    wifiPhy.EnableAsciiAll(stream)
+    csma.EnableAsciiAll(stream)
+    internet.EnableAsciiIpv4All(stream)
 
     #  Csma captures in non-promiscuous mode
     csma.EnablePcapAll("mixed-wireless", False)
@@ -355,11 +393,10 @@
     wifiPhy.EnablePcap("mixed-wireless", backboneDevices)
     wifiPhy.EnablePcap("mixed-wireless", appSink.GetId(), 0)
 
-#   #ifdef ENABLE_FOR_TRACING_EXAMPLE
-#     Config.Connect("/NodeList/*/$MobilityModel/CourseChange",
-#       MakeCallback(&CourseChangeCallback))
-#   #endif
-
+    #   #ifdef ENABLE_FOR_TRACING_EXAMPLE
+    #     Config.Connect("/NodeList/*/$MobilityModel/CourseChange",
+    #       MakeCallback(&CourseChangeCallback))
+    #   #endif
 
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     #                                                                        #
@@ -367,13 +404,13 @@
     #                                                                        #
     # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
-    print ("Run Simulation.")
+    print("Run Simulation.")
     ns.core.Simulator.Stop(ns.core.Seconds(stopTime.value))
     ns.core.Simulator.Run()
     ns.core.Simulator.Destroy()
 
-if __name__ == '__main__':
-    import sys
-    main(sys.argv)
 
+if __name__ == "__main__":
+    import sys
 
+    main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-80211e-txop.cc ns-3.41/examples/wireless/wifi-80211e-txop.cc
--- ns-3.40/examples/wireless/wifi-80211e-txop.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-80211e-txop.cc	2024-02-09 14:44:07.841789700 -0800
@@ -28,6 +28,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/wifi-mac.h"
 #include "ns3/wifi-net-device.h"
 #include "ns3/yans-wifi-channel.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-80211n-mimo.cc ns-3.41/examples/wireless/wifi-80211n-mimo.cc
--- ns-3.40/examples/wireless/wifi-80211n-mimo.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-80211n-mimo.cc	2024-02-09 14:44:07.841789700 -0800
@@ -51,6 +51,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-aggregation.cc ns-3.41/examples/wireless/wifi-aggregation.cc
--- ns-3.40/examples/wireless/wifi-aggregation.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-aggregation.cc	2024-02-09 14:44:07.841789700 -0800
@@ -28,6 +28,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/wifi-mac.h"
 #include "ns3/wifi-net-device.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-ap.py ns-3.41/examples/wireless/wifi-ap.py
--- ns-3.40/examples/wireless/wifi-ap.py	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-ap.py	2024-02-09 14:44:07.841789700 -0800
@@ -22,7 +22,14 @@
 
 import sys
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 # void
 # DevTxTrace (std::string context, Ptr<const Packet> p, Mac48Address address)
@@ -70,7 +77,8 @@
 #   std::cout << " start="<<start<<" duration="<<duration<<std::endl;
 # }
 
-ns.cppyy.cppdef("""
+ns.cppyy.cppdef(
+    """
     using namespace ns3;
     void AdvancePosition(Ptr<Node> node){
         Ptr<MobilityModel> mob = node->GetObject<MobilityModel>();
@@ -80,7 +88,9 @@
             return;
         mob->SetPosition(pos);
         Simulator::Schedule(Seconds(1.0), AdvancePosition, node);
-    }""")
+    }"""
+)
+
 
 def main(argv):
     ns.core.CommandLine().Parse(argv)
@@ -91,7 +101,7 @@
     mobility = ns.mobility.MobilityHelper()
     stas = ns.network.NodeContainer()
     ap = ns.network.NodeContainer()
-    #NetDeviceContainer staDevs;
+    # NetDeviceContainer staDevs;
     packetSocket = ns.network.PacketSocketHelper()
 
     stas.Create(2)
@@ -109,15 +119,16 @@
     wifiMac = ns.wifi.WifiMacHelper()
 
     # setup stas.
-    wifiMac.SetType("ns3::StaWifiMac",
-                    "ActiveProbing",
-                    ns.core.BooleanValue(True),
-                    "Ssid",
-                    ns.wifi.SsidValue(ssid))
+    wifiMac.SetType(
+        "ns3::StaWifiMac",
+        "ActiveProbing",
+        ns.core.BooleanValue(True),
+        "Ssid",
+        ns.wifi.SsidValue(ssid),
+    )
     staDevs = wifi.Install(wifiPhy, wifiMac, stas)
     # setup ap.
-    wifiMac.SetType("ns3::ApWifiMac",
-                    "Ssid", ns.wifi.SsidValue(ssid))
+    wifiMac.SetType("ns3::ApWifiMac", "Ssid", ns.wifi.SsidValue(ssid))
     wifi.Install(wifiPhy, wifiMac, ap)
 
     # mobility.
@@ -132,7 +143,7 @@
     socket.SetProtocol(1)
 
     onoff = ns.applications.OnOffHelper("ns3::PacketSocketFactory", socket.ConvertTo())
-    onoff.SetConstantRate (ns.network.DataRate ("500kb/s"))
+    onoff.SetConstantRate(ns.network.DataRate("500kb/s"))
 
     apps = onoff.Install(ns.network.NodeContainer(stas.Get(0)))
     apps.Start(ns.core.Seconds(0.5))
@@ -140,13 +151,12 @@
 
     ns.core.Simulator.Stop(ns.core.Seconds(44.0))
 
-  #   Config::Connect("/NodeList/*/DeviceList/*/Tx", MakeCallback(&DevTxTrace));
-  #   Config::Connect("/NodeList/*/DeviceList/*/Rx", MakeCallback(&DevRxTrace));
-  #   Config::Connect("/NodeList/*/DeviceList/*/Phy/RxOk", MakeCallback(&PhyRxOkTrace));
-  #   Config::Connect("/NodeList/*/DeviceList/*/Phy/RxError", MakeCallback(&PhyRxErrorTrace));
-  #   Config::Connect("/NodeList/*/DeviceList/*/Phy/Tx", MakeCallback(&PhyTxTrace));
-  #   Config::Connect("/NodeList/*/DeviceList/*/Phy/State", MakeCallback(&PhyStateTrace));
-
+    #   Config::Connect("/NodeList/*/DeviceList/*/Tx", MakeCallback(&DevTxTrace));
+    #   Config::Connect("/NodeList/*/DeviceList/*/Rx", MakeCallback(&DevRxTrace));
+    #   Config::Connect("/NodeList/*/DeviceList/*/Phy/RxOk", MakeCallback(&PhyRxOkTrace));
+    #   Config::Connect("/NodeList/*/DeviceList/*/Phy/RxError", MakeCallback(&PhyRxErrorTrace));
+    #   Config::Connect("/NodeList/*/DeviceList/*/Phy/Tx", MakeCallback(&PhyTxTrace));
+    #   Config::Connect("/NodeList/*/DeviceList/*/Phy/State", MakeCallback(&PhyStateTrace));
 
     ns.core.Simulator.Run()
     ns.core.Simulator.Destroy()
@@ -154,6 +164,5 @@
     return 0
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv))
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-backward-compatibility.cc ns-3.41/examples/wireless/wifi-backward-compatibility.cc
--- ns-3.40/examples/wireless/wifi-backward-compatibility.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-backward-compatibility.cc	2024-02-09 14:44:07.841789700 -0800
@@ -30,6 +30,7 @@
 #include "ns3/ssid.h"
 #include "ns3/tuple.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
@@ -149,7 +150,7 @@
     WifiMacHelper mac;
     WifiHelper wifi;
     Ssid ssid = Ssid("ns3");
-    TupleValue<UintegerValue, UintegerValue, EnumValue, UintegerValue> channelValue;
+    TupleValue<UintegerValue, UintegerValue, EnumValue<WifiPhyBand>, UintegerValue> channelValue;
 
     const auto& [staStandard, staBand] = ConvertStringToStandardAndBand(staVersion);
     wifi.SetStandard(staStandard);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-eht-network.cc ns-3.41/examples/wireless/wifi-eht-network.cc
--- ns-3.40/examples/wireless/wifi-eht-network.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-eht-network.cc	2024-02-09 14:44:07.841789700 -0800
@@ -25,7 +25,6 @@
 #include "ns3/enum.h"
 #include "ns3/internet-stack-helper.h"
 #include "ns3/ipv4-address-helper.h"
-#include "ns3/ipv4-global-routing-helper.h"
 #include "ns3/log.h"
 #include "ns3/mobility-helper.h"
 #include "ns3/multi-model-spectrum-channel.h"
@@ -37,6 +36,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/wifi-acknowledgment.h"
 #include "ns3/yans-wifi-channel.h"
@@ -93,7 +93,7 @@
         }
     }
     return rxBytes;
-};
+}
 
 /**
  * Print average throughput over an intermediate time interval.
@@ -146,7 +146,12 @@
     bool udp{true};
     bool downlink{true};
     bool useRts{false};
-    bool useExtendedBlockAck{false};
+    uint16_t mpduBufferSize{512};
+    std::string emlsrLinks;
+    uint16_t paddingDelayUsec{32};
+    uint16_t transitionDelayUsec{128};
+    uint16_t channelSwitchDelayUsec{100};
+    bool switchAuxPhy{true};
     double simulationTime{10}; // seconds
     double distance{1.0};      // meters
     double frequency{5};       // whether the first link operates in the 2.4, 5 or 6 GHz
@@ -181,6 +186,22 @@
         "Whether the third link operates in the 2.4, 5 or 6 GHz band (0 means the device has up to "
         "two links, otherwise the band must be different than first link and second link)",
         frequency3);
+    cmd.AddValue("emlsrLinks",
+                 "The comma separated list of IDs of EMLSR links (for MLDs only)",
+                 emlsrLinks);
+    cmd.AddValue("emlsrPaddingDelay",
+                 "The EMLSR padding delay in microseconds (0, 32, 64, 128 or 256)",
+                 paddingDelayUsec);
+    cmd.AddValue("emlsrTransitionDelay",
+                 "The EMLSR transition delay in microseconds (0, 16, 32, 64, 128 or 256)",
+                 transitionDelayUsec);
+    cmd.AddValue("emlsrAuxSwitch",
+                 "Whether Aux PHY should switch channel to operate on the link on which "
+                 "the Main PHY was operating before moving to the link of the Aux PHY. ",
+                 switchAuxPhy);
+    cmd.AddValue("channelSwitchDelay",
+                 "The PHY channel switch delay in microseconds",
+                 channelSwitchDelayUsec);
     cmd.AddValue("distance",
                  "Distance in meters between the station and the access point",
                  distance);
@@ -190,7 +211,9 @@
                  "Generate downlink flows if set to 1, uplink flows otherwise",
                  downlink);
     cmd.AddValue("useRts", "Enable/disable RTS/CTS", useRts);
-    cmd.AddValue("useExtendedBlockAck", "Enable/disable use of extended BACK", useExtendedBlockAck);
+    cmd.AddValue("mpduBufferSize",
+                 "Size (in number of MPDUs) of the BlockAck buffer",
+                 mpduBufferSize);
     cmd.AddValue("nStations", "Number of non-AP HE stations", nStations);
     cmd.AddValue("dlAckType",
                  "Ack sequence type for DL OFDMA (NO-OFDMA, ACK-SU-FORMAT, MU-BAR, AGGR-MU-BAR)",
@@ -286,6 +309,7 @@
 
                 wifi.SetStandard(WIFI_STANDARD_80211be);
                 std::array<std::string, 3> channelStr;
+                std::array<FrequencyRange, 3> freqRanges;
                 uint8_t nLinks = 0;
                 std::string dataModeStr = "EhtMcs" + std::to_string(mcs);
                 std::string ctrlRateStr;
@@ -308,6 +332,7 @@
                     if (freq == 6)
                     {
                         channelStr[nLinks] += "BAND_6GHZ, 0}";
+                        freqRanges[nLinks] = WIFI_SPECTRUM_6_GHZ;
                         Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
                                            DoubleValue(48));
                         wifi.SetRemoteStationManager(nLinks,
@@ -320,6 +345,7 @@
                     else if (freq == 5)
                     {
                         channelStr[nLinks] += "BAND_5GHZ, 0}";
+                        freqRanges[nLinks] = WIFI_SPECTRUM_5_GHZ;
                         ctrlRateStr = "OfdmRate" + std::to_string(nonHtRefRateMbps) + "Mbps";
                         wifi.SetRemoteStationManager(nLinks,
                                                      "ns3::ConstantRateWifiManager",
@@ -331,6 +357,7 @@
                     else if (freq == 2.4)
                     {
                         channelStr[nLinks] += "BAND_2_4GHZ, 0}";
+                        freqRanges[nLinks] = WIFI_SPECTRUM_2_4_GHZ;
                         Config::SetDefault("ns3::LogDistancePropagationLossModel::ReferenceLoss",
                                            DoubleValue(40));
                         ctrlRateStr = "ErpOfdmRate" + std::to_string(nonHtRefRateMbps) + "Mbps";
@@ -349,6 +376,11 @@
                     nLinks++;
                 }
 
+                if (nLinks > 1 && !emlsrLinks.empty())
+                {
+                    wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(true));
+                }
+
                 Ssid ssid = Ssid("ns3-80211be");
 
                 /*
@@ -357,21 +389,28 @@
                  * and one with 312.5 kHz bands for, e.g., non-HT PPDUs (for more details,
                  * see issue #408 (CLOSED))
                  */
-                Ptr<MultiModelSpectrumChannel> spectrumChannel =
-                    CreateObject<MultiModelSpectrumChannel>();
-
-                Ptr<LogDistancePropagationLossModel> lossModel =
-                    CreateObject<LogDistancePropagationLossModel>();
-                spectrumChannel->AddPropagationLossModel(lossModel);
-
                 SpectrumWifiPhyHelper phy(nLinks);
                 phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
-                phy.SetChannel(spectrumChannel);
+                phy.Set("ChannelSwitchDelay", TimeValue(MicroSeconds(channelSwitchDelayUsec)));
 
                 mac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid));
+                mac.SetEmlsrManager("ns3::DefaultEmlsrManager",
+                                    "EmlsrLinkSet",
+                                    StringValue(emlsrLinks),
+                                    "EmlsrPaddingDelay",
+                                    TimeValue(MicroSeconds(paddingDelayUsec)),
+                                    "EmlsrTransitionDelay",
+                                    TimeValue(MicroSeconds(transitionDelayUsec)),
+                                    "SwitchAuxPhy",
+                                    BooleanValue(switchAuxPhy));
                 for (uint8_t linkId = 0; linkId < nLinks; linkId++)
                 {
                     phy.Set(linkId, "ChannelSettings", StringValue(channelStr[linkId]));
+
+                    auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+                    auto lossModel = CreateObject<LogDistancePropagationLossModel>();
+                    spectrumChannel->AddPropagationLossModel(lossModel);
+                    phy.AddChannel(spectrumChannel, freqRanges[linkId]);
                 }
                 staDevices = wifi.Install(phy, mac, wifiStaNodes);
 
@@ -402,9 +441,8 @@
                 Config::Set(
                     "/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HeConfiguration/GuardInterval",
                     TimeValue(NanoSeconds(gi)));
-                Config::Set(
-                    "/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HeConfiguration/MpduBufferSize",
-                    UintegerValue(useExtendedBlockAck ? 256 : 64));
+                Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Mac/MpduBufferSize",
+                            UintegerValue(mpduBufferSize));
 
                 // mobility.
                 MobilityHelper mobility;
@@ -507,8 +545,6 @@
                                         simulationTime + 1);
                 }
 
-                Simulator::Schedule(Seconds(0), &Ipv4GlobalRoutingHelper::PopulateRoutingTables);
-
                 Simulator::Stop(Seconds(simulationTime + 1));
                 Simulator::Run();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-he-network.cc ns-3.41/examples/wireless/wifi-he-network.cc
--- ns-3.40/examples/wireless/wifi-he-network.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-he-network.cc	2024-02-09 14:44:07.841789700 -0800
@@ -37,6 +37,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/wifi-acknowledgment.h"
 #include "ns3/yans-wifi-channel.h"
@@ -250,11 +251,8 @@
                                              StringValue(ossDataMode.str()),
                                              "ControlMode",
                                              ctrlRate);
-                // Set guard interval and MPDU buffer size
-                wifi.ConfigHeOptions("GuardInterval",
-                                     TimeValue(NanoSeconds(gi)),
-                                     "MpduBufferSize",
-                                     UintegerValue(useExtendedBlockAck ? 256 : 64));
+                // Set guard interval
+                wifi.ConfigHeOptions("GuardInterval", TimeValue(NanoSeconds(gi)));
 
                 Ssid ssid = Ssid("ns3-80211ax");
 
@@ -277,7 +275,11 @@
                     phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
                     phy.SetChannel(spectrumChannel);
 
-                    mac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid));
+                    mac.SetType("ns3::StaWifiMac",
+                                "Ssid",
+                                SsidValue(ssid),
+                                "MpduBufferSize",
+                                UintegerValue(useExtendedBlockAck ? 256 : 64));
                     phy.Set("ChannelSettings", StringValue(channelStr));
                     staDevices = wifi.Install(phy, mac, wifiStaNodes);
 
@@ -305,7 +307,11 @@
                     phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
                     phy.SetChannel(channel.Create());
 
-                    mac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid));
+                    mac.SetType("ns3::StaWifiMac",
+                                "Ssid",
+                                SsidValue(ssid),
+                                "MpduBufferSize",
+                                UintegerValue(useExtendedBlockAck ? 256 : 64));
                     phy.Set("ChannelSettings", StringValue(channelStr));
                     staDevices = wifi.Install(phy, mac, wifiStaNodes);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-ht-network.cc ns-3.41/examples/wireless/wifi-ht-network.cc
--- ns-3.40/examples/wireless/wifi-ht-network.cc	2023-09-27 13:30:37.606839000 -0700
+++ ns-3.41/examples/wireless/wifi-ht-network.cc	2024-02-09 14:44:07.841789700 -0800
@@ -36,6 +36,7 @@
 #include "ns3/string.h"
 #include "ns3/tuple.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
@@ -176,7 +177,8 @@
                 wifi.ConfigHtOptions("ShortGuardIntervalSupported", BooleanValue(sgi));
 
                 Ssid ssid = Ssid("ns3-80211n");
-                TupleValue<UintegerValue, UintegerValue, EnumValue, UintegerValue> channelValue;
+                TupleValue<UintegerValue, UintegerValue, EnumValue<WifiPhyBand>, UintegerValue>
+                    channelValue;
                 WifiPhyBand band = (frequency == 5.0 ? WIFI_PHY_BAND_5GHZ : WIFI_PHY_BAND_2_4GHZ);
                 channelValue.Set(WifiPhy::ChannelTuple{0, channelWidth, band, 0});
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-mixed-network.cc ns-3.41/examples/wireless/wifi-mixed-network.cc
--- ns-3.40/examples/wireless/wifi-mixed-network.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-mixed-network.cc	2024-02-09 14:44:07.845789700 -0800
@@ -32,6 +32,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/wifi-mac.h"
 #include "ns3/wifi-net-device.h"
 #include "ns3/yans-wifi-channel.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-multi-tos.cc ns-3.41/examples/wireless/wifi-multi-tos.cc
--- ns-3.40/examples/wireless/wifi-multi-tos.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-multi-tos.cc	2024-02-09 14:44:07.845789700 -0800
@@ -191,7 +191,7 @@
     }
     else
     {
-        NS_LOG_ERROR("Obtained throughput is 0!");
+        std::cout << "Obtained throughput is 0!" << std::endl;
         exit(1);
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-power-adaptation-interference.cc ns-3.41/examples/wireless/wifi-power-adaptation-interference.cc
--- ns-3.40/examples/wireless/wifi-power-adaptation-interference.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-power-adaptation-interference.cc	2024-02-09 14:44:07.845789700 -0800
@@ -670,7 +670,7 @@
     for (auto i = stats.begin(); i != stats.end(); ++i)
     {
         Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(i->first);
-        if ((t.sourceAddress == "10.1.1.3" && t.destinationAddress == "10.1.1.1"))
+        if (t.sourceAddress == "10.1.1.3" && t.destinationAddress == "10.1.1.1")
         {
             NS_LOG_INFO("Flow " << i->first << " (" << t.sourceAddress << " -> "
                                 << t.destinationAddress << ")\n");
@@ -688,7 +688,7 @@
                         << i->second.jitterSum.GetSeconds() / (i->second.rxPackets - 1) << "\n");
             NS_LOG_INFO("  Tx Opp: " << 1 - (statisticsAp0.GetBusyTime() / simuTime));
         }
-        if ((t.sourceAddress == "10.1.1.4" && t.destinationAddress == "10.1.1.2"))
+        if (t.sourceAddress == "10.1.1.4" && t.destinationAddress == "10.1.1.2")
         {
             NS_LOG_INFO("Flow " << i->first << " (" << t.sourceAddress << " -> "
                                 << t.destinationAddress << ")\n");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-simple-ht-hidden-stations.cc ns-3.41/examples/wireless/wifi-simple-ht-hidden-stations.cc
--- ns-3.40/examples/wireless/wifi-simple-ht-hidden-stations.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-simple-ht-hidden-stations.cc	2024-02-09 14:44:07.845789700 -0800
@@ -25,9 +25,11 @@
 #include "ns3/ipv4-address-helper.h"
 #include "ns3/log.h"
 #include "ns3/mobility-helper.h"
+#include "ns3/rng-seed-manager.h"
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
@@ -63,6 +65,9 @@
     double minExpectedThroughput = 0;
     double maxExpectedThroughput = 0;
 
+    RngSeedManager::SetSeed(1);
+    RngSeedManager::SetRun(5);
+
     CommandLine cmd(__FILE__);
     cmd.AddValue("nMpdus", "Number of aggregated MPDUs", nMpdus);
     cmd.AddValue("payloadSize", "Payload size in bytes", payloadSize);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-spatial-reuse.cc ns-3.41/examples/wireless/wifi-spatial-reuse.cc
--- ns-3.40/examples/wireless/wifi-spatial-reuse.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-spatial-reuse.cc	2024-02-09 14:44:07.845789700 -0800
@@ -23,7 +23,7 @@
 //
 //  The geometry is as follows:
 //
-//                STA1          STA1
+//                STA1          STA2
 //                 |              |
 //              d1 |              |d2
 //                 |       d3     |
@@ -34,7 +34,7 @@
 //
 //  STA1 is continuously transmitting data to AP1, while STA2 is continuously sending data to AP2.
 //  Each STA has configurable traffic loads (inter packet interval and packet size).
-//  It is also possible to configure TX power per node as well as their CCA-ED tresholds.
+//  It is also possible to configure TX power per node as well as their CCA-ED thresholds.
 //  OBSS_PD spatial reuse feature can be enabled (default) or disabled, and the OBSS_PD
 //  threshold can be set as well (default: -72 dBm).
 //  A simple Friis path loss model is used and a constant PHY rate is considered.
@@ -81,6 +81,9 @@
 //  benefits of spatial reuse in this scenario. This can, for
 //  instance, be accomplished by setting --interval=0.0001.
 //
+//  Spatial reuse reset events are traced in two text files:
+//  - wifi-spatial-reuse-resets-bss-1.txt (for STA 1)
+//  - wifi-spatial-reuse-resets-bss-2.txt (for STA 2)
 
 #include "ns3/abort.h"
 #include "ns3/ap-wifi-mac.h"
@@ -89,8 +92,10 @@
 #include "ns3/config.h"
 #include "ns3/double.h"
 #include "ns3/he-configuration.h"
+#include "ns3/he-phy.h"
 #include "ns3/mobility-helper.h"
 #include "ns3/multi-model-spectrum-channel.h"
+#include "ns3/obss-pd-algorithm.h"
 #include "ns3/packet-socket-client.h"
 #include "ns3/packet-socket-helper.h"
 #include "ns3/packet-socket-server.h"
@@ -102,6 +107,8 @@
 using namespace ns3;
 
 std::vector<uint32_t> bytesReceived(4);
+std::ofstream g_resetFile1;
+std::ofstream g_resetFile2;
 
 uint32_t
 ContextToNodeId(std::string context)
@@ -118,6 +125,34 @@
     bytesReceived[nodeId] += p->GetSize();
 }
 
+void
+ResetTrace(std::string context,
+           uint8_t bssColor,
+           double rssiDbm,
+           bool powerRestricted,
+           double txPowerMaxDbmSiso,
+           double txPowerMaxDbmMimo)
+{
+    if (context == "1")
+    {
+        g_resetFile1 << Simulator::Now().GetSeconds() << " bssColor: " << +bssColor
+                     << " rssiDbm: " << rssiDbm << " powerRestricted: " << powerRestricted
+                     << " txPowerMaxDbmSiso: " << txPowerMaxDbmSiso
+                     << " txPowerMaxDbmMimo: " << txPowerMaxDbmMimo << std::endl;
+    }
+    else if (context == "2")
+    {
+        g_resetFile2 << Simulator::Now().GetSeconds() << " bssColor: " << +bssColor
+                     << " rssiDbm: " << rssiDbm << " powerRestricted: " << powerRestricted
+                     << " txPowerMaxDbmSiso: " << txPowerMaxDbmSiso
+                     << " txPowerMaxDbmMimo: " << txPowerMaxDbmMimo << std::endl;
+    }
+    else
+    {
+        NS_FATAL_ERROR("Unknown context " << context);
+    }
+}
+
 int
 main(int argc, char* argv[])
 {
@@ -164,6 +199,9 @@
     cmd.AddValue("mcs", "The constant MCS value to transmit HE PPDUs", mcs);
     cmd.Parse(argc, argv);
 
+    g_resetFile1.open("wifi-spatial-reuse-resets-bss-1.txt", std::ofstream::out);
+    g_resetFile2.open("wifi-spatial-reuse-resets-bss-2.txt", std::ofstream::out);
+
     NodeContainer wifiStaNodes;
     wifiStaNodes.Create(2);
 
@@ -318,6 +356,18 @@
     Config::Connect("/NodeList/*/ApplicationList/*/$ns3::PacketSocketServer/Rx",
                     MakeCallback(&SocketRx));
 
+    // Obtain pointers to the ObssPdAlgorithm objects and hook trace sinks
+    // to the Reset trace source on each STA.  Note that this trace connection
+    // cannot be done through the Config path system, so pointers are used.
+    auto deviceA = staDeviceA.Get(0)->GetObject<WifiNetDevice>();
+    auto hePhyA = DynamicCast<HePhy>(deviceA->GetPhy()->GetPhyEntity(WIFI_MOD_CLASS_HE));
+    // Pass in the context string "1" to allow the trace to distinguish objects
+    hePhyA->GetObssPdAlgorithm()->TraceConnect("Reset", "1", MakeCallback(&ResetTrace));
+    auto deviceB = staDeviceB.Get(0)->GetObject<WifiNetDevice>();
+    auto hePhyB = DynamicCast<HePhy>(deviceB->GetPhy()->GetPhyEntity(WIFI_MOD_CLASS_HE));
+    // Pass in the context string "2" to allow the trace to distinguish objects
+    hePhyB->GetObssPdAlgorithm()->TraceConnect("Reset", "2", MakeCallback(&ResetTrace));
+
     Simulator::Stop(Seconds(duration));
     Simulator::Run();
 
@@ -329,5 +379,8 @@
         std::cout << "Throughput for BSS " << i + 1 << ": " << throughput << " Mbit/s" << std::endl;
     }
 
+    g_resetFile1.close();
+    g_resetFile2.close();
+
     return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-spectrum-per-example.cc ns-3.41/examples/wireless/wifi-spectrum-per-example.cc
--- ns-3.40/examples/wireless/wifi-spectrum-per-example.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-spectrum-per-example.cc	2024-02-09 14:44:07.845789700 -0800
@@ -40,6 +40,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-spectrum-per-interference.cc ns-3.41/examples/wireless/wifi-spectrum-per-interference.cc
--- ns-3.40/examples/wireless/wifi-spectrum-per-interference.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-spectrum-per-interference.cc	2024-02-09 14:44:07.845789700 -0800
@@ -37,6 +37,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/waveform-generator-helper.h"
 #include "ns3/waveform-generator.h"
 #include "ns3/wifi-net-device.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-spectrum-saturation-example.cc ns-3.41/examples/wireless/wifi-spectrum-saturation-example.cc
--- ns-3.40/examples/wireless/wifi-spectrum-saturation-example.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-spectrum-saturation-example.cc	2024-02-09 14:44:07.845789700 -0800
@@ -36,6 +36,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-timing-attributes.cc ns-3.41/examples/wireless/wifi-timing-attributes.cc
--- ns-3.40/examples/wireless/wifi-timing-attributes.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-timing-attributes.cc	2024-02-09 14:44:07.845789700 -0800
@@ -29,6 +29,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-txop-aggregation.cc ns-3.41/examples/wireless/wifi-txop-aggregation.cc
--- ns-3.40/examples/wireless/wifi-txop-aggregation.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-txop-aggregation.cc	2024-02-09 14:44:07.845789700 -0800
@@ -30,6 +30,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/wifi-mac.h"
 #include "ns3/wifi-net-device.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/examples/wireless/wifi-vht-network.cc ns-3.41/examples/wireless/wifi-vht-network.cc
--- ns-3.40/examples/wireless/wifi-vht-network.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/examples/wireless/wifi-vht-network.cc	2024-02-09 14:44:07.845789700 -0800
@@ -32,6 +32,7 @@
 #include "ns3/ssid.h"
 #include "ns3/string.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 #include "ns3/vht-phy.h"
 #include "ns3/yans-wifi-channel.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/ns3 ns-3.41/ns3
--- ns-3.40/ns3	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/ns3	2024-02-09 14:44:07.845789700 -0800
@@ -2,6 +2,7 @@
 
 import argparse
 import atexit
+import functools
 import glob
 import os
 import re
@@ -10,6 +11,7 @@
 import sys
 
 ns3_path = os.path.dirname(os.path.abspath(__file__))
+append_to_ns3_path = functools.partial(os.path.join, ns3_path)
 out_dir = os.sep.join([ns3_path, "build"])
 lock_file = os.sep.join([ns3_path, ".lock-ns3_%s_build" % sys.platform])
 
@@ -30,7 +32,7 @@
         return
     if print_buffer == "":
         return
-    print_buffer = print_buffer.replace('\\', '/').replace('//', '/').replace('/', os.sep)
+    print_buffer = print_buffer.replace("\\", "/").replace("//", "/").replace("/", os.sep)
     if dry_run:
         print("The following commands would be executed:")
     elif run_verbose:
@@ -39,12 +41,18 @@
 
 
 def on_off_argument(parser, option_name, help_on, help_off=None):
-    parser.add_argument('--enable-%s' % option_name,
-                        help=('Enable %s' % help_on) if help_off is None else help_on,
-                        action="store_true", default=None)
-    parser.add_argument('--disable-%s' % option_name,
-                        help=('Disable %s' % help_on) if help_off is None else help_off,
-                        action="store_true", default=None)
+    parser.add_argument(
+        "--enable-%s" % option_name,
+        help=("Enable %s" % help_on) if help_off is None else help_on,
+        action="store_true",
+        default=None,
+    )
+    parser.add_argument(
+        "--disable-%s" % option_name,
+        help=("Disable %s" % help_on) if help_off is None else help_off,
+        action="store_true",
+        default=None,
+    )
     return parser
 
 
@@ -61,36 +69,42 @@
     return cmake_arg
 
 
-def add_argument_to_subparsers(parsers: list,
-                               arguments: list,
-                               help_msg: str,
-                               dest: str,
-                               action="store_true",
-                               default_value=None):
+def add_argument_to_subparsers(
+    parsers: list,
+    arguments: list,
+    help_msg: str,
+    dest: str,
+    action="store_true",
+    default_value=None,
+):
     # Instead of copying and pasting repeated arguments for each parser, we add them here
     for subparser in parsers:
         subparser_name = subparser.prog.replace("ns3", "").strip()
         destination = ("%s_%s" % (subparser_name, dest)) if subparser_name else dest
-        subparser.add_argument(*arguments,
-                               help=help_msg,
-                               action=action,
-                               default=default_value,
-                               dest=destination)
+        subparser.add_argument(
+            *arguments, help=help_msg, action=action, default=default_value, dest=destination
+        )
 
 
 def parse_args(argv):
-    parser = argparse.ArgumentParser(description="ns-3 wrapper for the CMake build system", add_help=False)
+    parser = argparse.ArgumentParser(
+        description="ns-3 wrapper for the CMake build system", add_help=False
+    )
 
     sub_parser = parser.add_subparsers()
 
-    parser.add_argument('-h', '--help',
-                        help="Print a summary of available commands",
-                        action="store_true", default=None, dest="main_help")
-    parser_help = sub_parser.add_parser('help',
-                                        help='Print a summary of available commands')
-    parser_help.add_argument('help',
-                             help='Print a summary of available commands',
-                             action="store_true", default=False)
+    parser.add_argument(
+        "-h",
+        "--help",
+        help="Print a summary of available commands",
+        action="store_true",
+        default=None,
+        dest="main_help",
+    )
+    parser_help = sub_parser.add_parser("help", help="Print a summary of available commands")
+    parser_help.add_argument(
+        "help", help="Print a summary of available commands", action="store_true", default=False
+    )
     # parser.add_argument('--docset',
     #               help=(
     #                   'Create Docset, without building. This requires the docsetutil tool from Xcode 9.2 or earlier.'
@@ -98,34 +112,56 @@
     #               action="store_true", default=None,
     #               dest="docset_build")
 
-    parser_build = sub_parser.add_parser('build',
-                                         help=('Accepts a list of targets to build,'
-                                               ' or builds the entire project if no target is given'),
-                                         formatter_class=argparse.RawTextHelpFormatter)
-    parser_build.add_argument('build',
-                              help=('Build the entire project or the specified target and its dependencies.\n'
-                                    'To get the list of targets, use:\n'
-                                    './ns3 show targets\n'),
-                              action="store", nargs='*', default=None, metavar='target')
-
-    parser_configure = sub_parser.add_parser('configure',
-                                             help='Try "./ns3 configure --help" for more configuration options')
-    parser_configure.add_argument('configure',
-                                  action='store_true', default=False)
-    parser_configure.add_argument('-d', '--build-profile',
-                                  help='Build profile',
-                                  dest='build_profile',
-                                  choices=["debug", "default", "release", "optimized", "minsizerel"],
-                                  action="store", type=str, default=None)
-
-    parser_configure.add_argument('-G',
-                                  help=('CMake generator '
-                                        '(e.g. https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)'),
-                                  action="store", type=str, default=None)
-
-    parser_configure.add_argument('--cxx-standard',
-                                  help='Compile NS-3 with the given C++ standard',
-                                  type=str, default=None)
+    parser_build = sub_parser.add_parser(
+        "build",
+        help=(
+            "Accepts a list of targets to build,"
+            " or builds the entire project if no target is given"
+        ),
+        formatter_class=argparse.RawTextHelpFormatter,
+    )
+    parser_build.add_argument(
+        "build",
+        help=(
+            "Build the entire project or the specified target and its dependencies.\n"
+            "To get the list of targets, use:\n"
+            "./ns3 show targets\n"
+        ),
+        action="store",
+        nargs="*",
+        default=None,
+        metavar="target",
+    )
+
+    parser_configure = sub_parser.add_parser(
+        "configure", help='Try "./ns3 configure --help" for more configuration options'
+    )
+    parser_configure.add_argument("configure", action="store_true", default=False)
+    parser_configure.add_argument(
+        "-d",
+        "--build-profile",
+        help="Build profile",
+        dest="build_profile",
+        choices=["debug", "default", "release", "optimized", "minsizerel"],
+        action="store",
+        type=str,
+        default=None,
+    )
+
+    parser_configure.add_argument(
+        "-G",
+        help=(
+            "CMake generator "
+            "(e.g. https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)"
+        ),
+        action="store",
+        type=str,
+        default=None,
+    )
+
+    parser_configure.add_argument(
+        "--cxx-standard", help="Compile NS-3 with the given C++ standard", type=str, default=None
+    )
 
     # On-Off options
     # First positional is transformed into --enable-option --disable-option
@@ -134,9 +170,11 @@
     # and the third is used as is as the 'disable' description
     on_off_options = [
         ("asserts", "the asserts regardless of the compile mode"),
-        ("des-metrics", "Logging all events in a json file with the name of the executable "
-                        "(which must call CommandLine::Parse(argc, argv))"
-         ),
+        (
+            "des-metrics",
+            "Logging all events in a json file with the name of the executable "
+            "(which must call CommandLine::Parse(argc, argv))",
+        ),
         ("build-version", "embedding git changes as a build version during build"),
         ("clang-tidy", "clang-tidy static analysis"),
         ("dpdk", "the fd-net-device DPDK features"),
@@ -148,181 +186,310 @@
         ("logs", "the logs regardless of the compile mode"),
         ("monolib", "a single shared library with all ns-3 modules"),
         ("mpi", "the MPI support for distributed simulation"),
-        ("ninja-tracing", "the conversion of the Ninja generator log file into about://tracing format"),
+        (
+            "ninja-tracing",
+            "the conversion of the Ninja generator log file into about://tracing format",
+        ),
         ("precompiled-headers", "precompiled headers"),
         ("python-bindings", "python bindings"),
         ("tests", "the ns-3 tests"),
         ("sanitizers", "address, memory leaks and undefined behavior sanitizers"),
-        ("static", "Build a single static library with all ns-3",
-         "Restore the shared libraries"
-         ),
+        ("static", "Build a single static library with all ns-3", "Restore the shared libraries"),
         ("sudo", "use of sudo to setup suid bits on ns3 executables."),
         ("verbose", "printing of additional build system messages"),
         ("warnings", "compiler warnings"),
-        ("werror", "Treat compiler warnings as errors",
-         "Treat compiler warnings as warnings"
-         ),
+        ("werror", "Treat compiler warnings as errors", "Treat compiler warnings as warnings"),
     ]
     for on_off_option in on_off_options:
         parser_configure = on_off_argument(parser_configure, *on_off_option)
 
-    parser_configure.add_argument('--enable-modules',
-                                  help='List of modules to build (e.g. \"core;network;internet\")',
-                                  action="store", type=str, default=None)
-    parser_configure.add_argument('--disable-modules',
-                                  help='List of modules not to build (e.g. \"lte;wimax\")',
-                                  action="store", type=str, default=None)
-    parser_configure.add_argument('--filter-module-examples-and-tests',
-                                  help=('List of modules that should have their examples '
-                                        'and tests built (e.g. \"lte;wifi\")'),
-                                  action="store", type=str, default=None)
-    parser_configure.add_argument('--lcov-report',
-                                  help=('Generate a code coverage report '
-                                        '(use this option after configuring with --enable-gcov and running a program)'),
-                                  action="store_true", default=None)
-    parser_configure.add_argument('--lcov-zerocounters',
-                                  help=('Zero the lcov counters'
-                                        ' (use this option before rerunning a program'
-                                        ' when generating repeated lcov reports)'),
-                                  action="store_true", default=None)
-
-    parser_configure.add_argument('--out', '--output-directory',
-                                  help=('Directory to store build artifacts'),
-                                  type=str, default=None, dest="output_directory")
-    parser_configure.add_argument('--with-brite',
-                                  help=('Use BRITE integration support, given by the indicated path,'
-                                        ' to allow the use of the BRITE topology generator'),
-                                  type=str, default=None)
-    parser_configure.add_argument('--with-click',
-                                  help='Path to Click source or installation prefix for NS-3 Click Integration support',
-                                  type=str, default=None)
-    parser_configure.add_argument('--with-openflow',
-                                  help='Path to OFSID source for NS-3 OpenFlow Integration support',
-                                  type=str, default=None)
-    parser_configure.add_argument('--force-refresh',
-                                  help='Force refresh the CMake cache by deleting'
-                                       ' the cache and reconfiguring the project',
-                                  action="store_true", default=None)
-    parser_configure.add_argument('--prefix',
-                                  help='Target output directory to install',
-                                  action="store", default=None)
-    parser_configure.add_argument('--trace-performance',
-                                  help="Generate a performance trace log for the CMake configuration",
-                                  action="store_true", default=None, dest="trace_cmake_perf")
-
-    parser_clean = sub_parser.add_parser('clean', help='Removes files created by ns3')
-    parser_clean.add_argument('clean', action="store_true", default=False)
-
-    parser_distclean = sub_parser.add_parser('distclean', help='Removes files created by ns3, tests and documentation')
-    parser_distclean.add_argument('distclean', action="store_true", default=False)
-
-    parser_install = sub_parser.add_parser('install', help='Install ns-3')
-    parser_install.add_argument('install', action="store_true", default=False)
-
-    parser_uninstall = sub_parser.add_parser('uninstall', help='Uninstall ns-3')
-    parser_uninstall.add_argument('uninstall', action="store_true", default=False)
-
-    parser_run = sub_parser.add_parser('run',
-                                       help='Try "./ns3 run --help" for more runtime options',
-                                       formatter_class=argparse.RawTextHelpFormatter)
-    parser_run.add_argument('run',
-                            help=('Build and run the target executable.\n'
-                                  'If --no-build is present, the build step is skipped.\n'
-                                  'To get the list of targets, use:\n'
-                                  './ns3 show targets\n'
-                                  'Arguments can be passed down to a program in one of the following ways:\n'
-                                  './ns3 run "target --help"\n'
-                                  './ns3 run target -- --help\n'
-                                  './ns3 run target --command-template="%%s --help"\n'),
-                            default='', nargs='?', metavar='target')
-    parser_run.add_argument('--no-build',
-                            help='Skip build step.',
-                            action="store_true", default=False)
-    parser_run.add_argument('--command-template',
-                            help=('Template of the command used to run the program given by run;'
-                                  ' It should be a shell command string containing %%s inside,'
-                                  ' which will be replaced by the actual program.'),
-                            type=str, default=None)
-    parser_run.add_argument('--cwd',
-                            help='Set the working directory for a program.',
-                            action="store", type=str, default=None)
-    parser_run.add_argument('--gdb',
-                            help='Change the default command template to run programs with gdb',
-                            action="store_true", default=None)
-    parser_run.add_argument('--lldb',
-                            help='Change the default command template to run programs with lldb',
-                            action="store_true", default=None)
-    parser_run.add_argument('-g', '--valgrind',
-                            help='Change the default command template to run programs with valgrind',
-                            action="store_true", default=None)
-    parser_run.add_argument('--memray',
-                            help='Use Memray memory profiler for Python scripts. Output will be saved to memray.output',
-                            action="store_true", default=None)
-    parser_run.add_argument('--heaptrack',
-                            help='Use Heaptrack memory profiler for C++',
-                            action="store_true", default=None)
-    parser_run.add_argument('--perf',
-                            help='Use Linux\'s perf to profile a program',
-                            action="store_true", default=None)
-    parser_run.add_argument('--vis', '--visualize',
-                            help='Modify --run arguments to enable the visualizer',
-                            action="store_true", dest="visualize", default=None)
-    parser_run.add_argument('--enable-sudo',
-                            help='Use sudo to setup suid bits on ns3 executables.',
-                            dest='enable_sudo', action='store_true',
-                            default=False)
-
-    parser_shell = sub_parser.add_parser('shell',
-                                         help='Try "./ns3 shell --help" for more shell options')
-    parser_shell.add_argument('shell',
-                              help='Export necessary environment variables and open a shell',
-                              action="store_true", default=False)
-
-    parser_docs = sub_parser.add_parser('docs',
-                                        help='Try "./ns3 docs --help" for more documentation options')
-    parser_docs.add_argument('docs',
-                             help='Build project documentation',
-                             choices=["contributing", "installation", "manual", "models", "tutorial",
-                                      "sphinx", "doxygen-no-build", "doxygen", "all"],
-                             action="store", type=str, default=None)
-
-    parser_show = sub_parser.add_parser('show',
-                                        help='Try "./ns3 show --help" for more show options')
-    parser_show.add_argument('show',
-                             help=('Print the current ns-3 build profile type, configuration or version, '
-                                   'or a list of buildable/runnable targets'),
-                             choices=["profile", "version", "config", "targets", "all"],
-                             action="store", type=str, nargs="?", default="all")
+    parser_configure.add_argument(
+        "--enable-modules",
+        help='List of modules to build (e.g. "core;network;internet")',
+        action="store",
+        type=str,
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--disable-modules",
+        help='List of modules not to build (e.g. "lte;wimax")',
+        action="store",
+        type=str,
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--filter-module-examples-and-tests",
+        help=(
+            "List of modules that should have their examples " 'and tests built (e.g. "lte;wifi")'
+        ),
+        action="store",
+        type=str,
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--lcov-report",
+        help=(
+            "Generate a code coverage report "
+            "(use this option after configuring with --enable-gcov and running a program)"
+        ),
+        action="store_true",
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--lcov-zerocounters",
+        help=(
+            "Zero the lcov counters"
+            " (use this option before rerunning a program"
+            " when generating repeated lcov reports)"
+        ),
+        action="store_true",
+        default=None,
+    )
+
+    parser_configure.add_argument(
+        "--out",
+        "--output-directory",
+        help=("Directory to store build artifacts"),
+        type=str,
+        default=None,
+        dest="output_directory",
+    )
+    parser_configure.add_argument(
+        "--with-brite",
+        help=(
+            "Use BRITE integration support, given by the indicated path,"
+            " to allow the use of the BRITE topology generator"
+        ),
+        type=str,
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--with-click",
+        help="Path to Click source or installation prefix for NS-3 Click Integration support",
+        type=str,
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--with-openflow",
+        help="Path to OFSID source for NS-3 OpenFlow Integration support",
+        type=str,
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--force-refresh",
+        help="Force refresh the CMake cache by deleting" " the cache and reconfiguring the project",
+        action="store_true",
+        default=None,
+    )
+    parser_configure.add_argument(
+        "--prefix", help="Target output directory to install", action="store", default=None
+    )
+    parser_configure.add_argument(
+        "--trace-performance",
+        help="Generate a performance trace log for the CMake configuration",
+        action="store_true",
+        default=None,
+        dest="trace_cmake_perf",
+    )
+
+    parser_clean = sub_parser.add_parser("clean", help="Removes files created by ns3")
+    parser_clean.add_argument("clean", action="store_true", default=False)
+
+    parser_distclean = sub_parser.add_parser(
+        "distclean", help="Removes files created by ns3, tests and documentation"
+    )
+    parser_distclean.add_argument("distclean", action="store_true", default=False)
+
+    parser_install = sub_parser.add_parser("install", help="Install ns-3")
+    parser_install.add_argument("install", action="store_true", default=False)
+
+    parser_uninstall = sub_parser.add_parser("uninstall", help="Uninstall ns-3")
+    parser_uninstall.add_argument("uninstall", action="store_true", default=False)
+
+    parser_run = sub_parser.add_parser(
+        "run",
+        help='Try "./ns3 run --help" for more runtime options',
+        formatter_class=argparse.RawTextHelpFormatter,
+    )
+    parser_run.add_argument(
+        "run",
+        help=(
+            "Build and run the target executable.\n"
+            "If --no-build is present, the build step is skipped.\n"
+            "To get the list of targets, use:\n"
+            "./ns3 show targets\n"
+            "Arguments can be passed down to a program in one of the following ways:\n"
+            './ns3 run "target --help"\n'
+            "./ns3 run target -- --help\n"
+            './ns3 run target --command-template="%%s --help"\n'
+        ),
+        default="",
+        nargs="?",
+        metavar="target",
+    )
+    parser_run.add_argument(
+        "--no-build", help="Skip build step.", action="store_true", default=False
+    )
+    parser_run.add_argument(
+        "--command-template",
+        help=(
+            "Template of the command used to run the program given by run;"
+            " It should be a shell command string containing %%s inside,"
+            " which will be replaced by the actual program."
+        ),
+        type=str,
+        default=None,
+    )
+    parser_run.add_argument(
+        "--cwd",
+        help="Set the working directory for a program.",
+        action="store",
+        type=str,
+        default=None,
+    )
+    parser_run.add_argument(
+        "--gdb",
+        help="Change the default command template to run programs with gdb",
+        action="store_true",
+        default=None,
+    )
+    parser_run.add_argument(
+        "--lldb",
+        help="Change the default command template to run programs with lldb",
+        action="store_true",
+        default=None,
+    )
+    parser_run.add_argument(
+        "-g",
+        "--valgrind",
+        help="Change the default command template to run programs with valgrind",
+        action="store_true",
+        default=None,
+    )
+    parser_run.add_argument(
+        "--memray",
+        help="Use Memray memory profiler for Python scripts. Output will be saved to memray.output",
+        action="store_true",
+        default=None,
+    )
+    parser_run.add_argument(
+        "--heaptrack",
+        help="Use Heaptrack memory profiler for C++",
+        action="store_true",
+        default=None,
+    )
+    parser_run.add_argument(
+        "--perf", help="Use Linux's perf to profile a program", action="store_true", default=None
+    )
+    parser_run.add_argument(
+        "--vis",
+        "--visualize",
+        help="Modify --run arguments to enable the visualizer",
+        action="store_true",
+        dest="visualize",
+        default=None,
+    )
+    parser_run.add_argument(
+        "--enable-sudo",
+        help="Use sudo to setup suid bits on ns3 executables.",
+        dest="enable_sudo",
+        action="store_true",
+        default=False,
+    )
+
+    parser_shell = sub_parser.add_parser(
+        "shell", help='Try "./ns3 shell --help" for more shell options'
+    )
+    parser_shell.add_argument(
+        "shell",
+        help="Export necessary environment variables and open a shell",
+        action="store_true",
+        default=False,
+    )
+
+    parser_docs = sub_parser.add_parser(
+        "docs", help='Try "./ns3 docs --help" for more documentation options'
+    )
+    parser_docs.add_argument(
+        "docs",
+        help="Build project documentation",
+        choices=[
+            "contributing",
+            "installation",
+            "manual",
+            "models",
+            "tutorial",
+            "sphinx",
+            "doxygen-no-build",
+            "doxygen",
+            "all",
+        ],
+        action="store",
+        type=str,
+        default=None,
+    )
+
+    parser_show = sub_parser.add_parser(
+        "show", help='Try "./ns3 show --help" for more show options'
+    )
+    parser_show.add_argument(
+        "show",
+        help=(
+            "Print the current ns-3 build profile type, configuration or version, "
+            "or a list of buildable/runnable targets"
+        ),
+        choices=["profile", "version", "config", "targets", "all"],
+        action="store",
+        type=str,
+        nargs="?",
+        default="all",
+    )
 
     add_argument_to_subparsers(
-        [parser, parser_build, parser_configure, parser_clean, parser_distclean, parser_docs, parser_run, parser_show],
+        [
+            parser,
+            parser_build,
+            parser_configure,
+            parser_clean,
+            parser_distclean,
+            parser_docs,
+            parser_run,
+            parser_show,
+        ],
         ["--dry-run"],
         help_msg="Do not execute the commands.",
-        dest="dry_run")
+        dest="dry_run",
+    )
 
-    add_argument_to_subparsers([parser, parser_build, parser_run],
-                               ['-j', '--jobs'],
-                               help_msg="Set number of parallel jobs.",
-                               dest="jobs",
-                               action="store",
-                               default_value=max_cpu_threads)
-
-    add_argument_to_subparsers([parser, parser_build, parser_configure, parser_run, parser_show],
-                               ["--quiet"],
-                               help_msg="Don't print task lines, i.e. messages saying which tasks are being executed.",
-                               dest="quiet")
-
-    add_argument_to_subparsers([parser, parser_build, parser_configure, parser_docs, parser_run],
-                               ['-v', '--verbose'],
-                               help_msg='Print which commands were executed',
-                               dest='verbose',
-                               default_value=False)
+    add_argument_to_subparsers(
+        [parser, parser_build, parser_run],
+        ["-j", "--jobs"],
+        help_msg="Set number of parallel jobs.",
+        dest="jobs",
+        action="store",
+        default_value=max_cpu_threads,
+    )
+
+    add_argument_to_subparsers(
+        [parser, parser_build, parser_configure, parser_run, parser_show],
+        ["--quiet"],
+        help_msg="Don't print task lines, i.e. messages saying which tasks are being executed.",
+        dest="quiet",
+    )
+
+    add_argument_to_subparsers(
+        [parser, parser_build, parser_configure, parser_docs, parser_run],
+        ["-v", "--verbose"],
+        help_msg="Print which commands were executed",
+        dest="verbose",
+        default_value=False,
+    )
 
     # Try to split -- separated arguments into two lists for ns3 and for the runnable target
     try:
-        args_separator_index = argv.index('--')
+        args_separator_index = argv.index("--")
         ns3_args = argv[:args_separator_index]
-        runnable_args = argv[args_separator_index + 1:]
+        runnable_args = argv[args_separator_index + 1 :]
     except ValueError:
         ns3_args = argv
         runnable_args = []
@@ -331,17 +498,19 @@
     args, unknown_args = parser.parse_known_args(ns3_args)
 
     # If run doesn't have a target, print the help message of the run parser
-    if "run" in args and args.run == '':
+    if "run" in args and args.run == "":
         parser_run.print_help()
         exit(-1)
 
     # Merge attributes
     attributes_to_merge = ["dry_run", "help", "verbose", "quiet"]
     filtered_attributes = list(
-        filter(lambda x: x if ("disable" not in x and "enable" not in x) else None, args.__dir__()))
+        filter(lambda x: x if ("disable" not in x and "enable" not in x) else None, args.__dir__())
+    )
     for attribute in attributes_to_merge:
         merging_attributes = list(
-            map(lambda x: args.__getattribute__(x) if attribute in x else None, filtered_attributes))
+            map(lambda x: args.__getattribute__(x) if attribute in x else None, filtered_attributes)
+        )
         setattr(args, attribute, merging_attributes.count(True) > 0)
 
     if args.help:
@@ -351,8 +520,8 @@
 
         # retrieve subparsers from parser
         subparsers_actions = [
-            action for action in parser._actions
-            if isinstance(action, argparse._SubParsersAction)]
+            action for action in parser._actions if isinstance(action, argparse._SubParsersAction)
+        ]
         # there will probably only be one subparser_action,
         # but better safe than sorry
         for subparsers_action in subparsers_actions:
@@ -362,18 +531,36 @@
                 if len(subcommand) > 1:
                     print(subcommand)
 
-        print(parser.format_help().replace(parser.description, "").replace(parser.format_usage(), ""))
+        print(
+            parser.format_help().replace(parser.description, "").replace(parser.format_usage(), "")
+        )
         exit(0)
 
     attributes_to_merge = ["jobs"]
-    filtered_attributes = list(filter(lambda x: x if ("disable" not in x and "enable" not in x) else 0, args.__dir__()))
+    filtered_attributes = list(
+        filter(lambda x: x if ("disable" not in x and "enable" not in x) else 0, args.__dir__())
+    )
     for attribute in attributes_to_merge:
         merging_attributes = list(
-            map(lambda x: int(args.__getattribute__(x)) if attribute in x else max_cpu_threads, filtered_attributes))
+            map(
+                lambda x: int(args.__getattribute__(x)) if attribute in x else max_cpu_threads,
+                filtered_attributes,
+            )
+        )
         setattr(args, attribute, min(merging_attributes))
 
     # If some positional options are not in args, set them to false.
-    for option in ["clean", "configure", "docs", "install", "run", "shell", "uninstall", "show", "distclean"]:
+    for option in [
+        "clean",
+        "configure",
+        "docs",
+        "install",
+        "run",
+        "shell",
+        "uninstall",
+        "show",
+        "distclean",
+    ]:
         if option not in args:
             setattr(args, option, False)
 
@@ -385,9 +572,11 @@
 
     # Emit error in case of unknown arguments
     if unknown_args:
-        msg = ("Unknown options were given: {options}.\n"
-               "To see the allowed options add the `--help` option.\n"
-               "To forward configuration or runtime options, put them after '--'.\n")
+        msg = (
+            "Unknown options were given: {options}.\n"
+            "To see the allowed options add the `--help` option.\n"
+            "To forward configuration or runtime options, put them after '--'.\n"
+        )
         if args.run:
             msg += "Try: ./ns3 run {target} -- {options}\n"
         if args.configure:
@@ -402,14 +591,15 @@
     ns3_modules_tests = []
     ns3_modules = None
 
-    build_info = {"NS3_ENABLED_MODULES": [],
-                  "BUILD_PROFILE": None,
-                  "VERSION": None,
-                  "ENABLE_EXAMPLES": False,
-                  "ENABLE_SUDO": False,
-                  "ENABLE_TESTS": False,
-                  "BUILD_VERSION_STRING": None
-                  }
+    build_info = {
+        "NS3_ENABLED_MODULES": [],
+        "BUILD_PROFILE": None,
+        "VERSION": None,
+        "ENABLE_EXAMPLES": False,
+        "ENABLE_SUDO": False,
+        "ENABLE_TESTS": False,
+        "BUILD_VERSION_STRING": None,
+    }
     if output_directory and os.path.exists(lock_file):
         exec(open(lock_file).read(), globals(), build_info)
         ns3_modules = build_info["NS3_ENABLED_MODULES"]
@@ -430,15 +620,18 @@
 def remove_dir(dir_to_remove, dry_run, directory_qualifier=""):
     dir_to_remove = os.path.abspath(dir_to_remove)
     if os.path.exists(dir_to_remove):
-        if (".." in os.path.relpath(dir_to_remove, ns3_path)
-            or os.path.abspath(dir_to_remove) == os.path.abspath(ns3_path)):
+        if ".." in os.path.relpath(dir_to_remove, ns3_path) or os.path.abspath(
+            dir_to_remove
+        ) == os.path.abspath(ns3_path):
             # In case the directory to remove isn't within
             # the current ns-3 directory, print an error
             # message for the dry-run case
             # Or throw an exception in a normal run
-            error_message = (f"The {directory_qualifier} directory '{dir_to_remove}' "
-                             "is not within the current ns-3 directory. "
-                             "Deleting it can cause data loss.")
+            error_message = (
+                f"The {directory_qualifier} directory '{dir_to_remove}' "
+                "is not within the current ns-3 directory. "
+                "Deleting it can cause data loss."
+            )
             if dry_run:
                 print_and_buffer(error_message)
                 return
@@ -466,28 +659,29 @@
         dirname = os.path.dirname(cmake_cache_file)
         remove_dir(dirname, dry_run, "CMake cache")
 
-    dirs_to_remove = [os.path.join(ns3_path, "testpy-output"),
-                      os.path.join(ns3_path, "__pycache__")
-                      ]
-    for dir_to_remove in dirs_to_remove:
+    dirs_to_remove = ["testpy-output", "__pycache__", "build", "cmake-cache"]
+    for dir_to_remove in map(append_to_ns3_path, dirs_to_remove):
         remove_dir(dir_to_remove, dry_run)
 
     remove_file(lock_file, dry_run)
 
 
 def clean_docs_and_tests_artifacts(dry_run=False):
-    docs_dir = os.path.join(ns3_path, 'doc')
+    docs_dir = append_to_ns3_path("doc")
 
-    file_artifacts = ["doxygen.log",
-                      "doxygen.warnings.log",
-                      "introspected-command-line.h",
-                      "introspected-doxygen.h",
-                      "ns3-object.txt"]
+    file_artifacts = [
+        "doxygen.log",
+        "doxygen.warnings.log",
+        "introspected-command-line.h",
+        "introspected-doxygen.h",
+        "ns3-object.txt",
+    ]
     docs_files = [os.path.join(docs_dir, filename) for filename in file_artifacts]
 
-    docs_and_tests_dirs = [os.path.join(docs_dir, "html"),
-                           os.path.join(docs_dir, "html-warn"),
-                           ]
+    docs_and_tests_dirs = [
+        os.path.join(docs_dir, "html"),
+        os.path.join(docs_dir, "html-warn"),
+    ]
     docs_and_tests_dirs.extend(glob.glob(f"{docs_dir}/**/build", recursive=True))
     docs_and_tests_dirs.extend(glob.glob(f"{docs_dir}/**/source-temp", recursive=True))
 
@@ -499,16 +693,13 @@
 
 
 def clean_pip_packaging_artifacts(dry_run=False):
-    pip_dirs = [os.path.join(ns3_path, "dist"),
-                os.path.join(ns3_path, "nsnam.egg-info"),
-                os.path.join(ns3_path, "wheelhouse")
-                ]
-    for directory in pip_dirs:
+    pip_dirs = ["dist", "nsnam.egg-info", "wheelhouse"]
+    for directory in map(append_to_ns3_path, pip_dirs):
         remove_dir(directory, dry_run)
 
 
 def clean_vcpkg_artifacts(dry_run=False):
-    remove_dir(os.path.join(ns3_path, "vcpkg"), dry_run)
+    remove_dir(append_to_ns3_path("vcpkg"), dry_run)
 
 
 def search_cmake_cache(build_profile):
@@ -552,10 +743,7 @@
 
     if not current_cmake_generator:
         # Search for available generators
-        cmake_generator_map = {"ninja": "Ninja",
-                               "make": "Unix Makefiles",
-                               "xcodebuild": "Xcode"
-                               }
+        cmake_generator_map = {"ninja": "Ninja", "make": "Unix Makefiles", "xcodebuild": "Xcode"}
         available_generators = []
         for generator in cmake_generator_map.keys():
             if shutil.which(generator):
@@ -595,9 +783,11 @@
     return True
 
 
-def configure_cmake(cmake, args, current_cmake_cache_folder, current_cmake_generator, output, dry_run=False):
+def configure_cmake(
+    cmake, args, current_cmake_cache_folder, current_cmake_generator, output, dry_run=False
+):
     # Aggregate all flags to configure CMake
-    cmake_args = [cmake]
+    cmake_args = [cmake, "-S", ns3_path]
 
     if not project_configured(current_cmake_cache_folder):
         # Create a new cmake_cache folder if one does not exist
@@ -619,10 +809,13 @@
         # CMake with the respective installation
         if args.enable_python_bindings is True:
             import sysconfig
+
             cmake_args.append(f"-DPython3_LIBRARY_DIRS={sysconfig.get_config_var('LIBDIR')}")
             cmake_args.append(f"-DPython3_INCLUDE_DIRS={sysconfig.get_config_var('INCLUDEPY')}")
             cmake_args.append(f"-DPython3_EXECUTABLE={sys.executable}")
 
+    cmake_args.extend(["-B", current_cmake_cache_folder])
+
     # C++ standard
     if args.cxx_standard is not None:
         cmake_args.append("-DCMAKE_CXX_STANDARD=%s" % args.cxx_standard)
@@ -630,54 +823,66 @@
     # Build type
     if args.build_profile is not None:
         args.build_profile = args.build_profile.lower()
-        if args.build_profile not in ["debug", "default", "release", "optimized", "minsizerel", "relwithdebinfo"]:
+        if args.build_profile not in [
+            "debug",
+            "default",
+            "release",
+            "optimized",
+            "minsizerel",
+            "relwithdebinfo",
+        ]:
             raise Exception("Unknown build type")
         else:
             if args.build_profile == "debug":
                 cmake_args.extend(
-                    "-DCMAKE_BUILD_TYPE=debug -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON".split())
+                    "-DCMAKE_BUILD_TYPE=debug -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON".split()
+                )
             elif args.build_profile in ["default", "relwithdebinfo"]:
                 cmake_args.extend(
-                    "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=OFF".split())
+                    "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=OFF".split()
+                )
             elif args.build_profile in ["release", "optimized"]:
                 cmake_args.extend(
-                    "-DCMAKE_BUILD_TYPE=release -DNS3_ASSERT=OFF -DNS3_LOG=OFF -DNS3_WARNINGS_AS_ERRORS=OFF".split())
+                    "-DCMAKE_BUILD_TYPE=release -DNS3_ASSERT=OFF -DNS3_LOG=OFF -DNS3_WARNINGS_AS_ERRORS=OFF".split()
+                )
             else:
                 cmake_args.extend(
                     "-DCMAKE_BUILD_TYPE=minsizerel -DNS3_ASSERT=OFF -DNS3_LOG=OFF -DNS3_WARNINGS_AS_ERRORS=OFF".split()
                 )
-            cmake_args.append("-DNS3_NATIVE_OPTIMIZATIONS=%s" % on_off((args.build_profile == "optimized")))
-
-    options = (("ASSERT", "asserts"),
-               ("CLANG_TIDY", "clang_tidy"),
-               ("COVERAGE", "gcov"),
-               ("DES_METRICS", "des_metrics"),
-               ("DPDK", "dpdk"),
-               ("EIGEN", "eigen"),
-               ("ENABLE_BUILD_VERSION", "build_version"),
-               ("ENABLE_SUDO", "sudo"),
-               ("EXAMPLES", "examples"),
-               ("GSL", "gsl"),
-               ("GTK3", "gtk"),
-               ("LOG", "logs"),
-               ("MONOLIB", "monolib"),
-               ("MPI", "mpi"),
-               ("NINJA_TRACING", "ninja_tracing"),
-               ("PRECOMPILE_HEADERS", "precompiled_headers"),
-               ("PYTHON_BINDINGS", "python_bindings"),
-               ("SANITIZE", "sanitizers"),
-               ("STATIC", "static"),
-               ("TESTS", "tests"),
-               ("VERBOSE", "verbose"),
-               ("WARNINGS", "warnings"),
-               ("WARNINGS_AS_ERRORS", "werror"),
-               )
-    for (cmake_flag, option_name) in options:
+            cmake_args.append(
+                "-DNS3_NATIVE_OPTIMIZATIONS=%s" % on_off((args.build_profile == "optimized"))
+            )
+
+    options = (
+        ("ASSERT", "asserts"),
+        ("CLANG_TIDY", "clang_tidy"),
+        ("COVERAGE", "gcov"),
+        ("DES_METRICS", "des_metrics"),
+        ("DPDK", "dpdk"),
+        ("EIGEN", "eigen"),
+        ("ENABLE_BUILD_VERSION", "build_version"),
+        ("ENABLE_SUDO", "sudo"),
+        ("EXAMPLES", "examples"),
+        ("GSL", "gsl"),
+        ("GTK3", "gtk"),
+        ("LOG", "logs"),
+        ("MONOLIB", "monolib"),
+        ("MPI", "mpi"),
+        ("NINJA_TRACING", "ninja_tracing"),
+        ("PRECOMPILE_HEADERS", "precompiled_headers"),
+        ("PYTHON_BINDINGS", "python_bindings"),
+        ("SANITIZE", "sanitizers"),
+        ("STATIC", "static"),
+        ("TESTS", "tests"),
+        ("VERBOSE", "verbose"),
+        ("WARNINGS", "warnings"),
+        ("WARNINGS_AS_ERRORS", "werror"),
+    )
+    for cmake_flag, option_name in options:
         arg = on_off_condition(args, cmake_flag, option_name)
         if arg:
             is_on = "=ON" in arg
-            reverse = arg.replace("=ON" if is_on else "=OFF",
-                                  "=OFF" if is_on else "=ON")
+            reverse = arg.replace("=ON" if is_on else "=OFF", "=OFF" if is_on else "=ON")
             if reverse in cmake_args:
                 cmake_args.remove(reverse)
             cmake_args.append(arg)
@@ -709,35 +914,35 @@
         cmake_args.append("-DNS3_DISABLED_MODULES=%s" % args.disable_modules)
 
     if args.filter_module_examples_and_tests is not None:
-        cmake_args.append("-DNS3_FILTER_MODULE_EXAMPLES_AND_TESTS=%s" % args.filter_module_examples_and_tests)
+        cmake_args.append(
+            "-DNS3_FILTER_MODULE_EXAMPLES_AND_TESTS=%s" % args.filter_module_examples_and_tests
+        )
 
     # Try to set specified generator (will probably fail if there is an old cache)
     if args.G:
         cmake_args.extend(["-G", args.G])
 
     if args.trace_cmake_perf:
-        cmake_performance_trace = os.path.join(os.path.relpath(ns3_path, current_cmake_cache_folder),
-                                               "cmake_performance_trace.log")
-        cmake_args.extend(["--profiling-format=google-trace",
-                           "--profiling-output=" + cmake_performance_trace])
+        cmake_performance_trace = os.path.join(
+            os.path.relpath(ns3_path, ns3_path), "cmake_performance_trace.log"
+        )
+        cmake_args.extend(
+            ["--profiling-format=google-trace", "--profiling-output=" + cmake_performance_trace]
+        )
+
+    # Enable warnings for uninitialized variable usage
+    cmake_args.append("--warn-uninitialized")
 
     # Append CMake flags passed using the -- separator
     cmake_args.extend(args.program_args)
 
-    # Configure cmake
-    cmake_args.append("..")  # for now, assuming the cmake_cache directory is inside the ns-3-dev folder
-
     # Echo out the configure command
-    print_and_buffer("cd %s; %s ; cd %s" % (os.path.relpath(current_cmake_cache_folder, ns3_path),
-                                            " ".join(cmake_args),
-                                            os.path.relpath(ns3_path, current_cmake_cache_folder)
-                                            )
-                     )
+    print_and_buffer(" ".join(cmake_args))
 
     # Run cmake
     if not dry_run:
         proc_env = os.environ.copy()
-        ret = subprocess.run(cmake_args, cwd=current_cmake_cache_folder, stdout=output, env=proc_env)
+        ret = subprocess.run(cmake_args, stdout=output, env=proc_env)
         if ret.returncode != 0:
             exit(ret.returncode)
 
@@ -746,7 +951,7 @@
 
 def update_scratches_list(current_cmake_cache_folder):
     # Store list of scratches to trigger a reconfiguration step if needed
-    current_scratch_sources = glob.glob(os.path.join(ns3_path, "scratch", "**", "*.cc"), recursive=True)
+    current_scratch_sources = glob.glob(append_to_ns3_path("scratch", "**", "*.cc"), recursive=True)
     with open(os.path.join(current_cmake_cache_folder, "ns3scratches"), "w") as f:
         f.write("\n".join(current_scratch_sources))
 
@@ -774,11 +979,13 @@
         temp_path = program.replace(out_dir, "")
         # Sometimes Windows uses \\, sometimes /
         # quite the mess
-        temp_path = temp_path.split(os.sep if os.sep in temp_path else '/')
+        temp_path = temp_path.split(os.sep if os.sep in temp_path else "/")
         temp_path.pop(0)  # remove first path separator
 
         # Remove version prefix and build type suffix from shortcuts (or keep them too?)
-        temp_path[-1] = temp_path[-1].replace("-" + build_profile, "").replace("ns" + ns3_version + "-", "")
+        temp_path[-1] = (
+            temp_path[-1].replace("-" + build_profile, "").replace("ns" + ns3_version + "-", "")
+        )
 
         # Deal with scratch subdirs
         if "scratch" in temp_path and len(temp_path) > 3:
@@ -788,7 +995,7 @@
         # Check if there is a .cc file for that specific program
         source_file_path = os.sep.join(temp_path) + ".cc"
         source_shortcut = False
-        if os.path.exists(os.path.join(ns3_path, source_file_path)):
+        if os.path.exists(append_to_ns3_path(source_file_path)):
             source_shortcut = True
 
         program = program.strip()
@@ -829,11 +1036,11 @@
 
     # Filter collisions
     collisions = list(filter(lambda x: x if len(x[1]) > 1 else None, longest_shortcut_map.items()))
-    for (colliding_shortcut, longest_shortcuts) in collisions:
+    for colliding_shortcut, longest_shortcuts in collisions:
         ns3_program_map[colliding_shortcut] = longest_shortcuts
 
     if programs_dict["ns3_runnable_scripts"]:
-        scratch_scripts = glob.glob(os.path.join(ns3_path, "scratch", "*.py"), recursive=True)
+        scratch_scripts = glob.glob(append_to_ns3_path("scratch", "*.py"), recursive=True)
         programs_dict["ns3_runnable_scripts"].extend(scratch_scripts)
 
     for program in programs_dict["ns3_runnable_scripts"]:
@@ -854,37 +1061,41 @@
 
 def cmake_check_version():
     # Check CMake version
+    minimum_cmake_version = "3.13.0"
     cmake3 = shutil.which("cmake3")
     cmake = cmake3 if cmake3 else shutil.which("cmake")
     if not cmake:
-        print("Error:  CMake not found; please install version 3.10 or greater, or modify", path_variable)
+        print(
+            f"Error:  CMake not found; please install version {minimum_cmake_version} or greater, or modify {path_variable}"
+        )
         exit(1)
     cmake = cmake.replace(".EXE", "").replace(".exe", "")  # Trim cmake executable extension
     cmake_output = subprocess.check_output([cmake, "--version"]).decode("utf-8")
     version = re.findall("version (.*)", cmake_output)[0]
-    if parse_version(version) < parse_version("3.10.0"):
-        print("Error:  CMake found at %s but version %s is older than 3.10" % (cmake, version))
+    if parse_version(version) < parse_version(minimum_cmake_version):
+        print(
+            f"Error:  CMake found at {cmake} but version {version} is older than {minimum_cmake_version}"
+        )
         exit(1)
     return cmake, version
 
 
-def cmake_build(current_cmake_cache_folder, output, jobs, target=None, dry_run=False, build_verbose=False):
+def cmake_build(
+    current_cmake_cache_folder, output, jobs, target=None, dry_run=False, build_verbose=False
+):
     cmake, version = cmake_check_version()
 
-    # Older CMake versions don't accept the number of jobs directly
-    jobs_part = ("-j %d" % jobs) if parse_version(version) >= parse_version("3.12.0") else ""
-    target_part = (" --target %s" % target) if target else ""
-    cmake_build_command = "%s --build . %s%s" % (cmake, jobs_part, target_part)
-
-    print_and_buffer("cd %s; %s ; cd %s" % (os.path.relpath(current_cmake_cache_folder, ns3_path),
-                                            cmake_build_command,
-                                            os.path.relpath(ns3_path, current_cmake_cache_folder)
-                                            )
-                     )
+    cmake_args = [cmake, "--build", current_cmake_cache_folder]
+    if jobs:
+        cmake_args.extend(["-j", str(jobs)])
+
+    if target:
+        cmake_args.extend(["--target", target])
+
+    print_and_buffer(" ".join(cmake_args))
     if not dry_run:
         # Assume quiet is not enabled, and print things normally
-        kwargs = {"stdout": None,
-                  "stderr": None}
+        kwargs = {"stdout": None, "stderr": None}
 
         proc_env = os.environ.copy()
         if build_verbose:
@@ -898,11 +1109,11 @@
             kwargs["stdout"] = subprocess.PIPE
             kwargs["stderr"] = subprocess.PIPE
 
-        ret = subprocess.run(cmake_build_command.split(),
-                             cwd=current_cmake_cache_folder,
-                             env=proc_env,
-                             **kwargs,
-                             )
+        ret = subprocess.run(
+            cmake_args,
+            env=proc_env,
+            **kwargs,
+        )
 
         # Print errors in case compilation fails and output != None (quiet)
         if ret.returncode != 0 and output is not None:
@@ -916,18 +1127,24 @@
 
 def extract_cmakecache_settings(current_cmake_cache_folder):
     try:
-        with open(current_cmake_cache_folder + os.sep + "CMakeCache.txt", "r", encoding="utf-8") as f:
+        with open(
+            current_cmake_cache_folder + os.sep + "CMakeCache.txt", "r", encoding="utf-8"
+        ) as f:
             contents = f.read()
     except FileNotFoundError as e:
         raise e
     current_settings = re.findall("(NS3_.*):.*=(.*)", contents)  # extract NS3 specific settings
-    current_settings.extend(re.findall("(CMAKE_BUILD_TYPE):.*=(.*)", contents))  # extract build type
+    current_settings.extend(
+        re.findall("(CMAKE_BUILD_TYPE):.*=(.*)", contents)
+    )  # extract build type
     current_settings.extend(re.findall("(CMAKE_GENERATOR):.*=(.*)", contents))  # extract generator
     current_settings.extend(re.findall("(CMAKE_CXX_COMPILER):.*=(.*)", contents))  # C++ compiler
     current_settings.extend(re.findall("(CMAKE_CXX_FLAGS):.*=(.*)", contents))  # C++ flags
     current_settings.extend(re.findall("(CMAKE_C_COMPILER):.*=(.*)", contents))  # C compiler
     current_settings.extend(re.findall("(CMAKE_C_FLAGS):.*=(.*)", contents))  # C flags
-    current_settings.extend(re.findall("(CMAKE_INSTALL_PREFIX):.*=(.*)", contents))  # installation directory
+    current_settings.extend(
+        re.findall("(CMAKE_INSTALL_PREFIX):.*=(.*)", contents)
+    )  # installation directory
 
     # Transform list into dictionary
     settings_dictionary = dict(current_settings)
@@ -939,6 +1156,7 @@
 
 def reconfigure_cmake_to_force_refresh(cmake, current_cmake_cache_folder, output, dry_run=False):
     import json
+
     settings_bak_file = "settings.json"
 
     # Extract settings or recover from the backup
@@ -967,11 +1185,14 @@
     cmake_args.append("..")
 
     # Echo out the configure command
-    print_and_buffer("cd %s; %s ; cd %s" % (os.path.relpath(ns3_path, current_cmake_cache_folder),
-                                            " ".join(cmake_args),
-                                            os.path.relpath(current_cmake_cache_folder, ns3_path)
-                                            )
-                     )
+    print_and_buffer(
+        "cd %s; %s ; cd %s"
+        % (
+            os.path.relpath(ns3_path, current_cmake_cache_folder),
+            " ".join(cmake_args),
+            os.path.relpath(current_cmake_cache_folder, ns3_path),
+        )
+    )
 
     # Call cmake
     if not dry_run:
@@ -981,8 +1202,10 @@
         if ret.returncode == 0:
             os.remove(settings_bak_file)
         else:
-            raise Exception("Reconfiguring CMake to force refresh failed. "
-                            "A backup of the settings was saved in %s" % settings_bak_file)
+            raise Exception(
+                "Reconfiguring CMake to force refresh failed. "
+                "A backup of the settings was saved in %s" % settings_bak_file
+            )
 
         update_scratches_list(current_cmake_cache_folder)
 
@@ -995,7 +1218,9 @@
     program_name = ""
 
     try:
-        program_name = "".join(*re.findall("(.*)ns%s-(.*)%s" % (ns3_version, build_profile_suffix), program_path))
+        program_name = "".join(
+            *re.findall("(.*)ns%s-(.*)%s" % (ns3_version, build_profile_suffix), program_path)
+        )
     except TypeError:
         print("Target to build does not exist: %s" % program_path)
         exit(1)
@@ -1009,8 +1234,9 @@
         return program_name.split("/")[-1]
 
 
-def configuration_step(current_cmake_cache_folder, current_cmake_generator, args,
-                       output, dry_run=False):
+def configuration_step(
+    current_cmake_cache_folder, current_cmake_generator, args, output, dry_run=False
+):
     # Search for the CMake binary
     cmake, _ = cmake_check_version()
 
@@ -1021,55 +1247,56 @@
         exit(0)
 
     # Call cmake to configure/reconfigure/refresh the project
-    configure_cmake(cmake,
-                    args,
-                    current_cmake_cache_folder,
-                    current_cmake_generator,
-                    output,
-                    dry_run
-                    )
+    configure_cmake(
+        cmake, args, current_cmake_cache_folder, current_cmake_generator, output, dry_run
+    )
 
     # If manually configuring, we end the script earlier
     exit(0)
 
 
-def build_step(args,
-               build_and_run,
-               target_to_run,
-               current_cmake_cache_folder,
-               ns3_modules,
-               ns3_version,
-               build_profile,
-               output):
+def build_step(
+    args,
+    build_and_run,
+    target_to_run,
+    current_cmake_cache_folder,
+    ns3_modules,
+    ns3_version,
+    build_profile,
+    output,
+):
     # There is one scenario where we build everything: ./ns3 build
     if "build" in args and len(args.build) == 0:
-        cmake_build(current_cmake_cache_folder,
-                    jobs=args.jobs,
-                    output=output,
-                    dry_run=args.dry_run,
-                    build_verbose=args.verbose
-                    )
+        cmake_build(
+            current_cmake_cache_folder,
+            jobs=args.jobs,
+            output=output,
+            dry_run=args.dry_run,
+            build_verbose=args.verbose,
+        )
 
     # If we are building specific targets, we build them one by one
     if "build" in args:
-        non_executable_targets = ["assemble-introspected-command-line",
-                                  "check-version",
-                                  "cmake-format",
-                                  "coverage_gcc",
-                                  "docs",
-                                  "doxygen",
-                                  "doxygen-no-build",
-                                  "installation",
-                                  "sphinx",
-                                  "manual",
-                                  "models",
-                                  "ninjaTrace",
-                                  "timeTraceReport",
-                                  "tutorial",
-                                  "contributing",
-                                  "install",
-                                  "uninstall",
-                                  ]
+        non_executable_targets = [
+            "assemble-introspected-command-line",
+            "check-version",
+            "cmake-format",
+            "cmake-format-check",
+            "coverage_gcc",
+            "docs",
+            "doxygen",
+            "doxygen-no-build",
+            "installation",
+            "sphinx",
+            "manual",
+            "models",
+            "ninjaTrace",
+            "timeTraceReport",
+            "tutorial",
+            "contributing",
+            "install",
+            "uninstall",
+        ]
         # Build targets in the list
         for target in args.build:
             if target in ns3_modules:
@@ -1090,22 +1317,25 @@
                     global run_verbose
                     run_verbose = False  # Do not print the equivalent cmake command
 
-            cmake_build(current_cmake_cache_folder,
-                        jobs=args.jobs,
-                        target=target,
-                        output=output,
-                        dry_run=args.dry_run,
-                        build_verbose=args.verbose)
+            cmake_build(
+                current_cmake_cache_folder,
+                jobs=args.jobs,
+                target=target,
+                output=output,
+                dry_run=args.dry_run,
+                build_verbose=args.verbose,
+            )
 
     # The remaining case is when we want to build something to run
     if build_and_run:
-        cmake_build(current_cmake_cache_folder,
-                    jobs=args.jobs,
-                    target=get_target_to_build(target_to_run, ns3_version, build_profile),
-                    output=output,
-                    dry_run=args.dry_run,
-                    build_verbose=args.verbose
-                    )
+        cmake_build(
+            current_cmake_cache_folder,
+            jobs=args.jobs,
+            target=get_target_to_build(target_to_run, ns3_version, build_profile),
+            output=output,
+            dry_run=args.dry_run,
+            build_verbose=args.verbose,
+        )
 
 
 def check_program_installed(program_name: str) -> str:
@@ -1118,6 +1348,7 @@
 
 def check_module_installed(module_name: str):
     import importlib
+
     try:
         importlib.import_module(module_name)
     except ImportError:
@@ -1128,14 +1359,15 @@
 def run_step(args, target_to_run, target_args):
     libdir = "%s/lib" % out_dir
 
-    custom_env = {"PATH": libdir,
-                  "PYTHONPATH": "%s/bindings/python" % out_dir,
-                  }
+    custom_env = {
+        "PATH": libdir,
+        "PYTHONPATH": "%s/bindings/python" % out_dir,
+    }
     if sys.platform != "win32":
         custom_env["LD_LIBRARY_PATH"] = libdir
 
     proc_env = os.environ.copy()
-    for (key, value) in custom_env.items():
+    for key, value in custom_env.items():
         if key in proc_env:
             proc_env[key] += path_sep + value
         else:
@@ -1158,7 +1390,14 @@
             # running with memray?
             if args.memray:
                 check_module_installed("memray")
-                target_args = ["-m", "memray", "run", "-o", "memray.output", "--native"] + target_args
+                target_args = [
+                    "-m",
+                    "memray",
+                    "run",
+                    "-o",
+                    "memray.output",
+                    "--native",
+                ] + target_args
 
         # running from ns-3-dev (ns3_path) or cwd
         if args.cwd:
@@ -1171,7 +1410,8 @@
         # running valgrind?
         if args.valgrind:
             debugging_software.extend(
-                [check_program_installed("valgrind"), "--leak-check=full", "--show-leak-kinds=all"])
+                [check_program_installed("valgrind"), "--leak-check=full", "--show-leak-kinds=all"]
+            )
 
         # running gdb?
         if args.gdb:
@@ -1186,11 +1426,16 @@
 
         # running with perf?
         if args.perf:
-            debugging_software.extend([
-                check_program_installed("perf"),
-                "record", "--call-graph", "dwarf", "-e",
-                "cache-misses,branch-misses,cpu-cycles,stalled-cycles-frontend,stalled-cycles-backend"
-            ])
+            debugging_software.extend(
+                [
+                    check_program_installed("perf"),
+                    "record",
+                    "--call-graph",
+                    "dwarf",
+                    "-e",
+                    "cache-misses,branch-misses,cpu-cycles,stalled-cycles-frontend,stalled-cycles-backend",
+                ]
+            )
 
         # running with the visualizer?
         if args.visualize:
@@ -1213,24 +1458,31 @@
 
     if run_verbose or args.dry_run:
         exported_variables = "export "
-        for (variable, value) in custom_env.items():
+        for variable, value in custom_env.items():
             if variable == "PATH":
                 value = path_variable + path_sep + libdir
             exported_variables += "%s=%s " % (variable, value)
-        print_and_buffer("cd %s; %s; %s" % (os.path.relpath(ns3_path, working_dir),
-                                            exported_variables,
-                                            " ".join(program_arguments)
-                                            )
-                         )
+        print_and_buffer(
+            "cd %s; %s; %s"
+            % (
+                os.path.relpath(ns3_path, working_dir),
+                exported_variables,
+                " ".join(program_arguments),
+            )
+        )
 
     if not args.dry_run:
         try:
-            subprocess.run(program_arguments, env=proc_env, cwd=working_dir, shell=use_shell, check=True)
+            subprocess.run(
+                program_arguments, env=proc_env, cwd=working_dir, shell=use_shell, check=True
+            )
         except subprocess.CalledProcessError as e:
             # Replace list of arguments with a single string
             e.cmd = " ".join(e.cmd)
             # Replace full path to binary to relative path
-            e.cmd = e.cmd.replace(os.path.abspath(target_to_run), os.path.relpath(target_to_run, ns3_path))
+            e.cmd = e.cmd.replace(
+                os.path.abspath(target_to_run), os.path.relpath(target_to_run, ns3_path)
+            )
             # Print error message and forward the return code
             print(e)
             exit(e.returncode)
@@ -1244,7 +1496,7 @@
 def non_ambiguous_program_target_list(programs: dict) -> list:
     # Assembles a dictionary of all the possible shortcuts a program have
     list_of_shortcuts = {}
-    for (shortcut, possible_programs) in programs.items():
+    for shortcut, possible_programs in programs.items():
         if len(possible_programs) == 1:
             if possible_programs[0] not in list_of_shortcuts:
                 list_of_shortcuts[possible_programs[0]] = [shortcut]
@@ -1294,11 +1546,13 @@
             output += large_item + "\n"
         return output
 
-    print("""Buildable targets:{buildables}\n\nRunnable/Buildable targets:{runnables}
-                  """.format(buildables=list_to_table(sorted(ns3_modules)),
-                             runnables=list_to_table(non_ambiguous_program_target_list(ns3_programs))
-                             )
-          )
+    print(
+        """Buildable targets:{buildables}\n\nRunnable/Buildable targets:{runnables}
+                  """.format(
+            buildables=list_to_table(sorted(ns3_modules)),
+            runnables=list_to_table(non_ambiguous_program_target_list(ns3_programs)),
+        )
+    )
     return
 
 
@@ -1316,16 +1570,26 @@
     if build_version_string is None:
         project_not_configured()
 
-    if build_version_string == '' and shutil.which("git") and os.path.exists(os.path.join(ns3_path, ".git")):
+    if (
+        build_version_string == ""
+        and shutil.which("git")
+        and os.path.exists(append_to_ns3_path(".git"))
+    ):
         try:
-            build_version_string = subprocess.check_output(["git", "describe", "--dirty"], cwd=ns3_path).decode()
-            build_version_string += ("Reconfigure with './ns3 configure --enable-build-version' "
-                                     "to bake the version into the libraries.")
+            build_version_string = subprocess.check_output(
+                ["git", "describe", "--dirty"], cwd=ns3_path
+            ).decode()
+            build_version_string += (
+                "Reconfigure with './ns3 configure --enable-build-version' "
+                "to bake the version into the libraries."
+            )
         except subprocess.CalledProcessError:
             pass
 
-    if build_version_string == '':
-        print("Build version feature disabled. Reconfigure ns-3 with ./ns3 configure --enable-build-version")
+    if build_version_string == "":
+        print(
+            "Build version feature disabled. Reconfigure ns-3 with ./ns3 configure --enable-build-version"
+        )
     else:
         print("ns-3 version: %s" % build_version_string)
 
@@ -1336,18 +1600,18 @@
 # Debugging this with PyCharm is a no no. It refuses to work hanging indefinitely
 def sudo_command(command: list, password: str):
     # Run command and feed the sudo password
-    proc = subprocess.Popen(['sudo', '-S', *command],
-                            stdin=subprocess.PIPE,
-                            stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE
-                            ).communicate(input=password.encode() + b'\n')
+    proc = subprocess.Popen(
+        ["sudo", "-S", *command],
+        stdin=subprocess.PIPE,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+    ).communicate(input=password.encode() + b"\n")
     stdout, stderr = proc[0].decode(), proc[1].decode()
 
     # Clean sudo password after each command
-    subprocess.Popen(["sudo", "-k"],
-                     stdin=subprocess.PIPE,
-                     stdout=subprocess.PIPE,
-                     stderr=subprocess.PIPE).communicate()
+    subprocess.Popen(
+        ["sudo", "-k"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    ).communicate()
 
     # Check if the password is wrong
     if "try again" in stderr:
@@ -1372,9 +1636,11 @@
     if not args.dry_run:
         if password is None:
             from getpass import getpass
+
             password = getpass(prompt="Sudo password:")
 
     import stat
+
     for target in targets_to_sudo:
         # Check if the file was already built
         if not os.path.exists(target):
@@ -1407,7 +1673,9 @@
     # Check if the user is root and refuse to run
     username = os.getenv("USER", "")
     if username == "root":
-        raise Exception("Refusing to run as root. --enable-sudo will request your password when needed")
+        raise Exception(
+            "Refusing to run as root. --enable-sudo will request your password when needed"
+        )
 
 
 def main():
@@ -1422,7 +1690,7 @@
 
     # If pybindgen exists in the parent directory
     # (e.g. ns3-all-in-one), add it to the PYTHONPATH
-    pybindgen_dir = glob.glob(os.path.abspath(os.path.join(ns3_path, "..", "pybindgen*")))
+    pybindgen_dir = glob.glob(os.path.abspath(append_to_ns3_path("..", "pybindgen*")))
     if pybindgen_dir:
         if "PYTHONPATH" not in os.environ:
             os.environ["PYTHONPATH"] = ""
@@ -1457,9 +1725,9 @@
 
     # Installation and uninstallation options become cmake targets
     if args.install:
-        args.build = ['install']
+        args.build = ["install"]
     if args.uninstall:
-        args.build = ['uninstall']
+        args.build = ["uninstall"]
 
     # Get build profile and other settings
     build_info, ns3_modules = check_lock_data(out_dir)
@@ -1471,10 +1739,14 @@
     # Docs subparser options become cmake targets
     if args.docs:
         args.build = [args.docs] if args.docs != "all" else ["sphinx", "doxygen"]
-        if "doxygen" in args.build and (not build_info["ENABLE_EXAMPLES"] or not build_info["ENABLE_TESTS"]):
-            print('The "./ns3 docs doxygen" and "./ns3 docs all" commands,\n'
-                  'requires examples and tests to generate introspected documentation.\n'
-                  'Try "./ns3 docs doxygen-no-build" or enable examples and tests.')
+        if "doxygen" in args.build and (
+            not build_info["ENABLE_EXAMPLES"] or not build_info["ENABLE_TESTS"]
+        ):
+            print(
+                'The "./ns3 docs doxygen" and "./ns3 docs all" commands,\n'
+                "requires examples and tests to generate introspected documentation.\n"
+                'Try "./ns3 docs doxygen-no-build" or enable examples and tests.'
+            )
             exit(1)
 
     if args.show == "profile":
@@ -1503,7 +1775,7 @@
         if len(target_to_run) > 0:
             # While testing a weird case appeared where the target to run is between quotes,
             # so we remove in case they exist
-            if target_to_run[0] in ["\"", "'"] and target_to_run[-1] in ["\"", "'"]:
+            if target_to_run[0] in ['"', "'"] and target_to_run[-1] in ['"', "'"]:
                 target_to_run = target_to_run[1:-1]
             target_to_run = target_to_run.split()
             target_to_run, target_args = target_to_run[0], target_to_run[1:]
@@ -1521,22 +1793,24 @@
 
         # Check for changes in scratch sources and trigger a reconfiguration if sources changed
         if current_cmake_cache_folder:
-            current_scratch_sources = glob.glob(os.path.join(ns3_path, "scratch", "**", "*.cc"),
-                                                recursive=True)
+            current_scratch_sources = glob.glob(
+                append_to_ns3_path("scratch", "**", "*.cc"), recursive=True
+            )
             scratches_file = os.path.join(current_cmake_cache_folder, "ns3scratches")
             if os.path.exists(scratches_file):
                 with open(scratches_file, "r") as f:
-                    previous_scratches_sources = f.read().split('\n')
+                    previous_scratches_sources = f.read().split("\n")
                     if previous_scratches_sources != current_scratch_sources:
                         refresh_cmake(current_cmake_cache_folder, output)
 
         if args.configure:
-            configuration_step(current_cmake_cache_folder,
-                               current_cmake_generator,
-                               args,
-                               output,
-                               args.dry_run,
-                               )
+            configuration_step(
+                current_cmake_cache_folder,
+                current_cmake_generator,
+                args,
+                output,
+                args.dry_run,
+            )
 
         if not project_configured(current_cmake_cache_folder):
             project_not_configured()
@@ -1560,14 +1834,19 @@
         check_config(current_cmake_cache_folder)
         print("---- Summary of buildable targets:")
         print("Buildable targets:          %4.d" % len(ns3_modules))
-        print("Runnable/Buildable targets: %4.d" % len(non_ambiguous_program_target_list(ns3_programs)))
+        print(
+            "Runnable/Buildable targets: %4.d"
+            % len(non_ambiguous_program_target_list(ns3_programs))
+        )
 
         exit(0)
 
     def check_ambiguous_target(target_type, target_to_check, programs):
         if len(programs[target_to_check]) > 1:
-            print('%s target "%s" is ambiguous. Try one of these: "%s"'
-                  % (target_type, target_to_check, '", "'.join(programs[target_to_check])))
+            print(
+                '%s target "%s" is ambiguous. Try one of these: "%s"'
+                % (target_type, target_to_check, '", "'.join(programs[target_to_check]))
+            )
             exit(1)
         return programs[target_to_check][0]
 
@@ -1584,33 +1863,46 @@
     if "build" in args:
         complete_targets = []
         for target in args.build:
-            build_target = check_ambiguous_target("Build", target, ns3_programs) if target in ns3_programs else target
+            build_target = (
+                check_ambiguous_target("Build", target, ns3_programs)
+                if target in ns3_programs
+                else target
+            )
             complete_targets.append(build_target)
         args.build = complete_targets
         del complete_targets
 
     if not run_only:
-        build_step(args,
-                   build_and_run,
-                   target_to_run,
-                   current_cmake_cache_folder,
-                   ns3_modules,
-                   ns3_version,
-                   build_profile,
-                   output
-                   )
+        build_step(
+            args,
+            build_and_run,
+            target_to_run,
+            current_cmake_cache_folder,
+            ns3_modules,
+            ns3_version,
+            build_profile,
+            output,
+        )
 
     if not args.shell and target_to_run and ".py" not in target_to_run:
         if sys.platform == "win32":
             target_to_run += ".exe"
 
         # If we're only trying to run the target, we need to check if it actually exists first
-        if (run_only or build_and_run) and ".py" not in target_to_run and not os.path.exists(target_to_run):
+        if (
+            (run_only or build_and_run)
+            and ".py" not in target_to_run
+            and not os.path.exists(target_to_run)
+        ):
             raise Exception("Executable has not been built yet")
 
     # Setup program as sudo
     if enable_sudo or (args.run and args.enable_sudo):
-        sudo_step(args, target_to_run, set(map(lambda x: x[0], ns3_programs.values())) if enable_sudo else set())
+        sudo_step(
+            args,
+            target_to_run,
+            set(map(lambda x: x[0], ns3_programs.values())) if enable_sudo else set(),
+        )
 
     # Finally, we try to run it
     if args.shell or run_only or build_and_run:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/pyproject.toml ns-3.41/pyproject.toml
--- ns-3.40/pyproject.toml	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/pyproject.toml	2024-02-09 14:44:07.845789700 -0800
@@ -14,3 +14,11 @@
 
 [tool.cibuildwheel]
 build-frontend = "build"
+
+[tool.black]
+line-length = 100
+include = "\\bns3$|\\.py$"
+
+[tool.isort]
+profile = "black"
+line_length = 100
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/README.md ns-3.41/README.md
--- ns-3.40/README.md	2023-09-27 13:30:37.526838800 -0700
+++ ns-3.41/README.md	2024-02-09 14:44:07.721789000 -0800
@@ -4,6 +4,8 @@
 [![Gitlab CI](https://gitlab.com/nsnam/ns-3-dev/badges/master/pipeline.svg)](https://gitlab.com/nsnam/ns-3-dev/-/pipelines)
 [![Github CI](https://github.com/nsnam/ns-3-dev-git/actions/workflows/per_commit.yml/badge.svg)](https://github.com/nsnam/ns-3-dev-git/actions)
 
+[![Latest Release](https://gitlab.com/nsnam/ns-3-dev/-/badges/release.svg)](https://gitlab.com/nsnam/ns-3-dev/-/releases)
+
 ## Table of Contents
 
 * [Overview](#overview-an-open-source-project)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/RELEASE_NOTES.md ns-3.41/RELEASE_NOTES.md
--- ns-3.40/RELEASE_NOTES.md	2023-09-27 13:30:37.526838800 -0700
+++ ns-3.41/RELEASE_NOTES.md	2024-02-09 14:44:07.721789000 -0800
@@ -13,6 +13,60 @@
 and references prefixed by '!' refer to a
 [GitLab.com merge request](https://gitlab.com/nsnam/ns-3-dev/-/merge_requests) number.
 
+Release 3.41
+------------
+
+### Availability
+
+This release is available from:
+<https://www.nsnam.org/release/ns-allinone-3.41.tar.bz2>
+
+### Supported platforms
+
+This release is intended to work on systems with the following minimal
+requirements (Note: not all ns-3 features are available on all systems):
+
+- g++-9 or later, or LLVM/clang++-10 or later
+- Python 3.6 or later
+- CMake 3.13 or later
+- (macOS only) Xcode 11 or later
+- (Windows only) Msys2/MinGW64 toolchain or WSL2
+
+Python API requires [Cppyy](https://cppyy.readthedocs.io/en/latest/installation.html) and works for Linux only.  Specifically, avoid Cppyy version 3; stay with version 2.4.2 for this release.
+
+### New user-visible features
+
+- (antenna) !1337 - `UniformPlanarArray` is extended to support multiple horizontal and vertical antenna ports, and dual-polarized antennas.
+- (core) !1364 - The `MakeEnumAccessor` was changed to support `enum class` types
+- (core) !1802 - Added support for Bernoulli and Binomial random variables (`BernoulliRandomVariable`, `BinomialRandomVariable`)
+- (internet) #1001 - TCP Cubic now supports Reno-friendly operation by default.
+- (internet) !1817 - Resolved inconsistency in behavior regarding the Strong End System Model between IPv4 and IPv6. Attributes `Ipv6L3Protocol::StrongEndSystemModel` and `Ipv4::WeakEsModel` have been aligned to provide a consistent user experience and avoid confusion
+- (lr-wpan) !1686 - Change CapabilityField to standard bitmap
+- (lr-wpan) !1698 - Change SuperframeField to standard bitmap
+- (lr-wpan) !1711 - Add `pCurrentPage` and `pCurrentChannel` attributes to MLME-GET.request primitive
+- (lr-wpan) !1706 - Create MAC layer abstraction (decoupling, alternative MACs) via a new `LrWpanBase` class
+- (lr-wpan) !1794 - Group MAC primitives status enumerations into a single enumeration
+- (spectrum)!1337 - `ThreeGppSpectrumPropagationLossModel` and `ThreeGppChannelModel` are extended to support multi-port and dual-polarized antenna arrays which is a basis for enabling 3GPP MIMO simulations in ns-3.
+- (wifi) - Added support for BlockAck buffer size of up to 1024 MPDUs (EHT STAs only)
+- (wifi) - Aligned default RTS threshold to 802.11-2020
+- (wifi) - Added EHT support for Ideal rate manager
+
+### Bugs fixed
+
+- (lr-wpan) !1673 - Fixes PHY BUSY_RX -> RX_ON operation
+- (lr-wpan) !1769 - `DoDispose` SIGSEGV and beacon fixes
+- (network) !1746 - `PacketSocketClient` not scheduling `Send()` for packet bursts
+- (network) !1793 - Fix string to MAC address parsing logic
+- (tap-bridge) !1540 - Update examples to support modern Wi-Fi standards
+- (tcp) !1788, !1825 - Fix several small issues with `examples/tcp/tcp-bbr-example.cc` and `examples/tcp/tcp-linux-reno.cc`
+- (tcp) !1812 - Several small fixes to TCP retransmission logic were added, based on trace comparison with OMNeT++
+- (tcp) #1026 - TcpRateLinux (delivery rate estimation) had an incorrect update to the rate sample
+- (visualizer) !1730 - Fix label positioning in PyViz
+- (wifi) - Fix agreement not always properly torn down when Block Ack inactivity timeout is elapsed
+- (wifi) - Stop A-MSDU aggregation when an A-MSDU is found in the queue
+- (wifi) - ReportAmpduTxStatus called twice when sending explicit BAR upon missed BlockAck
+- (wifi) - Fix regression causing BlockAckReq frames to be sent with data rates instead of control rates
+
 Release 3.40
 ------------
 
@@ -1264,7 +1318,7 @@
 - Bug 2587 - tcp: Avoid overflow in htcp.cc
 - Bug 2590 - traffic-control: Minor enhancements in red-queue-disc{.h, .cc}
 - Bug 2591 - wifi: 802.11e Block Ack cannot be enabled on HT/VHT stations
-- Bug 2594 - wifi: vht-wifi-network very low throughtput at MCS 6, 160 MHz, SGI
+- Bug 2594 - wifi: vht-wifi-network very low throughput at MCS 6, 160 MHz, SGI
 - Bug 2596 - network: EthernetTrailer::GetFcs() should be const
 - Bug 2601 - wifi: HT stations should use 40 MHz width if configured  80 or 160z
 - Bug 2604 - wifi: QosData frames separation with Block Ack enabled
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/scratch/CMakeLists.txt ns-3.41/scratch/CMakeLists.txt
--- ns-3.40/scratch/CMakeLists.txt	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/scratch/CMakeLists.txt	2024-02-09 14:44:07.845789700 -0800
@@ -14,14 +14,24 @@
     file(READ ${source_file} source_file_contents)
     string(REGEX MATCHALL "main[(| (]" main_position "${source_file_contents}")
     if(CMAKE_MATCH_0)
-      set(scratch_src ${source_file})
+      list(APPEND scratch_src ${source_file})
     endif()
   endforeach()
 
-  if(NOT scratch_src)
-    return()
+  list(LENGTH scratch_src scratch_src_len)
+
+  # If there is no main function, raise an error
+  if(${scratch_src_len} EQUAL 0)
+    message(FATAL_ERROR "The following scratch source files do not contain a main function: ${source_files}")
+  endif()
+
+  # If there are multiple main functions, raise an error
+  if(${scratch_src_len} GREATER 1)
+    message(FATAL_ERROR "The following scratch source files contain ${scratch_src_len} files with a main function: ${scratch_src}")
   endif()
 
+  # If there is a single main function, continue normally
+
   # Get parent directory name
   get_filename_component(scratch_dirname ${scratch_src} DIRECTORY)
   string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" scratch_dirname
@@ -69,10 +79,9 @@
   create_scratch(${scratch_src})
 endforeach()
 
-# Scan *.cc files in ns-3-dev/scratch subdirectories and build a target for each
-# subdirectory
+# Scan ns-3-dev/scratch subdirectories
 file(
-  GLOB_RECURSE scratch_subdirectories
+  GLOB scratch_subdirectories
   CONFIGURE_DEPENDS
   LIST_DIRECTORIES true
   ${CMAKE_CURRENT_SOURCE_DIR}/**
@@ -84,6 +93,7 @@
   endif()
 endforeach()
 
+# Build scratches per directory or following CMakeLists.txt instructions
 foreach(subdir ${scratch_subdirectories})
   if(EXISTS ${subdir}/CMakeLists.txt)
     # If the subdirectory contains a CMakeLists.txt file
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/setup.py ns-3.41/setup.py
--- ns-3.40/setup.py	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/setup.py	2024-02-09 14:44:07.845789700 -0800
@@ -1,14 +1,15 @@
-import cmake_build_extension
-import setuptools
 import sys
 import sysconfig
 
+import cmake_build_extension
+import setuptools
+
 setuptools.setup(
     cmdclass=dict(build_ext=cmake_build_extension.BuildExtension),
-    packages=['ns', 'visualizer'],
+    packages=["ns", "visualizer"],
     package_dir={
-      'ns': './build-support/pip-wheel/ns',
-      'visualizer': './build-support/pip-wheel/visualizer'
+        "ns": "./build-support/pip-wheel/ns",
+        "visualizer": "./build-support/pip-wheel/visualizer",
     },
     ext_modules=[
         cmake_build_extension.CMakeExtension(
@@ -23,13 +24,12 @@
                 "-DNS3_BINDINGS_INSTALL_DIR:STRING=INSTALL_PREFIX",
                 "-DNS3_FETCH_OPTIONAL_COMPONENTS:BOOL=ON",
                 "-DNS3_PIP_PACKAGING:BOOL=ON",
-                "-DNS3_USE_LIB64:BOOL=ON",
                 # Make CMake find python components from the currently running python
                 # https://catherineh.github.io/programming/2021/11/16/python-binary-distributions-whls-with-c17-cmake-auditwheel-and-manylinux
                 f"-DPython3_LIBRARY_DIRS={sysconfig.get_config_var('LIBDIR')}",
                 f"-DPython3_INCLUDE_DIRS={sysconfig.get_config_var('INCLUDEPY')}",
-                f"-DPython3_EXECUTABLE={sys.executable}"
-            ]
+                f"-DPython3_EXECUTABLE={sys.executable}",
+            ],
         ),
     ],
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/doc/source/antenna-design.rst ns-3.41/src/antenna/doc/source/antenna-design.rst
--- ns-3.40/src/antenna/doc/source/antenna-design.rst	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/doc/source/antenna-design.rst	2024-02-09 14:44:07.849789600 -0800
@@ -146,7 +146,7 @@
 
 Derived classes must implement the following functions:
 
-* GetNumberOfElements: returns the number of antenna elements
+* GetNumElems: returns the number of antenna elements
 * GetElementLocation: returns the location of the antenna element with the specified index, normalized with respect to the wavelength
 * GetElementFieldPattern: returns the horizontal and vertical components of the antenna element field pattern at the specified direction. Same polarization (configurable) for all antenna elements of the array is considered.
 
@@ -160,10 +160,10 @@
 
 The class UniformPlanarArray is a generic implementation of Uniform Planar Arrays (UPAs),
 supporting rectangular and linear regular lattices.
-It loosely follows the implementation described in the 3GPP TR 38.901 [38901]_,
-considering only a single a single panel, i.e., :math:`N_{g} = M_{g} = 1`.
+It closely follows the implementation described in the 3GPP TR 38.901 [38901]_,
+considering only a single panel, i.e., :math:`N_{g} = M_{g} = 1`.
 
-By default, the array is orthogonal to the x-axis, pointing towards the positive
+By default, the antenna array is orthogonal to the x-axis, pointing towards the positive
 direction, but the orientation can be changed through the attributes "BearingAngle",
 which adjusts the azimuth angle, and "DowntiltAngle", which adjusts the elevation angle.
 The slant angle is instead fixed and assumed to be 0.
@@ -173,8 +173,24 @@
 and vertical elements can be configured through the attributes "AntennaHorizontalSpacing"
 and "AntennaVerticalSpacing".
 
-The polarization of each antenna element in the array is determined by the polarization
-slant angle through the attribute "PolSlantAngle", as described in [38901]_ (i.e., :math:`{\zeta}`).
+UniformPlannarArray supports the concept of antenna ports following the sub-array partition
+model for TXRU virtualization, as described in Section 5.2.2 of 3GPP TR 36.897 [36897]_.
+The number of antenna ports in vertical and horizontal directions can be configured through
+the attributes "NumVerticalPorts" and "NumHorizontalPorts", respectively. For example,
+if "NumRows" and "NumColumns" are configured to 2 and 4, and the number of
+"NumVerticalPorts" and "NumHorizontalPorts" to 1 and 2, then the antenna elements belonging
+to the first two columns of the antenna array will belong to the first antenna port,
+and the third and the fourth columns will belong to the second antenna port. Note that
+"NumRows" and "NumColumns" must be a multiple of "NumVerticalPorts" and "NumHorizontalPorts",
+respectively.
+
+Whether the antenna is dual-polarized or not is configured through the attribute
+"IsDualPolarized". In case the antenna array is dual polarized, the total number
+of antenna elements is doubled and the two polarizations are overlapped in space.
+The polarization slant angle of the antenna elements belonging to the first polarization
+are configured through the attribute "PolSlantAngle"; while the antenna elements of
+the second polarization have the polarization slant angle minus 90 degrees,
+as described in [38901]_ (i.e., :math:`{\zeta}`).
 
 
 .. [Balanis] C.A. Balanis, "Antenna Theory - Analysis and Design",  Wiley, 2nd Ed.
@@ -193,3 +209,6 @@
 .. [38901] 3GPP. 2018. TR 38.901, Study on channel model for frequencies from 0.5 to 100 GHz, V15.0.0. (2018-06).
 
 .. [Mailloux] Robert J. Mailloux, "Phased Array Antenna Handbook", Artech House, 2nd Ed.
+
+.. [TR36897] 3GPP. 2015. TR 36.897. Study on elevation beamforming / Full-Dimension (FD)
+   Multiple Input Multiple Output (MIMO) for LTE. V13.0.0. (2015-06)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/doc/source/conf.py ns-3.41/src/antenna/doc/source/conf.py
--- ns-3.40/src/antenna/doc/source/conf.py	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/doc/source/conf.py	2024-02-09 14:44:07.849789600 -0800
@@ -11,205 +11,209 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'index'
+master_doc = "index"
 
 # General information about the project.
-project = u'LENA'
-copyright = u'2011-2012, CTTC'
+project = "LENA"
+copyright = "2011-2012, CTTC"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3-dev'
+version = "ns-3-dev"
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3-dev'
+release = "ns-3-dev"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = "default"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-#htmlhelp_basename = 'ns-3doc'
+# htmlhelp_basename = 'ns-3doc'
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('antenna', 'antenna.tex', u'Antenna Module Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
+    (
+        "antenna",
+        "antenna.tex",
+        "Antenna Module Documentation",
+        "Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)",
+        "manual",
+    ),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output --------------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-model-library', u'ns-3 Model Library',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-model-library", "ns-3 Model Library", ["ns-3 project"], 1)]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/model/phased-array-model.cc ns-3.41/src/antenna/model/phased-array-model.cc
--- ns-3.40/src/antenna/model/phased-array-model.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/model/phased-array-model.cc	2024-02-09 14:44:07.849789600 -0800
@@ -34,28 +34,6 @@
 
 NS_OBJECT_ENSURE_REGISTERED(PhasedArrayModel);
 
-std::ostream&
-operator<<(std::ostream& os, const PhasedArrayModel::ComplexVector& cv)
-{
-    size_t N = cv.GetSize();
-
-    // empty
-    if (N == 0)
-    {
-        os << "[]";
-        return os;
-    }
-
-    // non-empty
-    os << "[";
-    for (std::size_t i = 0; i < N - 1; ++i)
-    {
-        os << cv[i] << ", ";
-    }
-    os << cv[N - 1] << "]";
-    return os;
-}
-
 PhasedArrayModel::PhasedArrayModel()
     : m_isBfVectorValid{false}
 {
@@ -85,8 +63,8 @@
 PhasedArrayModel::SetBeamformingVector(const ComplexVector& beamformingVector)
 {
     NS_LOG_FUNCTION(this << beamformingVector);
-    NS_ASSERT_MSG(beamformingVector.GetSize() == GetNumberOfElements(),
-                  beamformingVector.GetSize() << " != " << GetNumberOfElements());
+    NS_ASSERT_MSG(beamformingVector.GetSize() == GetNumElems(),
+                  beamformingVector.GetSize() << " != " << GetNumElems());
     m_beamformingVector = beamformingVector;
     m_isBfVectorValid = true;
 }
@@ -101,15 +79,32 @@
     return m_beamformingVector;
 }
 
+const PhasedArrayModel::ComplexVector&
+PhasedArrayModel::GetBeamformingVectorRef() const
+{
+    NS_LOG_FUNCTION(this);
+    NS_ASSERT_MSG(m_isBfVectorValid,
+                  "The beamforming vector should be Set before it's Get, and should refer to the "
+                  "current array configuration");
+    return m_beamformingVector;
+}
+
 PhasedArrayModel::ComplexVector
 PhasedArrayModel::GetBeamformingVector(Angles a) const
 {
     NS_LOG_FUNCTION(this << a);
 
     ComplexVector beamformingVector = GetSteeringVector(a);
-    double normRes = norm(beamformingVector);
+    // The normalization takes into account the total number of ports as only a
+    // portion (K,L) of beam weights associated with a specific port are non-zero.
+    // See 3GPP Section 5.2.2 36.897. This normalization corresponds to
+    // a sub-array partition model (which is different from the full-connection
+    // model). Note that the total number of ports used to perform normalization
+    // is the ratio between the total number of antenna elements and the
+    // number of antenna elements per port.
+    double normRes = norm(beamformingVector) / sqrt(GetNumPorts());
 
-    for (size_t i = 0; i < GetNumberOfElements(); i++)
+    for (size_t i = 0; i < GetNumElems(); i++)
     {
         beamformingVector[i] = std::conj(beamformingVector[i]) / normRes;
     }
@@ -120,8 +115,8 @@
 PhasedArrayModel::ComplexVector
 PhasedArrayModel::GetSteeringVector(Angles a) const
 {
-    ComplexVector steeringVector(GetNumberOfElements());
-    for (size_t i = 0; i < GetNumberOfElements(); i++)
+    ComplexVector steeringVector(GetNumElems());
+    for (size_t i = 0; i < GetNumElems(); i++)
     {
         Vector loc = GetElementLocation(i);
         double phase = -2 * M_PI *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/model/phased-array-model.h ns-3.41/src/antenna/model/phased-array-model.h
--- ns-3.40/src/antenna/model/phased-array-model.h	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/model/phased-array-model.h	2024-02-09 14:44:07.849789600 -0800
@@ -73,17 +73,7 @@
     }
 
     /**
-     * Returns the horizontal and vertical components of the antenna element field
-     * pattern at the specified direction. Single polarization is considered.
-     * \param a the angle indicating the interested direction
-     * \return a pair in which the first element is the horizontal component
-     *         of the field pattern and the second element is the vertical
-     *         component of the field pattern
-     */
-    virtual std::pair<double, double> GetElementFieldPattern(Angles a) const = 0;
-
-    /**
-     * Returns the location of the antenna element with the specified
+     * \brief Returns the location of the antenna element with the specified
      * index, normalized with respect to the wavelength.
      * \param index the index of the antenna element
      * \return the 3D vector that represents the position of the element
@@ -91,10 +81,128 @@
     virtual Vector GetElementLocation(uint64_t index) const = 0;
 
     /**
-     * Returns the number of antenna elements
+     * \brief Returns the number of antenna elements
      * \return the number of antenna elements
      */
-    virtual size_t GetNumberOfElements() const = 0;
+    virtual size_t GetNumElems() const = 0;
+
+    /**
+     * \brief Returns the horizontal and vertical components of the antenna element field
+     * pattern at the specified direction. Single polarization is considered.
+     * \param a the angle indicating the interested direction
+     * \param polIndex the index of the polarization for which will be retrieved the field
+     * pattern
+     * \return a pair in which the first element is the horizontal component of the field
+     * pattern and the second element is the vertical component of the field pattern
+     */
+    virtual std::pair<double, double> GetElementFieldPattern(Angles a,
+                                                             uint8_t polIndex = 0) const = 0;
+
+    /**
+     * \brief Set the vertical number of ports
+     * \param nPorts the vertical number of ports
+     */
+    virtual void SetNumVerticalPorts(uint16_t nPorts) = 0;
+
+    /**
+     * \brief Set the horizontal number of ports
+     * \param nPorts the horizontal number of ports
+     */
+    virtual void SetNumHorizontalPorts(uint16_t nPorts) = 0;
+
+    /**
+     * \brief Get the vertical number of ports
+     * \return the vertical number of ports
+     */
+    virtual uint16_t GetNumVerticalPorts() const = 0;
+
+    /**
+     * \brief Get the horizontal number of ports
+     * \return the horizontal number of ports
+     */
+    virtual uint16_t GetNumHorizontalPorts() const = 0;
+
+    /**
+     * \brief Get the number of ports
+     * \return the number of ports
+     */
+    virtual uint16_t GetNumPorts() const = 0;
+
+    /**
+     * \brief Get the number of polarizations
+     * \return the number of polarizations
+     */
+    virtual uint8_t GetNumPols() const = 0;
+
+    /**
+     * \brief Get the vertical number of antenna elements per port
+     * \return the vertical number of antenna elements per port
+     */
+    virtual size_t GetVElemsPerPort() const = 0;
+    /**
+     * \brief Get the horizontal number of antenna elements per port
+     * \return the horizontal number of antenna elements per port
+     */
+    virtual size_t GetHElemsPerPort() const = 0;
+
+    /**
+     * \brief Get the number of elements per port
+     * \return the number of elements per port
+     */
+    virtual size_t GetNumElemsPerPort() const = 0;
+
+    /**
+     * \brief Set the number of columns
+     * \param nColumns the number of columns to be set
+     */
+    virtual void SetNumColumns(uint32_t nColumns) = 0;
+
+    /**
+     * \brief Set the number of rows
+     * \param nRows the number of rows to be set
+     */
+    virtual void SetNumRows(uint32_t nRows) = 0;
+
+    /**
+     * \brief Get the number of columns
+     * \return the number of columns in the antenna array
+     */
+    virtual uint32_t GetNumColumns() const = 0;
+
+    /**
+     * \brief Get the number of rows
+     * \return the number of rows in the antenna array
+     */
+    virtual uint32_t GetNumRows() const = 0;
+
+    /**
+     * \brief Get the polarization slant angle
+     * \return the polarization slant angle
+     */
+    virtual double GetPolSlant() const = 0;
+
+    /**
+     * \brief Get the indication whether the antenna array is dual polarized
+     * \return Returns true if the antenna array is dual polarized
+     */
+    virtual bool IsDualPol() const = 0;
+
+    /**
+     * \brief Calculate the index in the antenna array from the port index and the element in the
+     * port
+     * \param portIndex the port index
+     * \param subElementIndex the element index in the port
+     * \return the antenna element index in the antenna array
+     */
+    virtual uint16_t ArrayIndexFromPortIndex(uint16_t portIndex,
+                                             uint16_t subElementIndex) const = 0;
+
+    /**
+     * Returns the index of the polarization to which belongs that antenna element
+     * \param elementIndex the antenna element for which will be returned the polarization index
+     * \return the polarization index
+     */
+    virtual uint8_t GetElemPol(size_t elementIndex) const = 0;
 
     /**
      * Sets the beamforming vector to be used
@@ -109,6 +217,12 @@
     ComplexVector GetBeamformingVector() const;
 
     /**
+     * Returns the const reference of the beamforming vector that is currently being used
+     * \return the const reference of the current beamforming vector
+     */
+    const PhasedArrayModel::ComplexVector& GetBeamformingVectorRef() const;
+
+    /**
      * Returns the beamforming vector that points towards the specified position
      * \param a the beamforming angle
      * \return the beamforming vector
@@ -149,15 +263,6 @@
     uint32_t m_id{0}; //!< the ID of this antenna array instance
 };
 
-/**
- * \brief Stream insertion operator.
- *
- * \param [in] os The reference to the output stream.
- * \param [in] cv A vector of complex values.
- * \returns The reference to the output stream.
- */
-std::ostream& operator<<(std::ostream& os, const PhasedArrayModel::ComplexVector& cv);
-
 } /* namespace ns3 */
 
 #endif /* PHASED_ARRAY_MODEL_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/model/uniform-planar-array.cc ns-3.41/src/antenna/model/uniform-planar-array.cc
--- ns-3.40/src/antenna/model/uniform-planar-array.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/model/uniform-planar-array.cc	2024-02-09 14:44:07.849789600 -0800
@@ -84,8 +84,27 @@
             .AddAttribute("PolSlantAngle",
                           "The polarization slant angle in radians",
                           DoubleValue(0.0),
-                          MakeDoubleAccessor(&UniformPlanarArray::SetPolSlant),
-                          MakeDoubleChecker<double>(-M_PI, M_PI));
+                          MakeDoubleAccessor(&UniformPlanarArray::SetPolSlant,
+                                             &UniformPlanarArray::GetPolSlant),
+                          MakeDoubleChecker<double>(-M_PI, M_PI))
+            .AddAttribute("NumVerticalPorts",
+                          "Vertical number of ports",
+                          UintegerValue(1),
+                          MakeUintegerAccessor(&UniformPlanarArray::GetNumVerticalPorts,
+                                               &UniformPlanarArray::SetNumVerticalPorts),
+                          MakeUintegerChecker<uint32_t>())
+            .AddAttribute("NumHorizontalPorts",
+                          "Horizontal number of ports",
+                          UintegerValue(1),
+                          MakeUintegerAccessor(&UniformPlanarArray::GetNumHorizontalPorts,
+                                               &UniformPlanarArray::SetNumHorizontalPorts),
+                          MakeUintegerChecker<uint32_t>())
+            .AddAttribute("IsDualPolarized",
+                          "If true, dual polarized antenna",
+                          BooleanValue(false),
+                          MakeBooleanAccessor(&UniformPlanarArray::SetDualPol,
+                                              &UniformPlanarArray::IsDualPol),
+                          MakeBooleanChecker());
     return tid;
 }
 
@@ -143,8 +162,8 @@
 UniformPlanarArray::SetPolSlant(double polSlant)
 {
     m_polSlant = polSlant;
-    m_cosPolSlant = cos(m_polSlant);
-    m_sinPolSlant = sin(m_polSlant);
+    m_cosPolSlant[0] = cos(m_polSlant);
+    m_sinPolSlant[0] = sin(m_polSlant);
 }
 
 void
@@ -186,9 +205,10 @@
 }
 
 std::pair<double, double>
-UniformPlanarArray::GetElementFieldPattern(Angles a) const
+UniformPlanarArray::GetElementFieldPattern(Angles a, uint8_t polIndex) const
 {
     NS_LOG_FUNCTION(this << a);
+    NS_ASSERT_MSG(polIndex < GetNumPols(), "Polarization index can be 0 or 1.");
 
     // convert the theta and phi angles from GCS to LCS using eq. 7.1-7 and 7.1-8 in 3GPP TR 38.901
     // NOTE we assume a fixed slant angle of 0 degrees
@@ -208,8 +228,10 @@
     // NOTE: the slant angle (assumed to be 0) differs from the polarization slant angle
     // (m_polSlant, given by the attribute), in 3GPP TR 38.901
     double aPrimeDb = m_antennaElement->GetGainDb(aPrime);
-    double fieldThetaPrime = pow(10, aPrimeDb / 20) * m_cosPolSlant; // convert to linear magnitude
-    double fieldPhiPrime = pow(10, aPrimeDb / 20) * m_sinPolSlant;   // convert to linear magnitude
+    double fieldThetaPrime =
+        pow(10, aPrimeDb / 20) * m_cosPolSlant[polIndex]; // convert to linear magnitude
+    double fieldPhiPrime =
+        pow(10, aPrimeDb / 20) * m_sinPolSlant[polIndex]; // convert to linear magnitude
 
     // compute psi using eq. 7.1-15 in 3GPP TR 38.901, assuming that the slant
     // angle (gamma) is 0
@@ -232,13 +254,19 @@
 UniformPlanarArray::GetElementLocation(uint64_t index) const
 {
     NS_LOG_FUNCTION(this << index);
-
+    uint64_t tmpIndex = index;
+    // for dual polarization, the top half corresponds to one polarization and
+    // lower half corresponds to the other polarization
+    if (m_isDualPolarized && tmpIndex >= m_numRows * m_numColumns)
+    {
+        tmpIndex -= m_numRows * m_numColumns;
+    }
     // compute the element coordinates in the LCS
     // assume the left bottom corner is (0,0,0), and the rectangular antenna array is on the y-z
     // plane.
     double xPrime = 0;
-    double yPrime = m_disH * (index % m_numColumns);
-    double zPrime = m_disV * floor(index / m_numColumns);
+    double yPrime = m_disH * (tmpIndex % m_numColumns);
+    double zPrime = m_disV * floor(tmpIndex / m_numColumns);
 
     // convert the coordinates to the GCS using the rotation matrix 7.1-4 in 3GPP
     // TR 38.901
@@ -249,10 +277,129 @@
     return loc;
 }
 
+uint8_t
+UniformPlanarArray::GetNumPols() const
+{
+    return m_isDualPolarized ? 2 : 1;
+}
+
 size_t
-UniformPlanarArray::GetNumberOfElements() const
+UniformPlanarArray::GetNumElems() const
+{
+    // From 38.901  [M, N, P, Mg, Ng] = [m_numRows, m_numColumns, 2, 1, 1]
+    return GetNumPols() * m_numRows * m_numColumns;
+    // with dual polarization, the number of antenna elements double up
+}
+
+void
+UniformPlanarArray::SetNumVerticalPorts(uint16_t nPorts)
+{
+    NS_LOG_FUNCTION(this);
+    NS_ASSERT_MSG(nPorts > 0, "Ports should be greater than 0");
+    NS_ASSERT_MSG(((m_numRows % nPorts) == 0),
+                  "The number of vertical ports must divide number of rows");
+    m_numVPorts = nPorts;
+}
+
+void
+UniformPlanarArray::SetNumHorizontalPorts(uint16_t nPorts)
+{
+    NS_ASSERT_MSG(nPorts > 0, "Ports should be greater than 0");
+    NS_ASSERT_MSG(((m_numColumns % nPorts) == 0),
+                  "The number of horizontal ports must divide number of columns");
+    m_numHPorts = nPorts;
+}
+
+uint16_t
+UniformPlanarArray::GetNumVerticalPorts() const
+{
+    return m_numVPorts;
+}
+
+uint16_t
+UniformPlanarArray::GetNumHorizontalPorts() const
+{
+    return m_numHPorts;
+}
+
+uint16_t
+UniformPlanarArray::GetNumPorts() const
+{
+    return GetNumPols() * m_numVPorts * m_numHPorts;
+}
+
+size_t
+UniformPlanarArray::GetVElemsPerPort() const
+{
+    return m_numRows / m_numVPorts;
+}
+
+size_t
+UniformPlanarArray::GetHElemsPerPort() const
+{
+    return m_numColumns / m_numHPorts;
+}
+
+size_t
+UniformPlanarArray::GetNumElemsPerPort() const
+{
+    // Multiply the number of rows and number of columns belonging to one antenna port.
+    // This also holds for dual polarization, where each polarization belongs to a separate port.
+    return GetVElemsPerPort() * GetHElemsPerPort();
+}
+
+uint16_t
+UniformPlanarArray::ArrayIndexFromPortIndex(uint16_t portIndex, uint16_t subElementIndex) const
+{
+    NS_ASSERT_MSG(portIndex < GetNumPorts(), "Port should be less than total Ports");
+    NS_ASSERT(subElementIndex < (GetHElemsPerPort() * GetVElemsPerPort()));
+
+    // In case the array is dual-polarized, change to the index that belongs to the first
+    // polarization
+    auto firstPolPortIdx = portIndex;
+    auto polarizationOffset = 0;
+    auto arraySize = GetNumHorizontalPorts() * GetNumVerticalPorts();
+    if (firstPolPortIdx >= arraySize)
+    {
+        firstPolPortIdx = portIndex - arraySize;
+        polarizationOffset = GetNumColumns() * GetNumRows();
+    }
+    // column-major indexing
+    auto hPortIdx = firstPolPortIdx / GetNumVerticalPorts();
+    auto vPortIdx = firstPolPortIdx % GetNumVerticalPorts();
+    auto hElemIdx = (hPortIdx * GetHElemsPerPort()) + (subElementIndex % GetHElemsPerPort());
+    auto vElemIdx = (vPortIdx * GetVElemsPerPort()) + (subElementIndex / GetHElemsPerPort());
+    return (vElemIdx * GetNumColumns() + hElemIdx + polarizationOffset);
+}
+
+bool
+UniformPlanarArray::IsDualPol() const
+{
+    return m_isDualPolarized;
+}
+
+void
+UniformPlanarArray::SetDualPol(bool isDualPol)
+{
+    m_isDualPolarized = isDualPol;
+    if (isDualPol)
+    {
+        m_cosPolSlant[1] = cos(m_polSlant - M_PI / 2);
+        m_sinPolSlant[1] = sin(m_polSlant - M_PI / 2);
+    }
+}
+
+double
+UniformPlanarArray::GetPolSlant() const
+{
+    return m_polSlant;
+}
+
+uint8_t
+UniformPlanarArray::GetElemPol(size_t elemIndex) const
 {
-    return m_numRows * m_numColumns;
+    NS_ASSERT(elemIndex < GetNumElems());
+    return (elemIndex < GetNumRows() * GetNumColumns()) ? 0 : 1;
 }
 
 } /* namespace ns3 */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/model/uniform-planar-array.h ns-3.41/src/antenna/model/uniform-planar-array.h
--- ns-3.40/src/antenna/model/uniform-planar-array.h	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/model/uniform-planar-array.h	2024-02-09 14:44:07.849789600 -0800
@@ -31,7 +31,7 @@
  * \brief Class implementing Uniform Planar Array (UPA) model.
  *
  * \note the current implementation supports the modeling of antenna arrays
- * composed of a single panel and with single (configured) polarization.
+ * composed of a single panel and with single or dual polarization.
  */
 class UniformPlanarArray : public PhasedArrayModel
 {
@@ -54,13 +54,15 @@
 
     /**
      * Returns the horizontal and vertical components of the antenna element field
-     * pattern at the specified direction. Single polarization is considered.
+     * pattern at the specified direction and for the specified polarization.
      * \param a the angle indicating the interested direction
+     * \param polIndex the index of the polarization for which will be retrieved the field
+     * pattern
      * \return a pair in which the first element is the horizontal component
      *         of the field pattern and the second element is the vertical
      *         component of the field pattern
      */
-    std::pair<double, double> GetElementFieldPattern(Angles a) const override;
+    std::pair<double, double> GetElementFieldPattern(Angles a, uint8_t polIndex = 0) const override;
 
     /**
      * Returns the location of the antenna element with the specified
@@ -72,32 +74,55 @@
      * |  3 4 5
      * |  0 1 2
      * ----------> y
+     * In case of dual-polarized antennas, the antennas of the first and the second polarization
+     * are overlapped in space.
      *
      * \param index index of the antenna element
-     * \return the 3D vector that represents the position of the element
+     * \return the 3D vector that
+     * represents the position of the element
      */
     Vector GetElementLocation(uint64_t index) const override;
 
     /**
-     * Returns the number of antenna elements
+     *  Check if an antenna array contains dual-polarized elements
+     *
+     * \return true if antenna has two polarization otherwise false
+     */
+    bool IsDualPol() const override;
+
+    /**
+     *  Returns polarization angle of first polarization
+     *  \return polarization angle in radians
+     */
+    double GetPolSlant() const override;
+
+    /**
+     * Returns the number of polarizations, 2 in the case that
+     * the antenna is dual-polarized, otherwise 1
+     * \return
+     */
+    uint8_t GetNumPols() const override;
+
+    /**
+     * Returns the number of total antenna elements. Note that if the antenna
+     * is dual-polarized the number of total antenna elements is doubled.
      * \return the number of antenna elements
      */
-    size_t GetNumberOfElements() const override;
+    size_t GetNumElems() const override;
 
-  private:
     /**
      * Set the number of columns of the phased array
      * This method resets the stored beamforming vector to a ComplexVector
      * of the correct size, but zero-filled
      * \param n the number of columns
      */
-    void SetNumColumns(uint32_t n);
+    void SetNumColumns(uint32_t n) override;
 
     /**
      * Get the number of columns of the phased array
      * \return the number of columns
      */
-    uint32_t GetNumColumns() const;
+    uint32_t GetNumColumns() const override;
 
     /**
      * Set the number of rows of the phased array
@@ -105,13 +130,69 @@
      * of the correct size, but zero-filled
      * \param n the number of rows
      */
-    void SetNumRows(uint32_t n);
+    void SetNumRows(uint32_t n) override;
 
     /**
      * Get the number of rows of the phased array
      * \return the number of rows
      */
-    uint32_t GetNumRows() const;
+    uint32_t GetNumRows() const override;
+
+    /**
+     * Set the number of vertical antenna ports
+     * \param nPorts The number of vertical ports to be configured
+     */
+    void SetNumVerticalPorts(uint16_t nPorts) override;
+
+    /**
+     * Set the number of horizontal antenna ports
+     * \param nPorts
+     */
+    void SetNumHorizontalPorts(uint16_t nPorts) override;
+
+    /**
+     * Get the number of vertical antenna ports
+     * \return the number of vertical antenna ports
+     */
+    uint16_t GetNumVerticalPorts() const override;
+
+    /**
+     * Get the number of horizontal antenna ports
+     * \return the number of horizontal antenna ports
+     */
+    uint16_t GetNumHorizontalPorts() const override;
+
+    /**
+     * Get the total number of antenna ports
+     * \return the number of antenna ports
+     */
+    uint16_t GetNumPorts() const override;
+
+    /**
+     * Get the number of vertical elements belonging to each port
+     * \return the number of vertical elements belonging of each port
+     */
+    size_t GetVElemsPerPort() const override;
+
+    /**
+     * Get the number of horizontal elements belonging to each port
+     * \return the number of horizontal elements belonging to each port
+     */
+    size_t GetHElemsPerPort() const override;
+
+    /**
+     * Get the total number of elements belonging to each port
+     * \return the number of elements per port
+     */
+    size_t GetNumElemsPerPort() const override;
+
+    /**
+     * Maps element within a port to an index of element within the antenna array
+     * \param portIndex the port index
+     * \param subElementIndex the element index within the port
+     * \return the element index in the antenna array
+     */
+    uint16_t ArrayIndexFromPortIndex(uint16_t portIndex, uint16_t subElementIndex) const override;
 
     /**
      * \brief Set the bearing angle
@@ -165,19 +246,37 @@
      */
     double GetAntennaVerticalSpacing() const;
 
+    /**
+     * Set the polarization
+     * \param isDualPol whether to set the antenna array to be dual-polarized,
+     * if true, antenna will be dual-polarized
+     */
+    void SetDualPol(bool isDualPol);
+
+    /**
+     * Returns the index of polarization to which belongs the antenna element with a specific index
+     * \param elemIndex the antenna element index
+     * \return the polarization index
+     */
+    uint8_t GetElemPol(size_t elemIndex) const override;
+
+  private:
     uint32_t m_numColumns{1}; //!< number of columns
     uint32_t m_numRows{1};    //!< number of rows
     double m_disV{0.5}; //!< antenna spacing in the vertical direction in multiples of wave length
     double m_disH{0.5}; //!< antenna spacing in the horizontal direction in multiples of wave length
-    double m_alpha{0.0};       //!< the bearing angle in radians
-    double m_cosAlpha{1.0};    //!< the cosine of alpha
-    double m_sinAlpha{0.0};    //!< the sine of alpha
-    double m_beta{0.0};        //!< the downtilt angle in radians
-    double m_cosBeta{1.0};     //!< the cosine of Beta
-    double m_sinBeta{0.0};     //!< the sine of Beta
-    double m_polSlant{0.0};    //!< the polarization slant angle in radians
-    double m_cosPolSlant{1.0}; //!< the cosine of polarization slant angle
-    double m_sinPolSlant{0.0}; //!< the sine polarization slant angle
+    double m_alpha{0.0};                          //!< the bearing angle in radians
+    double m_cosAlpha{1.0};                       //!< the cosine of alpha
+    double m_sinAlpha{0.0};                       //!< the sine of alpha
+    double m_beta{0.0};                           //!< the downtilt angle in radians
+    double m_cosBeta{1.0};                        //!< the cosine of Beta
+    double m_sinBeta{0.0};                        //!< the sine of Beta
+    double m_polSlant{0.0};                       //!< the polarization slant angle in radians
+    bool m_isDualPolarized{false};                //!< if true antenna elements are dual-polarized
+    uint16_t m_numVPorts{1};                      //!< Number of vertical ports
+    uint16_t m_numHPorts{1};                      //!< Number of horizontal ports
+    std::vector<double> m_cosPolSlant{1.0, 0.0};  //!< the cosine of polarization slant angle
+    std::vector<double> m_sinPolSlant{0.0, -1.0}; //!< the sine polarization slant angle
 };
 
 } /* namespace ns3 */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/test/test-cosine-antenna.cc ns-3.41/src/antenna/test/test-cosine-antenna.cc
--- ns-3.40/src/antenna/test/test-cosine-antenna.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/test/test-cosine-antenna.cc	2024-02-09 14:44:07.849789600 -0800
@@ -721,7 +721,7 @@
                                                -7,
                                                EQUAL),
                 TestCase::QUICK);
-};
+}
 
 /// Static variable for test initialization
 static CosineAntennaModelTestSuite g_staticCosineAntennaModelTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/test/test-degrees-radians.cc ns-3.41/src/antenna/test/test-degrees-radians.cc
--- ns-3.40/src/antenna/test/test-degrees-radians.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/test/test-degrees-radians.cc	2024-02-09 14:44:07.849789600 -0800
@@ -155,7 +155,7 @@
     AddTestCase(new RadiansToDegreesTestCase(M_PI + M_PI, 360), TestCase::QUICK);
     AddTestCase(new RadiansToDegreesTestCase(-M_PI_2, -90), TestCase::QUICK);
     AddTestCase(new RadiansToDegreesTestCase(4.5 * M_PI, 810), TestCase::QUICK);
-};
+}
 
 /// Static variable for test initialization
 static DegreesRadiansTestSuite g_staticDegreesRadiansTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/test/test-isotropic-antenna.cc ns-3.41/src/antenna/test/test-isotropic-antenna.cc
--- ns-3.40/src/antenna/test/test-isotropic-antenna.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/test/test-isotropic-antenna.cc	2024-02-09 14:44:07.849789600 -0800
@@ -105,7 +105,7 @@
     AddTestCase(new IsotropicAntennaModelTestCase(Angles(M_PI_2, 0), 0.0), TestCase::QUICK);
     AddTestCase(new IsotropicAntennaModelTestCase(Angles(M_PI_2, M_PI), 0.0), TestCase::QUICK);
     AddTestCase(new IsotropicAntennaModelTestCase(Angles(M_PI_2, M_PI_2), 0.0), TestCase::QUICK);
-};
+}
 
 /// Static variable for test initialization
 static IsotropicAntennaModelTestSuite g_staticIsotropicAntennaModelTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/test/test-parabolic-antenna.cc ns-3.41/src/antenna/test/test-parabolic-antenna.cc
--- ns-3.40/src/antenna/test/test-parabolic-antenna.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/test/test-parabolic-antenna.cc	2024-02-09 14:44:07.849789600 -0800
@@ -589,7 +589,7 @@
                                           -3,
                                           EQUAL),
         TestCase::QUICK);
-};
+}
 
 /// Static variable for test initialization
 static ParabolicAntennaModelTestSuite g_staticParabolicAntennaModelTestSuiteInstance;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/antenna/test/test-uniform-planar-array.cc ns-3.41/src/antenna/test/test-uniform-planar-array.cc
--- ns-3.40/src/antenna/test/test-uniform-planar-array.cc	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/antenna/test/test-uniform-planar-array.cc	2024-02-09 14:44:07.849789600 -0800
@@ -153,11 +153,9 @@
 {
     // compute gain
     PhasedArrayModel::ComplexVector sv = a->GetSteeringVector(m_direction);
-    NS_TEST_EXPECT_MSG_EQ(sv.GetSize(), a->GetNumberOfElements(), "steering vector of wrong size");
+    NS_TEST_EXPECT_MSG_EQ(sv.GetSize(), a->GetNumElems(), "steering vector of wrong size");
     PhasedArrayModel::ComplexVector bf = a->GetBeamformingVector(m_direction);
-    NS_TEST_EXPECT_MSG_EQ(bf.GetSize(),
-                          a->GetNumberOfElements(),
-                          "beamforming vector of wrong size");
+    NS_TEST_EXPECT_MSG_EQ(bf.GetSize(), a->GetNumElems(), "beamforming vector of wrong size");
     std::pair<double, double> fp = a->GetElementFieldPattern(m_direction);
 
     // scalar product dot (sv, bf)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/aodv/CMakeLists.txt ns-3.41/src/aodv/CMakeLists.txt
--- ns-3.40/src/aodv/CMakeLists.txt	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/aodv/CMakeLists.txt	2024-02-09 14:44:07.849789600 -0800
@@ -18,8 +18,10 @@
     model/aodv-routing-protocol.h
     model/aodv-rqueue.h
     model/aodv-rtable.h
-  LIBRARIES_TO_LINK ${libinternet}
-                    ${libwifi}
+  LIBRARIES_TO_LINK
+    ${libapplications}
+    ${libinternet-apps}
+    ${libwifi}
   TEST_SOURCES
     test/aodv-id-cache-test-suite.cc
     test/aodv-regression.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/aodv/model/aodv-packet.h ns-3.41/src/aodv/model/aodv-packet.h
--- ns-3.40/src/aodv/model/aodv-packet.h	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/aodv/model/aodv-packet.h	2024-02-09 14:44:07.849789600 -0800
@@ -485,7 +485,7 @@
      */
     void SetPrefixSize(uint8_t sz);
     /**
-     * \brief Set the pefix size
+     * \brief Set the prefix size
      * \return the prefix size
      */
     uint8_t GetPrefixSize() const;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/aodv/model/aodv-routing-protocol.cc ns-3.41/src/aodv/model/aodv-routing-protocol.cc
--- ns-3.40/src/aodv/model/aodv-routing-protocol.cc	2023-09-27 13:30:37.614839000 -0700
+++ ns-3.41/src/aodv/model/aodv-routing-protocol.cc	2024-02-09 14:44:07.853789800 -0800
@@ -1379,7 +1379,7 @@
             /*iface=*/m_ipv4->GetAddress(m_ipv4->GetInterfaceForAddress(receiver), 0),
             /*hops=*/hop,
             /*nextHop=*/src,
-            /*lifetime=*/Time((2 * m_netTraversalTime - 2 * hop * m_nodeTraversalTime)));
+            /*lifetime=*/Time(2 * m_netTraversalTime - 2 * hop * m_nodeTraversalTime));
         m_routingTable.AddRoute(newEntry);
     }
     else
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/aodv/model/aodv-rqueue.cc ns-3.41/src/aodv/model/aodv-rqueue.cc
--- ns-3.40/src/aodv/model/aodv-rqueue.cc	2023-09-27 13:30:37.614839000 -0700
+++ ns-3.41/src/aodv/model/aodv-rqueue.cc	2024-02-09 14:44:07.853789800 -0800
@@ -121,14 +121,13 @@
  */
 struct IsExpired
 {
-    bool
     /**
      * Check if the entry is expired
      *
      * \param e QueueEntry entry
      * \return true if expired, false otherwise
      */
-    operator()(const QueueEntry& e) const
+    bool operator()(const QueueEntry& e) const
     {
         return (e.GetExpireTime() < Seconds(0));
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/aodv/test/bug-772.cc ns-3.41/src/aodv/test/bug-772.cc
--- ns-3.40/src/aodv/test/bug-772.cc	2023-09-27 13:30:37.614839000 -0700
+++ ns-3.41/src/aodv/test/bug-772.cc	2024-02-09 14:44:07.853789800 -0800
@@ -37,7 +37,6 @@
 #include "ns3/simulator.h"
 #include "ns3/string.h"
 #include "ns3/uinteger.h"
-#include "ns3/v4ping-helper.h"
 #include "ns3/yans-wifi-helper.h"
 
 #include <sstream>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/aodv/test/loopback.cc ns-3.41/src/aodv/test/loopback.cc
--- ns-3.40/src/aodv/test/loopback.cc	2023-09-27 13:30:37.614839000 -0700
+++ ns-3.41/src/aodv/test/loopback.cc	2024-02-09 14:44:07.853789800 -0800
@@ -36,7 +36,6 @@
 #include "ns3/udp-echo-helper.h"
 #include "ns3/udp-socket-factory.h"
 #include "ns3/uinteger.h"
-#include "ns3/v4ping.h"
 #include "ns3/yans-wifi-helper.h"
 
 #include <sstream>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/applications/CMakeLists.txt ns-3.41/src/applications/CMakeLists.txt
--- ns-3.40/src/applications/CMakeLists.txt	2023-09-27 13:30:37.614839000 -0700
+++ ns-3.41/src/applications/CMakeLists.txt	2024-02-09 14:44:07.853789800 -0800
@@ -49,7 +49,6 @@
     model/udp-server.h
     model/udp-trace-client.h
   LIBRARIES_TO_LINK ${libinternet}
-                    ${libstats}
   TEST_SOURCES
     test/three-gpp-http-client-server-test.cc
     test/bulk-send-application-test-suite.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/applications/helper/bulk-send-helper.h ns-3.41/src/applications/helper/bulk-send-helper.h
--- ns-3.40/src/applications/helper/bulk-send-helper.h	2023-09-27 13:30:37.614839000 -0700
+++ ns-3.41/src/applications/helper/bulk-send-helper.h	2024-02-09 14:44:07.853789800 -0800
@@ -49,7 +49,7 @@
      * \param protocol the name of the protocol to use to send traffic
      *        by the applications. This string identifies the socket
      *        factory type used to create sockets for the applications.
-     *        A typical value would be ns3::UdpSocketFactory.
+     *        A typical value would be ns3::TcpSocketFactory.
      * \param address the address of the remote node to send traffic
      *        to.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/applications/model/udp-trace-client.h ns-3.41/src/applications/model/udp-trace-client.h
--- ns-3.40/src/applications/model/udp-trace-client.h	2023-09-27 13:30:37.614839000 -0700
+++ ns-3.41/src/applications/model/udp-trace-client.h	2024-02-09 14:44:07.857789800 -0800
@@ -41,7 +41,7 @@
  *
  * Sends UDP packets based on a trace file of a MPEG4 stream.
  * Trace files can be downloaded from:
- * http://trace.eas.asu.edu/mpeg4/index.html
+ * https://web.archive.org/web/20210113211420/http://trace.eas.asu.edu/mpeg4/index.html
  * (the 2 first lines of the file should be removed) A valid trace file is a file with 4 columns:
  * \li -1- the first one represents the frame index
  * \li -2- the second one indicates the type of the frame: I, P or B
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/applications/test/udp-client-server-test.cc ns-3.41/src/applications/test/udp-client-server-test.cc
--- ns-3.40/src/applications/test/udp-client-server-test.cc	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/applications/test/udp-client-server-test.cc	2024-02-09 14:44:07.857789800 -0800
@@ -31,6 +31,7 @@
 #include "ns3/test.h"
 #include "ns3/udp-client-server-helper.h"
 #include "ns3/udp-echo-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/uinteger.h"
 
 #include <fstream>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/bridge/CMakeLists.txt ns-3.41/src/bridge/CMakeLists.txt
--- ns-3.40/src/bridge/CMakeLists.txt	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/bridge/CMakeLists.txt	2024-02-09 14:44:07.857789800 -0800
@@ -9,5 +9,4 @@
     model/bridge-channel.h
     model/bridge-net-device.h
   LIBRARIES_TO_LINK ${libnetwork}
-  TEST_SOURCES ${test_sources}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/bridge/examples/csma-bridge.py ns-3.41/src/bridge/examples/csma-bridge.py
--- ns-3.40/src/bridge/examples/csma-bridge.py	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/bridge/examples/csma-bridge.py	2024-02-09 14:44:07.857789800 -0800
@@ -1,17 +1,17 @@
-# /*
-#  * This program is free software; you can redistribute it and/or modify
-#  * it under the terms of the GNU General Public License version 2 as
-#  * published by the Free Software Foundation
-#  *
-#  * 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; if not, write to the Free Software
-#  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#  */
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation
+#
+# 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; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
 
 # Network topology
 #
@@ -32,12 +32,18 @@
 #  \ingroup bridge
 #  Bridge example connecting two broadcast domains.
 
-
-from ns import ns
+## Import ns-3
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 
 def main(argv):
-
     #
     # Allow the user to override any of the defaults and the above Bind() at
     # run-time, via command-line arguments
@@ -48,14 +54,14 @@
     #
     # Explicitly create the nodes required by the topology(shown above).
     #
-    #print "Create nodes."
+    # print "Create nodes."
     terminals = ns.network.NodeContainer()
     terminals.Create(4)
 
     csmaSwitch = ns.network.NodeContainer()
     csmaSwitch.Create(1)
 
-    #print "Build Topology"
+    # print "Build Topology"
     csma = ns.csma.CsmaHelper()
     csma.SetChannelAttribute("DataRate", ns.network.DataRateValue(ns.network.DataRate(5000000)))
     csma.SetChannelAttribute("Delay", ns.core.TimeValue(ns.core.MilliSeconds(2)))
@@ -66,7 +72,9 @@
     switchDevices = ns.network.NetDeviceContainer()
 
     for i in range(4):
-        link = csma.Install(ns.network.NodeContainer(ns.network.NodeContainer(terminals.Get(i)), csmaSwitch))
+        link = csma.Install(
+            ns.network.NodeContainer(ns.network.NodeContainer(terminals.Get(i)), csmaSwitch)
+        )
         terminalDevices.Add(link.Get(0))
         switchDevices.Add(link.Get(1))
 
@@ -84,7 +92,7 @@
 
     # We've got the "hardware" in place.  Now we need to add IP addresses.
     #
-    #print "Assign IP Addresses."
+    # print "Assign IP Addresses."
     ipv4 = ns.internet.Ipv4AddressHelper()
     ipv4.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
     ipv4.Assign(terminalDevices)
@@ -92,12 +100,12 @@
     #
     # Create an OnOff application to send UDP datagrams from node zero to node 1.
     #
-    #print "Create Applications."
-    port = 9   # Discard port(RFC 863)
+    # print "Create Applications."
+    port = 9  # Discard port(RFC 863)
 
     inet_sock_address = ns.network.InetSocketAddress(ns.network.Ipv4Address("10.1.1.2"), port)
     onoff = ns.applications.OnOffHelper("ns3::UdpSocketFactory", inet_sock_address.ConvertTo())
-    onoff.SetConstantRate (ns.network.DataRate ("500kb/s"))
+    onoff.SetConstantRate(ns.network.DataRate("500kb/s"))
 
     app = onoff.Install(ns.network.NodeContainer(terminals.Get(0)))
     # Start the application
@@ -114,8 +122,7 @@
     # Create a similar flow from n3 to n0, starting at time 1.1 seconds
     #
     inet_address = ns.network.InetSocketAddress(ns.network.Ipv4Address("10.1.1.1"), port)
-    onoff.SetAttribute("Remote",
-                       ns.network.AddressValue(inet_address.ConvertTo()))
+    onoff.SetAttribute("Remote", ns.network.AddressValue(inet_address.ConvertTo()))
     app = onoff.Install(ns.network.NodeContainer(terminals.Get(3)))
     app.Start(ns.core.Seconds(1.1))
     app.Stop(ns.core.Seconds(10.0))
@@ -127,9 +134,9 @@
     # Configure tracing of all enqueue, dequeue, and NetDevice receive events.
     # Trace output will be sent to the file "csma-bridge.tr"
     #
-    #print "Configure Tracing."
-    #ascii = ns.network.AsciiTraceHelper();
-    #csma.EnableAsciiAll(ascii.CreateFileStream ("csma-bridge.tr"));
+    # print "Configure Tracing."
+    # ascii = ns.network.AsciiTraceHelper();
+    # csma.EnableAsciiAll(ascii.CreateFileStream ("csma-bridge.tr"));
 
     #
     # Also configure some tcpdump traces; each interface will be traced.
@@ -143,14 +150,13 @@
     #
     # Now, do the actual simulation.
     #
-    #print "Run Simulation."
+    # print "Run Simulation."
     ns.core.Simulator.Run()
     ns.core.Simulator.Destroy()
-    #print "Done."
-
+    # print "Done."
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     import sys
-    main(sys.argv)
 
+    main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/brite/CMakeLists.txt ns-3.41/src/brite/CMakeLists.txt
--- ns-3.40/src/brite/CMakeLists.txt	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/brite/CMakeLists.txt	2024-02-09 14:44:07.861789700 -0800
@@ -14,6 +14,7 @@
   HEADER_NAME Brite.h
   LIBRARY_NAME brite
   SEARCH_PATHS ${NS3_WITH_BRITE}
+  OUTPUT_VARIABLE "NS3_BRITE_REASON"
 )
 
 if((NOT
@@ -49,9 +50,7 @@
   SOURCE_FILES helper/brite-topology-helper.cc
   HEADER_FILES helper/brite-topology-helper.h
   LIBRARIES_TO_LINK
-    ${libnetwork}
-    ${libcore}
-    ${libinternet}
+    ${libapplications}
     ${libpoint-to-point}
     ${brite_LIBRARIES}
   TEST_SOURCES test/brite-test-topology.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/brite/examples/brite-generic-example.py ns-3.41/src/brite/examples/brite-generic-example.py
--- ns-3.40/src/brite/examples/brite-generic-example.py	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/brite/examples/brite-generic-example.py	2024-02-09 14:44:07.861789700 -0800
@@ -18,7 +18,14 @@
 # Modified by: Gabriel Ferreira <gabrielcarvfer@gmail.com>
 #
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 ns.LogComponentEnable("BriteTopologyHelper", ns.LOG_LEVEL_ALL)
 
@@ -88,7 +95,7 @@
 
 echoClient = ns.UdpEchoClientHelper(serverInterfaces.GetAddress(0).ConvertTo(), 9)
 echoClient.SetAttribute("MaxPackets", ns.UintegerValue(1))
-echoClient.SetAttribute("Interval", ns.TimeValue(ns.Seconds(1.)))
+echoClient.SetAttribute("Interval", ns.TimeValue(ns.Seconds(1.0)))
 echoClient.SetAttribute("PacketSize", ns.UintegerValue(1024))
 
 clientApps = echoClient.Install(client.Get(0))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/brite/helper/brite-topology-helper.h ns-3.41/src/brite/helper/brite-topology-helper.h
--- ns-3.40/src/brite/helper/brite-topology-helper.h	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/brite/helper/brite-topology-helper.h	2024-02-09 14:44:07.861789700 -0800
@@ -32,7 +32,7 @@
 // here we just need a forward declaration.
 namespace brite
 {
-struct Topology;
+class Topology;
 };
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/CMakeLists.txt ns-3.41/src/buildings/CMakeLists.txt
--- ns-3.40/src/buildings/CMakeLists.txt	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/CMakeLists.txt	2024-02-09 14:44:07.861789700 -0800
@@ -30,8 +30,7 @@
     model/oh-buildings-propagation-loss-model.h
     model/random-walk-2d-outdoor-mobility-model.h
     model/three-gpp-v2v-channel-condition-model.h
-  LIBRARIES_TO_LINK ${libmobility}
-                    ${libpropagation}
+  LIBRARIES_TO_LINK ${libpropagation}
   TEST_SOURCES
     test/buildings-channel-condition-model-test.cc
     test/buildings-helper-test.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/doc/source/conf.py ns-3.41/src/buildings/doc/source/conf.py
--- ns-3.40/src/buildings/doc/source/conf.py	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/doc/source/conf.py	2024-02-09 14:44:07.861789700 -0800
@@ -11,206 +11,209 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.imgmath',
-              'sphinxcontrib.seqdiag']
+extensions = ["sphinx.ext.imgmath", "sphinxcontrib.seqdiag"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'buildings'
+master_doc = "buildings"
 
 # General information about the project.
-project = u'LENA'
-copyright = u'2011-2012, CTTC'
+project = "LENA"
+copyright = "2011-2012, CTTC"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'M2'
+version = "M2"
 # The full version, including alpha/beta/rc tags.
-release = 'M2'
+release = "M2"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = "default"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-#htmlhelp_basename = 'ns-3doc'
+# htmlhelp_basename = 'ns-3doc'
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('buildings', 'buildings.tex', u'Buildings Module Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
+    (
+        "buildings",
+        "buildings.tex",
+        "Buildings Module Documentation",
+        "Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)",
+        "manual",
+    ),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
 
 
 # -- Options for manual page output --------------------------------------------
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-model-library', u'ns-3 Model Library',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-model-library", "ns-3 Model Library", ["ns-3 project"], 1)]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/helper/building-allocator.cc ns-3.41/src/buildings/helper/building-allocator.cc
--- ns-3.40/src/buildings/helper/building-allocator.cc	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/helper/building-allocator.cc	2024-02-09 14:44:07.861789700 -0800
@@ -99,7 +99,8 @@
             .AddAttribute("LayoutType",
                           "The type of layout.",
                           EnumValue(GridPositionAllocator::ROW_FIRST),
-                          MakeEnumAccessor(&GridBuildingAllocator::m_layoutType),
+                          MakeEnumAccessor<GridPositionAllocator::LayoutType>(
+                              &GridBuildingAllocator::m_layoutType),
                           MakeEnumChecker(GridPositionAllocator::ROW_FIRST,
                                           "RowFirst",
                                           GridPositionAllocator::COLUMN_FIRST,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/model/building.cc ns-3.41/src/buildings/model/building.cc
--- ns-3.40/src/buildings/model/building.cc	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/model/building.cc	2024-02-09 14:44:07.861789700 -0800
@@ -73,7 +73,8 @@
             .AddAttribute("Type",
                           "The type of building",
                           EnumValue(Building::Residential),
-                          MakeEnumAccessor(&Building::GetBuildingType, &Building::SetBuildingType),
+                          MakeEnumAccessor<BuildingType_t>(&Building::GetBuildingType,
+                                                           &Building::SetBuildingType),
                           MakeEnumChecker(Building::Residential,
                                           "Residential",
                                           Building::Office,
@@ -83,7 +84,8 @@
             .AddAttribute("ExternalWallsType",
                           "The type of material of which the external walls are made",
                           EnumValue(Building::ConcreteWithWindows),
-                          MakeEnumAccessor(&Building::GetExtWallsType, &Building::SetExtWallsType),
+                          MakeEnumAccessor<ExtWallsType_t>(&Building::GetExtWallsType,
+                                                           &Building::SetExtWallsType),
                           MakeEnumChecker(Building::Wood,
                                           "Wood",
                                           Building::ConcreteWithWindows,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/model/buildings-propagation-loss-model.cc ns-3.41/src/buildings/model/buildings-propagation-loss-model.cc
--- ns-3.40/src/buildings/model/buildings-propagation-loss-model.cc	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/model/buildings-propagation-loss-model.cc	2024-02-09 14:44:07.861789700 -0800
@@ -54,7 +54,7 @@
 double
 BuildingsPropagationLossModel::ShadowingLoss::GetLoss() const
 {
-    return (m_shadowingValue);
+    return m_shadowingValue;
 }
 
 Ptr<MobilityModel>
@@ -130,7 +130,7 @@
     {
         loss = 12;
     }
-    return (loss);
+    return loss;
 }
 
 double
@@ -140,7 +140,7 @@
 
     int nfloors = node->GetFloorNumber() - 1;
     loss = -2 * (nfloors);
-    return (loss);
+    return loss;
 }
 
 double
@@ -166,7 +166,7 @@
         auto bit = ait->second.find(b);
         if (bit != ait->second.end())
         {
-            return (bit->second.GetLoss());
+            return bit->second.GetLoss();
         }
         else
         {
@@ -175,7 +175,7 @@
             // sigma is standard deviation, not variance
             double shadowingValue = m_randVariable->GetValue(0.0, (sigma * sigma));
             ait->second[b] = ShadowingLoss(shadowingValue, b);
-            return (ait->second[b].GetLoss());
+            return ait->second[b].GetLoss();
         }
     }
     else
@@ -185,7 +185,7 @@
         // sigma is standard deviation, not variance
         double shadowingValue = m_randVariable->GetValue(0.0, (sigma * sigma));
         m_shadowingLossMap[a][b] = ShadowingLoss(shadowingValue, b);
-        return (m_shadowingLossMap[a][b].GetLoss());
+        return m_shadowingLossMap[a][b].GetLoss();
     }
 }
 
@@ -200,24 +200,24 @@
     {
         if (!isBIndoor) // b is outdoor
         {
-            return (m_shadowingSigmaOutdoor);
+            return m_shadowingSigmaOutdoor;
         }
         else
         {
             double sigma = std::sqrt((m_shadowingSigmaOutdoor * m_shadowingSigmaOutdoor) +
                                      (m_shadowingSigmaExtWalls * m_shadowingSigmaExtWalls));
-            return (sigma);
+            return sigma;
         }
     }
     else if (isBIndoor) // b is indoor
     {
-        return (m_shadowingSigmaIndoor);
+        return m_shadowingSigmaIndoor;
     }
     else
     {
         double sigma = std::sqrt((m_shadowingSigmaOutdoor * m_shadowingSigmaOutdoor) +
                                  (m_shadowingSigmaExtWalls * m_shadowingSigmaExtWalls));
-        return (sigma);
+        return sigma;
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/model/hybrid-buildings-propagation-loss-model.cc ns-3.41/src/buildings/model/hybrid-buildings-propagation-loss-model.cc
--- ns-3.40/src/buildings/model/hybrid-buildings-propagation-loss-model.cc	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/model/hybrid-buildings-propagation-loss-model.cc	2024-02-09 14:44:07.865790000 -0800
@@ -83,7 +83,8 @@
             .AddAttribute("Environment",
                           "Environment Scenario",
                           EnumValue(UrbanEnvironment),
-                          MakeEnumAccessor(&HybridBuildingsPropagationLossModel::SetEnvironment),
+                          MakeEnumAccessor<EnvironmentType>(
+                              &HybridBuildingsPropagationLossModel::SetEnvironment),
                           MakeEnumChecker(UrbanEnvironment,
                                           "Urban",
                                           SubUrbanEnvironment,
@@ -95,7 +96,7 @@
                 "CitySize",
                 "Dimension of the city",
                 EnumValue(LargeCity),
-                MakeEnumAccessor(&HybridBuildingsPropagationLossModel::SetCitySize),
+                MakeEnumAccessor<CitySize>(&HybridBuildingsPropagationLossModel::SetCitySize),
                 MakeEnumChecker(SmallCity, "Small", MediumCity, "Medium", LargeCity, "Large"))
 
             .AddAttribute(
@@ -278,11 +279,11 @@
 {
     if (a->GetDistanceFrom(b) < m_itu1411NlosThreshold)
     {
-        return (m_ituR1411Los->GetLoss(a, b));
+        return m_ituR1411Los->GetLoss(a, b);
     }
     else
     {
-        return (m_ituR1411NlosOverRooftop->GetLoss(a, b));
+        return m_ituR1411NlosOverRooftop->GetLoss(a, b);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/model/mobility-building-info.cc ns-3.41/src/buildings/model/mobility-building-info.cc
--- ns-3.40/src/buildings/model/mobility-building-info.cc	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/model/mobility-building-info.cc	2024-02-09 14:44:07.865790000 -0800
@@ -139,28 +139,28 @@
 MobilityBuildingInfo::GetFloorNumber()
 {
     NS_LOG_FUNCTION(this);
-    return (m_nFloor);
+    return m_nFloor;
 }
 
 uint8_t
 MobilityBuildingInfo::GetRoomNumberX()
 {
     NS_LOG_FUNCTION(this);
-    return (m_roomX);
+    return m_roomX;
 }
 
 uint8_t
 MobilityBuildingInfo::GetRoomNumberY()
 {
     NS_LOG_FUNCTION(this);
-    return (m_roomY);
+    return m_roomY;
 }
 
 Ptr<Building>
 MobilityBuildingInfo::GetBuilding()
 {
     NS_LOG_FUNCTION(this);
-    return (m_myBuilding);
+    return m_myBuilding;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/buildings/model/random-walk-2d-outdoor-mobility-model.cc ns-3.41/src/buildings/model/random-walk-2d-outdoor-mobility-model.cc
--- ns-3.40/src/buildings/model/random-walk-2d-outdoor-mobility-model.cc	2023-09-27 13:30:37.618839000 -0700
+++ ns-3.41/src/buildings/model/random-walk-2d-outdoor-mobility-model.cc	2024-02-09 14:44:07.865790000 -0800
@@ -68,7 +68,7 @@
                           "The mode indicates the condition used to "
                           "change the current speed and direction",
                           EnumValue(RandomWalk2dOutdoorMobilityModel::MODE_DISTANCE),
-                          MakeEnumAccessor(&RandomWalk2dOutdoorMobilityModel::m_mode),
+                          MakeEnumAccessor<Mode>(&RandomWalk2dOutdoorMobilityModel::m_mode),
                           MakeEnumChecker(RandomWalk2dOutdoorMobilityModel::MODE_DISTANCE,
                                           "Distance",
                                           RandomWalk2dOutdoorMobilityModel::MODE_TIME,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/click/CMakeLists.txt ns-3.41/src/click/CMakeLists.txt
--- ns-3.40/src/click/CMakeLists.txt	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/click/CMakeLists.txt	2024-02-09 14:44:07.865790000 -0800
@@ -17,6 +17,7 @@
   PATH_SUFFIXES userlevel
                 ns
   SEARCH_PATHS ${NS3_WITH_CLICK}
+  OUTPUT_VARIABLE "NS3_CLICK_REASON"
 )
 
 if((NOT
@@ -58,10 +59,7 @@
     helper/click-internet-stack-helper.h
     model/ipv4-click-routing.h
     model/ipv4-l3-click-protocol.h
-  LIBRARIES_TO_LINK
-    ${libcore}
-    ${libnetwork}
-    ${libinternet}
-    ${click_LIBRARIES}
+  LIBRARIES_TO_LINK ${libinternet}
+                    ${click_LIBRARIES}
   TEST_SOURCES test/ipv4-click-routing-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/click/examples/nsclick-simple-lan.py ns-3.41/src/click/examples/nsclick-simple-lan.py
--- ns-3.40/src/click/examples/nsclick-simple-lan.py	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/click/examples/nsclick-simple-lan.py	2024-02-09 14:44:07.865790000 -0800
@@ -18,7 +18,14 @@
 
 import os.path
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 ns.LogComponentEnable("Ipv4ClickRouting", ns.LOG_LEVEL_ALL)
 ns.LogComponentEnable("Ipv4L3ClickProtocol", ns.LOG_LEVEL_ALL)
@@ -40,8 +47,9 @@
 
 # Install Click on node A
 clickinternet = ns.ClickInternetStackHelper()
-clickinternet.SetClickFile(csmaNodes.Get(0),
-                           clickConfigFolder + "/nsclick-lan-single-interface.click")
+clickinternet.SetClickFile(
+    csmaNodes.Get(0), clickConfigFolder + "/nsclick-lan-single-interface.click"
+)
 clickinternet.SetRoutingTableElement(csmaNodes.Get(0), "rt")
 clickinternet.Install(csmaNodes.Get(0))
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/click/model/ipv4-l3-click-protocol.cc ns-3.41/src/click/model/ipv4-l3-click-protocol.cc
--- ns-3.40/src/click/model/ipv4-l3-click-protocol.cc	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/click/model/ipv4-l3-click-protocol.cc	2024-02-09 14:44:07.869789800 -0800
@@ -238,7 +238,7 @@
         return true;
     }
 
-    if (GetWeakEsModel()) // Check other interfaces
+    if (!GetStrongEndSystemModel()) // Check other interfaces
     {
         for (uint32_t j = 0; j < GetNInterfaces(); j++)
         {
@@ -287,13 +287,25 @@
 void
 Ipv4L3ClickProtocol::SetWeakEsModel(bool model)
 {
-    m_weakEsModel = model;
+    m_strongEndSystemModel = !model;
 }
 
 bool
 Ipv4L3ClickProtocol::GetWeakEsModel() const
 {
-    return m_weakEsModel;
+    return !m_strongEndSystemModel;
+}
+
+void
+Ipv4L3ClickProtocol::SetStrongEndSystemModel(bool model)
+{
+    m_strongEndSystemModel = model;
+}
+
+bool
+Ipv4L3ClickProtocol::GetStrongEndSystemModel() const
+{
+    return m_strongEndSystemModel;
 }
 
 Ptr<NetDevice>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/click/model/ipv4-l3-click-protocol.h ns-3.41/src/click/model/ipv4-l3-click-protocol.h
--- ns-3.40/src/click/model/ipv4-l3-click-protocol.h	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/click/model/ipv4-l3-click-protocol.h	2024-02-09 14:44:07.869789800 -0800
@@ -21,6 +21,7 @@
 #ifndef IPV4_L3_CLICK_PROTOCOL_H
 #define IPV4_L3_CLICK_PROTOCOL_H
 
+#include "ns3/deprecated.h"
 #include "ns3/ipv4-interface.h"
 #include "ns3/ipv4-routing-protocol.h"
 #include "ns3/ipv4.h"
@@ -266,9 +267,15 @@
 
     void SetIpForward(bool forward) override;
     bool GetIpForward() const override;
+
+    NS_DEPRECATED_3_41("Use SetStrongEndSystemModel instead")
     void SetWeakEsModel(bool model) override;
+    NS_DEPRECATED_3_41("Use GetStrongEndSystemModel instead")
     bool GetWeakEsModel() const override;
 
+    void SetStrongEndSystemModel(bool model) override;
+    bool GetStrongEndSystemModel() const override;
+
     /**
      * \brief List of IPv4 interfaces.
      */
@@ -296,7 +303,7 @@
 
     Ptr<Ipv4RoutingProtocol> m_routingProtocol; //!< IPv4 routing protocol
     bool m_ipForward;                           //!< Whether IP forwarding is enabled
-    bool m_weakEsModel;                         //!< Whether to use weak Es model
+    bool m_strongEndSystemModel;                //!< Whether to use Strong End System Model
     L4List_t m_protocols;                       //!< List of IPv4 L4 protocols
     Ipv4InterfaceList m_interfaces;             //!< List of interfaces
     Ipv4InterfaceReverseContainer
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/click/test/examples-to-run.py ns-3.41/src/click/test/examples-to-run.py
--- ns-3.40/src/click/test/examples-to-run.py	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/click/test/examples-to-run.py	2024-02-09 14:44:07.869789800 -0800
@@ -9,8 +9,16 @@
 cpp_examples = [
     ("nsclick-simple-lan --clickConfigFolder=../../src/click/examples", "NSCLICK == True", "False"),
     ("nsclick-raw-wlan --clickConfigFolder=../../src/click/examples", "NSCLICK == True", "False"),
-    ("nsclick-udp-client-server-csma --clickConfigFolder=../../src/click/examples", "NSCLICK == True", "False"),
-    ("nsclick-udp-client-server-wifi --clickConfigFolder=../../src/click/examples", "NSCLICK == True", "False"),
+    (
+        "nsclick-udp-client-server-csma --clickConfigFolder=../../src/click/examples",
+        "NSCLICK == True",
+        "False",
+    ),
+    (
+        "nsclick-udp-client-server-wifi --clickConfigFolder=../../src/click/examples",
+        "NSCLICK == True",
+        "False",
+    ),
     ("nsclick-routing --clickConfigFolder=../../src/click/examples", "NSCLICK == True", "False"),
     ("nsclick-defines --clickConfigFolder=../../src/click/examples", "NSCLICK == True", "False"),
 ]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/CMakeLists.txt ns-3.41/src/CMakeLists.txt
--- ns-3.40/src/CMakeLists.txt	2023-09-27 13:30:37.610839000 -0700
+++ ns-3.41/src/CMakeLists.txt	2024-02-09 14:44:07.845789700 -0800
@@ -15,9 +15,8 @@
     message(STATUS "Processing src/${libname}")
     add_subdirectory(${libname})
   else()
-    message(
-      ${HIGHLIGHTED_STATUS}
-      "Skipping src/${libname} : it does not contain a CMakeLists.txt file"
+    message(${HIGHLIGHTED_STATUS}
+            "Skipping src/${libname}: it does not contain a CMakeLists.txt file"
     )
   endif()
 endforeach()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/config-store/CMakeLists.txt ns-3.41/src/config-store/CMakeLists.txt
--- ns-3.40/src/config-store/CMakeLists.txt	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/config-store/CMakeLists.txt	2024-02-09 14:44:07.869789800 -0800
@@ -1,3 +1,6 @@
+set(gtk3_sources)
+set(gtk3_headers)
+set(gtk_libraries)
 if(${GTK3_FOUND})
   set(gtk3_sources
       model/display-functions.cc
@@ -19,11 +22,10 @@
         -Wl,--allow-shlib-undefined
     )
   endif()
-  if(${GCC})
-    add_definitions(-Wno-parentheses)
-  endif()
 endif()
 
+set(xml2_sources)
+set(xml2_libraries)
 if(${LIBXML2_FOUND})
   set(xml2_sources
       model/xml-config.cc
@@ -49,7 +51,6 @@
     model/config-store.h
   LIBRARIES_TO_LINK
     ${libcore}
-    ${libnetwork}
     ${xml2_libraries}
     ${gtk_libraries}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/config-store/model/config-store.cc ns-3.41/src/config-store/model/config-store.cc
--- ns-3.40/src/config-store/model/config-store.cc	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/config-store/model/config-store.cc	2024-02-09 14:44:07.869789800 -0800
@@ -56,7 +56,7 @@
             .AddAttribute("Mode",
                           "Configuration mode",
                           EnumValue(ConfigStore::NONE),
-                          MakeEnumAccessor(&ConfigStore::SetMode),
+                          MakeEnumAccessor<Mode>(&ConfigStore::SetMode),
                           MakeEnumChecker(ConfigStore::NONE,
                                           "None",
                                           ConfigStore::LOAD,
@@ -72,7 +72,7 @@
                 "FileFormat",
                 "Type of file format",
                 EnumValue(ConfigStore::RAW_TEXT),
-                MakeEnumAccessor(&ConfigStore::SetFileFormat),
+                MakeEnumAccessor<FileFormat>(&ConfigStore::SetFileFormat),
                 MakeEnumChecker(ConfigStore::RAW_TEXT, "RawText", ConfigStore::XML, "Xml"))
             .AddAttribute("SaveDeprecated",
                           "Save DEPRECATED attributes",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/CMakeLists.txt ns-3.41/src/core/CMakeLists.txt
--- ns-3.40/src/core/CMakeLists.txt	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/core/CMakeLists.txt	2024-02-09 14:44:07.869789800 -0800
@@ -14,38 +14,12 @@
 endif()
 
 # Check for dependencies and add sources accordingly
-if(${CMAKE_VERSION}
-   VERSION_LESS
-   "3.11.0"
+check_include_files(
+  "boost/units/quantity.hpp;boost/units/systems/si.hpp"
+  HAVE_BOOST_UNITS
+  LANGUAGE
+  CXX
 )
-  check_include_file_cxx(
-    boost/units/quantity.hpp
-    HAVE_BOOST_UNITS_QUANTITY
-  )
-  check_include_file_cxx(
-    boost/units/systems/si.hpp
-    HAVE_BOOST_UNITS_SI
-  )
-  if(${HAVE_BOOST_UNITS_QUANTITY}
-     AND ${HAVE_BOOST_UNITS_SI}
-  )
-    set(HAVE_BOOST_UNITS
-        TRUE
-    )
-  else()
-    set(HAVE_BOOST_UNITS
-        FALSE
-    )
-  endif()
-else()
-  # Fast path for CMake >= 3.11
-  check_include_files(
-    "boost/units/quantity.hpp;boost/units/systems/si.hpp"
-    HAVE_BOOST_UNITS
-    LANGUAGE
-    CXX
-  )
-endif()
 
 if(${HAVE_BOOST_UNITS})
   add_definitions(
@@ -97,6 +71,9 @@
   )
 endif()
 
+set(example_as_test_sources)
+set(example_as_test_headers)
+set(example_as_test_suite)
 if(${ENABLE_EXAMPLES})
   set(example_as_test_sources
       model/example-as-test.cc
@@ -177,7 +154,6 @@
     model/boolean.cc
     model/integer.cc
     model/uinteger.cc
-    model/enum.cc
     model/double.cc
     model/int64x64.cc
     model/string.cc
@@ -305,7 +281,7 @@
     model/type-name.h
     model/type-traits.h
     model/uinteger.h
-    model/unused.h
+    model/uniform-random-bit-generator.h
     model/valgrind.h
     model/vector.h
     model/warnings.h
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/examples/main-random-variable-stream.cc ns-3.41/src/core/examples/main-random-variable-stream.cc
--- ns-3.40/src/core/examples/main-random-variable-stream.cc	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/core/examples/main-random-variable-stream.cc	2024-02-09 14:44:07.869789800 -0800
@@ -556,6 +556,37 @@
     }
 
     {
+        std::cout << "BinomialRandomVariable......." << std::flush;
+        Gnuplot plot;
+        plot.SetTitle("BinomialRandomVariable");
+        plot.AppendExtra("set yrange [0:10]");
+
+        auto x = CreateObject<BinomialRandomVariable>();
+        x->SetAttribute("Trials", IntegerValue(10));
+        x->SetAttribute("Probability", DoubleValue(0.5));
+
+        plot.AddDataset(Histogram(x, probes, precision, "BinomialRandomVariable n=10 p=0.5"));
+
+        gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
+    }
+
+    {
+        std::cout << "BernoulliRandomVariable......." << std::flush;
+        Gnuplot plot;
+        plot.SetTitle("BernoulliRandomVariable");
+        plot.AppendExtra("set yrange [0:1]");
+
+        auto x = CreateObject<BernoulliRandomVariable>();
+        x->SetAttribute("Probability", DoubleValue(0.5));
+
+        plot.AddDataset(Histogram(x, probes, precision, "BernoulliRandomVariable p=0.5"));
+
+        gnuplots.AddPlot(plot);
+        std::cout << "done" << std::endl;
+    }
+
+    {
         std::string gnuFile = plotFile + ".plt";
         std::cout << "Writing Gnuplot file: " << gnuFile << "..." << std::flush;
         std::ofstream gnuStream(gnuFile);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/examples/sample-rng-plot.py ns-3.41/src/core/examples/sample-rng-plot.py
--- ns-3.40/src/core/examples/sample-rng-plot.py	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/core/examples/sample-rng-plot.py	2024-02-09 14:44:07.869789800 -0800
@@ -1,18 +1,17 @@
-# -*- Mode:Python; -*-
-# /*
-#  * This program is free software; you can redistribute it and/or modify
-#  * it under the terms of the GNU General Public License version 2 as
-#  * published by the Free Software Foundation
-#  *
-#  * 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; if not, write to the Free Software
-#  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#  */
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation
+#
+# 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; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
 
 ## @file
 #  @ingroup core-examples
@@ -23,18 +22,26 @@
 #  This is adapted from Gustavo Carneiro's ns-3 tutorial
 
 
-import numpy as np
-import matplotlib.pyplot as plt
-import sys
 import argparse
-from ns import ns
+import sys
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+## Import ns-3
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 
 def main():
     parser = argparse.ArgumentParser("sample-rng-plot")
-    parser.add_argument("--not-blocking",
-                        action="store_true",
-                        default=False)
+    parser.add_argument("--not-blocking", action="store_true", default=False)
     args = parser.parse_args(sys.argv[1:])
 
     # mu, var = 100, 225
@@ -52,16 +59,16 @@
     ## Make a probability density histogram
     density = 1
     ## Plot color
-    facecolor = 'g'
+    facecolor = "g"
     ## Plot alpha value (transparency)
     alpha = 0.75
 
     # We don't really need the plot results, we're just going to show it later.
     # n, bins, patches = plt.hist(x, 50, density=1, facecolor='g', alpha=0.75)
-    n, bins, patches = plt.hist(x, 50, density=True, facecolor='g', alpha=0.75)
+    n, bins, patches = plt.hist(x, 50, density=True, facecolor="g", alpha=0.75)
 
-    plt.title('ns-3 histogram')
-    plt.text(60, .025, r'$\mu=100,\ \sigma=15$')
+    plt.title("ns-3 histogram")
+    plt.text(60, 0.025, r"$\mu=100,\ \sigma=15$")
     plt.axis([40, 160, 0, 0.03])
     plt.grid(True)
     plt.show(block=not args.not_blocking)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/examples/sample-simulator.py ns-3.41/src/core/examples/sample-simulator.py
--- ns-3.40/src/core/examples/sample-simulator.py	2023-09-27 13:30:37.622839000 -0700
+++ ns-3.41/src/core/examples/sample-simulator.py	2024-02-09 14:44:07.869789800 -0800
@@ -1,22 +1,21 @@
-# -*- Mode:Python; -*-
-# /*
-#  * Copyright (c) 2010 INRIA
-#  *
-#  * This program is free software; you can redistribute it and/or modify
-#  * it under the terms of the GNU General Public License version 2 as
-#  * published by the Free Software Foundation;
-#  *
-#  * 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; if not, write to the Free Software
-#  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#  *
-#  * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
-#  */
+#
+# Copyright (c) 2010 INRIA
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
+#
+# 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; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+#
 #
 # Python version of sample-simulator.cc
 
@@ -25,20 +24,31 @@
 #  \ingroup simulator
 #  Python example program demonstrating use of various Schedule functions.
 
+## Import ns-3
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
-from ns import ns
 
 ## Example function - triggered at a random time.
 ## \return None.
 def RandomFunction():
-    print ("RandomFunction received event at", ns.core.Simulator.Now().GetSeconds(), "s")
+    print("RandomFunction received event at", ns.core.Simulator.Now().GetSeconds(), "s")
+
 
 ## Example function - triggered if an event is canceled (should not be called).
 ## \return None.
 def CancelledEvent():
-    print ("I should never be called... ")
+    print("I should never be called... ")
+
 
-ns.cppyy.cppdef("""
+ns.cppyy.cppdef(
+    """
     #include "CPyCppyy/API.h"
 
     using namespace ns3;
@@ -100,7 +110,8 @@
     {
         return MakeEvent(&CancelledFunctionCpp);
     }
-   """)
+   """
+)
 
 
 def main(argv):
@@ -126,6 +137,8 @@
 
     ns.core.Simulator.Destroy()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     import sys
+
     main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/default-simulator-impl.cc ns-3.41/src/core/model/default-simulator-impl.cc
--- ns-3.40/src/core/model/default-simulator-impl.cc	2023-09-27 13:30:37.626839000 -0700
+++ ns-3.41/src/core/model/default-simulator-impl.cc	2024-02-09 14:44:07.873789800 -0800
@@ -209,11 +209,11 @@
     m_stop = true;
 }
 
-void
+EventId
 DefaultSimulatorImpl::Stop(const Time& delay)
 {
     NS_LOG_FUNCTION(this << delay.GetTimeStep());
-    Simulator::Schedule(delay, &Simulator::Stop);
+    return Simulator::Schedule(delay, &Simulator::Stop);
 }
 
 //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/default-simulator-impl.h ns-3.41/src/core/model/default-simulator-impl.h
--- ns-3.40/src/core/model/default-simulator-impl.h	2023-09-27 13:30:37.626839000 -0700
+++ ns-3.41/src/core/model/default-simulator-impl.h	2024-02-09 14:44:07.873789800 -0800
@@ -61,7 +61,7 @@
     void Destroy() override;
     bool IsFinished() const override;
     void Stop() override;
-    void Stop(const Time& delay) override;
+    EventId Stop(const Time& delay) override;
     EventId Schedule(const Time& delay, EventImpl* event) override;
     void ScheduleWithContext(uint32_t context, const Time& delay, EventImpl* event) override;
     EventId ScheduleNow(EventImpl* event) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/deprecated.h ns-3.41/src/core/model/deprecated.h
--- ns-3.40/src/core/model/deprecated.h	2023-09-27 13:30:37.626839000 -0700
+++ ns-3.41/src/core/model/deprecated.h	2024-02-09 14:44:07.873789800 -0800
@@ -76,37 +76,16 @@
 
 /**
  * \ingroup core
- * \def NS_DEPRECATED_3_40
- * Tag for things deprecated in version ns-3.40.
- */
-#define NS_DEPRECATED_3_40(msg) NS_DEPRECATED(msg)
-
-/**
- * \ingroup core
- * \def NS_DEPRECATED_3_39
- * Tag for things deprecated in version ns-3.39.
- */
-#define NS_DEPRECATED_3_39(msg) NS_DEPRECATED(msg)
-
-/**
- * \ingroup core
- * \def NS_DEPRECATED_3_38
- * Tag for things deprecated in version ns-3.38.
+ * \def NS_DEPRECATED_3_41
+ * Tag for things deprecated in version ns-3.41.
  */
-#define NS_DEPRECATED_3_38(msg) NS_DEPRECATED(msg)
+#define NS_DEPRECATED_3_41(msg) NS_DEPRECATED(msg)
 
 /**
  * \ingroup core
- * \def NS_DEPRECATED_3_37
- * Tag for things deprecated in version ns-3.37.
- */
-#define NS_DEPRECATED_3_37(msg) NS_DEPRECATED(msg)
-
-/**
- * \ingroup core
- * \def NS_DEPRECATED_3_36
- * Tag for things deprecated in version ns-3.36.
+ * \def NS_DEPRECATED_3_40
+ * Tag for things deprecated in version ns-3.40.
  */
-#define NS_DEPRECATED_3_36(msg) NS_DEPRECATED(msg)
+#define NS_DEPRECATED_3_40(msg) NS_DEPRECATED(msg)
 
 #endif /* NS3_DEPRECATED_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/enum.cc ns-3.41/src/core/model/enum.cc
--- ns-3.40/src/core/model/enum.cc	2023-09-27 13:30:37.626839000 -0700
+++ ns-3.41/src/core/model/enum.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,214 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
- */
-#include "enum.h"
-
-#include "fatal-error.h"
-#include "log.h"
-
-#include <algorithm> // find_if
-#include <numeric>   // std::accumulate
-#include <sstream>
-
-/**
- * \file
- * \ingroup attribute_Enum
- * ns3::EnumValue attribute value implementation.
- */
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("Enum");
-
-EnumValue::EnumValue()
-    : m_value()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-EnumValue::EnumValue(int value)
-    : m_value(value)
-{
-    NS_LOG_FUNCTION(this << value);
-}
-
-void
-EnumValue::Set(int value)
-{
-    NS_LOG_FUNCTION(this << value);
-    m_value = value;
-}
-
-int
-EnumValue::Get() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_value;
-}
-
-Ptr<AttributeValue>
-EnumValue::Copy() const
-{
-    NS_LOG_FUNCTION(this);
-    return ns3::Create<EnumValue>(*this);
-}
-
-std::string
-EnumValue::SerializeToString(Ptr<const AttributeChecker> checker) const
-{
-    NS_LOG_FUNCTION(this << checker);
-    const auto p = dynamic_cast<const EnumChecker*>(PeekPointer(checker));
-    NS_ASSERT(p != nullptr);
-    std::string name = p->GetName(m_value);
-    return name;
-}
-
-bool
-EnumValue::DeserializeFromString(std::string value, Ptr<const AttributeChecker> checker)
-{
-    NS_LOG_FUNCTION(this << value << checker);
-    const auto p = dynamic_cast<const EnumChecker*>(PeekPointer(checker));
-    NS_ASSERT(p != nullptr);
-    m_value = p->GetValue(value);
-    return true;
-}
-
-EnumChecker::EnumChecker()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-EnumChecker::AddDefault(int value, std::string name)
-{
-    NS_LOG_FUNCTION(this << value << name);
-    m_valueSet.emplace_front(value, name);
-}
-
-void
-EnumChecker::Add(int value, std::string name)
-{
-    NS_LOG_FUNCTION(this << value << name);
-    m_valueSet.emplace_back(value, name);
-}
-
-std::string
-EnumChecker::GetName(int value) const
-{
-    auto it = std::find_if(m_valueSet.begin(), m_valueSet.end(), [value](Value v) {
-        return v.first == value;
-    });
-    NS_ASSERT_MSG(it != m_valueSet.end(),
-                  "invalid enum value " << value << "! Missed entry in MakeEnumChecker?");
-    return it->second;
-}
-
-int
-EnumChecker::GetValue(const std::string name) const
-{
-    auto it = std::find_if(m_valueSet.begin(), m_valueSet.end(), [name](Value v) {
-        return v.second == name;
-    });
-    NS_ASSERT_MSG(
-        it != m_valueSet.end(),
-        "name "
-            << name
-            << " is not a valid enum value. Missed entry in MakeEnumChecker?\nAvailable values: "
-            << std::accumulate(m_valueSet.begin(),
-                               m_valueSet.end(),
-                               std::string{},
-                               [](std::string a, Value v) {
-                                   if (a.empty())
-                                   {
-                                       return v.second;
-                                   }
-                                   else
-                                   {
-                                       return std::move(a) + ", " + v.second;
-                                   }
-                               }));
-    return it->first;
-}
-
-bool
-EnumChecker::Check(const AttributeValue& value) const
-{
-    NS_LOG_FUNCTION(this << &value);
-    const auto p = dynamic_cast<const EnumValue*>(&value);
-    if (p == nullptr)
-    {
-        return false;
-    }
-    auto pvalue = p->Get();
-    auto it = std::find_if(m_valueSet.begin(), m_valueSet.end(), [pvalue](Value v) {
-        return v.first == pvalue;
-    });
-    return (it != m_valueSet.end());
-}
-
-std::string
-EnumChecker::GetValueTypeName() const
-{
-    NS_LOG_FUNCTION(this);
-    return "ns3::EnumValue";
-}
-
-bool
-EnumChecker::HasUnderlyingTypeInformation() const
-{
-    NS_LOG_FUNCTION(this);
-    return true;
-}
-
-std::string
-EnumChecker::GetUnderlyingTypeInformation() const
-{
-    NS_LOG_FUNCTION(this);
-    std::ostringstream oss;
-    bool moreValues = false;
-    for (const auto& i : m_valueSet)
-    {
-        oss << (moreValues ? "|" : "") << i.second;
-        moreValues = true;
-    }
-    return oss.str();
-}
-
-Ptr<AttributeValue>
-EnumChecker::Create() const
-{
-    NS_LOG_FUNCTION(this);
-    return ns3::Create<EnumValue>();
-}
-
-bool
-EnumChecker::Copy(const AttributeValue& source, AttributeValue& destination) const
-{
-    NS_LOG_FUNCTION(this << &source << &destination);
-    const auto src = dynamic_cast<const EnumValue*>(&source);
-    auto dst = dynamic_cast<EnumValue*>(&destination);
-    if (src == nullptr || dst == nullptr)
-    {
-        return false;
-    }
-    *dst = *src;
-    return true;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/enum.h ns-3.41/src/core/model/enum.h
--- ns-3.40/src/core/model/enum.h	2023-09-27 13:30:37.626839000 -0700
+++ ns-3.41/src/core/model/enum.h	2024-02-09 14:44:07.877790000 -0800
@@ -22,7 +22,12 @@
 #include "attribute-accessor-helper.h"
 #include "attribute.h"
 
+#include <algorithm> // find_if
 #include <list>
+#include <numeric> // std::accumulate
+#include <sstream>
+#include <type_traits>
+#include <typeinfo>
 
 /**
  * \file
@@ -52,6 +57,7 @@
  *                       EnumValue (RipNg::NO_SPLIT_HORIZON));
  * \endcode
  */
+template <typename T>
 class EnumValue : public AttributeValue
 {
   public:
@@ -61,10 +67,10 @@
      *
      * \param [in] value The value to begin with.
      */
-    EnumValue(int value);
-    void Set(int value);
-    int Get() const;
-    template <typename T>
+    EnumValue(T value);
+    void Set(T value);
+    T Get() const;
+
     bool GetAccessor(T& value) const;
 
     Ptr<AttributeValue> Copy() const override;
@@ -72,17 +78,48 @@
     bool DeserializeFromString(std::string value, Ptr<const AttributeChecker> checker) override;
 
   private:
-    int m_value; //!< The stored integer value.
+    T m_value; //!< The stored value.
 };
 
 template <typename T>
+EnumValue<T>::EnumValue() = default;
+
+template <typename T>
+EnumValue<T>::EnumValue(T value)
+    : m_value(value)
+{
+}
+
+template <typename T>
+void
+EnumValue<T>::Set(T value)
+{
+    m_value = value;
+}
+
+template <typename T>
+T
+EnumValue<T>::Get() const
+{
+    return m_value;
+}
+
+template <typename T>
 bool
-EnumValue::GetAccessor(T& value) const
+EnumValue<T>::GetAccessor(T& value) const
 {
-    value = T(m_value);
+    value = static_cast<T>(m_value);
     return true;
 }
 
+template <typename T>
+Ptr<AttributeValue>
+EnumValue<T>::Copy() const
+{
+    return Create<EnumValue>(*this);
+}
+
+template <typename T>
 class EnumChecker : public AttributeChecker
 {
   public:
@@ -93,27 +130,27 @@
      * \param [in] value The value.
      * \param [in] name Then enum symbol name.
      */
-    void AddDefault(int value, std::string name);
+    void AddDefault(T value, std::string name);
     /**
      * Add a new value.
      * \param [in] value The value.
      * \param [in] name The enum symbol name.
      */
-    void Add(int value, std::string name);
+    void Add(T value, std::string name);
 
     /**
      * Get the enum symbol name by value.
      * \param [in] value The value.
      * \return The enum symbol name.
      */
-    std::string GetName(int value) const;
+    std::string GetName(T value) const;
 
     /**
      * Get the enum value by name.
      * \param [in] name Then enum symbol name.
      * \returns The enum value.
      */
-    int GetValue(const std::string name) const;
+    T GetValue(const std::string name) const;
 
     // Inherited
     bool Check(const AttributeValue& value) const override;
@@ -125,19 +162,13 @@
 
   private:
     /** Type for the pair value, name */
-    typedef std::pair<int, std::string> Value;
+    using Value = std::pair<T, std::string>;
     /** Type of container for storing Enum values and symbol names. */
-    typedef std::list<Value> ValueSet;
+    using ValueSet = std::list<Value>;
     /** The stored Enum values and symbol names. */
     ValueSet m_valueSet;
 };
 
-template <typename T1>
-Ptr<const AttributeAccessor> MakeEnumAccessor(T1 a1);
-
-template <typename T1, typename T2>
-Ptr<const AttributeAccessor> MakeEnumAccessor(T1 a1, T2 a2);
-
 /**
  * Make an EnumChecker pre-configured with a set of allowed
  * values by name.
@@ -152,17 +183,17 @@
  * \see AttributeChecker
  *
  * \tparam Ts The type list of additional parameters. Additional parameters
- *            should be int, string pairs.
+ *            should be T, string pairs.
  * \returns The AttributeChecker
  * \param [in] v  The default enum value.
  * \param [in] n  The corresponding name.
  * \param [in] args Any additional arguments.
  */
-template <typename... Ts>
+template <typename T, typename... Ts>
 Ptr<const AttributeChecker>
-MakeEnumChecker(int v, std::string n, Ts... args)
+MakeEnumChecker(T v, std::string n, Ts... args)
 {
-    Ptr<EnumChecker> checker = Create<EnumChecker>();
+    Ptr<EnumChecker<T>> checker = Create<EnumChecker<T>>();
     checker->AddDefault(v, n);
     return MakeEnumChecker(checker, args...);
 }
@@ -171,17 +202,16 @@
  * Handler for enum value, name pairs other than the default.
  *
  * \tparam Ts The type list of additional parameters. Additional parameters
- *            should be in int, string pairs.
+ *            should be T, string pairs.
  * \returns The AttributeChecker
  * \param [in] checker The AttributeChecker.
  * \param [in] v  The next enum value.
  * \param [in] n  The corresponding name.
  * \param [in] args Any additional arguments.
  */
-
-template <typename... Ts>
+template <typename T, typename... Ts>
 Ptr<const AttributeChecker>
-MakeEnumChecker(Ptr<EnumChecker> checker, int v, std::string n, Ts... args)
+MakeEnumChecker(Ptr<EnumChecker<T>> checker, T v, std::string n, Ts... args)
 {
     checker->Add(v, n);
     return MakeEnumChecker(checker, args...);
@@ -194,24 +224,171 @@
  * \param [in] checker The AttributeChecker.
  */
 // inline to allow tail call optimization
+template <typename T>
 inline Ptr<const AttributeChecker>
-MakeEnumChecker(Ptr<EnumChecker> checker)
+MakeEnumChecker(Ptr<EnumChecker<T>> checker)
 {
     return checker;
 }
 
-template <typename T1>
+template <typename T, typename T1>
 Ptr<const AttributeAccessor>
 MakeEnumAccessor(T1 a1)
 {
-    return MakeAccessorHelper<EnumValue>(a1);
+    return MakeAccessorHelper<EnumValue<T>>(a1);
 }
 
-template <typename T1, typename T2>
+template <typename T, typename T1, typename T2>
 Ptr<const AttributeAccessor>
 MakeEnumAccessor(T1 a1, T2 a2)
 {
-    return MakeAccessorHelper<EnumValue>(a1, a2);
+    return MakeAccessorHelper<EnumValue<T>>(a1, a2);
+}
+
+template <typename T>
+std::string
+EnumValue<T>::SerializeToString(Ptr<const AttributeChecker> checker) const
+{
+    const auto p = dynamic_cast<const EnumChecker<T>*>(PeekPointer(checker));
+    NS_ASSERT(p != nullptr);
+    std::string name = p->GetName(m_value);
+    return name;
+}
+
+template <typename T>
+bool
+EnumValue<T>::DeserializeFromString(std::string value, Ptr<const AttributeChecker> checker)
+{
+    const auto p = dynamic_cast<const EnumChecker<T>*>(PeekPointer(checker));
+    NS_ASSERT(p != nullptr);
+    m_value = p->GetValue(value);
+    return true;
+}
+
+template <typename T>
+EnumChecker<T>::EnumChecker()
+{
+}
+
+template <typename T>
+void
+EnumChecker<T>::AddDefault(T value, std::string name)
+{
+    m_valueSet.emplace_front(value, name);
+}
+
+template <typename T>
+void
+EnumChecker<T>::Add(T value, std::string name)
+{
+    m_valueSet.emplace_back(value, name);
+}
+
+template <typename T>
+std::string
+EnumChecker<T>::GetName(T value) const
+{
+    auto it = std::find_if(m_valueSet.begin(), m_valueSet.end(), [value](Value v) {
+        return v.first == value;
+    });
+
+    NS_ASSERT_MSG(it != m_valueSet.end(),
+                  "invalid enum value " << static_cast<int>(value)
+                                        << "! Missed entry in MakeEnumChecker?");
+    return it->second;
+}
+
+template <typename T>
+T
+EnumChecker<T>::GetValue(const std::string name) const
+{
+    auto it = std::find_if(m_valueSet.begin(), m_valueSet.end(), [name](Value v) {
+        return v.second == name;
+    });
+    NS_ASSERT_MSG(
+        it != m_valueSet.end(),
+        "name "
+            << name
+            << " is not a valid enum value. Missed entry in MakeEnumChecker?\nAvailable values: "
+            << std::accumulate(m_valueSet.begin(),
+                               m_valueSet.end(),
+                               std::string{},
+                               [](std::string a, Value v) {
+                                   if (a.empty())
+                                   {
+                                       return v.second;
+                                   }
+                                   else
+                                   {
+                                       return std::move(a) + ", " + v.second;
+                                   }
+                               }));
+    return it->first;
+}
+
+template <typename T>
+bool
+EnumChecker<T>::Check(const AttributeValue& value) const
+{
+    const auto p = dynamic_cast<const EnumValue<T>*>(&value);
+    if (!p)
+    {
+        return false;
+    }
+    auto pvalue = p->Get();
+    auto it = std::find_if(m_valueSet.begin(), m_valueSet.end(), [pvalue](Value v) {
+        return v.first == pvalue;
+    });
+    return (it != m_valueSet.end());
+}
+
+template <typename T>
+std::string
+EnumChecker<T>::GetValueTypeName() const
+{
+    return "ns3::EnumValue<" + std::string(typeid(T).name()) + ">";
+}
+
+template <typename T>
+bool
+EnumChecker<T>::HasUnderlyingTypeInformation() const
+{
+    return true;
+}
+
+template <typename T>
+std::string
+EnumChecker<T>::GetUnderlyingTypeInformation() const
+{
+    std::ostringstream oss;
+    bool moreValues = false;
+    for (const auto& i : m_valueSet)
+    {
+        oss << (moreValues ? "|" : "") << i.second;
+        moreValues = true;
+    }
+    return oss.str();
+}
+
+template <typename T>
+Ptr<AttributeValue>
+EnumChecker<T>::Create() const
+{
+    return ns3::Create<EnumValue<T>>();
+}
+
+template <typename T>
+bool
+EnumChecker<T>::Copy(const AttributeValue& source, AttributeValue& destination) const
+{
+    const auto src = dynamic_cast<const EnumValue<T>*>(&source);
+    auto dst = dynamic_cast<EnumValue<T>*>(&destination);
+    if (!src || !dst)
+    {
+        return false;
+    }
+    *dst = *src;
+    return true;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/int64x64-128.cc ns-3.41/src/core/model/int64x64-128.cc
--- ns-3.40/src/core/model/int64x64-128.cc	2023-09-27 13:30:37.626839000 -0700
+++ ns-3.41/src/core/model/int64x64-128.cc	2024-02-09 14:44:07.877790000 -0800
@@ -225,7 +225,7 @@
     a <<= 64;
     int64x64_t result;
     result._v = Udiv(a, v);
-    int64x64_t tmp = int64x64_t(v, false);
+    int64x64_t tmp(v, 0);
     tmp.MulByInvert(result);
     if (tmp.GetHigh() != 1)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/log.cc ns-3.41/src/core/model/log.cc
--- ns-3.40/src/core/model/log.cc	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/log.cc	2024-02-09 14:44:07.881790000 -0800
@@ -535,36 +535,4 @@
     }
 }
 
-template <>
-ParameterLogger&
-ParameterLogger::operator<< <std::string>(const std::string& param)
-{
-    CommaRest();
-    m_os << "\"" << param << "\"";
-    return *this;
-}
-
-ParameterLogger&
-ParameterLogger::operator<<(const char* param)
-{
-    (*this) << std::string(param);
-    return *this;
-}
-
-template <>
-ParameterLogger&
-ParameterLogger::operator<< <int8_t>(const int8_t param)
-{
-    (*this) << static_cast<int16_t>(param);
-    return *this;
-}
-
-template <>
-ParameterLogger&
-ParameterLogger::operator<< <uint8_t>(const uint8_t param)
-{
-    (*this) << static_cast<uint16_t>(param);
-    return *this;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/log.h ns-3.41/src/core/model/log.h
--- ns-3.40/src/core/model/log.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/log.h	2024-02-09 14:44:07.881790000 -0800
@@ -447,25 +447,11 @@
     /**
      * Write a function parameter on the output stream,
      * separating parameters after the first by `,` strings.
-     * Overload for arithmetic types (integral type or floating point type),
-     * enabling the parameter to be passed by value.
      *
      * \param [in] param The function parameter.
      * \return This ParameterLogger, so it's chainable.
      */
-    template <typename T, typename U = std::enable_if_t<std::is_arithmetic_v<T>>>
-    ParameterLogger& operator<<(T param);
-
-    /**
-     * Write a function parameter on the output stream,
-     * separating parameters after the first by `,` strings.
-     * Overload for non-arithmetic types, enabling the parameter
-     * to be passed by reference.
-     *
-     * \param [in] param The function parameter.
-     * \return This ParameterLogger, so it's chainable.
-     */
-    template <typename T, typename U = std::enable_if_t<!std::is_arithmetic_v<T>>>
+    template <typename T>
     ParameterLogger& operator<<(const T& param);
 
     /**
@@ -477,14 +463,6 @@
     template <typename T>
     ParameterLogger& operator<<(const std::vector<T>& vector);
 
-    /**
-     * Overload for C-strings.
-     *
-     * \param [in] param The C-string
-     * \return This ParameterLogger, so it's chainable.
-     */
-    ParameterLogger& operator<<(const char* param);
-
   private:
     /** Add `, ` before every parameter after the first. */
     void CommaRest();
@@ -493,21 +471,26 @@
     std::ostream& m_os; //!< Underlying output stream.
 };
 
-template <typename T, typename U>
-ParameterLogger&
-ParameterLogger::operator<<(T param)
-{
-    CommaRest();
-    m_os << param;
-    return *this;
-}
-
-template <typename T, typename U>
+template <typename T>
 ParameterLogger&
 ParameterLogger::operator<<(const T& param)
 {
     CommaRest();
-    m_os << param;
+
+    if constexpr (std::is_convertible_v<T, std::string>)
+    {
+        m_os << "\"" << param << "\"";
+    }
+    else if constexpr (std::is_arithmetic_v<T>)
+    {
+        // Use + unary operator to cast uint8_t / int8_t to uint32_t / int32_t, respectively
+        m_os << +param;
+    }
+    else
+    {
+        m_os << param;
+    }
+
     return *this;
 }
 
@@ -522,30 +505,6 @@
     return *this;
 }
 
-/**
- * Specialization for strings.
- * \param [in] param The function parameter.
- * \return This ParameterLogger, so it's chainable.
- */
-template <>
-ParameterLogger& ParameterLogger::operator<< <std::string>(const std::string& param);
-
-/**
- * Specialization for int8_t.
- * \param [in] param The function parameter.
- * \return This ParameterLogger, so it's chainable.
- */
-template <>
-ParameterLogger& ParameterLogger::operator<< <int8_t>(const int8_t param);
-
-/**
- * Specialization for uint8_t.
- * \param [in] param The function parameter.
- * \return This ParameterLogger, so it's chainable.
- */
-template <>
-ParameterLogger& ParameterLogger::operator<< <uint8_t>(const uint8_t param);
-
 } // namespace ns3
 
 /**@}*/ // \ingroup logging
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/log-macros-enabled.h ns-3.41/src/core/model/log-macros-enabled.h
--- ns-3.40/src/core/model/log-macros-enabled.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/log-macros-enabled.h	2024-02-09 14:44:07.881790000 -0800
@@ -189,7 +189,9 @@
             NS_LOG_APPEND_CONTEXT;                                                                 \
             NS_LOG_APPEND_FUNC_PREFIX;                                                             \
             NS_LOG_APPEND_LEVEL_PREFIX(level);                                                     \
+            auto flags = std::clog.setf(std::ios_base::boolalpha);                                 \
             std::clog << msg << std::endl;                                                         \
+            std::clog.flags(flags);                                                                \
         }                                                                                          \
     } while (false)
 
@@ -245,7 +247,9 @@
             NS_LOG_APPEND_NODE_PREFIX;                                                             \
             NS_LOG_APPEND_CONTEXT;                                                                 \
             std::clog << g_log.Name() << ":" << __FUNCTION__ << "(";                               \
+            auto flags = std::clog.setf(std::ios_base::boolalpha);                                 \
             ns3::ParameterLogger(std::clog) << parameters;                                         \
+            std::clog.flags(flags);                                                                \
             std::clog << ")" << std::endl;                                                         \
         }                                                                                          \
     } while (false)
@@ -261,7 +265,9 @@
     NS_LOG_CONDITION                                                                               \
     do                                                                                             \
     {                                                                                              \
+        auto flags = std::clog.setf(std::ios_base::boolalpha);                                     \
         std::clog << msg << std::endl;                                                             \
+        std::clog.flags(flags);                                                                    \
     } while (false)
 
 #endif /* NS3_LOG_ENABLE */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/matrix-array.h ns-3.41/src/core/model/matrix-array.h
--- ns-3.40/src/core/model/matrix-array.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/matrix-array.h	2024-02-09 14:44:07.881790000 -0800
@@ -83,7 +83,7 @@
 {
   public:
     // instruct the compiler to generate the default constructor
-    MatrixArray<T>() = default;
+    MatrixArray() = default;
     /**
      * \brief Constructor "pages" number of matrices that are of size "numRows"x"numCols",
      * and are initialized with all-zero elements.
@@ -92,25 +92,25 @@
      * \param numCols the number of columns
      * \param numPages the number of pages
      */
-    MatrixArray<T>(size_t numRows, size_t numCols = 1, size_t numPages = 1);
+    MatrixArray(size_t numRows, size_t numCols = 1, size_t numPages = 1);
     /**
      * \brief Constructor creates a single array of values.size () elements and 1 column,
      * and uses std::valarray<T> values to initialize the elements.
      * \param values std::valarray<T> that will be used to initialize elements of 1D array
      */
-    explicit MatrixArray<T>(const std::valarray<T>& values);
+    explicit MatrixArray(const std::valarray<T>& values);
     /**
      * \brief Constructor creates a single array of values.size () elements and 1 column,
      * and moves std::valarray<T> values to initialize the elements.
      * \param values std::valarray<T> that will be moved to initialize elements of 1D array
      */
-    MatrixArray<T>(std::valarray<T>&& values);
+    MatrixArray(std::valarray<T>&& values);
     /**
      * \brief Constructor creates a single array of values.size () elements and 1 column,
      * and uses std::vector<T> values to initialize the elements.
      * \param values std::vector<T> that will be used to initialize elements of 1D array
      */
-    explicit MatrixArray<T>(const std::vector<T>& values);
+    explicit MatrixArray(const std::vector<T>& values);
     /**
      * \brief Constructor creates a single matrix of numRows and numCols, and uses
      * std::valarray<T> values to initialize the elements.
@@ -118,7 +118,7 @@
      * \param numCols the number of columns
      * \param values std::valarray<T> that will be used to initialize elements of 3D array
      */
-    MatrixArray<T>(size_t numRows, size_t numCols, const std::valarray<T>& values);
+    MatrixArray(size_t numRows, size_t numCols, const std::valarray<T>& values);
     /**
      * \brief Constructor creates a single matrix of numRows and numCols, and moves
      * std::valarray<T> values to initialize the elements.
@@ -126,7 +126,7 @@
      * \param numCols the number of columns
      * \param values std::valarray<T> that will be moved to initialize elements of 3D array
      */
-    MatrixArray<T>(size_t numRows, size_t numCols, std::valarray<T>&& values);
+    MatrixArray(size_t numRows, size_t numCols, std::valarray<T>&& values);
     /**
      * \brief Constructor creates the array of numPages matrices of numRows x numCols dimensions,
      * and uses std::valarray<T> values to initialize all the elements.
@@ -135,7 +135,7 @@
      * \param numPages the number of pages
      * \param values std::valarray<T> that will be used to initialize elements of 3D array
      */
-    MatrixArray<T>(size_t numRows, size_t numCols, size_t numPages, const std::valarray<T>& values);
+    MatrixArray(size_t numRows, size_t numCols, size_t numPages, const std::valarray<T>& values);
     /**
      * \brief Constructor creates the array of numPages matrices of numRows x numCols dimensions,
      * and moves std::valarray<T> values to initialize all the elements.
@@ -144,49 +144,32 @@
      * \param numPages the number of pages
      * \param values std::valarray<T> that will be used to initialize elements of 3D array
      */
-    MatrixArray<T>(size_t numRows, size_t numCols, size_t numPages, std::valarray<T>&& values);
-    /** instruct the compiler to generate the implicitly declared destructor*/
-    ~MatrixArray<T>() override = default;
-    /** instruct the compiler to generate the implicitly declared copy constructor*/
-    MatrixArray<T>(const MatrixArray<T>&) = default;
-    /**
-     * \brief Copy assignment operator.
-     * Instruct the compiler to generate the implicitly declared copy assignment operator.
-     * \return a reference to the result of the assignment
-     */
-    MatrixArray<T>& operator=(const MatrixArray<T>&) = default;
-    /** instruct the compiler to generate the implicitly declared move constructor*/
-    MatrixArray<T>(MatrixArray<T>&&) = default;
-    /**
-     * \brief Move assignment operator.
-     * Instruct the compiler to generate the implicitly declared move assignment operator.
-     * \return a reference to the result of the assignment
-     */
-    MatrixArray<T>& operator=(MatrixArray<T>&&) = default;
+    MatrixArray(size_t numRows, size_t numCols, size_t numPages, std::valarray<T>&& values);
+
     /**
      * \brief Element-wise multiplication with a scalar value.
      * \param rhs is a scalar value of type T
      * \returns The matrix in which each element has been multiplied by the given
      * scalar value.
      */
-    MatrixArray<T> operator*(const T& rhs) const;
+    MatrixArray operator*(const T& rhs) const;
     /**
      * \brief operator+ definition for MatrixArray<T>.
      * \param rhs The rhs MatrixArray object
      * \returns The result of operator+ of this MatrixArray and rhs MatrixArray
      */
-    MatrixArray<T> operator+(const MatrixArray<T>& rhs) const;
+    MatrixArray operator+(const MatrixArray<T>& rhs) const;
     /**
      * \brief binary operator- definition for MatrixArray<T>.
      * \param rhs The rhs MatrixArray object
      * \returns The result of operator- of this MatrixArray and rhs MatrixArray
      */
-    MatrixArray<T> operator-(const MatrixArray<T>& rhs) const;
+    MatrixArray operator-(const MatrixArray<T>& rhs) const;
     /**
      * \brief unary operator- definition for MatrixArray<T>.
      * \return the result of the operator-
      */
-    MatrixArray<T> operator-() const;
+    MatrixArray operator-() const;
     /**
      * \brief Page-wise matrix multiplication.
      * This operator interprets the 3D array as an array of matrices,
@@ -198,14 +181,14 @@
      * \param rhs is another MatrixArray instance
      * \returns The array of results of matrix multiplications.
      */
-    MatrixArray<T> operator*(const MatrixArray<T>& rhs) const;
+    MatrixArray operator*(const MatrixArray<T>& rhs) const;
     /**
      * \brief This operator interprets the 3D array as an array of matrices,
      * and performs a linear algebra operation on each of the matrices (pages),
      * i.e., transposes the matrix or array of matrices definition for MatrixArray<T>.
      * \return The resulting MatrixArray composed of the array of transposed matrices.
      */
-    MatrixArray<T> Transpose() const;
+    MatrixArray Transpose() const;
     /**
      *\brief Multiply each matrix in the array by the left and the right matrix.
      * For each page of this MatrixArray the operation performed is
@@ -228,8 +211,8 @@
      * \returns Returns the result of the multiplication which is a 3D MatrixArray
      * with dimensions J x K x P as explained previously.
      */
-    MatrixArray<T> MultiplyByLeftAndRightMatrix(const MatrixArray<T>& lMatrix,
-                                                const MatrixArray<T>& rMatrix) const;
+    MatrixArray MultiplyByLeftAndRightMatrix(const MatrixArray<T>& lMatrix,
+                                             const MatrixArray<T>& rMatrix) const;
 
     using ValArray<T>::GetPagePtr;
     using ValArray<T>::EqualDims;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/nstime.h ns-3.41/src/core/model/nstime.h
--- ns-3.40/src/core/model/nstime.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/nstime.h	2024-02-09 14:44:07.881790000 -0800
@@ -271,9 +271,10 @@
      * - `d`  (days)
      * - `y`  (years)
      *
-     * There can be no white space between the numerical portion
-     * and the units.  Any otherwise malformed string causes a fatal error to
-     * occur.
+     * There must be no whitespace between the numerical portion
+     * and the unit. If the string only contains a number, it is treated as seconds.
+     * Any otherwise malformed string causes a fatal error to occur.
+     *
      * \param [in] s The string to parse into a Time
      */
     explicit Time(const std::string& s);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/ptr.h ns-3.41/src/core/model/ptr.h
--- ns-3.40/src/core/model/ptr.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/ptr.h	2024-02-09 14:44:07.881790000 -0800
@@ -21,7 +21,6 @@
 #define PTR_H
 
 #include "assert.h"
-#include "deprecated.h"
 
 #include <iostream>
 #include <stdint.h>
@@ -80,23 +79,6 @@
     /** The pointer. */
     T* m_ptr;
 
-    /**
-     * Helper to test for null pointer.
-     *
-     * \note This has been deprecated; \see operator bool() instead.
-     *
-     * This supports the "safe-bool" idiom, see `operator Tester * ()`
-     */
-    // Don't deprecate the class because the warning fires
-    // every time ptr.h is merely included, masking the real uses of Tester
-    // Leave the macro here so we can find this later to actually remove it.
-    class /* NS_DEPRECATED_3_37 ("see operator bool") */ Tester
-    {
-      public:
-        // Delete operator delete to avoid misuse
-        void operator delete(void*) = delete;
-    };
-
     /** Interoperate with const instances. */
     friend class Ptr<const T>;
 
@@ -198,23 +180,6 @@
     T& operator*();
 
     /**
-     * Test for non-NULL Ptr.
-     *
-     * \note This has been deprecated; \see operator bool() instead.
-     *
-     * This enables simple pointer checks like
-     * \code
-     *   Ptr<...> p = ...;
-     *   if (p) ...
-     * \endcode
-     * This also disables deleting a Ptr
-     *
-     * This supports the "safe-bool" idiom; see [More C++ Idioms/Safe
-     * bool](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Safe_bool)
-     */
-    NS_DEPRECATED_3_37("see operator bool")
-    operator Tester*() const;
-    /**
      * Test for non-NULL pointer.
      *
      * This enables simple pointer checks like
@@ -774,17 +739,6 @@
 }
 
 template <typename T>
-Ptr<T>::operator Tester*() const // NS_DEPRECATED_3_37
-{
-    if (m_ptr == 0)
-    {
-        return 0;
-    }
-    static Tester test;
-    return &test;
-}
-
-template <typename T>
 Ptr<T>::operator bool() const
 {
     return m_ptr != nullptr;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/random-variable-stream.cc ns-3.41/src/core/model/random-variable-stream.cc
--- ns-3.40/src/core/model/random-variable-stream.cc	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/random-variable-stream.cc	2024-02-09 14:44:07.881790000 -0800
@@ -1738,4 +1738,123 @@
     m_validated = true;
 }
 
+NS_OBJECT_ENSURE_REGISTERED(BinomialRandomVariable);
+
+TypeId
+BinomialRandomVariable::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::BinomialRandomVariable")
+            .SetParent<RandomVariableStream>()
+            .SetGroupName("Core")
+            .AddConstructor<BinomialRandomVariable>()
+            .AddAttribute("Trials",
+                          "The number of trials.",
+                          IntegerValue(10),
+                          MakeIntegerAccessor(&BinomialRandomVariable::m_trials),
+                          MakeIntegerChecker<uint32_t>(0))
+            .AddAttribute("Probability",
+                          "The probability of success in each trial.",
+                          DoubleValue(0.5),
+                          MakeDoubleAccessor(&BinomialRandomVariable::m_probability),
+                          MakeDoubleChecker<double>(0));
+    return tid;
+}
+
+BinomialRandomVariable::BinomialRandomVariable()
+{
+    // m_trials and m_probability are initialized after constructor by attributes
+    NS_LOG_FUNCTION(this);
+}
+
+double
+BinomialRandomVariable::GetValue(uint32_t trials, double probability)
+{
+    NS_LOG_FUNCTION(this << trials << probability);
+
+    double successes = 0;
+
+    for (uint32_t i = 0; i < trials; ++i)
+    {
+        double v = Peek()->RandU01();
+        if (IsAntithetic())
+        {
+            v = (1 - v);
+        }
+
+        if (v <= probability)
+        {
+            successes += 1;
+        }
+    }
+
+    return successes;
+}
+
+uint32_t
+BinomialRandomVariable::GetInteger(uint32_t trials, uint32_t probability)
+{
+    NS_LOG_FUNCTION(this << trials << probability);
+    return static_cast<uint32_t>(GetValue(trials, probability));
+}
+
+double
+BinomialRandomVariable::GetValue()
+{
+    NS_LOG_FUNCTION(this);
+    return GetValue(m_trials, m_probability);
+}
+
+NS_OBJECT_ENSURE_REGISTERED(BernoulliRandomVariable);
+
+TypeId
+BernoulliRandomVariable::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::BernoulliRandomVariable")
+            .SetParent<RandomVariableStream>()
+            .SetGroupName("Core")
+            .AddConstructor<BernoulliRandomVariable>()
+            .AddAttribute("Probability",
+                          "The probability of the random variable returning a value of 1.",
+                          DoubleValue(0.5),
+                          MakeDoubleAccessor(&BernoulliRandomVariable::m_probability),
+                          MakeDoubleChecker<double>(0));
+    return tid;
+}
+
+BernoulliRandomVariable::BernoulliRandomVariable()
+{
+    // m_probability is initialized after constructor by attributes
+    NS_LOG_FUNCTION(this);
+}
+
+double
+BernoulliRandomVariable::GetValue(double probability)
+{
+    NS_LOG_FUNCTION(this << probability);
+
+    double v = Peek()->RandU01();
+    if (IsAntithetic())
+    {
+        v = (1 - v);
+    }
+
+    return (v <= probability) ? 1.0 : 0.0;
+}
+
+uint32_t
+BernoulliRandomVariable::GetInteger(uint32_t probability)
+{
+    NS_LOG_FUNCTION(this << probability);
+    return static_cast<uint32_t>(GetValue(probability));
+}
+
+double
+BernoulliRandomVariable::GetValue()
+{
+    NS_LOG_FUNCTION(this);
+    return GetValue(m_probability);
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/random-variable-stream.h ns-3.41/src/core/model/random-variable-stream.h
--- ns-3.40/src/core/model/random-variable-stream.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/random-variable-stream.h	2024-02-09 14:44:07.885789900 -0800
@@ -18,6 +18,7 @@
  * Authors: Rajib Bhattacharjea<raj.b@gatech.edu>
  *          Hadi Arbabi<marbabi@cs.odu.edu>
  *          Mathieu Lacage <mathieu.lacage@gmail.com>
+ *          Alessio Bugetti <alessiobugetti98@gmail.com>
  *
  * Modified by Mitch Watrous <watrous@u.washington.edu>
  *
@@ -2074,6 +2075,202 @@
 
 }; // class EmpiricalRandomVariable
 
+/**
+ * \ingroup randomvariable
+ * \brief The binomial distribution Random Number Generator (RNG).
+ *
+ * This class supports the creation of objects that return random numbers
+ * from a fixed binomial distribution. It also supports the generation of
+ * single random numbers from various binomial distributions.
+ *
+ * The probability mass function of a binomial variable
+ * is defined as:
+ *
+ *   \f[
+ *      P(k; n, p) = \binom{n}{k} p^k (1-p)^{n-k}, \\
+ *          \quad k \in [0, n]
+ *   \f]
+ *
+ * where \f$ n \f$ is the number of trials and \f$ p \f$ is the probability
+ * of success in each trial. The mean of this distribution
+ * is \f$ \mu = np \f$ and the variance is \f$ \sigma^2 = np(1-p) \f$.
+ *
+ * The Binomial RNG value \f$n\f$ for a given number of trials \f$ n \f$
+ * and success probability \f$ p \f$ is generated by
+ *
+ *   \f[
+ *    k = \sum_{i=1}^{n} I(u_i \leq p)
+ *   \f]
+ *
+ * where \f$u_i\f$ is a uniform random variable on [0,1) for each trial,
+ * and \f$I\f$ is an indicator function that is 1 if \f$u_i \leq p\f$ and 0 otherwise.
+ * The sum of these indicator functions over all trials gives the total
+ * number of successes, which is the value of the binomial random variable.
+ *
+ * \par Example
+ *
+ * Here is an example of how to use this class:
+ * \code{.cc}
+ *   uint32_t trials = 10;
+ *   double probability = 0.5;
+ *
+ *   Ptr<BinomialRandomVariable> x = CreateObject<BinomialRandomVariable> ();
+ *   x->SetAttribute ("Trials", UintegerValue (trials));
+ *   x->SetAttribute ("Probability", DoubleValue (probability));
+ *
+ *   double successes = x->GetValue ();
+ * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$n'\f$, for the Binomial process is determined by:
+ *
+ *   \f[
+ *      k' = \sum_{i=1}^{n} I((1 - u_i) \leq p)
+ *   \f]
+ *
+ * where \f$u_i\f$ is a uniform random variable on [0,1) for each trial.
+ * The antithetic approach uses \f$(1 - u_i)\f$ instead of \f$u_i\f$ in the indicator function.
+ *
+ * @par Efficiency and Alternative Methods
+ *
+ * There are alternative methods for generating binomial distributions that may offer greater
+ * efficiency. However, this implementation opts for a simpler approach. Although not as efficient,
+ * this method was chosen for its simplicity and sufficiency in most applications.
+ */
+class BinomialRandomVariable : public RandomVariableStream
+{
+  public:
+    /**
+     * \brief Register this type.
+     * \return The object TypeId.
+     */
+    static TypeId GetTypeId();
+
+    BinomialRandomVariable();
+
+    /**
+     * \copydoc GetValue()
+     * \param [in] trials Number of trials.
+     * \param [in] probability Probability of success in each trial.
+     * \return Returns a number within the range [0, trials] indicating the number of successful
+     * trials.
+     */
+    double GetValue(uint32_t trials, double probability);
+
+    /**
+     * \copydoc GetValue(uint32_t,double)
+     * This function is similar to GetValue(), but it returns a uint32_t instead of a double.
+     */
+    uint32_t GetInteger(uint32_t trials, uint32_t probability);
+
+    // Inherited
+    double GetValue() override;
+    using RandomVariableStream::GetInteger;
+
+  private:
+    /** The number of trials. */
+    uint32_t m_trials;
+
+    /** The probability of success in each trial. */
+    double m_probability;
+
+}; // class BinomialRandomVariable
+
+/**
+ * \ingroup randomvariable
+ * \brief The Bernoulli distribution Random Number Generator (RNG).
+ *
+ * This class supports the creation of objects that return random numbers
+ * from a fixed Bernoulli distribution. It also supports the generation of
+ * single random numbers from various Bernoulli distributions.
+ *
+ * The probability mass function of a Bernoulli variable
+ * is defined as:
+ *
+ *   \f[
+ *      P(n; p) = p^n (1-p)^{1-n}, \\
+ *          \quad n \in \{0, 1\}
+ *   \f]
+ *
+ * where \f$ p \f$ is the probability of success and n is the indicator of success, with n=1
+ * representing success and n=0 representing failure. The mean of this distribution is \f$ \mu = p
+ * \f$ and the variance is \f$ \sigma^2 = p(1-p) \f$.
+ *
+ * The Bernoulli RNG value \f$n\f$ is generated by
+ *
+ *   \f[
+ *    n =
+ *    \begin{cases}
+ *    1 & \text{if } u \leq p \\
+ *    0 & \text{otherwise}
+ *    \end{cases}
+ *   \f]
+ *
+ * where \f$u\f$ is a uniform random variable on [0,1) and \f$p\f$ is the probability of success.
+ *
+ * \par Example
+ *
+ * Here is an example of how to use this class:
+ * \code{.cc}
+ *   double probability = 0.5;
+ *
+ *   Ptr<BernoulliRandomVariable> x = CreateObject<BernoulliRandomVariable> ();
+ *   x->SetAttribute ("Probability", DoubleValue (probability));
+ *
+ *   double success = x->GetValue ();
+ * \endcode
+ *
+ * \par Antithetic Values.
+ *
+ * If an instance of this RNG is configured to return antithetic values,
+ * the actual value returned, \f$x'\f$, is determined by:
+ *
+ *   \f[
+ *      x' =
+ *      \begin{cases}
+ *      1 & \text{if } (1 - u) \leq p \\
+ *      0 & \text{otherwise}
+ *      \end{cases}
+ *   \f]
+ *
+ * where \f$u\f$ is a uniform random variable on [0,1) and \f$p\f$ is the probability of success.
+ */
+class BernoulliRandomVariable : public RandomVariableStream
+{
+  public:
+    /**
+     * \brief Register this type.
+     * \return The object TypeId.
+     */
+    static TypeId GetTypeId();
+
+    BernoulliRandomVariable();
+
+    /**
+     * \copydoc GetValue()
+     * \param [in] probability Probability of success.
+     * \return Returns 1 if the trial is successful, or 0 if it is not.
+     */
+    double GetValue(double probability);
+
+    /**
+     * \copydoc GetValue(double)
+     * This function is similar to GetValue(), but it returns a uint32_t instead of a double.
+     */
+    uint32_t GetInteger(uint32_t probability);
+
+    // Inherited
+    double GetValue() override;
+    using RandomVariableStream::GetInteger;
+
+  private:
+    /** The probability of success. */
+    double m_probability;
+
+}; // class BernoulliRandomVariable
+
 } // namespace ns3
 
 #endif /* RANDOM_VARIABLE_STREAM_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/realtime-simulator-impl.cc ns-3.41/src/core/model/realtime-simulator-impl.cc
--- ns-3.40/src/core/model/realtime-simulator-impl.cc	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/realtime-simulator-impl.cc	2024-02-09 14:44:07.885789900 -0800
@@ -62,7 +62,8 @@
                 "SynchronizationMode",
                 "What to do if the simulation cannot keep up with real time.",
                 EnumValue(SYNC_BEST_EFFORT),
-                MakeEnumAccessor(&RealtimeSimulatorImpl::SetSynchronizationMode),
+                MakeEnumAccessor<SynchronizationMode>(
+                    &RealtimeSimulatorImpl::SetSynchronizationMode),
                 MakeEnumChecker(SYNC_BEST_EFFORT, "BestEffort", SYNC_HARD_LIMIT, "HardLimit"))
             .AddAttribute("HardLimit",
                           "Maximum acceptable real-time jitter (used in conjunction with "
@@ -453,16 +454,15 @@
             }
         }
 
-        if (!process)
+        if (process)
         {
-            // Sleep until signalled
-            tsNow = m_synchronizer->Synchronize(tsNow, tsDelay);
-
-            // Re-check event queue
-            continue;
+            ProcessOneEvent();
+        }
+        else
+        {
+            // Sleep until signalled and re-check event queue
+            m_synchronizer->Synchronize(tsNow, tsDelay);
         }
-
-        ProcessOneEvent();
     }
 
     //
@@ -498,11 +498,11 @@
     m_stop = true;
 }
 
-void
+EventId
 RealtimeSimulatorImpl::Stop(const Time& delay)
 {
     NS_LOG_FUNCTION(this << delay);
-    Simulator::Schedule(delay, &Simulator::Stop);
+    return Simulator::Schedule(delay, &Simulator::Stop);
 }
 
 //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/realtime-simulator-impl.h ns-3.41/src/core/model/realtime-simulator-impl.h
--- ns-3.40/src/core/model/realtime-simulator-impl.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/realtime-simulator-impl.h	2024-02-09 14:44:07.885789900 -0800
@@ -91,7 +91,7 @@
     void Destroy() override;
     bool IsFinished() const override;
     void Stop() override;
-    void Stop(const Time& delay) override;
+    EventId Stop(const Time& delay) override;
     EventId Schedule(const Time& delay, EventImpl* event) override;
     void ScheduleWithContext(uint32_t context, const Time& delay, EventImpl* event) override;
     EventId ScheduleNow(EventImpl* event) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/rng-stream.cc ns-3.41/src/core/model/rng-stream.cc
--- ns-3.40/src/core/model/rng-stream.cc	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/rng-stream.cc	2024-02-09 14:44:07.885789900 -0800
@@ -370,7 +370,7 @@
     m_currentState[5] = p2;
 
     /* Combination */
-    u = ((p1 > p2) ? (p1 - p2) * norm : (p1 - p2 + m1) * norm);
+    u = ((p1 > p2) ? (p1 - p2) * MRG32k3a::norm : (p1 - p2 + m1) * MRG32k3a::norm);
 
     return u;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/simulation-singleton.h ns-3.41/src/core/model/simulation-singleton.h
--- ns-3.40/src/core/model/simulation-singleton.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/simulation-singleton.h	2024-02-09 14:44:07.885789900 -0800
@@ -44,9 +44,9 @@
 {
   public:
     // Delete default constructor, copy constructor and assignment operator to avoid misuse
-    SimulationSingleton<T>() = delete;
-    SimulationSingleton<T>(const SimulationSingleton<T>&) = delete;
-    SimulationSingleton<T>& operator=(const SimulationSingleton<T>&) = delete;
+    SimulationSingleton() = delete;
+    SimulationSingleton(const SimulationSingleton<T>&) = delete;
+    SimulationSingleton& operator=(const SimulationSingleton<T>&) = delete;
 
     /**
      * Get a pointer to the singleton instance.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/simulator.cc ns-3.41/src/core/model/simulator.cc
--- ns-3.40/src/core/model/simulator.cc	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/simulator.cc	2024-02-09 14:44:07.885789900 -0800
@@ -54,6 +54,8 @@
 // of causing recursions leading to stack overflow
 NS_LOG_COMPONENT_DEFINE("Simulator");
 
+EventId Simulator::m_stopEvent;
+
 /**
  * \ingroup simulator
  * \anchor GlobalValueSimulatorImplementationType
@@ -188,11 +190,18 @@
     GetImpl()->Stop();
 }
 
-void
+EventId
 Simulator::Stop(const Time& delay)
 {
     NS_LOG_FUNCTION(delay);
-    GetImpl()->Stop(delay);
+    m_stopEvent = GetImpl()->Stop(delay);
+    return m_stopEvent;
+}
+
+EventId
+Simulator::GetStopEvent()
+{
+    return m_stopEvent;
 }
 
 Time
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/simulator.h ns-3.41/src/core/model/simulator.h
--- ns-3.40/src/core/model/simulator.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/simulator.h	2024-02-09 14:44:07.885789900 -0800
@@ -166,8 +166,16 @@
      * or equal to the stop time.  The stop time is relative to the
      * current simulation time.
      * @param [in] delay The stop time, relative to the current time.
+     * @return The stop EventId.
      */
-    static void Stop(const Time& delay);
+    static EventId Stop(const Time& delay);
+
+    /**
+     * Returns the Stop Event, or an invalid event if the simulation
+     * does not have a scheduled stop time.
+     * @return The stop EventId.
+     */
+    static EventId GetStopEvent();
 
     /**
      * Get the current simulation context.
@@ -520,6 +528,11 @@
      */
     static EventId DoScheduleDestroy(EventImpl* event);
 
+    /**
+     * Stop event (if present)
+     */
+    static EventId m_stopEvent;
+
 }; // class Simulator
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/simulator-impl.h ns-3.41/src/core/model/simulator-impl.h
--- ns-3.40/src/core/model/simulator-impl.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/simulator-impl.h	2024-02-09 14:44:07.885789900 -0800
@@ -61,7 +61,7 @@
     /** \copydoc Simulator::Stop() */
     virtual void Stop() = 0;
     /** \copydoc Simulator::Stop(const Time&) */
-    virtual void Stop(const Time& delay) = 0;
+    virtual EventId Stop(const Time& delay) = 0;
     /** \copydoc Simulator::Schedule(const Time&,const Ptr<EventImpl>&) */
     virtual EventId Schedule(const Time& delay, EventImpl* event) = 0;
     /** \copydoc Simulator::ScheduleWithContext(uint32_t,const Time&,EventImpl*) */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/singleton.h ns-3.41/src/core/model/singleton.h
--- ns-3.40/src/core/model/singleton.h	2023-09-27 13:30:37.630839000 -0700
+++ ns-3.41/src/core/model/singleton.h	2024-02-09 14:44:07.885789900 -0800
@@ -61,8 +61,8 @@
 {
   public:
     // Delete copy constructor and assignment operator to avoid misuse
-    Singleton<T>(const Singleton<T>&) = delete;
-    Singleton<T>& operator=(const Singleton<T>&) = delete;
+    Singleton(const Singleton<T>&) = delete;
+    Singleton& operator=(const Singleton<T>&) = delete;
 
     /**
      * Get a pointer to the singleton instance.
@@ -76,12 +76,12 @@
 
   protected:
     /** Constructor. */
-    Singleton<T>()
+    Singleton()
     {
     }
 
     /** Destructor. */
-    virtual ~Singleton<T>()
+    virtual ~Singleton()
     {
     }
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/type-id.cc ns-3.41/src/core/model/type-id.cc
--- ns-3.40/src/core/model/type-id.cc	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/model/type-id.cc	2024-02-09 14:44:07.889790000 -0800
@@ -383,7 +383,8 @@
 {
     NS_LOG_FUNCTION(IID << name);
     // Type names are definitive: equal names are equal types
-    NS_ASSERT_MSG(m_namemap.count(name) == 0, "Trying to allocate twice the same uid: " << name);
+    NS_ABORT_MSG_UNLESS(m_namemap.count(name) == 0,
+                        "Trying to allocate twice the same uid: " << name);
 
     TypeId::hash_t hash = Hasher(name) & (~HashChainFlag);
     if (m_hashmap.count(hash) == 1)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/uniform-random-bit-generator.h ns-3.41/src/core/model/uniform-random-bit-generator.h
--- ns-3.40/src/core/model/uniform-random-bit-generator.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/core/model/uniform-random-bit-generator.h	2024-02-09 14:44:07.889790000 -0800
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Stefano Avallone <stavallo@unina.it>
+ */
+#ifndef UNIFORM_RANDOM_BIT_GENERATOR_H
+#define UNIFORM_RANDOM_BIT_GENERATOR_H
+
+#include "random-variable-stream.h"
+
+#include <limits>
+
+namespace ns3
+{
+
+/**
+ * Wraps a UniformRandomVariable into a class that meets the requirements of a
+ * UniformRandomBitGenerator as specified by the C++11 standard, thus allowing
+ * the usage of ns-3 style UniformRandomVariables as generators in the functions
+ * of the standard random library.
+ */
+class UniformRandomBitGenerator
+{
+  public:
+    UniformRandomBitGenerator()
+        : m_rv(CreateObject<UniformRandomVariable>())
+    {
+    }
+
+    /**
+     * \return the ns-3 style uniform random variable
+     */
+    Ptr<UniformRandomVariable> GetRv() const
+    {
+        return m_rv;
+    }
+
+    /// Typedef needed to meet requirements. Result type must be an unsigned integer type.
+    using result_type = uint32_t;
+
+    /**
+     * \return the smallest value that operator() may return
+     */
+    static constexpr result_type min()
+    {
+        return 0;
+    }
+
+    /**
+     * \return the largest value that operator() may return
+     */
+    static constexpr result_type max()
+    {
+        return std::numeric_limits<result_type>::max();
+    }
+
+    /**
+     * \return a value in the closed interval [min(), max()]
+     */
+    result_type operator()()
+    {
+        return m_rv->GetInteger(min(), max());
+    }
+
+  private:
+    Ptr<UniformRandomVariable> m_rv; //!< ns-3 style uniform random variable
+};
+
+} // namespace ns3
+
+#endif /* UNIFORM_RANDOM_BIT_GENERATOR_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/unused.h ns-3.41/src/core/model/unused.h
--- ns-3.40/src/core/model/unused.h	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/model/unused.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2012 Andrey Mazo
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Andrey Mazo <ahippo@yandex.com>
- */
-
-#ifndef UNUSED_H
-#define UNUSED_H
-
-#include "deprecated.h"
-
-/**
- * \file
- * \ingroup core
- * NS_UNUSED and NS_UNUSED_GLOBAL macro definitions.
- */
-
-/**
- * \ingroup core
- * \def NS_UNUSED()
- * Mark a local variable as unused.
- *
- * \deprecated Please use `[[maybe_unused]]` directly. This macro is being
- * kept temporarily to support older code.
- */
-
-// We can't use NS_DEPRECATED_3_36 here because NS_UNUSED
-// is used as a statement following a declaration:
-//   int x;
-//   NS_UNUSED (x);
-// NS_DEPRECATED needs to be applied to the declaration
-//   NS_DEPRECATED_3_36 ("use double instead") int x;
-// Instead we resort to a pragma
-
-#ifndef NS_UNUSED
-#define NS_UNUSED(x)                                                                               \
-    _Pragma("GCC warning \"NS_UNUSED is deprecated, use [[maybe_unused]] directly\"")((void)(x))
-#endif
-
-/**
- * \ingroup core
- * \def NS_UNUSED_GLOBAL()
- * Mark a variable at file scope as unused.
- *
- * \deprecated Please use `[[maybe_unused]]` directly. This macro is being
- * kept temporarily to support older code.
- */
-#ifndef NS_UNUSED_GLOBAL
-#if defined(__GNUC__)
-#define NS_UNUSED_GLOBAL(x)                                                                        \
-    NS_DEPRECATED_3_36("NS_UNUSED_GLOBAL is deprecated, use [[maybe_unused]] directly")            \
-    [[maybe_unused]] x
-#elif defined(__LCLINT__)
-#define NS_UNUSED_GLOBAL(x)                                                                        \
-    NS_DEPRECATED_3_36("NS_UNUSED_GLOBAL is deprecated, use [[maybe_unused]] directly")            \
-    /*@unused@*/ x
-#else
-#define NS_UNUSED_GLOBAL(x)                                                                        \
-    NS_DEPRECATED_3_36("NS_UNUSED_GLOBAL is deprecated, use [[maybe_unused]] directly")            \
-    x
-#endif
-#endif
-
-#endif /* UNUSED_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/val-array.h ns-3.41/src/core/model/val-array.h
--- ns-3.40/src/core/model/val-array.h	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/model/val-array.h	2024-02-09 14:44:07.889790000 -0800
@@ -80,7 +80,7 @@
 {
   public:
     // instruct the compiler to generate the default constructor
-    ValArray<T>() = default;
+    ValArray() = default;
     /**
      * \brief Constructor that creates "numPages" number of 2D arrays that are of
      * dimensions "numRows"x"numCols", and are initialized with all-zero elements.
@@ -89,25 +89,25 @@
      * \param numCols the number of columns
      * \param numPages the number of pages
      */
-    ValArray<T>(size_t numRows, size_t numCols = 1, size_t numPages = 1);
+    ValArray(size_t numRows, size_t numCols = 1, size_t numPages = 1);
     /**
      * \brief Constructor creates a single 1D array of values.size () elements and 1 column,
      * and uses std::valarray<T> values to initialize the elements.
      * \param values std::valarray<T> that will be used to initialize elements of 1D array
      */
-    explicit ValArray<T>(const std::valarray<T>& values);
+    explicit ValArray(const std::valarray<T>& values);
     /**
      * \brief Constructor creates a single 1D array of values.size () elements and 1 column,
      * and moves std::valarray<T> values to initialize the elements.
      * \param values std::valarray<T> that will be moved to initialize elements of 1D array
      */
-    ValArray<T>(std::valarray<T>&& values);
+    ValArray(std::valarray<T>&& values);
     /**
      * \brief Constructor creates a single 1D array of values.size () elements and 1 column,
      * and uses values std::vector<T> to initialize the elements.
      * \param values std::vector<T> that will be used to initialize elements of 1D array
      */
-    explicit ValArray<T>(const std::vector<T>& values);
+    explicit ValArray(const std::vector<T>& values);
     /**
      * \brief Constructor creates a single 2D array of numRows and numCols, and uses
      * std::valarray<T> values to initialize the elements.
@@ -115,7 +115,7 @@
      * \param numCols the number of columns
      * \param values valarray<T> that will be used to initialize elements of 3D array
      */
-    ValArray<T>(size_t numRows, size_t numCols, const std::valarray<T>& values);
+    ValArray(size_t numRows, size_t numCols, const std::valarray<T>& values);
     /**
      * \brief Constructor creates a single 2D array of numRows and numCols, and moves
      * std::valarray<T> values to initialize the elements.
@@ -123,7 +123,7 @@
      * \param numCols the number of columns
      * \param values valarray<T> that will be used to initialize elements of 3D array
      */
-    ValArray<T>(size_t numRows, size_t numCols, std::valarray<T>&& values);
+    ValArray(size_t numRows, size_t numCols, std::valarray<T>&& values);
     /**
      * \brief Constructor creates the 3D array of numRows x numCols x numPages dimensions,
      * and uses std::valarray<T> values to initialize all the 2D arrays, where first
@@ -133,7 +133,7 @@
      * \param numPages the number of pages
      * \param values valarray<T> that will be used to initialize elements of 3D array
      */
-    ValArray<T>(size_t numRows, size_t numCols, size_t numPages, const std::valarray<T>& values);
+    ValArray(size_t numRows, size_t numCols, size_t numPages, const std::valarray<T>& values);
     /**
      * \brief Constructor creates the 3D array of numRows x numCols x numPages dimensions,
      * and moves std::valarray<T> values to initialize all the 2D arrays, where first
@@ -143,19 +143,19 @@
      * \param numPages the number of pages
      * \param values valarray<T> that will be used to initialize elements of 3D array
      */
-    ValArray<T>(size_t numRows, size_t numCols, size_t numPages, std::valarray<T>&& values);
+    ValArray(size_t numRows, size_t numCols, size_t numPages, std::valarray<T>&& values);
     /** instruct the compiler to generate the implicitly declared destructor*/
-    virtual ~ValArray<T>() = default;
+    virtual ~ValArray() = default;
     /** instruct the compiler to generate the implicitly declared copy constructor*/
-    ValArray<T>(const ValArray<T>&) = default;
+    ValArray(const ValArray<T>&) = default;
     /**
      * \brief Copy assignment operator.
      * Instruct the compiler to generate the implicitly declared copy assignment operator.
      * \return a reference to the assigned object
      */
-    ValArray<T>& operator=(const ValArray<T>&) = default;
+    ValArray& operator=(const ValArray<T>&) = default;
     /** instruct the compiler to generate the implicitly declared move constructor*/
-    ValArray<T>(ValArray<T>&&) = default;
+    ValArray(ValArray<T>&&) = default;
     /**
      * \brief Move assignment operator.
      * Instruct the compiler to generate the implicitly declared move assignment operator.
@@ -240,24 +240,24 @@
      * \returns ValArray in which each element has been multiplied by the given
      * scalar value.
      */
-    ValArray<T> operator*(const T& rhs) const;
+    ValArray operator*(const T& rhs) const;
     /**
      * \brief operator+ definition for ValArray<T>.
      * \param rhs The rhs ValArray to be added to this ValArray.
      * \return the ValArray instance that holds the results of the operator+
      */
-    ValArray<T> operator+(const ValArray<T>& rhs) const;
+    ValArray operator+(const ValArray<T>& rhs) const;
     /**
      * \brief binary operator- definition for ValArray<T>.
      * \param rhs The rhs ValArray to be subtracted from this ValArray.
      * \return the ValArray instance that holds the results of the operator-
      */
-    ValArray<T> operator-(const ValArray<T>& rhs) const;
+    ValArray operator-(const ValArray<T>& rhs) const;
     /**
      * \brief unary operator- definition for ValArray<T>.
      * \return the ValArray instance that holds the results of the operator-
      */
-    ValArray<T> operator-() const;
+    ValArray operator-() const;
     /**
      * \brief operator+= definition for ValArray<T>.
      * \param rhs The rhs ValArray to be added to this ValArray.
@@ -379,21 +379,21 @@
 ValArray<T>::GetNumRows() const
 {
     return m_numRows;
-};
+}
 
 template <class T>
 inline size_t
 ValArray<T>::GetNumCols() const
 {
     return m_numCols;
-};
+}
 
 template <class T>
 inline size_t
 ValArray<T>::GetNumPages() const
 {
     return m_numPages;
-};
+}
 
 template <class T>
 inline size_t
@@ -411,7 +411,7 @@
     NS_ASSERT_MSG(pageIndex < m_numPages, "Pages index out of bounds");
     size_t index = (rowIndex + m_numRows * (colIndex + m_numCols * pageIndex));
     return m_values[index];
-};
+}
 
 template <class T>
 inline const T&
@@ -422,7 +422,7 @@
     NS_ASSERT_MSG(pageIndex < m_numPages, "Pages index out of bounds");
     size_t index = (rowIndex + m_numRows * (colIndex + m_numCols * pageIndex));
     return m_values[index];
-};
+}
 
 template <class T>
 inline T&
@@ -430,7 +430,7 @@
 {
     NS_ASSERT_MSG(m_numPages == 1, "Cannot use 2D access operator for 3D ValArray.");
     return (*this)(rowIndex, colIndex, 0);
-};
+}
 
 template <class T>
 inline const T&
@@ -438,7 +438,7 @@
 {
     NS_ASSERT_MSG(m_numPages == 1, "Cannot use 2D access operator for 3D ValArray.");
     return (*this)(rowIndex, colIndex, 0);
-};
+}
 
 template <class T>
 inline T&
@@ -451,7 +451,7 @@
                       (m_numRows == 1 && m_numCols == 1),
                   "Access operator allowed only for 1D ValArray.");
     return m_values[index];
-};
+}
 
 template <class T>
 inline const T&
@@ -464,7 +464,7 @@
                       (m_numRows == 1 && m_numCols == 1),
                   "Access operator allowed only for 1D ValArray.");
     return m_values[index];
-};
+}
 
 template <class T>
 inline ValArray<T>
@@ -523,7 +523,7 @@
 {
     NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index.");
     return &(m_values[m_numRows * m_numCols * pageIndex]);
-};
+}
 
 template <class T>
 inline const T*
@@ -531,7 +531,7 @@
 {
     NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index.");
     return &(m_values[m_numRows * m_numCols * pageIndex]);
-};
+}
 
 template <class T>
 inline bool
@@ -567,14 +567,14 @@
 ValArray<T>::Elem(size_t row, size_t col, size_t page)
 {
     return (*this)(row, col, page);
-};
+}
 
 template <class T>
 inline const T&
 ValArray<T>::Elem(size_t row, size_t col, size_t page) const
 {
     return (*this)(row, col, page);
-};
+}
 
 /*************************************************
  **  Class ValArray non-inline implementations
@@ -587,7 +587,7 @@
       m_numPages{numPages}
 {
     m_values.resize(m_numRows * m_numCols * m_numPages);
-};
+}
 
 template <class T>
 ValArray<T>::ValArray(const std::valarray<T>& values)
@@ -626,7 +626,7 @@
 {
     NS_ASSERT_MSG(m_numRows * m_numCols == values.size(),
                   "Dimensions and the initialization array size do not match.");
-};
+}
 
 template <class T>
 ValArray<T>::ValArray(size_t numRows, size_t numCols, std::valarray<T>&& values)
@@ -637,7 +637,7 @@
     NS_ASSERT_MSG(m_numRows * m_numCols == values.size(),
                   "Dimensions and the initialization array size do not match.");
     m_values = std::move(values);
-};
+}
 
 template <class T>
 ValArray<T>::ValArray(size_t numRows,
@@ -651,7 +651,7 @@
 {
     NS_ASSERT_MSG(m_numRows * m_numCols * m_numPages == values.size(),
                   "Dimensions and the initialization array size do not match.");
-};
+}
 
 template <class T>
 ValArray<T>::ValArray(size_t numRows, size_t numCols, size_t numPages, std::valarray<T>&& values)
@@ -662,7 +662,7 @@
     NS_ASSERT_MSG(m_numRows * m_numCols * m_numPages == values.size(),
                   "Dimensions and the initialization array size do not match.");
     m_values = std::move(values);
-};
+}
 
 template <class T>
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/version.cache ns-3.41/src/core/model/version.cache
--- ns-3.40/src/core/model/version.cache	2023-09-27 13:31:04.826903800 -0700
+++ ns-3.41/src/core/model/version.cache	2024-02-09 14:46:04.290617700 -0800
@@ -1,10 +1,10 @@
-CLOSEST_TAG = '"ns-3.40"'
-VERSION_COMMIT_HASH = '"g4fdc985b5"'
+CLOSEST_TAG = '"ns-3.41"'
+VERSION_COMMIT_HASH = '"ge5039092b"'
 VERSION_DIRTY_FLAG = '0'
 VERSION_MAJOR = '3'
-VERSION_MINOR = '40'
+VERSION_MINOR = '41'
 VERSION_PATCH = '0'
 VERSION_RELEASE_CANDIDATE = '""'
-VERSION_TAG = '"ns-3.40"'
+VERSION_TAG = '"ns-3.41"'
 VERSION_TAG_DISTANCE = '0'
 VERSION_BUILD_PROFILE = 'default'
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/model/wall-clock-synchronizer.cc ns-3.41/src/core/model/wall-clock-synchronizer.cc
--- ns-3.40/src/core/model/wall-clock-synchronizer.cc	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/model/wall-clock-synchronizer.cc	2024-02-09 14:44:07.889790000 -0800
@@ -128,7 +128,7 @@
     // then subtract the current real time in normalized nanoseconds we just got
     // from the normalized simulation time in nanoseconds that is passed in as
     // the parameter ns.  We return an integer difference, but in reality all of
-    // the mechanisms that cause wall-clock to simuator time drift cause events
+    // the mechanisms that cause wall-clock to simulator time drift cause events
     // to be late.  That means that the wall-clock will be higher than the
     // simulation time and drift will be positive.  I would be astonished to
     // see a negative drift, but the possibility is admitted for other
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/test/attribute-test-suite.cc ns-3.41/src/core/test/attribute-test-suite.cc
--- ns-3.40/src/core/test/attribute-test-suite.cc	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/test/attribute-test-suite.cc	2024-02-09 14:44:07.889790000 -0800
@@ -157,6 +157,14 @@
         TEST_C  //!< Test value C.
     };
 
+    /// Test enumerator.
+    enum class Test_ec
+    {
+        TEST_D, //!< Test value D.
+        TEST_E, //!< Test value E.
+        TEST_F  //!< Test value F.
+    };
+
     /**
      * \brief Get the type ID.
      * \return The object TypeId.
@@ -203,14 +211,35 @@
                 .AddAttribute("TestEnum",
                               "help text",
                               EnumValue(TEST_A),
-                              MakeEnumAccessor(&AttributeObjectTest::m_enum),
+                              MakeEnumAccessor<Test_e>(&AttributeObjectTest::m_enum),
                               MakeEnumChecker(TEST_A, "TestA", TEST_B, "TestB", TEST_C, "TestC"))
                 .AddAttribute("TestEnumSetGet",
                               "help text",
                               EnumValue(TEST_B),
-                              MakeEnumAccessor(&AttributeObjectTest::DoSetEnum,
-                                               &AttributeObjectTest::DoGetEnum),
+                              MakeEnumAccessor<Test_e>(&AttributeObjectTest::DoSetEnum,
+                                                       &AttributeObjectTest::DoGetEnum),
                               MakeEnumChecker(TEST_A, "TestA", TEST_B, "TestB", TEST_C, "TestC"))
+                .AddAttribute("TestEnumClass",
+                              "help text",
+                              EnumValue(Test_ec::TEST_D),
+                              MakeEnumAccessor<Test_ec>(&AttributeObjectTest::m_enumclass),
+                              MakeEnumChecker(Test_ec::TEST_D,
+                                              "TestD",
+                                              Test_ec::TEST_E,
+                                              "TestE",
+                                              Test_ec::TEST_F,
+                                              "TestF"))
+                .AddAttribute("TestEnumClassSetGet",
+                              "help text",
+                              EnumValue(Test_ec::TEST_E),
+                              MakeEnumAccessor<Test_ec>(&AttributeObjectTest::DoSetEnumClass,
+                                                        &AttributeObjectTest::DoGetEnumClass),
+                              MakeEnumChecker(Test_ec::TEST_D,
+                                              "TestD",
+                                              Test_ec::TEST_E,
+                                              "TestE",
+                                              Test_ec::TEST_F,
+                                              "TestF"))
                 .AddAttribute("TestRandom",
                               "help text",
                               StringValue("ns3::ConstantRandomVariable[Constant=1.0]"),
@@ -268,11 +297,12 @@
                               BooleanValue(false),
                               MakeBooleanAccessor(&AttributeObjectTest::m_boolSrc),
                               MakeBooleanChecker())
-                .AddAttribute("EnumTraceSource",
-                              "help text",
-                              EnumValue(TEST_A),
-                              MakeEnumAccessor(&AttributeObjectTest::m_enumSrc),
-                              MakeEnumChecker(TEST_A, "TestA"))
+                .AddAttribute(
+                    "EnumTraceSource",
+                    "help text",
+                    EnumValue(TEST_A),
+                    MakeEnumAccessor<TracedValue<Test_e>>(&AttributeObjectTest::m_enumSrc),
+                    MakeEnumChecker(TEST_A, "TestA"))
                 .AddAttribute("ValueClassSource",
                               "help text",
                               ValueClassTestValue(ValueClassTest()),
@@ -492,6 +522,26 @@
         return m_enumSetGet;
     }
 
+    /**
+     * Set the m_enumClassSetGet value.
+     * \param v The value to set.
+     * \return true.
+     */
+    bool DoSetEnumClass(Test_ec v)
+    {
+        m_enumClassSetGet = v;
+        return true;
+    }
+
+    /**
+     * Get the m_enumClassSetGet value.
+     * \return the value of m_enumSetGet.
+     */
+    Test_ec DoGetEnumClass() const
+    {
+        return m_enumClassSetGet;
+    }
+
     bool m_boolTestA;                        //!< Boolean test A.
     bool m_boolTest;                         //!< Boolean test.
     bool m_boolTestDeprecated;               //!< Boolean test deprecated.
@@ -502,6 +552,8 @@
     float m_float;                           //!< float.
     Test_e m_enum;                           //!< Enum.
     Test_e m_enumSetGet;                     //!< Enum set-get.
+    Test_ec m_enumclass;                     //!< Enum class.
+    Test_ec m_enumClassSetGet;               //!< Enum class set-get.
     Ptr<RandomVariableStream> m_random;      //!< Random number generator.
     std::vector<Ptr<Derived>> m_vector1;     //!< First vector of derived objects.
     std::vector<Ptr<Derived>> m_vector2;     //!< Second vector of derived objects.
@@ -604,10 +656,7 @@
 void
 AttributeTestCase<BooleanValue>::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -618,7 +667,7 @@
     p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
-    ok = CheckGetCodePaths(p, "TestBoolName", "true", BooleanValue(true));
+    bool ok = CheckGetCodePaths(p, "TestBoolName", "true", BooleanValue(true));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
 
     std::string expected("Attribute 'TestDeprecated' is deprecated: DEPRECATED test working.\n");
@@ -705,17 +754,14 @@
 void
 AttributeTestCase<IntegerValue>::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
     // When the object is first created, the Attribute should have the default
     // value.
     //
-    ok = CheckGetCodePaths(p, "TestInt16", "-2", IntegerValue(-2));
+    bool ok = CheckGetCodePaths(p, "TestInt16", "-2", IntegerValue(-2));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
 
     //
@@ -840,17 +886,14 @@
 void
 AttributeTestCase<UintegerValue>::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
     // When the object is first created, the Attribute should have the default
     // value.
     //
-    ok = CheckGetCodePaths(p, "TestUint8", "1", UintegerValue(1));
+    bool ok = CheckGetCodePaths(p, "TestUint8", "1", UintegerValue(1));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
 
     //
@@ -923,17 +966,14 @@
 void
 AttributeTestCase<DoubleValue>::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
     // When the object is first created, the Attribute should have the default
     // value.
     //
-    ok = CheckGetCodePaths(p, "TestFloat", "-1.1", DoubleValue(-1.1F));
+    bool ok = CheckGetCodePaths(p, "TestFloat", "-1.1", DoubleValue(-1.1F));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
 
     //
@@ -950,19 +990,16 @@
 
 template <>
 void
-AttributeTestCase<EnumValue>::DoRun()
+AttributeTestCase<EnumValue<AttributeObjectTest::Test_e>>::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
     // When the object is first created, the Attribute should have the default
     // value.
     //
-    ok = CheckGetCodePaths(p, "TestEnum", "TestA", EnumValue(AttributeObjectTest::TEST_A));
+    bool ok = CheckGetCodePaths(p, "TestEnum", "TestA", EnumValue(AttributeObjectTest::TEST_A));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
 
     //
@@ -1028,12 +1065,106 @@
 
 template <>
 void
-AttributeTestCase<TimeValue>::DoRun()
+AttributeTestCase<EnumValue<AttributeObjectTest::Test_ec>>::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
+    auto p = CreateObject<AttributeObjectTest>();
+    NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
-    p = CreateObject<AttributeObjectTest>();
+    //
+    // When the object is first created, the Attribute should have the default
+    // value.
+    //
+    bool ok = CheckGetCodePaths(p,
+                                "TestEnumClass",
+                                "TestD",
+                                EnumValue(AttributeObjectTest::Test_ec::TEST_D));
+    NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
+
+    //
+    // Set the Attribute using the EnumValue type.
+    //
+    ok = p->SetAttributeFailSafe("TestEnumClass", EnumValue(AttributeObjectTest::Test_ec::TEST_F));
+    NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() to TEST_F");
+
+    ok = CheckGetCodePaths(p,
+                           "TestEnumClass",
+                           "TestF",
+                           EnumValue(AttributeObjectTest::Test_ec::TEST_F));
+    NS_TEST_ASSERT_MSG_EQ(ok,
+                          true,
+                          "Attribute not set properly by SetAttributeFailSafe() via EnumValue");
+
+    //
+    // When the object is first created, the Attribute should have the default
+    // value.
+    //
+    ok = CheckGetCodePaths(p,
+                           "TestEnumClassSetGet",
+                           "TestE",
+                           EnumValue(AttributeObjectTest::Test_ec::TEST_E));
+    NS_TEST_ASSERT_MSG_EQ(ok, true, "Attribute not set properly by default value");
+
+    //
+    // Set the Attribute using the EnumValue type.
+    //
+    ok = p->SetAttributeFailSafe("TestEnumClassSetGet",
+                                 EnumValue(AttributeObjectTest::Test_ec::TEST_F));
+    NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() to TEST_F");
+
+    ok = CheckGetCodePaths(p,
+                           "TestEnumClassSetGet",
+                           "TestF",
+                           EnumValue(AttributeObjectTest::Test_ec::TEST_F));
+    NS_TEST_ASSERT_MSG_EQ(ok,
+                          true,
+                          "Attribute not set properly by SetAttributeFailSafe() via EnumValue");
+
+    //
+    // Set the Attribute using the StringValue type.
+    //
+    ok = p->SetAttributeFailSafe("TestEnumClass", StringValue("TestE"));
+    NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() to TEST_E");
+
+    ok = CheckGetCodePaths(p,
+                           "TestEnumClass",
+                           "TestE",
+                           EnumValue(AttributeObjectTest::Test_ec::TEST_E));
+    NS_TEST_ASSERT_MSG_EQ(ok,
+                          true,
+                          "Attribute not set properly by SetAttributeFailSafe() via StringValue");
+
+    //
+    // Try to set the Attribute to a bogus enum using the StringValue type
+    // throws a fatal error.
+    //
+    //  ok = p->SetAttributeFailSafe ("TestEnumClass", StringValue ("TestG"));
+    //  NS_TEST_ASSERT_MSG_EQ (ok, false, "Unexpectedly could SetAttributeFailSafe() to TEST_G"); //
+
+    ok = CheckGetCodePaths(p,
+                           "TestEnumClass",
+                           "TestE",
+                           EnumValue(AttributeObjectTest::Test_ec::TEST_E));
+    NS_TEST_ASSERT_MSG_EQ(ok, true, "Error in SetAttributeFailSafe() but value changes");
+
+    //
+    // Try to set the Attribute to a bogus enum using an integer implicit conversion
+    // and make sure the underlying value doesn't change.
+    //
+    ok = p->SetAttributeFailSafe("TestEnumClass", EnumValue(5));
+    NS_TEST_ASSERT_MSG_EQ(ok, false, "Unexpectedly could SetAttributeFailSafe() to 5");
+
+    ok = CheckGetCodePaths(p,
+                           "TestEnumClass",
+                           "TestE",
+                           EnumValue(AttributeObjectTest::Test_ec::TEST_E));
+    NS_TEST_ASSERT_MSG_EQ(ok, true, "Error in SetAttributeFailSafe() but value changes");
+}
+
+template <>
+void
+AttributeTestCase<TimeValue>::DoRun()
+{
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     // The test vectors assume ns resolution
@@ -1042,7 +1173,7 @@
     //
     // Set value
     //
-    ok = p->SetAttributeFailSafe("TestTimeWithBounds", TimeValue(Seconds(5)));
+    bool ok = p->SetAttributeFailSafe("TestTimeWithBounds", TimeValue(Seconds(5)));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() via TimeValue to 5s");
 
     ok = CheckGetCodePaths(p, "TestTimeWithBounds", "+5e+09ns", TimeValue(Seconds(5)));
@@ -1168,17 +1299,14 @@
 void
 RandomVariableStreamAttributeTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
     // Try to set a UniformRandomVariable
     //
-    ok = p->SetAttributeFailSafe("TestRandom",
-                                 StringValue("ns3::UniformRandomVariable[Min=0.|Max=1.]"));
+    bool ok = p->SetAttributeFailSafe("TestRandom",
+                                      StringValue("ns3::UniformRandomVariable[Min=0.|Max=1.]"));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() a UniformRandomVariable");
 
     //
@@ -1221,10 +1349,9 @@
 void
 ObjectVectorAttributeTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
     ObjectVectorValue vector;
 
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -1306,10 +1433,9 @@
 void
 ObjectMapAttributeTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
     ObjectMapValue map;
 
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -1423,11 +1549,9 @@
 void
 IntegerTraceSourceAttributeTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
     IntegerValue iv;
-    bool ok;
 
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -1440,7 +1564,7 @@
     //
     // Set the Attribute to a positive value through an IntegerValue.
     //
-    ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue(5));
+    bool ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue(5));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() via IntegerValue to 5");
 
     p->GetAttribute("IntegerTraceSource1", iv);
@@ -1548,10 +1672,7 @@
 void
 IntegerTraceSourceTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -1560,7 +1681,7 @@
     //
     m_got1 = 1234;
 
-    ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue(-1));
+    bool ok = p->SetAttributeFailSafe("IntegerTraceSource1", IntegerValue(-1));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() via IntegerValue to -1");
 
     //
@@ -1651,10 +1772,7 @@
 void
 TracedCallbackTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -1680,8 +1798,9 @@
     // Now, wire the TracedCallback up to a trace sink.  This sink will just set
     // m_got2 to the first argument.
     //
-    ok = p->TraceConnectWithoutContext("Source2",
-                                       MakeCallback(&TracedCallbackTestCase::NotifySource2, this));
+    bool ok =
+        p->TraceConnectWithoutContext("Source2",
+                                      MakeCallback(&TracedCallbackTestCase::NotifySource2, this));
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not TraceConnectWithoutContext() to NotifySource2");
 
     //
@@ -1752,10 +1871,7 @@
 void
 PointerAttributeTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -1776,7 +1892,7 @@
     // to that object.  We can then set the PointerValue Attribute to that Ptr.
     //
     derived = Create<Derived>();
-    ok = p->SetAttributeFailSafe("Pointer", PointerValue(derived));
+    bool ok = p->SetAttributeFailSafe("Pointer", PointerValue(derived));
     NS_TEST_ASSERT_MSG_EQ(ok,
                           true,
                           "Could not SetAttributeFailSafe() a PointerValue of the correct type");
@@ -1910,10 +2026,7 @@
 void
 CallbackValueTestCase::DoRun()
 {
-    Ptr<AttributeObjectTest> p;
-    bool ok;
-
-    p = CreateObject<AttributeObjectTest>();
+    auto p = CreateObject<AttributeObjectTest>();
     NS_TEST_ASSERT_MSG_NE(p, nullptr, "Unable to CreateObject");
 
     //
@@ -1938,7 +2051,7 @@
 
     NS_TEST_ASSERT_MSG_EQ(m_gotCbValue, 1, "Callback unexpectedly fired");
 
-    ok = p->SetAttributeFailSafe("Callback", cbValue);
+    bool ok = p->SetAttributeFailSafe("Callback", cbValue);
     NS_TEST_ASSERT_MSG_EQ(ok, true, "Could not SetAttributeFailSafe() a CallbackValue");
 
     //
@@ -1981,7 +2094,11 @@
                 TestCase::QUICK);
     AddTestCase(new AttributeTestCase<DoubleValue>("Check Attributes of type DoubleValue"),
                 TestCase::QUICK);
-    AddTestCase(new AttributeTestCase<EnumValue>("Check Attributes of type EnumValue"),
+    AddTestCase(new AttributeTestCase<EnumValue<AttributeObjectTest::Test_e>>(
+                    "Check Attributes of type EnumValue"),
+                TestCase::QUICK);
+    AddTestCase(new AttributeTestCase<EnumValue<AttributeObjectTest::Test_ec>>(
+                    "Check Attributes of type EnumValue (wrapping an enum class)"),
                 TestCase::QUICK);
     AddTestCase(new AttributeTestCase<TimeValue>("Check Attributes of type TimeValue"),
                 TestCase::QUICK);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/test/int64x64-test-suite.cc ns-3.41/src/core/test/int64x64-test-suite.cc
--- ns-3.40/src/core/test/int64x64-test-suite.cc	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/test/int64x64-test-suite.cc	2024-02-09 14:44:07.893790000 -0800
@@ -485,7 +485,7 @@
     const int64x64_t zero(0, 0);
     const int64x64_t one(1, 0);
     const int64x64_t two(2, 0);
-    const int64x64_t thre(3, 0);
+    const int64x64_t three(3, 0);
 
     std::cout << std::endl;
     std::cout << GetParent()->GetName() << " Arithmetic: " << GetName() << std::endl;
@@ -497,14 +497,14 @@
     Check(3, one - two, -one);
     Check(4, one - (-one), two);
     Check(5, (-one) - (-two), one);
-    Check(6, (-one) - two, -thre);
+    Check(6, (-one) - two, -three);
 
     Check(7, zero + zero, zero);
     Check(8, zero + one, one);
     Check(9, one + one, two);
-    Check(10, one + two, thre);
+    Check(10, one + two, three);
     Check(11, one + (-one), zero);
-    Check(12, (-one) + (-two), -thre);
+    Check(12, (-one) + (-two), -three);
     Check(13, (-one) + two, one);
 
     Check(14, zero * zero, zero);
@@ -514,7 +514,7 @@
     Check(18, one * (-one), -one);
     Check(19, (-one) * (-one), one);
 
-    Check(20, (two * thre) / thre, two);
+    Check(20, (two * three) / three, two);
     // NOLINTEND(misc-redundant-expression)
 
     const int64x64_t frac = int64x64_t(0, 0xc000000000000000ULL); // 0.75
@@ -526,7 +526,7 @@
     const int64x64_t zerof = zero + frac;
     const int64x64_t onef = one + frac;
     const int64x64_t twof = two + frac;
-    const int64x64_t thref = thre + frac;
+    const int64x64_t thref = three + frac;
 
     // NOLINTBEGIN(misc-redundant-expression)
     Check(23, zerof, frac);
@@ -559,11 +559,11 @@
     // NOLINTEND(misc-redundant-expression)
 
     // Multiplication followed by division is exact:
-    Check(46, (two * thre) / thre, two);
+    Check(46, (two * three) / three, two);
     Check(47, (twof * thref) / thref, twof);
 
     // Division followed by multiplication loses a bit or two:
-    Check(48, (two / thre) * thre, two, 2 * tol1);
+    Check(48, (two / three) * three, two, 2 * tol1);
     Check(49, (twof / thref) * thref, twof, 3 * tol1);
 
     // The example below shows that we really do not lose
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/test/matrix-array-test-suite.cc ns-3.41/src/core/test/matrix-array-test-suite.cc
--- ns-3.40/src/core/test/matrix-array-test-suite.cc	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/test/matrix-array-test-suite.cc	2024-02-09 14:44:07.893790000 -0800
@@ -44,21 +44,21 @@
 class MatrixArrayTestCase : public TestCase
 {
   public:
-    MatrixArrayTestCase<T>() = default;
+    MatrixArrayTestCase() = default;
     /**
      * Constructor
      *
      * \param [in] name reference name
      */
-    MatrixArrayTestCase<T>(const std::string& name);
+    MatrixArrayTestCase(const std::string& name);
 
     /** Destructor. */
-    ~MatrixArrayTestCase<T>() override;
+    ~MatrixArrayTestCase() override;
     /**
      * \brief Copy constructor.
      * Instruct the compiler to generate the implicitly declared copy constructor
      */
-    MatrixArrayTestCase<T>(const MatrixArrayTestCase<T>&) = default;
+    MatrixArrayTestCase(const MatrixArrayTestCase<T>&) = default;
     /**
      * \brief Copy assignment operator.
      * Instruct the compiler to generate the implicitly declared copy assignment operator.
@@ -69,7 +69,7 @@
      * \brief Move constructor.
      * Instruct the compiler to generate the implicitly declared move constructor
      */
-    MatrixArrayTestCase<T>(MatrixArrayTestCase<T>&&) = default;
+    MatrixArrayTestCase(MatrixArrayTestCase<T>&&) = default;
     /**
      * \brief Move assignment operator.
      * Instruct the compiler to generate the implicitly declared copy constructor
@@ -89,7 +89,7 @@
 }
 
 template <class T>
-MatrixArrayTestCase<T>::~MatrixArrayTestCase<T>()
+MatrixArrayTestCase<T>::~MatrixArrayTestCase()
 {
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/test/random-variable-stream-test-suite.cc ns-3.41/src/core/test/random-variable-stream-test-suite.cc
--- ns-3.40/src/core/test/random-variable-stream-test-suite.cc	2023-09-27 13:30:37.634839000 -0700
+++ ns-3.41/src/core/test/random-variable-stream-test-suite.cc	2024-02-09 14:44:07.893790000 -0800
@@ -34,6 +34,7 @@
 #include <fstream>
 #include <gsl/gsl_cdf.h>
 #include <gsl/gsl_histogram.h>
+#include <gsl/gsl_randist.h>
 #include <gsl/gsl_sf_zeta.h>
 
 using namespace ns3;
@@ -2567,6 +2568,312 @@
 
 /**
  * \ingroup rng-tests
+ * Test case for bernoulli distribution random variable stream generator
+ */
+class BernoulliTestCase : public TestCaseBase
+{
+  public:
+    // Constructor
+    BernoulliTestCase();
+
+    // Inherited
+    double ChiSquaredTest(Ptr<RandomVariableStream> rng) const override;
+
+  private:
+    // Inherited
+    void DoRun() override;
+
+    /** Tolerance for testing rng values against expectation, in rms. */
+    static constexpr double TOLERANCE{5};
+};
+
+BernoulliTestCase::BernoulliTestCase()
+    : TestCaseBase("Bernoulli Random Variable Stream Generator")
+{
+}
+
+double
+BernoulliTestCase::ChiSquaredTest(Ptr<RandomVariableStream> rng) const
+{
+    gsl_histogram* h = gsl_histogram_alloc(2);
+    auto range = UniformHistogramBins(h, 0, 1);
+
+    double p = 0.5;
+    std::vector<double> expected = {N_MEASUREMENTS * (1 - p), N_MEASUREMENTS * p};
+
+    double chiSquared = ChiSquared(h, expected, rng);
+
+    gsl_histogram_free(h);
+    return chiSquared;
+}
+
+void
+BernoulliTestCase::DoRun()
+{
+    NS_LOG_FUNCTION(this);
+    SetTestSuiteSeed();
+
+    auto generator = RngGenerator<BernoulliRandomVariable>();
+    double sum = ChiSquaredsAverage(&generator, N_RUNS);
+    double maxStatistic = gsl_cdf_chisq_Qinv(0.05, 1);
+    NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
+
+    double probability = 0.5;
+
+    // Create the RNG with the specified range.
+    Ptr<BernoulliRandomVariable> x = CreateObject<BernoulliRandomVariable>();
+    x->SetAttribute("Probability", DoubleValue(probability));
+
+    // Calculate the mean of these values.
+    double mean = probability;
+    double valueMean = Average(x);
+    double expectedMean = mean;
+    double expectedRms = std::sqrt(mean / N_MEASUREMENTS);
+
+    // Test that values have approximately the right mean value.
+    NS_TEST_ASSERT_MSG_EQ_TOL(valueMean,
+                              expectedMean,
+                              expectedRms * TOLERANCE,
+                              "Wrong mean value.");
+}
+
+/**
+ * \ingroup rng-tests
+ * Test case for antithetic bernoulli distribution random variable stream generator
+ */
+class BernoulliAntitheticTestCase : public TestCaseBase
+{
+  public:
+    // Constructor
+    BernoulliAntitheticTestCase();
+
+    // Inherited
+    double ChiSquaredTest(Ptr<RandomVariableStream> rng) const override;
+
+  private:
+    // Inherited
+    void DoRun() override;
+
+    /** Tolerance for testing rng values against expectation, in rms. */
+    static constexpr double TOLERANCE{5};
+};
+
+BernoulliAntitheticTestCase::BernoulliAntitheticTestCase()
+    : TestCaseBase("Antithetic Bernoulli Random Variable Stream Generator")
+{
+}
+
+double
+BernoulliAntitheticTestCase::ChiSquaredTest(Ptr<RandomVariableStream> rng) const
+{
+    gsl_histogram* h = gsl_histogram_alloc(2);
+    auto range = UniformHistogramBins(h, 0, 1);
+
+    double p = 0.5;
+    std::vector<double> expected = {N_MEASUREMENTS * (1 - p), N_MEASUREMENTS * p};
+
+    double chiSquared = ChiSquared(h, expected, rng);
+
+    gsl_histogram_free(h);
+    return chiSquared;
+}
+
+void
+BernoulliAntitheticTestCase::DoRun()
+{
+    NS_LOG_FUNCTION(this);
+    SetTestSuiteSeed();
+
+    auto generator = RngGenerator<BernoulliRandomVariable>(true);
+    double sum = ChiSquaredsAverage(&generator, N_RUNS);
+    double maxStatistic = gsl_cdf_chisq_Qinv(0.05, 1);
+    NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
+
+    double probability = 0.5;
+
+    // Create the RNG with the specified range.
+    Ptr<BernoulliRandomVariable> x = CreateObject<BernoulliRandomVariable>();
+    x->SetAttribute("Probability", DoubleValue(probability));
+
+    // Make this generate antithetic values.
+    x->SetAttribute("Antithetic", BooleanValue(true));
+
+    // Calculate the mean of these values.
+    double mean = probability;
+    double valueMean = Average(x);
+    double expectedMean = mean;
+    double expectedRms = std::sqrt(mean / N_MEASUREMENTS);
+
+    // Test that values have approximately the right mean value.
+    NS_TEST_ASSERT_MSG_EQ_TOL(valueMean,
+                              expectedMean,
+                              expectedRms * TOLERANCE,
+                              "Wrong mean value.");
+}
+
+/**
+ * \ingroup rng-tests
+ * Test case for binomial distribution random variable stream generator
+ */
+class BinomialTestCase : public TestCaseBase
+{
+  public:
+    // Constructor
+    BinomialTestCase();
+
+    // Inherited
+    double ChiSquaredTest(Ptr<RandomVariableStream> rng) const override;
+
+  private:
+    // Inherited
+    void DoRun() override;
+
+    /** Tolerance for testing rng values against expectation, in rms. */
+    static constexpr double TOLERANCE{5};
+};
+
+BinomialTestCase::BinomialTestCase()
+    : TestCaseBase("Binomial Random Variable Stream Generator")
+{
+}
+
+double
+BinomialTestCase::ChiSquaredTest(Ptr<RandomVariableStream> rng) const
+{
+    uint32_t trials = 10;
+    double probability = 0.5;
+
+    gsl_histogram* h = gsl_histogram_alloc(trials + 1);
+    auto range = UniformHistogramBins(h, 0, trials);
+
+    std::vector<double> expected(trials + 1);
+    for (std::size_t i = 0; i < trials + 1; ++i)
+    {
+        expected[i] = N_MEASUREMENTS * gsl_ran_binomial_pdf(i, probability, trials);
+    }
+
+    double chiSquared = ChiSquared(h, expected, rng);
+
+    gsl_histogram_free(h);
+    return chiSquared;
+}
+
+void
+BinomialTestCase::DoRun()
+{
+    NS_LOG_FUNCTION(this);
+    SetTestSuiteSeed();
+
+    uint32_t trials = 10;
+    double probability = 0.5;
+
+    auto generator = RngGenerator<BinomialRandomVariable>();
+    double sum = ChiSquaredsAverage(&generator, N_RUNS);
+    double maxStatistic = gsl_cdf_chisq_Qinv(0.05, trials);
+    NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
+
+    // Create the RNG with the specified range.
+    Ptr<BinomialRandomVariable> x = CreateObject<BinomialRandomVariable>();
+    x->SetAttribute("Trials", IntegerValue(trials));
+    x->SetAttribute("Probability", DoubleValue(probability));
+
+    // Calculate the mean of these values.
+    double mean = trials * probability;
+    double valueMean = Average(x);
+    double expectedMean = mean;
+    double expectedRms = std::sqrt(mean / N_MEASUREMENTS);
+
+    // Test that values have approximately the right mean value.
+    NS_TEST_ASSERT_MSG_EQ_TOL(valueMean,
+                              expectedMean,
+                              expectedRms * TOLERANCE,
+                              "Wrong mean value.");
+}
+
+/**
+ * \ingroup rng-tests
+ * Test case for antithetic binomial distribution random variable stream generator
+ */
+class BinomialAntitheticTestCase : public TestCaseBase
+{
+  public:
+    // Constructor
+    BinomialAntitheticTestCase();
+
+    // Inherited
+    double ChiSquaredTest(Ptr<RandomVariableStream> rng) const override;
+
+  private:
+    // Inherited
+    void DoRun() override;
+
+    /** Tolerance for testing rng values against expectation, in rms. */
+    static constexpr double TOLERANCE{5};
+};
+
+BinomialAntitheticTestCase::BinomialAntitheticTestCase()
+    : TestCaseBase("Antithetic Binomial Random Variable Stream Generator")
+{
+}
+
+double
+BinomialAntitheticTestCase::ChiSquaredTest(Ptr<RandomVariableStream> rng) const
+{
+    uint32_t trials = 10;
+    double probability = 0.5;
+
+    gsl_histogram* h = gsl_histogram_alloc(trials + 1);
+    auto range = UniformHistogramBins(h, 0, trials);
+
+    std::vector<double> expected(trials + 1);
+    for (std::size_t i = 0; i < trials + 1; ++i)
+    {
+        expected[i] = N_MEASUREMENTS * gsl_ran_binomial_pdf(i, probability, trials);
+    }
+
+    double chiSquared = ChiSquared(h, expected, rng);
+
+    gsl_histogram_free(h);
+    return chiSquared;
+}
+
+void
+BinomialAntitheticTestCase::DoRun()
+{
+    NS_LOG_FUNCTION(this);
+    SetTestSuiteSeed();
+
+    uint32_t trials = 10;
+    double probability = 0.5;
+
+    auto generator = RngGenerator<BinomialRandomVariable>(true);
+    double sum = ChiSquaredsAverage(&generator, N_RUNS);
+    double maxStatistic = gsl_cdf_chisq_Qinv(0.05, trials);
+    NS_TEST_ASSERT_MSG_LT(sum, maxStatistic, "Chi-squared statistic out of range");
+
+    // Create the RNG with the specified range.
+    Ptr<BinomialRandomVariable> x = CreateObject<BinomialRandomVariable>();
+    x->SetAttribute("Trials", IntegerValue(trials));
+    x->SetAttribute("Probability", DoubleValue(probability));
+
+    // Make this generate antithetic values.
+    x->SetAttribute("Antithetic", BooleanValue(true));
+
+    // Calculate the mean of these values.
+    double mean = trials * probability;
+    double valueMean = Average(x);
+    double expectedMean = mean;
+    double expectedRms = std::sqrt(mean / N_MEASUREMENTS);
+
+    // Test that values have approximately the right mean value.
+    NS_TEST_ASSERT_MSG_EQ_TOL(valueMean,
+                              expectedMean,
+                              expectedRms * TOLERANCE,
+                              "Wrong mean value.");
+}
+
+/**
+ * \ingroup rng-tests
  * RandomVariableStream test suite, covering all random number variable
  * stream generator types.
  */
@@ -2617,6 +2924,10 @@
     AddTestCase(new EmpiricalAntitheticTestCase);
     /// Issue #302:  NormalRandomVariable produces stale values
     AddTestCase(new NormalCachingTestCase);
+    AddTestCase(new BernoulliTestCase);
+    AddTestCase(new BernoulliAntitheticTestCase);
+    AddTestCase(new BinomialTestCase);
+    AddTestCase(new BinomialAntitheticTestCase);
 }
 
 static RandomVariableSuite randomVariableSuite; //!< Static variable for test initialization
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/test/tuple-value-test-suite.cc ns-3.41/src/core/test/tuple-value-test-suite.cc
--- ns-3.40/src/core/test/tuple-value-test-suite.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/core/test/tuple-value-test-suite.cc	2024-02-09 14:44:07.893790000 -0800
@@ -62,8 +62,9 @@
 
     // NOTE EnumValue::Get() return an int, so the tuple element type must be an int
     // in place of the enum type
-    using Tuple1Value = TupleValue<StringValue, StringValue, EnumValue>; //!< Tuple1 attribute value
-    using Tuple1 = Tuple1Value::result_type;                             //!< tuple of values
+    using Tuple1Value =
+        TupleValue<StringValue, StringValue, EnumValue<TupleTestEnum>>; //!< Tuple1 attribute value
+    using Tuple1 = Tuple1Value::result_type;                            //!< tuple of values
     using Tuple1Pack = Tuple1Value::value_type; //!< tuple of attribute values
 
     using Tuple2 = std::tuple<double, uint16_t, std::string>; //!< Tuple2 typedef
@@ -207,7 +208,7 @@
             TupleObject::Tuple1{"Norwegian", "Wood", TupleObject::VALUE2}));
     NS_TEST_ASSERT_MSG_EQ(ret1, true, "Setting valid values to tuple 1 failed");
 
-    TupleValue<StringValue, StringValue, EnumValue> tupleValue1;
+    TupleValue<StringValue, StringValue, EnumValue<TupleObject::TupleTestEnum>> tupleValue1;
     ret1 = tupleObject->GetAttributeFailSafe("StringStringEnumTuple", tupleValue1);
     NS_TEST_ASSERT_MSG_EQ(ret1, true, "Getting values for tuple 1 failed");
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/core/test/val-array-test-suite.cc ns-3.41/src/core/test/val-array-test-suite.cc
--- ns-3.40/src/core/test/val-array-test-suite.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/core/test/val-array-test-suite.cc	2024-02-09 14:44:07.893790000 -0800
@@ -42,21 +42,21 @@
 {
   public:
     /** Default constructor*/
-    ValArrayTestCase<T>() = default;
+    ValArrayTestCase() = default;
     /**
      * Constructor
      *
      * \param [in] name reference name
      */
-    ValArrayTestCase<T>(const std::string& name);
+    ValArrayTestCase(const std::string& name);
 
     /** Destructor. */
-    ~ValArrayTestCase<T>() override;
+    ~ValArrayTestCase() override;
     /**
      * \brief Copy constructor.
      * Instruct the compiler to generate the implicitly declared copy constructor
      */
-    ValArrayTestCase<T>(const ValArrayTestCase<T>&) = default;
+    ValArrayTestCase(const ValArrayTestCase<T>&) = default;
     /**
      * \brief Copy assignment operator.
      * Instruct the compiler to generate the implicitly declared copy assignment operator.
@@ -67,7 +67,7 @@
      * \brief Move constructor.
      * Instruct the compiler to generate the implicitly declared move constructor
      */
-    ValArrayTestCase<T>(ValArrayTestCase<T>&&) = default;
+    ValArrayTestCase(ValArrayTestCase<T>&&) = default;
     /**
      * \brief Move assignment operator.
      * Instruct the compiler to generate the implicitly declared copy constructor
@@ -86,7 +86,7 @@
 }
 
 template <class T>
-ValArrayTestCase<T>::~ValArrayTestCase<T>()
+ValArrayTestCase<T>::~ValArrayTestCase()
 {
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/csma/CMakeLists.txt ns-3.41/src/csma/CMakeLists.txt
--- ns-3.40/src/csma/CMakeLists.txt	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/csma/CMakeLists.txt	2024-02-09 14:44:07.893790000 -0800
@@ -11,5 +11,4 @@
     model/csma-channel.h
     model/csma-net-device.h
   LIBRARIES_TO_LINK ${libnetwork}
-  TEST_SOURCES ${test_sources}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/csma/model/csma-channel.cc ns-3.41/src/csma/model/csma-channel.cc
--- ns-3.40/src/csma/model/csma-channel.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/csma/model/csma-channel.cc	2024-02-09 14:44:07.897790000 -0800
@@ -191,7 +191,7 @@
     }
 
     NS_LOG_LOGIC("switch to TRANSMITTING");
-    m_currentPkt = p->Copy();
+    m_currentPkt = p;
     m_currentSrc = srcId;
     m_state = TRANSMITTING;
     return true;
@@ -200,7 +200,7 @@
 bool
 CsmaChannel::IsActive(uint32_t deviceId)
 {
-    return (m_deviceList[deviceId].active);
+    return m_deviceList[deviceId].active;
 }
 
 bool
@@ -234,7 +234,7 @@
                                            m_delay,
                                            &CsmaNetDevice::Receive,
                                            it->devicePtr,
-                                           m_currentPkt->Copy(),
+                                           m_currentPkt,
                                            m_deviceList[m_currentSrc].devicePtr);
         }
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/csma/model/csma-channel.h ns-3.41/src/csma/model/csma-channel.h
--- ns-3.40/src/csma/model/csma-channel.h	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/csma/model/csma-channel.h	2024-02-09 14:44:07.897790000 -0800
@@ -325,7 +325,7 @@
      * packet to have been transmitted on the channel if the channel is
      * free.)
      */
-    Ptr<Packet> m_currentPkt;
+    Ptr<const Packet> m_currentPkt;
 
     /**
      * Device Id of the source that is currently transmitting on the
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/csma/model/csma-net-device.cc ns-3.41/src/csma/model/csma-net-device.cc
--- ns-3.40/src/csma/model/csma-net-device.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/csma/model/csma-net-device.cc	2024-02-09 14:44:07.897790000 -0800
@@ -62,7 +62,7 @@
             .AddAttribute("EncapsulationMode",
                           "The link-layer encapsulation type to use.",
                           EnumValue(DIX),
-                          MakeEnumAccessor(&CsmaNetDevice::SetEncapsulationMode),
+                          MakeEnumAccessor<EncapsulationMode>(&CsmaNetDevice::SetEncapsulationMode),
                           MakeEnumChecker(DIX, "Dix", LLC, "Llc"))
             .AddAttribute("SendEnable",
                           "Enable or disable the transmitter section of the device.",
@@ -695,7 +695,7 @@
 }
 
 void
-CsmaNetDevice::Receive(Ptr<Packet> packet, Ptr<CsmaNetDevice> senderDevice)
+CsmaNetDevice::Receive(Ptr<const Packet> packet, Ptr<CsmaNetDevice> senderDevice)
 {
     NS_LOG_FUNCTION(packet << senderDevice);
     NS_LOG_LOGIC("UID is " << packet->GetUid());
@@ -724,7 +724,9 @@
         return;
     }
 
-    if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt(packet))
+    Ptr<Packet> pktCopy = packet->Copy();
+
+    if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt(pktCopy))
     {
         NS_LOG_LOGIC("Dropping pkt due to error model ");
         m_phyRxDropTrace(packet);
@@ -735,16 +737,15 @@
     // Trace sinks will expect complete packets, not packets without some of the
     // headers.
     //
-    Ptr<Packet> originalPacket = packet->Copy();
 
     EthernetTrailer trailer;
-    packet->RemoveTrailer(trailer);
+    pktCopy->RemoveTrailer(trailer);
     if (Node::ChecksumEnabled())
     {
         trailer.EnableFcs(true);
     }
 
-    bool crcGood = trailer.CheckFcs(packet);
+    bool crcGood = trailer.CheckFcs(pktCopy);
     if (!crcGood)
     {
         NS_LOG_INFO("CRC error on Packet " << packet);
@@ -753,7 +754,7 @@
     }
 
     EthernetHeader header(false);
-    packet->RemoveHeader(header);
+    pktCopy->RemoveHeader(header);
 
     NS_LOG_LOGIC("Pkt source is " << header.GetSource());
     NS_LOG_LOGIC("Pkt destination is " << header.GetDestination());
@@ -767,16 +768,16 @@
     //
     if (header.GetLengthType() <= 1500)
     {
-        NS_ASSERT(packet->GetSize() >= header.GetLengthType());
-        uint32_t padlen = packet->GetSize() - header.GetLengthType();
+        NS_ASSERT(pktCopy->GetSize() >= header.GetLengthType());
+        uint32_t padlen = pktCopy->GetSize() - header.GetLengthType();
         NS_ASSERT(padlen <= 46);
         if (padlen > 0)
         {
-            packet->RemoveAtEnd(padlen);
+            pktCopy->RemoveAtEnd(padlen);
         }
 
         LlcSnapHeader llc;
-        packet->RemoveHeader(llc);
+        pktCopy->RemoveHeader(llc);
         protocol = llc.GetType();
     }
     else
@@ -811,12 +812,12 @@
     // hook and pass a copy up to the promiscuous callback.  Pass a copy to
     // make sure that nobody messes with our packet.
     //
-    m_promiscSnifferTrace(originalPacket);
+    m_promiscSnifferTrace(packet);
     if (!m_promiscRxCallback.IsNull())
     {
-        m_macPromiscRxTrace(originalPacket);
+        m_macPromiscRxTrace(packet);
         m_promiscRxCallback(this,
-                            packet,
+                            pktCopy,
                             protocol,
                             header.GetSource(),
                             header.GetDestination(),
@@ -830,9 +831,9 @@
     //
     if (packetType != PACKET_OTHERHOST)
     {
-        m_snifferTrace(originalPacket);
-        m_macRxTrace(originalPacket);
-        m_rxCallback(this, packet, protocol, header.GetSource());
+        m_snifferTrace(packet);
+        m_macRxTrace(packet);
+        m_rxCallback(this, pktCopy, protocol, header.GetSource());
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/csma/model/csma-net-device.h ns-3.41/src/csma/model/csma-net-device.h
--- ns-3.40/src/csma/model/csma-net-device.h	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/csma/model/csma-net-device.h	2024-02-09 14:44:07.897790000 -0800
@@ -172,7 +172,7 @@
      * \param p a reference to the received packet
      * \param sender the CsmaNetDevice that transmitted the packet in the first place
      */
-    void Receive(Ptr<Packet> p, Ptr<CsmaNetDevice> sender);
+    void Receive(Ptr<const Packet> p, Ptr<CsmaNetDevice> sender);
 
     /**
      * Is the send side of the network device enabled?
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/csma-layout/CMakeLists.txt ns-3.41/src/csma-layout/CMakeLists.txt
--- ns-3.40/src/csma-layout/CMakeLists.txt	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/csma-layout/CMakeLists.txt	2024-02-09 14:44:07.893790000 -0800
@@ -3,7 +3,6 @@
   SOURCE_FILES model/csma-star-helper.cc
   HEADER_FILES model/csma-star-helper.h
   LIBRARIES_TO_LINK
-    ${libnetwork}
     ${libinternet}
     ${libcsma}
     ${libpoint-to-point}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsdv/CMakeLists.txt ns-3.41/src/dsdv/CMakeLists.txt
--- ns-3.40/src/dsdv/CMakeLists.txt	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsdv/CMakeLists.txt	2024-02-09 14:44:07.897790000 -0800
@@ -12,6 +12,7 @@
     model/dsdv-packet.h
     model/dsdv-routing-protocol.h
     model/dsdv-rtable.h
-  LIBRARIES_TO_LINK ${libinternet}
+  LIBRARIES_TO_LINK ${libmesh}
+                    ${libinternet-apps}
   TEST_SOURCES test/dsdv-testcase.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsdv/model/dsdv-routing-protocol.cc ns-3.41/src/dsdv/model/dsdv-routing-protocol.cc
--- ns-3.40/src/dsdv/model/dsdv-routing-protocol.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsdv/model/dsdv-routing-protocol.cc	2024-02-09 14:44:07.897790000 -0800
@@ -932,7 +932,7 @@
             else
             {
                 dsdvHeader.SetDst(i->second.GetDestination());
-                dsdvHeader.SetDstSeqno((i->second.GetSeqNo()));
+                dsdvHeader.SetDstSeqno(i->second.GetSeqNo());
                 dsdvHeader.SetHopCount(i->second.GetHop() + 1);
                 packet->AddHeader(dsdvHeader);
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsdv/model/dsdv-rtable.h ns-3.41/src/dsdv/model/dsdv-rtable.h
--- ns-3.40/src/dsdv/model/dsdv-rtable.h	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsdv/model/dsdv-rtable.h	2024-02-09 14:44:07.897790000 -0800
@@ -233,7 +233,7 @@
      */
     Time GetSettlingTime() const
     {
-        return (m_settlingTime);
+        return m_settlingTime;
     }
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsdv/test/dsdv-testcase.cc ns-3.41/src/dsdv/test/dsdv-testcase.cc
--- ns-3.40/src/dsdv/test/dsdv-testcase.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsdv/test/dsdv-testcase.cc	2024-02-09 14:44:07.897790000 -0800
@@ -41,7 +41,6 @@
 #include "ns3/string.h"
 #include "ns3/test.h"
 #include "ns3/uinteger.h"
-#include "ns3/v4ping-helper.h"
 
 using namespace ns3;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsr/CMakeLists.txt ns-3.41/src/dsr/CMakeLists.txt
--- ns-3.40/src/dsr/CMakeLists.txt	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsr/CMakeLists.txt	2024-02-09 14:44:07.897790000 -0800
@@ -30,7 +30,6 @@
     model/dsr-routing.h
     model/dsr-rreq-table.h
     model/dsr-rsendbuff.h
-  LIBRARIES_TO_LINK ${libinternet}
-                    ${libwifi}
+  LIBRARIES_TO_LINK ${libmesh}
   TEST_SOURCES test/dsr-test-suite.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsr/examples/dsr.cc ns-3.41/src/dsr/examples/dsr.cc
--- ns-3.40/src/dsr/examples/dsr.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsr/examples/dsr.cc	2024-02-09 14:44:07.897790000 -0800
@@ -29,7 +29,6 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/dsr-module.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsr/model/dsr-options.cc ns-3.41/src/dsr/model/dsr-options.cc
--- ns-3.40/src/dsr/model/dsr-options.cc	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsr/model/dsr-options.cc	2024-02-09 14:44:07.901790100 -0800
@@ -755,7 +755,7 @@
                     //   {
                     //     dsr->UseExtends (m_finalRoute);
                     //   }
-                    sourceRoute.SetSegmentsLeft((m_finalRoute.size() - 2));
+                    sourceRoute.SetSegmentsLeft(m_finalRoute.size() - 2);
                     // The salvage value here is 0
                     sourceRoute.SetSalvage(0);
                     Ipv4Address nextHop =
@@ -860,7 +860,7 @@
                     //   {
                     //     dsr->UseExtends (saveRoute);
                     //   }
-                    sourceRoute.SetSegmentsLeft((saveRoute.size() - 2));
+                    sourceRoute.SetSegmentsLeft(saveRoute.size() - 2);
                     uint8_t salvage = 0;
                     sourceRoute.SetSalvage(salvage);
                     Ipv4Address nextHop =
@@ -1123,7 +1123,7 @@
             DsrOptionSRHeader sourceRoute;
             NS_LOG_DEBUG("The route length " << nodeList.size());
             sourceRoute.SetNodesAddress(nodeList);
-            sourceRoute.SetSegmentsLeft((nodeList.size() - 2));
+            sourceRoute.SetSegmentsLeft(nodeList.size() - 2);
             sourceRoute.SetSalvage(0);
             Ipv4Address nextHop = SearchNextHop(ipv4Address, nodeList); // Get the next hop address
             NS_LOG_DEBUG("The nextHop address " << nextHop);
@@ -1759,7 +1759,7 @@
     DsrOptionAckReqHeader ackReq;
     p->RemoveHeader(ackReq);
     /*
-     * Get the node with ip address and get the dsr extension and reoute cache objects
+     * Get the node with ip address and get the dsr extension and route cache objects
      */
     Ptr<Node> node = GetNodeWithAddress(ipv4Address);
     Ptr<dsr::DsrRouting> dsr = node->GetObject<dsr::DsrRouting>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsr/model/dsr-rcache.h ns-3.41/src/dsr/model/dsr-rcache.h
--- ns-3.40/src/dsr/model/dsr-rcache.h	2023-09-27 13:30:37.638839000 -0700
+++ ns-3.41/src/dsr/model/dsr-rcache.h	2024-02-09 14:44:07.901790100 -0800
@@ -345,41 +345,15 @@
     void Print(std::ostream& os) const;
 
     /**
-     * \brief Compare the route cache entry
+     * \brief Compare the route cache entry. Only the paths are compared.
      * \param o entry to compare
-     * \return true if equal
+     * \return true if both route cache entries are equal
      */
     bool operator==(const DsrRouteCacheEntry& o) const
     {
-        if (m_path.size() != o.m_path.size())
-        {
-            NS_ASSERT(false);
-            return false;
-        }
-        auto j = o.m_path.begin();
-        for (auto i = m_path.begin(); i != m_path.end(); i++, j++)
-        {
-            /*
-             * Verify if neither the entry are not 0 and they equal to each other
-             */
-            if (((*i) == nullptr) || ((*j) == nullptr))
-            {
-                return false;
-            }
-            else if (!((*i) == (*j)))
-            {
-                return false;
-            }
-            else
-            {
-                return true;
-            }
-        }
-        return false;
+        return m_path == o.m_path;
     }
 
-    // \}
-
   private:
     Timer m_ackTimer;             ///< RREP_ACK timer
     Ipv4Address m_dst;            ///< The destination Ip address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/dsr/model/dsr-routing.cc ns-3.41/src/dsr/model/dsr-routing.cc
--- ns-3.40/src/dsr/model/dsr-routing.cc	2023-09-27 13:30:37.642839000 -0700
+++ ns-3.41/src/dsr/model/dsr-routing.cc	2024-02-09 14:44:07.901790100 -0800
@@ -927,7 +927,7 @@
                     {
                         m_routeCache->UseExtends(errorRoute);
                     }
-                    sourceRoute.SetSegmentsLeft((errorRoute.size() - 2));
+                    sourceRoute.SetSegmentsLeft(errorRoute.size() - 2);
                     uint8_t salvage = 0;
                     sourceRoute.SetSalvage(salvage);
                     Ipv4Address nextHop =
@@ -1000,12 +1000,12 @@
                     return;
                 }
                 uint8_t salvage = 0;
-                sourceRoute.SetNodesAddress(
-                    nodeList); // Save the whole route in the source route header of the packet
-                sourceRoute.SetSegmentsLeft(
-                    (nodeList.size() - 2)); // The segmentsLeft field will indicate the hops to go
+                // Save the whole route in the source route header of the packet
+                sourceRoute.SetNodesAddress(nodeList);
+                // The segmentsLeft field will indicate the hops to go
+                sourceRoute.SetSegmentsLeft(nodeList.size() - 2);
                 sourceRoute.SetSalvage(salvage);
-                /// When found a route and use it, UseExtends to the link cache
+                // When found a route and use it, UseExtends to the link cache
                 if (m_routeCache->IsLinkCache())
                 {
                     m_routeCache->UseExtends(nodeList);
@@ -1273,8 +1273,8 @@
         {
             m_routeCache->UseExtends(nodeList);
         }
-        sourceRoute.SetSegmentsLeft(
-            (nodeList.size() - 2)); // The segmentsLeft field will indicate the hops to go
+        // The segmentsLeft field will indicate the hops to go
+        sourceRoute.SetSegmentsLeft(nodeList.size() - 2);
         sourceRoute.SetSalvage(salvage);
 
         uint8_t length = sourceRoute.GetLength();
@@ -1431,7 +1431,7 @@
         {
             m_routeCache->UseExtends(nodeList);
         }
-        sourceRoute.SetSegmentsLeft((nodeList.size() - 2));
+        sourceRoute.SetSegmentsLeft(nodeList.size() - 2);
         uint8_t srLength = sourceRoute.GetLength();
         uint8_t length = (srLength + rerrLength);
 
@@ -1600,8 +1600,8 @@
             {
                 m_routeCache->UseExtends(nodeList);
             }
-            sourceRoute.SetSegmentsLeft(
-                (nodeList.size() - 2)); // The segmentsLeft field will indicate the hops to go
+            // The segmentsLeft field will indicate the hops to go
+            sourceRoute.SetSegmentsLeft(nodeList.size() - 2);
             sourceRoute.SetSalvage(salvage);
 
             uint8_t length = sourceRoute.GetLength();
@@ -2491,10 +2491,10 @@
         salvage++;
         DsrOptionSRHeader sourceRoute;
         sourceRoute.SetSalvage(salvage);
-        sourceRoute.SetNodesAddress(
-            nodeList); // Save the whole route in the source route header of the packet
-        sourceRoute.SetSegmentsLeft(
-            (nodeList.size() - 2)); // The segmentsLeft field will indicate the hops to go
+        // Save the whole route in the source route header of the packet
+        sourceRoute.SetNodesAddress(nodeList);
+        // The segmentsLeft field will indicate the hops to go
+        sourceRoute.SetSegmentsLeft(nodeList.size() - 2);
         /// When found a route and use it, UseExtends to the link cache
         if (m_routeCache->IsLinkCache())
         {
@@ -3000,7 +3000,7 @@
         {
             m_routeCache->UseExtends(ip);
         }
-        sourceRoute.SetSegmentsLeft((ip.size() - 2));
+        sourceRoute.SetSegmentsLeft(ip.size() - 2);
         sourceRoute.SetSalvage(salvage);
         Ipv4Address nextHop = SearchNextHop(m_mainAddress, ip); // Get the next hop address
         NS_LOG_DEBUG("The nextHop address " << nextHop);
@@ -3237,7 +3237,7 @@
         {
             m_routeCache->UseExtends(ip);
         }
-        sourceRoute.SetSegmentsLeft((ip.size() - 2));
+        sourceRoute.SetSegmentsLeft(ip.size() - 2);
         /// Set the salvage value to 0
         sourceRoute.SetSalvage(0);
         Ipv4Address nextHop = SearchNextHop(m_mainAddress, ip); // Get the next hop address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/energy/examples/basic-energy-model-test.cc ns-3.41/src/energy/examples/basic-energy-model-test.cc
--- ns-3.40/src/energy/examples/basic-energy-model-test.cc	2023-09-27 13:30:37.642839000 -0700
+++ ns-3.41/src/energy/examples/basic-energy-model-test.cc	2024-02-09 14:44:07.905790000 -0800
@@ -145,7 +145,7 @@
     // retrieve device energy model from energy source
     DeviceEnergyModelContainer models = source->FindDeviceEnergyModels("ns3::WifiRadioEnergyModel");
     // check list
-    if ((models.GetN() == 0))
+    if (models.GetN() == 0)
     {
         std::cerr << "Model list is empty!." << std::endl;
         return true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/energy/examples/CMakeLists.txt ns-3.41/src/energy/examples/CMakeLists.txt
--- ns-3.40/src/energy/examples/CMakeLists.txt	2023-09-27 13:30:37.642839000 -0700
+++ ns-3.41/src/energy/examples/CMakeLists.txt	2024-02-09 14:44:07.905790000 -0800
@@ -5,7 +5,7 @@
                     ${libenergy}
 )
 
-build_example(
+build_lib_example(
   NAME generic-battery-wifiradio-example
   SOURCE_FILES generic-battery-wifiradio-example.cc
   LIBRARIES_TO_LINK
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/energy/examples/generic-battery-discharge-example.py ns-3.41/src/energy/examples/generic-battery-discharge-example.py
--- ns-3.40/src/energy/examples/generic-battery-discharge-example.py	2023-09-27 13:30:37.642839000 -0700
+++ ns-3.41/src/energy/examples/generic-battery-discharge-example.py	2024-02-09 14:44:07.905790000 -0800
@@ -20,7 +20,15 @@
 #  Demonstrates the discharge behavior of a NIMH battery discharged with a
 #  constant current of 6.5 A (1C)
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
+
 
 def main(argv):
     """The main function in this Battery discharge example
@@ -32,7 +40,7 @@
     ns.core.LogComponentEnable("GenericBatteryModel", ns.core.LOG_LEVEL_DEBUG)
 
     node = ns.network.Node()
-    batteryHelper =  ns.energy.GenericBatteryModelHelper()
+    batteryHelper = ns.energy.GenericBatteryModelHelper()
     batteryModel = ns.CreateObject("GenericBatteryModel")
     devicesEnergyModel = ns.energy.SimpleDeviceEnergyModel()
 
@@ -40,14 +48,14 @@
     batteryModel.SetAttribute("MaxCapacity", ns.core.DoubleValue(7.0))  # Q
 
     batteryModel.SetAttribute("NominalVoltage", ns.core.DoubleValue(1.18))  # Vnom
-    batteryModel.SetAttribute("NominalCapacity", ns.core.DoubleValue(6.25)) # QNom
+    batteryModel.SetAttribute("NominalCapacity", ns.core.DoubleValue(6.25))  # QNom
 
-    batteryModel.SetAttribute("ExponentialVoltage", ns.core.DoubleValue(1.28)) # Vexp
-    batteryModel.SetAttribute("ExponentialCapacity", ns.core.DoubleValue(1.3)) # Qexp
+    batteryModel.SetAttribute("ExponentialVoltage", ns.core.DoubleValue(1.28))  # Vexp
+    batteryModel.SetAttribute("ExponentialCapacity", ns.core.DoubleValue(1.3))  # Qexp
 
-    batteryModel.SetAttribute("InternalResistance", ns.core.DoubleValue(0.0046))   # R
-    batteryModel.SetAttribute("TypicalDischargeCurrent", ns.core.DoubleValue(1.3)) # i typical
-    batteryModel.SetAttribute("CutoffVoltage", ns.core.DoubleValue(1.0))   # End of charge.
+    batteryModel.SetAttribute("InternalResistance", ns.core.DoubleValue(0.0046))  # R
+    batteryModel.SetAttribute("TypicalDischargeCurrent", ns.core.DoubleValue(1.3))  # i typical
+    batteryModel.SetAttribute("CutoffVoltage", ns.core.DoubleValue(1.0))  # End of charge.
 
     batteryModel.SetAttribute("BatteryType", ns.core.EnumValue(ns.NIMH_NICD))  # Battery type
 
@@ -57,14 +65,12 @@
 
     devicesEnergyModel.SetCurrentA(6.5)
 
-
     ns.core.Simulator.Stop(ns.core.Seconds(3600))
     ns.core.Simulator.Run()
     ns.core.Simulator.Destroy()
 
 
-
-if __name__ == '__main__':
+if __name__ == "__main__":
     import sys
-    main(sys.argv)
 
+    main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/energy/model/generic-battery-model.cc ns-3.41/src/energy/model/generic-battery-model.cc
--- ns-3.40/src/energy/model/generic-battery-model.cc	2023-09-27 13:30:37.642839000 -0700
+++ ns-3.41/src/energy/model/generic-battery-model.cc	2024-02-09 14:44:07.905790000 -0800
@@ -104,7 +104,7 @@
             .AddAttribute("BatteryType",
                           "Indicates the battery type used by the model",
                           EnumValue(LION_LIPO),
-                          MakeEnumAccessor(&GenericBatteryModel::m_batteryType),
+                          MakeEnumAccessor<GenericBatteryType>(&GenericBatteryModel::m_batteryType),
                           MakeEnumChecker(LION_LIPO,
                                           "LION_LIPO",
                                           NIMH_NICD,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/energy/model/li-ion-energy-source.h ns-3.41/src/energy/model/li-ion-energy-source.h
--- ns-3.40/src/energy/model/li-ion-energy-source.h	2023-09-27 13:30:37.642839000 -0700
+++ ns-3.41/src/energy/model/li-ion-energy-source.h	2024-02-09 14:44:07.905790000 -0800
@@ -22,6 +22,7 @@
 
 #include "energy-source.h"
 
+#include "ns3/deprecated.h"
 #include "ns3/event-id.h"
 #include "ns3/nstime.h"
 #include "ns3/traced-value.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/fd-net-device/CMakeLists.txt ns-3.41/src/fd-net-device/CMakeLists.txt
--- ns-3.40/src/fd-net-device/CMakeLists.txt	2023-09-27 13:30:37.642839000 -0700
+++ ns-3.41/src/fd-net-device/CMakeLists.txt	2024-02-09 14:44:07.909790000 -0800
@@ -26,6 +26,9 @@
 )
 
 include(FindPkgConfig)
+set(DPDK_INCLUDE_DIRS
+    ""
+)
 if(PKG_CONFIG_FOUND)
   pkg_check_modules(
     DPDK
@@ -137,6 +140,8 @@
     FdNetDevice
   )
 
+  set(emu_sources)
+  set(emu_headers)
   if(${ENABLE_EMUNETDEV})
     set(emu_sources
         helper/emu-fd-net-device-helper.cc
@@ -172,6 +177,8 @@
     )
   endif()
 
+  set(tap_sources)
+  set(tap_headers)
   if(${ENABLE_TAPNETDEV})
     set(tap_sources
         helper/tap-fd-net-device-helper.cc
@@ -190,6 +197,7 @@
         helper/creator-utils.cc
         helper/encode-decode.cc
         helper/tap-device-creator.cc
+      LIBRARIES_TO_LINK ${libnetwork}
       EXECUTABLE_DIRECTORY_PATH
         ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/src/fd-net-device/
       INSTALL_DIRECTORY_PATH ${CMAKE_INSTALL_LIBEXECDIR}/ns3
@@ -208,6 +216,8 @@
     )
   endif()
 
+  set(netmap_sources)
+  set(netmap_headers)
   if(${ENABLE_NETMAP_EMU})
     set(netmap_sources
         helper/netmap-net-device-helper.cc
@@ -241,6 +251,9 @@
     )
   endif()
 
+  set(dpdk_sources)
+  set(dpdk_headers)
+  set(dpdk_libraries)
   if(${ENABLE_DPDKDEVNET})
     set(dpdk_sources
         model/dpdk-net-device.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/fd-net-device/examples/realtime-fd2fd-onoff.cc ns-3.41/src/fd-net-device/examples/realtime-fd2fd-onoff.cc
--- ns-3.40/src/fd-net-device/examples/realtime-fd2fd-onoff.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/fd-net-device/examples/realtime-fd2fd-onoff.cc	2024-02-09 14:44:07.909790000 -0800
@@ -28,10 +28,10 @@
 //  |  fd-net-device |--------------|  fd-net-device |
 //  +----------------+              +----------------+
 //
-// This example is aimed at measuring the thoughput of the FdNetDevice
+// This example is aimed at measuring the throughput of the FdNetDevice
 // in a pure simulation. For this purpose two FdNetDevices, attached to
 // different nodes but in a same simulation, are connected using a socket pair.
-// TCP traffic is sent at a saturating data rate. Then the thoughput can
+// TCP traffic is sent at a saturating data rate. Then the throughput can
 // be obtained from the generated .pcap files.
 //
 // Steps to run the experiment:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/fd-net-device/helper/tap-device-creator.cc ns-3.41/src/fd-net-device/helper/tap-device-creator.cc
--- ns-3.40/src/fd-net-device/helper/tap-device-creator.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/fd-net-device/helper/tap-device-creator.cc	2024-02-09 14:44:07.909790000 -0800
@@ -17,6 +17,8 @@
 
 #include "creator-utils.h"
 
+#include "ns3/mac48-address.h"
+
 #include <arpa/inet.h>
 #include <cstring>
 #include <errno.h>
@@ -38,18 +40,6 @@
 
 #define TAP_MAGIC 95549
 
-//
-// Lots of the following helper code taken from corresponding functions in src/node.
-//
-#define ASCII_DOT (0x2e)
-#define ASCII_ZERO (0x30)
-#define ASCII_a (0x41)
-#define ASCII_z (0x5a)
-#define ASCII_A (0x61)
-#define ASCII_Z (0x7a)
-#define ASCII_COLON (0x3a)
-#define ASCII_ZERO (0x30)
-
 using namespace ns3;
 
 /**
@@ -62,54 +52,6 @@
     int32_t ifr6_ifindex;      //!< interface index
 };
 
-char
-AsciiToLowCase(char c)
-{
-    if (c >= ASCII_a && c <= ASCII_z)
-    {
-        return c;
-    }
-    else if (c >= ASCII_A && c <= ASCII_Z)
-    {
-        return c + (ASCII_a - ASCII_A);
-    }
-    else
-    {
-        return c;
-    }
-}
-
-void
-AsciiToMac48(const char* str, uint8_t addr[6])
-{
-    int i = 0;
-    while (*str != 0 && i < 6)
-    {
-        uint8_t byte = 0;
-        while (*str != ASCII_COLON && *str != 0)
-        {
-            byte <<= 4;
-            char low = AsciiToLowCase(*str);
-            if (low >= ASCII_a)
-            {
-                byte |= low - ASCII_a + 10;
-            }
-            else
-            {
-                byte |= low - ASCII_ZERO;
-            }
-            str++;
-        }
-        addr[i] = byte;
-        i++;
-        if (*str == 0)
-        {
-            break;
-        }
-        str++;
-    }
-}
-
 void
 SetIpv4(const char* deviceName, const char* ip, const char* netmask)
 {
@@ -189,7 +131,7 @@
     memset(&ifr, 0, sizeof(struct ifreq));
 
     ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h
-    AsciiToMac48(mac, (uint8_t*)ifr.ifr_hwaddr.sa_data);
+    Mac48Address(mac).CopyTo((uint8_t*)ifr.ifr_hwaddr.sa_data);
     ABORT_IF(ioctl(fd, SIOCSIFHWADDR, &ifr) == -1, "Could not set MAC address", true);
     LOG("Set device MAC address to " << mac);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/fd-net-device/model/fd-net-device.cc ns-3.41/src/fd-net-device/model/fd-net-device.cc
--- ns-3.40/src/fd-net-device/model/fd-net-device.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/fd-net-device/model/fd-net-device.cc	2024-02-09 14:44:07.909790000 -0800
@@ -106,7 +106,7 @@
             .AddAttribute("EncapsulationMode",
                           "The link-layer encapsulation type to use.",
                           EnumValue(DIX),
-                          MakeEnumAccessor(&FdNetDevice::m_encapMode),
+                          MakeEnumAccessor<EncapsulationMode>(&FdNetDevice::m_encapMode),
                           MakeEnumChecker(DIX, "Dix", LLC, "Llc", DIXPI, "DixPi"))
             .AddAttribute("RxQueueSize",
                           "Maximum size of the read queue.  "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/flow-monitor/CMakeLists.txt ns-3.41/src/flow-monitor/CMakeLists.txt
--- ns-3.40/src/flow-monitor/CMakeLists.txt	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/flow-monitor/CMakeLists.txt	2024-02-09 14:44:07.909790000 -0800
@@ -19,5 +19,4 @@
     model/ipv6-flow-classifier.h
     model/ipv6-flow-probe.h
   LIBRARIES_TO_LINK ${libinternet}
-                    ${libstats}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/flow-monitor/examples/flowmon-parse-results.py ns-3.41/src/flow-monitor/examples/flowmon-parse-results.py
--- ns-3.40/src/flow-monitor/examples/flowmon-parse-results.py	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/flow-monitor/examples/flowmon-parse-results.py	2024-02-09 14:44:07.909790000 -0800
@@ -1,18 +1,20 @@
 from __future__ import division
-import sys
+
 import os
+import sys
+
 try:
     from xml.etree import cElementTree as ElementTree
 except ImportError:
     from xml.etree import ElementTree
 
+
 def parse_time_ns(tm):
-    if tm.endswith('ns'):
+    if tm.endswith("ns"):
         return float(tm[:-2])
     raise ValueError(tm)
 
 
-
 ## FiveTuple
 class FiveTuple(object):
     ## class variables
@@ -28,17 +30,19 @@
     #  destination port
     ## @var __slots_
     #  class variable list
-    __slots_ = ['sourceAddress', 'destinationAddress', 'protocol', 'sourcePort', 'destinationPort']
+    __slots_ = ["sourceAddress", "destinationAddress", "protocol", "sourcePort", "destinationPort"]
+
     def __init__(self, el):
-        '''! The initializer.
+        """! The initializer.
         @param self The object pointer.
         @param el The element.
-        '''
-        self.sourceAddress = el.get('sourceAddress')
-        self.destinationAddress = el.get('destinationAddress')
-        self.sourcePort = int(el.get('sourcePort'))
-        self.destinationPort = int(el.get('destinationPort'))
-        self.protocol = int(el.get('protocol'))
+        """
+        self.sourceAddress = el.get("sourceAddress")
+        self.destinationAddress = el.get("destinationAddress")
+        self.sourcePort = int(el.get("sourcePort"))
+        self.destinationPort = int(el.get("destinationPort"))
+        self.protocol = int(el.get("protocol"))
+
 
 ## Histogram
 class Histogram(object):
@@ -47,17 +51,21 @@
     #  histogram bins
     ## @var __slots_
     #  class variable list
-    __slots_ = 'bins', 'nbins', 'number_of_flows'
+    __slots_ = "bins", "nbins", "number_of_flows"
+
     def __init__(self, el=None):
-        '''! The initializer.
+        """! The initializer.
         @param self The object pointer.
         @param el The element.
-        '''
+        """
         self.bins = []
         if el is not None:
-            #self.nbins = int(el.get('nBins'))
-            for bin in el.findall('bin'):
-                self.bins.append( (float(bin.get("start")), float(bin.get("width")), int(bin.get("count"))) )
+            # self.nbins = int(el.get('nBins'))
+            for bin in el.findall("bin"):
+                self.bins.append(
+                    (float(bin.get("start")), float(bin.get("width")), int(bin.get("count")))
+                )
+
 
 ## Flow
 class Flow(object):
@@ -84,46 +92,63 @@
     #  receive duration
     ## @var __slots_
     #  class variable list
-    __slots_ = ['flowId', 'delayMean', 'packetLossRatio', 'rxBitrate', 'txBitrate',
-                'fiveTuple', 'packetSizeMean', 'probe_stats_unsorted',
-                'hopCount', 'flowInterruptionsHistogram', 'rx_duration']
+    __slots_ = [
+        "flowId",
+        "delayMean",
+        "packetLossRatio",
+        "rxBitrate",
+        "txBitrate",
+        "fiveTuple",
+        "packetSizeMean",
+        "probe_stats_unsorted",
+        "hopCount",
+        "flowInterruptionsHistogram",
+        "rx_duration",
+    ]
+
     def __init__(self, flow_el):
-        '''! The initializer.
+        """! The initializer.
         @param self The object pointer.
         @param flow_el The element.
-        '''
-        self.flowId = int(flow_el.get('flowId'))
-        rxPackets = float(flow_el.get('rxPackets'))
-        txPackets = float(flow_el.get('txPackets'))
-
-        tx_duration = (parse_time_ns (flow_el.get('timeLastTxPacket')) - parse_time_ns(flow_el.get('timeFirstTxPacket')))*1e-9
-        rx_duration = (parse_time_ns (flow_el.get('timeLastRxPacket')) - parse_time_ns(flow_el.get('timeFirstRxPacket')))*1e-9
+        """
+        self.flowId = int(flow_el.get("flowId"))
+        rxPackets = float(flow_el.get("rxPackets"))
+        txPackets = float(flow_el.get("txPackets"))
+
+        tx_duration = (
+            parse_time_ns(flow_el.get("timeLastTxPacket"))
+            - parse_time_ns(flow_el.get("timeFirstTxPacket"))
+        ) * 1e-9
+        rx_duration = (
+            parse_time_ns(flow_el.get("timeLastRxPacket"))
+            - parse_time_ns(flow_el.get("timeFirstRxPacket"))
+        ) * 1e-9
         self.rx_duration = rx_duration
         self.probe_stats_unsorted = []
         if rxPackets:
-            self.hopCount = float(flow_el.get('timesForwarded')) / rxPackets + 1
+            self.hopCount = float(flow_el.get("timesForwarded")) / rxPackets + 1
         else:
             self.hopCount = -1000
         if rxPackets:
-            self.delayMean = float(flow_el.get('delaySum')[:-2]) / rxPackets * 1e-9
-            self.packetSizeMean = float(flow_el.get('rxBytes')) / rxPackets
+            self.delayMean = float(flow_el.get("delaySum")[:-2]) / rxPackets * 1e-9
+            self.packetSizeMean = float(flow_el.get("rxBytes")) / rxPackets
         else:
             self.delayMean = None
             self.packetSizeMean = None
         if rx_duration > 0:
-            self.rxBitrate = float(flow_el.get('rxBytes'))*8 / rx_duration
+            self.rxBitrate = float(flow_el.get("rxBytes")) * 8 / rx_duration
         else:
             self.rxBitrate = None
         if tx_duration > 0:
-            self.txBitrate = float(flow_el.get('txBytes'))*8 / tx_duration
+            self.txBitrate = float(flow_el.get("txBytes")) * 8 / tx_duration
         else:
             self.txBitrate = None
-        lost = float(flow_el.get('lostPackets'))
-        #print "rxBytes: %s; txPackets: %s; rxPackets: %s; lostPackets: %s" % (flow_el.get('rxBytes'), txPackets, rxPackets, lost)
+        lost = float(flow_el.get("lostPackets"))
+        # print "rxBytes: %s; txPackets: %s; rxPackets: %s; lostPackets: %s" % (flow_el.get('rxBytes'), txPackets, rxPackets, lost)
         if rxPackets == 0:
             self.packetLossRatio = None
         else:
-            self.packetLossRatio = (lost / (rxPackets + lost))
+            self.packetLossRatio = lost / (rxPackets + lost)
 
         interrupt_hist_elem = flow_el.find("flowInterruptionsHistogram")
         if interrupt_hist_elem is None:
@@ -131,6 +156,7 @@
         else:
             self.flowInterruptionsHistogram = Histogram(interrupt_hist_elem)
 
+
 ## ProbeFlowStats
 class ProbeFlowStats(object):
     ## class variables
@@ -140,7 +166,8 @@
     #  bytes
     ## @var __slots_
     #  class variable list
-    __slots_ = ['probeId', 'packets', 'bytes', 'delayFromFirstProbe']
+    __slots_ = ["probeId", "packets", "bytes", "delayFromFirstProbe"]
+
 
 ## Simulation
 class Simulation(object):
@@ -148,31 +175,33 @@
     ## @var flows
     #  list of flows
     def __init__(self, simulation_el):
-        '''! The initializer.
+        """! The initializer.
         @param self The object pointer.
         @param simulation_el The element.
-        '''
+        """
         self.flows = []
-        FlowClassifier_el, = simulation_el.findall("Ipv4FlowClassifier")
+        (FlowClassifier_el,) = simulation_el.findall("Ipv4FlowClassifier")
         flow_map = {}
         for flow_el in simulation_el.findall("FlowStats/Flow"):
             flow = Flow(flow_el)
             flow_map[flow.flowId] = flow
             self.flows.append(flow)
         for flow_cls in FlowClassifier_el.findall("Flow"):
-            flowId = int(flow_cls.get('flowId'))
+            flowId = int(flow_cls.get("flowId"))
             flow_map[flowId].fiveTuple = FiveTuple(flow_cls)
 
         for probe_elem in simulation_el.findall("FlowProbes/FlowProbe"):
-            probeId = int(probe_elem.get('index'))
+            probeId = int(probe_elem.get("index"))
             for stats in probe_elem.findall("FlowStats"):
-                flowId = int(stats.get('flowId'))
+                flowId = int(stats.get("flowId"))
                 s = ProbeFlowStats()
-                s.packets = int(stats.get('packets'))
-                s.bytes = float(stats.get('bytes'))
+                s.packets = int(stats.get("packets"))
+                s.bytes = float(stats.get("bytes"))
                 s.probeId = probeId
                 if s.packets > 0:
-                    s.delayFromFirstProbe =  parse_time_ns(stats.get('delayFromFirstProbeSum')) / float(s.packets)
+                    s.delayFromFirstProbe = parse_time_ns(
+                        stats.get("delayFromFirstProbeSum")
+                    ) / float(s.packets)
                 else:
                     s.delayFromFirstProbe = 0
                 flow_map[flowId].probe_stats_unsorted.append(s)
@@ -190,38 +219,46 @@
                 level += 1
             if event == "end":
                 level -= 1
-                if level == 0 and elem.tag == 'FlowMonitor':
+                if level == 0 and elem.tag == "FlowMonitor":
                     sim = Simulation(elem)
                     sim_list.append(sim)
-                    elem.clear() # won't need this any more
+                    elem.clear()  # won't need this any more
                     sys.stdout.write(".")
                     sys.stdout.flush()
     print(" done.")
 
-
     for sim in sim_list:
         for flow in sim.flows:
             t = flow.fiveTuple
-            proto = {6: 'TCP', 17: 'UDP'} [t.protocol]
-            print("FlowID: %i (%s %s/%s --> %s/%i)" % \
-                (flow.flowId, proto, t.sourceAddress, t.sourcePort, t.destinationAddress, t.destinationPort))
+            proto = {6: "TCP", 17: "UDP"}[t.protocol]
+            print(
+                "FlowID: %i (%s %s/%s --> %s/%i)"
+                % (
+                    flow.flowId,
+                    proto,
+                    t.sourceAddress,
+                    t.sourcePort,
+                    t.destinationAddress,
+                    t.destinationPort,
+                )
+            )
             if flow.txBitrate is None:
                 print("\tTX bitrate: None")
             else:
-                print("\tTX bitrate: %.2f kbit/s" % (flow.txBitrate*1e-3,))
+                print("\tTX bitrate: %.2f kbit/s" % (flow.txBitrate * 1e-3,))
             if flow.rxBitrate is None:
                 print("\tRX bitrate: None")
             else:
-                print("\tRX bitrate: %.2f kbit/s" % (flow.rxBitrate*1e-3,))
+                print("\tRX bitrate: %.2f kbit/s" % (flow.rxBitrate * 1e-3,))
             if flow.delayMean is None:
                 print("\tMean Delay: None")
             else:
-                print("\tMean Delay: %.2f ms" % (flow.delayMean*1e3,))
+                print("\tMean Delay: %.2f ms" % (flow.delayMean * 1e3,))
             if flow.packetLossRatio is None:
                 print("\tPacket Loss Ratio: None")
             else:
-                print("\tPacket Loss Ratio: %.2f %%" % (flow.packetLossRatio*100))
+                print("\tPacket Loss Ratio: %.2f %%" % (flow.packetLossRatio * 100))
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/flow-monitor/examples/wifi-olsr-flowmon.py ns-3.41/src/flow-monitor/examples/wifi-olsr-flowmon.py
--- ns-3.40/src/flow-monitor/examples/wifi-olsr-flowmon.py	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/flow-monitor/examples/wifi-olsr-flowmon.py	2024-02-09 14:44:07.909790000 -0800
@@ -17,17 +17,25 @@
 #  Authors: Gustavo Carneiro <gjc@inescporto.pt>
 
 from __future__ import print_function
+
 import sys
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
-DISTANCE = 20 # (m)
+DISTANCE = 20  # (m)
 NUM_NODES_SIDE = 3
 
 
 def main(argv):
+    from ctypes import c_bool, c_char_p, c_int, create_string_buffer
 
-    from ctypes import c_int, c_bool, c_char_p, create_string_buffer
     NumNodesSide = c_int(2)
     Plot = c_bool(False)
     BUFFLEN = 4096
@@ -35,7 +43,11 @@
     Results = c_char_p(ResultsBuffer.raw)
 
     cmd = ns.CommandLine(__file__)
-    cmd.AddValue("NumNodesSide", "Grid side number of nodes (total number of nodes will be this number squared)", NumNodesSide)
+    cmd.AddValue(
+        "NumNodesSide",
+        "Grid side number of nodes (total number of nodes will be this number squared)",
+        NumNodesSide,
+    )
     cmd.AddValue("Results", "Write XML results to file", Results, BUFFLEN)
     cmd.AddValue("Plot", "Plot the results using the matplotlib python module", Plot)
     cmd.Parse(argv)
@@ -46,8 +58,7 @@
     wifiChannel = ns.wifi.YansWifiChannelHelper.Default()
     wifiPhy.SetChannel(wifiChannel.Create())
     ssid = ns.wifi.Ssid("wifi-default")
-    wifiMac.SetType ("ns3::AdhocWifiMac",
-                     "Ssid", ns.wifi.SsidValue(ssid))
+    wifiMac.SetType("ns3::AdhocWifiMac", "Ssid", ns.wifi.SsidValue(ssid))
 
     internet = ns.internet.InternetStackHelper()
     list_routing = ns.internet.Ipv4ListRoutingHelper()
@@ -60,12 +71,16 @@
     ipv4Addresses = ns.internet.Ipv4AddressHelper()
     ipv4Addresses.SetBase(ns.network.Ipv4Address("10.0.0.0"), ns.network.Ipv4Mask("255.255.255.0"))
 
-    port = 9   # Discard port(RFC 863)
+    port = 9  # Discard port(RFC 863)
     inetAddress = ns.network.InetSocketAddress(ns.network.Ipv4Address("10.0.0.1"), port)
     onOffHelper = ns.applications.OnOffHelper("ns3::UdpSocketFactory", inetAddress.ConvertTo())
     onOffHelper.SetAttribute("DataRate", ns.network.DataRateValue(ns.network.DataRate("100kbps")))
-    onOffHelper.SetAttribute("OnTime", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=1]"))
-    onOffHelper.SetAttribute("OffTime", ns.core.StringValue ("ns3::ConstantRandomVariable[Constant=0]"))
+    onOffHelper.SetAttribute(
+        "OnTime", ns.core.StringValue("ns3::ConstantRandomVariable[Constant=1]")
+    )
+    onOffHelper.SetAttribute(
+        "OffTime", ns.core.StringValue("ns3::ConstantRandomVariable[Constant=0]")
+    )
 
     addresses = []
     nodes = []
@@ -75,18 +90,17 @@
     else:
         num_nodes_side = NumNodesSide.value
 
-    nodes = ns.NodeContainer(num_nodes_side*num_nodes_side)
+    nodes = ns.NodeContainer(num_nodes_side * num_nodes_side)
     accumulator = 0
     for xi in range(num_nodes_side):
         for yi in range(num_nodes_side):
-
             node = nodes.Get(accumulator)
             accumulator += 1
             container = ns.network.NodeContainer(node)
             internet.Install(container)
 
             mobility = ns.CreateObject("ConstantPositionMobilityModel")
-            mobility.SetPosition(ns.core.Vector(xi*DISTANCE, yi*DISTANCE, 0))
+            mobility.SetPosition(ns.core.Vector(xi * DISTANCE, yi * DISTANCE, 0))
             node.AggregateObject(mobility)
 
             device = wifi.Install(wifiPhy, wifiMac, node)
@@ -95,17 +109,20 @@
 
     for i, node in [(i, nodes.Get(i)) for i in range(nodes.GetN())]:
         destaddr = addresses[(len(addresses) - 1 - i) % len(addresses)]
-        #print (i, destaddr)
-        onOffHelper.SetAttribute("Remote", ns.network.AddressValue(ns.network.InetSocketAddress(destaddr, port).ConvertTo()))
+        # print (i, destaddr)
+        onOffHelper.SetAttribute(
+            "Remote",
+            ns.network.AddressValue(ns.network.InetSocketAddress(destaddr, port).ConvertTo()),
+        )
         container = ns.network.NodeContainer(node)
         app = onOffHelper.Install(container)
-        urv = ns.CreateObject("UniformRandomVariable")#ns.cppyy.gbl.get_rng()
+        urv = ns.CreateObject("UniformRandomVariable")  # ns.cppyy.gbl.get_rng()
         startDelay = ns.Seconds(urv.GetValue(20, 30))
         app.Start(startDelay)
 
-    #internet.EnablePcapAll("wifi-olsr")
+    # internet.EnablePcapAll("wifi-olsr")
     flowmon_helper = ns.flow_monitor.FlowMonitorHelper()
-    #flowmon_helper.SetMonitorAttribute("StartTime", ns.core.TimeValue(ns.core.Seconds(31)))
+    # flowmon_helper.SetMonitorAttribute("StartTime", ns.core.TimeValue(ns.core.Seconds(31)))
     monitor = flowmon_helper.InstallAll()
     monitor = flowmon_helper.GetMonitor()
     monitor.SetAttribute("DelayBinWidth", ns.core.DoubleValue(0.001))
@@ -116,33 +133,60 @@
     ns.core.Simulator.Run()
 
     def print_stats(os, st):
-        print ("  Tx Bytes: ", st.txBytes, file=os)
-        print ("  Rx Bytes: ", st.rxBytes, file=os)
-        print ("  Tx Packets: ", st.txPackets, file=os)
-        print ("  Rx Packets: ", st.rxPackets, file=os)
-        print ("  Lost Packets: ", st.lostPackets, file=os)
+        print("  Tx Bytes: ", st.txBytes, file=os)
+        print("  Rx Bytes: ", st.rxBytes, file=os)
+        print("  Tx Packets: ", st.txPackets, file=os)
+        print("  Rx Packets: ", st.rxPackets, file=os)
+        print("  Lost Packets: ", st.lostPackets, file=os)
         if st.rxPackets > 0:
-            print ("  Mean{Delay}: ", (st.delaySum.GetSeconds() / st.rxPackets), file=os)
-            print ("  Mean{Jitter}: ", (st.jitterSum.GetSeconds() / (st.rxPackets-1)), file=os)
-            print ("  Mean{Hop Count}: ", float(st.timesForwarded) / st.rxPackets + 1, file=os)
+            print("  Mean{Delay}: ", (st.delaySum.GetSeconds() / st.rxPackets), file=os)
+            print("  Mean{Jitter}: ", (st.jitterSum.GetSeconds() / (st.rxPackets - 1)), file=os)
+            print("  Mean{Hop Count}: ", float(st.timesForwarded) / st.rxPackets + 1, file=os)
 
         if 0:
-            print ("Delay Histogram", file=os)
-            for i in range(st.delayHistogram.GetNBins () ):
-              print (" ",i,"(", st.delayHistogram.GetBinStart (i), "-", \
-                  st.delayHistogram.GetBinEnd (i), "): ", st.delayHistogram.GetBinCount (i), file=os)
-            print ("Jitter Histogram", file=os)
-            for i in range(st.jitterHistogram.GetNBins () ):
-              print (" ",i,"(", st.jitterHistogram.GetBinStart (i), "-", \
-                  st.jitterHistogram.GetBinEnd (i), "): ", st.jitterHistogram.GetBinCount (i), file=os)
-            print ("PacketSize Histogram", file=os)
-            for i in range(st.packetSizeHistogram.GetNBins () ):
-              print (" ",i,"(", st.packetSizeHistogram.GetBinStart (i), "-", \
-                  st.packetSizeHistogram.GetBinEnd (i), "): ", st.packetSizeHistogram.GetBinCount (i), file=os)
+            print("Delay Histogram", file=os)
+            for i in range(st.delayHistogram.GetNBins()):
+                print(
+                    " ",
+                    i,
+                    "(",
+                    st.delayHistogram.GetBinStart(i),
+                    "-",
+                    st.delayHistogram.GetBinEnd(i),
+                    "): ",
+                    st.delayHistogram.GetBinCount(i),
+                    file=os,
+                )
+            print("Jitter Histogram", file=os)
+            for i in range(st.jitterHistogram.GetNBins()):
+                print(
+                    " ",
+                    i,
+                    "(",
+                    st.jitterHistogram.GetBinStart(i),
+                    "-",
+                    st.jitterHistogram.GetBinEnd(i),
+                    "): ",
+                    st.jitterHistogram.GetBinCount(i),
+                    file=os,
+                )
+            print("PacketSize Histogram", file=os)
+            for i in range(st.packetSizeHistogram.GetNBins()):
+                print(
+                    " ",
+                    i,
+                    "(",
+                    st.packetSizeHistogram.GetBinStart(i),
+                    "-",
+                    st.packetSizeHistogram.GetBinEnd(i),
+                    "): ",
+                    st.packetSizeHistogram.GetBinCount(i),
+                    file=os,
+                )
 
         for reason, drops in enumerate(st.packetsDropped):
-            print ("  Packets dropped by reason %i: %i" % (reason, drops), file=os)
-        #for reason, drops in enumerate(st.bytesDropped):
+            print("  Packets dropped by reason %i: %i" % (reason, drops), file=os)
+        # for reason, drops in enumerate(st.bytesDropped):
         #    print "Bytes dropped by reason %i: %i" % (reason, drops)
 
     monitor.CheckForLostPackets()
@@ -151,17 +195,26 @@
     if Results.value != b"output.xml":
         for flow_id, flow_stats in monitor.GetFlowStats():
             t = classifier.FindFlow(flow_id)
-            proto = {6: 'TCP', 17: 'UDP'} [t.protocol]
-            print ("FlowID: %i (%s %s/%s --> %s/%i)" % \
-                (flow_id, proto, t.sourceAddress, t.sourcePort, t.destinationAddress, t.destinationPort))
+            proto = {6: "TCP", 17: "UDP"}[t.protocol]
+            print(
+                "FlowID: %i (%s %s/%s --> %s/%i)"
+                % (
+                    flow_id,
+                    proto,
+                    t.sourceAddress,
+                    t.sourcePort,
+                    t.destinationAddress,
+                    t.destinationPort,
+                )
+            )
             print_stats(sys.stdout, flow_stats)
     else:
         res = monitor.SerializeToXmlFile(Results.value.decode("utf-8"), True, True)
-        print (res)
-
+        print(res)
 
     if Plot.value:
         import pylab
+
         delays = []
         for flow_id, flow_stats in monitor.GetFlowStats():
             tupl = classifier.FindFlow(flow_id)
@@ -176,6 +229,5 @@
     return 0
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv))
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/CMakeLists.txt ns-3.41/src/internet/CMakeLists.txt
--- ns-3.40/src/internet/CMakeLists.txt	2023-09-27 13:30:37.650839000 -0700
+++ ns-3.41/src/internet/CMakeLists.txt	2024-02-09 14:44:07.917790200 -0800
@@ -129,7 +129,6 @@
 )
 
 set(header_files
-    ${header_files}
     helper/internet-stack-helper.h
     helper/internet-trace-helper.h
     helper/ipv4-address-helper.h
@@ -341,10 +340,7 @@
   SOURCE_FILES ${source_files}
   HEADER_FILES ${header_files}
   PRIVATE_HEADER_FILES ${private_header_files}
-  LIBRARIES_TO_LINK
-    ${libnetwork}
-    ${libcore}
-    ${libbridge}
-    ${libtraffic-control}
+  LIBRARIES_TO_LINK ${libbridge}
+                    ${libtraffic-control}
   TEST_SOURCES ${test_sources}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/doc/routing-overview.rst ns-3.41/src/internet/doc/routing-overview.rst
--- ns-3.40/src/internet/doc/routing-overview.rst	2023-09-27 13:30:37.650839000 -0700
+++ ns-3.41/src/internet/doc/routing-overview.rst	2024-02-09 14:44:07.917790200 -0800
@@ -248,7 +248,7 @@
 GlobalRouteManager executes the OSPF shortest path first (SPF) computation on
 the database, and populates the routing tables on each node.
 
-The quagga (`<http://www.quagga.net>`_) OSPF implementation was used as the
+The quagga (`<https://www.nongnu.org/quagga/>`_) OSPF implementation was used as the
 basis for the routing computation logic. One benefit of following an existing
 OSPF SPF implementation is that OSPF already has defined link state
 advertisements for all common types of network links:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/doc/tcp.rst ns-3.41/src/internet/doc/tcp.rst
--- ns-3.40/src/internet/doc/tcp.rst	2023-09-27 13:30:37.650839000 -0700
+++ ns-3.41/src/internet/doc/tcp.rst	2024-02-09 14:44:07.917790200 -0800
@@ -57,7 +57,7 @@
 +++++++++++++
 
 Until the ns-3.10 release, |ns3| contained a port of the TCP model from `GTNetS
-<http://www.ece.gatech.edu/research/labs/MANIACS/GTNetS/index.html>`_,
+<https://web.archive.org/web/20210928123443/http://griley.ece.gatech.edu/MANIACS/GTNetS/index.html>`_,
 developed initially by George Riley and ported to |ns3| by Raj Bhattacharjea.
 This implementation was substantially rewritten by Adriam Tam for ns-3.10.
 In 2015, the TCP module was redesigned in order to create a better
@@ -89,6 +89,10 @@
 In the ns-3.34 release, the default congestion control algorithm was set
 to CUBIC from NewReno.
 
+CUBIC was extended to support Reno-friendliness (see RFC 9438 Section 4.3) in
+the ns-3.41 release.  This feature is called 'TCP friendliness' in earlier
+versions of the CUBIC RFCs, and in the Linux and ns-3 implementations.
+
 Acknowledgments
 +++++++++++++++
 
@@ -417,11 +421,10 @@
 phase of window growth to try to exit slow start before window growth
 causes queue overflow.
 
-CUBIC is documented in :rfc:`8312`, and the |ns3| implementation is based
-on the RFC more so than the Linux implementation, although the Linux 4.4
-kernel implementation (through the Direct Code Execution environment) has
-been used to validate the behavior and is fairly well aligned (see below
-section on validation).
+CUBIC is documented in :rfc:`9438`, and the |ns3| implementation is patterned
+partly on the Linux implementation and partly on the RFC, although the Linux
+4.4 kernel implementation (through the Direct Code Execution environment) has
+been used to validate the behavior.
 
 Linux Reno
 ^^^^^^^^^^
@@ -1030,6 +1033,13 @@
   them back-to-back. Currently, ns-3 paces out all packets eligible to
   be sent in the same manner.
 
+It is important to also note that the current model does not implement
+Section 3.5 of RFC 8257 regarding the handling of packet loss.  This
+requirement states that DCTCP must react to lost packets in the same way
+as does a conventional TCP (as specified in RFC 5681).  The current
+DCTCP model does not implement this, and should therefore only be used
+in simulations that do not involve any packet loss on the DCTCP flows.
+
 More information about DCTCP is available in the RFC 8257:
 https://tools.ietf.org/html/rfc8257
 
@@ -1596,7 +1606,7 @@
 In the case when SACK options are disabled, the NewReno modification handles
 the recovery.
 
-At the start of recovery phase the congestion window is reduced diffently for
+At the start of recovery phase the congestion window is reduced differently for
 NewReno and SACK based recovery. For NewReno the reduction is done as given below:
 
 .. math::  cWnd = ssThresh
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/arp-l3-protocol.cc ns-3.41/src/internet/model/arp-l3-protocol.cc
--- ns-3.40/src/internet/model/arp-l3-protocol.cc	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/arp-l3-protocol.cc	2024-02-09 14:44:07.921790100 -0800
@@ -191,7 +191,7 @@
     //
     // If we're connected to a real world network, then some of the fields sizes
     // in an ARP packet can vary in ways not seen in simulations.  We need to be
-    // able to detect ARP packets with headers we don't recongnize and not process
+    // able to detect ARP packets with headers we don't recognize and not process
     // them instead of crashing.  The ArpHeader will return 0 if it can't deal
     // with the received header.
     //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/global-routing.h ns-3.41/src/internet/model/global-routing.h
--- ns-3.40/src/internet/model/global-routing.h	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/global-routing.h	2024-02-09 14:44:07.925790300 -0800
@@ -82,6 +82,6 @@
  * By using a standard data format for reporting topology, existing
  * OSPF route computation code can be reused, and that is what is done
  * by the ns3::GlobalRouteManager.  The main computation functions are
- * ported from the quagga routing suite (http://www.quagga.net).
+ * ported from the quagga routing suite (https://www.nongnu.org/quagga/).
  *
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/icmpv4-l4-protocol.cc ns-3.41/src/internet/model/icmpv4-l4-protocol.cc
--- ns-3.40/src/internet/model/icmpv4-l4-protocol.cc	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/icmpv4-l4-protocol.cc	2024-02-09 14:44:07.925790300 -0800
@@ -222,14 +222,18 @@
 Icmpv4L4Protocol::HandleEcho(Ptr<Packet> p,
                              Icmpv4Header header,
                              Ipv4Address source,
-                             Ipv4Address destination)
+                             Ipv4Address destination,
+                             uint8_t tos)
 {
-    NS_LOG_FUNCTION(this << p << header << source << destination);
+    NS_LOG_FUNCTION(this << p << header << source << destination << tos);
 
     Ptr<Packet> reply = Create<Packet>();
     Icmpv4Echo echo;
     p->RemoveHeader(echo);
     reply->AddHeader(echo);
+    SocketIpTosTag ipTosTag;
+    ipTosTag.SetTos(tos);
+    reply->ReplacePacketTag(ipTosTag);
     SendMessage(reply, destination, source, Icmpv4Header::ICMPV4_ECHO_REPLY, 0, nullptr);
 }
 
@@ -327,7 +331,7 @@
                 }
             }
         }
-        HandleEcho(p, icmp, header.GetSource(), dst);
+        HandleEcho(p, icmp, header.GetSource(), dst, header.GetTos());
         break;
     }
     case Icmpv4Header::ICMPV4_DEST_UNREACH:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/icmpv4-l4-protocol.h ns-3.41/src/internet/model/icmpv4-l4-protocol.h
--- ns-3.40/src/internet/model/icmpv4-l4-protocol.h	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/icmpv4-l4-protocol.h	2024-02-09 14:44:07.925790300 -0800
@@ -143,11 +143,13 @@
      * \param header the IP header
      * \param source the source address
      * \param destination the destination address
+     * \param tos the type of service
      */
     void HandleEcho(Ptr<Packet> p,
                     Icmpv4Header header,
                     Ipv4Address source,
-                    Ipv4Address destination);
+                    Ipv4Address destination,
+                    uint8_t tos);
     /**
      * \brief Handles an incoming ICMP Destination Unreachable packet
      * \param p the packet
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/icmpv6-l4-protocol.cc ns-3.41/src/internet/model/icmpv6-l4-protocol.cc
--- ns-3.40/src/internet/model/icmpv6-l4-protocol.cc	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/icmpv6-l4-protocol.cc	2024-02-09 14:44:07.925790300 -0800
@@ -1250,7 +1250,7 @@
     NS_LOG_LOGIC("Send NA ( from " << src << " to " << dst << " target " << src << ")");
     na.SetIpv6Target(src);
 
-    if ((flags & 1))
+    if (flags & 1)
     {
         na.SetFlagO(true);
     }
@@ -1258,7 +1258,7 @@
     {
         na.SetFlagS(true);
     }
-    if ((flags & 4))
+    if (flags & 4)
     {
         na.SetFlagR(true);
     }
@@ -1618,7 +1618,7 @@
     p->AddHeader(llOption);
     na.SetIpv6Target(src);
 
-    if ((flags & 1))
+    if (flags & 1)
     {
         na.SetFlagO(true);
     }
@@ -1626,7 +1626,7 @@
     {
         na.SetFlagS(true);
     }
-    if ((flags & 4))
+    if (flags & 4)
     {
         na.SetFlagR(true);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv4.cc ns-3.41/src/internet/model/ipv4.cc
--- ns-3.40/src/internet/model/ipv4.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv4.cc	2024-02-09 14:44:07.929790300 -0800
@@ -23,6 +23,7 @@
 #include "ns3/boolean.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
+#include "ns3/warnings.h"
 
 namespace ns3
 {
@@ -34,6 +35,7 @@
 TypeId
 Ipv4::GetTypeId()
 {
+    NS_WARNING_PUSH_DEPRECATED;
     static TypeId tid =
         TypeId("ns3::Ipv4")
             .SetParent<Object>()
@@ -49,7 +51,16 @@
                           "another interface",
                           BooleanValue(true),
                           MakeBooleanAccessor(&Ipv4::SetWeakEsModel, &Ipv4::GetWeakEsModel),
-                          MakeBooleanChecker())
+                          MakeBooleanChecker(),
+                          TypeId::DEPRECATED,
+                          "DEPRECATED since ns-3.41. Use the StrongEndSystemModel attribute.")
+            .AddAttribute(
+                "StrongEndSystemModel",
+                "Reject packets for an address not configured on the interface they're "
+                "coming from (RFC1122, section 3.3.4.2).",
+                BooleanValue(false),
+                MakeBooleanAccessor(&Ipv4::SetStrongEndSystemModel, &Ipv4::GetStrongEndSystemModel),
+                MakeBooleanChecker())
 #if 0
     .AddAttribute ("MtuDiscover", "If enabled, every outgoing ip packet will have the DF flag set.",
                    BooleanValue (false),
@@ -58,6 +69,7 @@
                    MakeBooleanChecker ())
 #endif
         ;
+    NS_WARNING_POP;
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv4.h ns-3.41/src/internet/model/ipv4.h
--- ns-3.40/src/internet/model/ipv4.h	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv4.h	2024-02-09 14:44:07.933790200 -0800
@@ -23,6 +23,7 @@
 #include "ipv4-route.h"
 
 #include "ns3/callback.h"
+#include "ns3/deprecated.h"
 #include "ns3/ipv4-address.h"
 #include "ns3/object.h"
 #include "ns3/socket.h"
@@ -220,9 +221,9 @@
      * a multicast group that the host has joined (and the incoming device
      * is acceptable), or if address corresponds to a broadcast address.
      *
-     * If the Ipv4 attribute WeakEsModel is true, the unicast address may
-     * match any of the Ipv4 addresses on any interface.  If the attribute is
-     * false, the address must match one assigned to the incoming device.
+     * If the Ipv4 attribute StrongEndSystemModel is true, the address must match one assigned to
+     * the incoming device. If the attribute is false, the unicast address may match any of the Ipv4
+     * addresses on any interface.
      */
     virtual bool IsDestinationAddress(Ipv4Address address, uint32_t iif) const = 0;
 
@@ -455,19 +456,39 @@
     virtual bool GetIpForward() const = 0;
 
     /**
+     * \brief Get the Weak Es Model status
+     *
+     * RFC1122 term for whether host accepts datagram with a dest. address on another interface
+     * \returns true for Weak Es Model activated
+     * \deprecated Deprecated since ns-3.41. Use SetStrongEndSystemModel instead.
+     */
+    NS_DEPRECATED_3_41("Use GetStrongEndSystemModel instead")
+    virtual bool GetWeakEsModel() const = 0;
+
+    /**
      * \brief Set or unset the Weak Es Model
      *
      * RFC1122 term for whether host accepts datagram with a dest. address on another interface
      * \param model true for Weak Es Model
+     * \deprecated Deprecated since ns-3.41. Use SetStrongEndSystemModel instead.
      */
+    NS_DEPRECATED_3_41("Use SetStrongEndSystemModel instead")
     virtual void SetWeakEsModel(bool model) = 0;
+
     /**
-     * \brief Get the Weak Es Model status
+     * \brief Set or unset the Strong End System Model
      *
-     * RFC1122 term for whether host accepts datagram with a dest. address on another interface
-     * \returns true for Weak Es Model activated
+     * RFC1122 term for whether host rejects datagram with a dest. address on another interface
+     * \param model true for Strong End System Model
      */
-    virtual bool GetWeakEsModel() const = 0;
+    virtual void SetStrongEndSystemModel(bool model) = 0;
+    /**
+     * \brief Get the Strong End System Model status
+     *
+     * RFC1122 term for whether host rejects datagram with a dest. address on another interface
+     * \returns true for Strong End System Model activated
+     */
+    virtual bool GetStrongEndSystemModel() const = 0;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv4-interface.cc ns-3.41/src/internet/model/ipv4-interface.cc
--- ns-3.40/src/internet/model/ipv4-interface.cc	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/ipv4-interface.cc	2024-02-09 14:44:07.925790300 -0800
@@ -237,12 +237,14 @@
         if (dest == (*i).GetLocal())
         {
             p->AddHeader(hdr);
-            m_tc->Receive(m_device,
-                          p,
-                          Ipv4L3Protocol::PROT_NUMBER,
-                          m_device->GetBroadcast(),
-                          m_device->GetBroadcast(),
-                          NetDevice::PACKET_HOST);
+            Simulator::ScheduleNow(&TrafficControlLayer::Receive,
+                                   m_tc,
+                                   m_device,
+                                   p,
+                                   Ipv4L3Protocol::PROT_NUMBER,
+                                   m_device->GetBroadcast(),
+                                   m_device->GetBroadcast(),
+                                   NetDevice::PACKET_HOST);
             return;
         }
     }
@@ -349,7 +351,7 @@
         NS_FATAL_ERROR("index " << index << " out of bounds");
     }
     Ipv4InterfaceAddress addr;
-    return (addr); // quiet compiler
+    return addr; // quiet compiler
 }
 
 Ipv4InterfaceAddress
@@ -379,7 +381,7 @@
     }
     NS_FATAL_ERROR("Address " << index << " not found");
     Ipv4InterfaceAddress addr;
-    return (addr); // quiet compiler
+    return addr; // quiet compiler
 }
 
 Ipv4InterfaceAddress
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv4-l3-protocol.cc ns-3.41/src/internet/model/ipv4-l3-protocol.cc
--- ns-3.40/src/internet/model/ipv4-l3-protocol.cc	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/ipv4-l3-protocol.cc	2024-02-09 14:44:07.929790300 -0800
@@ -546,7 +546,7 @@
         return true;
     }
 
-    if (GetWeakEsModel()) // Check other interfaces
+    if (!GetStrongEndSystemModel()) // Check other interfaces
     {
         for (uint32_t j = 0; j < GetNInterfaces(); j++)
         {
@@ -1441,14 +1441,28 @@
 Ipv4L3Protocol::SetWeakEsModel(bool model)
 {
     NS_LOG_FUNCTION(this << model);
-    m_weakEsModel = model;
+    m_strongEndSystemModel = !model;
 }
 
 bool
 Ipv4L3Protocol::GetWeakEsModel() const
 {
     NS_LOG_FUNCTION(this);
-    return m_weakEsModel;
+    return !m_strongEndSystemModel;
+}
+
+void
+Ipv4L3Protocol::SetStrongEndSystemModel(bool model)
+{
+    NS_LOG_FUNCTION(this << model);
+    m_strongEndSystemModel = model;
+}
+
+bool
+Ipv4L3Protocol::GetStrongEndSystemModel() const
+{
+    NS_LOG_FUNCTION(this);
+    return m_strongEndSystemModel;
 }
 
 void
@@ -1880,7 +1894,7 @@
 
     auto iter = --m_timeoutEventList.end();
 
-    return (iter);
+    return iter;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv4-l3-protocol.h ns-3.41/src/internet/model/ipv4-l3-protocol.h
--- ns-3.40/src/internet/model/ipv4-l3-protocol.h	2023-09-27 13:30:37.654839000 -0700
+++ ns-3.41/src/internet/model/ipv4-l3-protocol.h	2024-02-09 14:44:07.929790300 -0800
@@ -24,6 +24,7 @@
 #include "ipv4-routing-protocol.h"
 #include "ipv4.h"
 
+#include "ns3/deprecated.h"
 #include "ns3/ipv4-address.h"
 #include "ns3/net-device.h"
 #include "ns3/nstime.h"
@@ -295,9 +296,15 @@
     // class Ipv4 attributes
     void SetIpForward(bool forward) override;
     bool GetIpForward() const override;
+
+    NS_DEPRECATED_3_41("Use SetStrongEndSystemModel instead")
     void SetWeakEsModel(bool model) override;
+    NS_DEPRECATED_3_41("Use GetStrongEndSystemModel instead")
     bool GetWeakEsModel() const override;
 
+    void SetStrongEndSystemModel(bool model) override;
+    bool GetStrongEndSystemModel() const override;
+
     /**
      * \brief Decrease the identification value for a dropped or recursed packet
      * \param source source IPv4 address
@@ -460,7 +467,7 @@
     typedef std::map<L4ListKey_t, Ptr<IpL4Protocol>> L4List_t;
 
     bool m_ipForward;               //!< Forwarding packets (i.e. router mode) state.
-    bool m_weakEsModel;             //!< Weak ES model state
+    bool m_strongEndSystemModel;    //!< Strong End System Model state
     L4List_t m_protocols;           //!< List of transport protocol.
     Ipv4InterfaceList m_interfaces; //!< List of IPv4 interfaces.
     Ipv4InterfaceReverseContainer
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv4-raw-socket-impl.cc ns-3.41/src/internet/model/ipv4-raw-socket-impl.cc
--- ns-3.40/src/internet/model/ipv4-raw-socket-impl.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv4-raw-socket-impl.cc	2024-02-09 14:44:07.929790300 -0800
@@ -135,7 +135,7 @@
 Ipv4RawSocketImpl::Bind6()
 {
     NS_LOG_FUNCTION(this);
-    return (-1);
+    return -1;
 }
 
 int
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv4-route.cc ns-3.41/src/internet/model/ipv4-route.cc
--- ns-3.40/src/internet/model/ipv4-route.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv4-route.cc	2024-02-09 14:44:07.929790300 -0800
@@ -167,7 +167,7 @@
 Ipv4MulticastRoute::GetOutputTtlMap() const
 {
     NS_LOG_FUNCTION(this);
-    return (m_ttls);
+    return m_ttls;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6-address-generator.cc ns-3.41/src/internet/model/ipv6-address-generator.cc
--- ns-3.40/src/internet/model/ipv6-address-generator.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6-address-generator.cc	2024-02-09 14:44:07.933790200 -0800
@@ -553,7 +553,7 @@
             taddr[j] = (*i).addrLow[j];
         }
         taddr[15] -= 1;
-        if ((Ipv6Address(addr) == Ipv6Address(taddr)))
+        if (Ipv6Address(addr) == Ipv6Address(taddr))
         {
             NS_LOG_LOGIC("New addrLow = " << Ipv6Address(addr));
             for (uint32_t j = 0; j < 16; j++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6.cc ns-3.41/src/internet/model/ipv6.cc
--- ns-3.40/src/internet/model/ipv6.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6.cc	2024-02-09 14:44:07.933790200 -0800
@@ -47,7 +47,14 @@
                           "If disabled, every interface will have its MTU set to 1280 bytes.",
                           BooleanValue(true),
                           MakeBooleanAccessor(&Ipv6::SetMtuDiscover, &Ipv6::GetMtuDiscover),
-                          MakeBooleanChecker());
+                          MakeBooleanChecker())
+            .AddAttribute(
+                "StrongEndSystemModel",
+                "Reject packets for an address not configured on the interface they're "
+                "coming from (RFC1122, section 3.3.4.2).",
+                BooleanValue(true),
+                MakeBooleanAccessor(&Ipv6::SetStrongEndSystemModel, &Ipv6::GetStrongEndSystemModel),
+                MakeBooleanChecker());
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6-extension.cc ns-3.41/src/internet/model/ipv6-extension.cc
--- ns-3.40/src/internet/model/ipv6-extension.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6-extension.cc	2024-02-09 14:44:07.933790200 -0800
@@ -686,7 +686,7 @@
 
     auto iter = --m_timeoutEventList.end();
 
-    return (iter);
+    return iter;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6.h ns-3.41/src/internet/model/ipv6.h
--- ns-3.40/src/internet/model/ipv6.h	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6.h	2024-02-09 14:44:07.933790200 -0800
@@ -424,6 +424,21 @@
      * \return MTU discover state (enabled or not)
      */
     virtual bool GetMtuDiscover() const = 0;
+
+    /**
+     * \brief Set or unset the Strong End System Model
+     *
+     * RFC1122 term for whether host rejects datagram with a dest. address on another interface
+     * \param model true for Strong End System Model
+     */
+    virtual void SetStrongEndSystemModel(bool model) = 0;
+    /**
+     * \brief Get the Strong End System Model status
+     *
+     * RFC1122 term for whether host rejects datagram with a dest. address on another interface
+     * \returns true for Strong End System Model activated
+     */
+    virtual bool GetStrongEndSystemModel() const = 0;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6-interface.cc ns-3.41/src/internet/model/ipv6-interface.cc
--- ns-3.40/src/internet/model/ipv6-interface.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6-interface.cc	2024-02-09 14:44:07.933790200 -0800
@@ -435,12 +435,14 @@
         if (dest == it->first.GetAddress())
         {
             p->AddHeader(hdr);
-            m_tc->Receive(m_device,
-                          p,
-                          Ipv6L3Protocol::PROT_NUMBER,
-                          m_device->GetBroadcast(),
-                          m_device->GetBroadcast(),
-                          NetDevice::PACKET_HOST);
+            Simulator::ScheduleNow(&TrafficControlLayer::Receive,
+                                   m_tc,
+                                   m_device,
+                                   p,
+                                   Ipv6L3Protocol::PROT_NUMBER,
+                                   m_device->GetBroadcast(),
+                                   m_device->GetBroadcast(),
+                                   NetDevice::PACKET_HOST);
             return;
         }
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6-l3-protocol.cc ns-3.41/src/internet/model/ipv6-l3-protocol.cc
--- ns-3.40/src/internet/model/ipv6-l3-protocol.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6-l3-protocol.cc	2024-02-09 14:44:07.933790200 -0800
@@ -88,12 +88,6 @@
                           MakeBooleanAccessor(&Ipv6L3Protocol::SetSendIcmpv6Redirect,
                                               &Ipv6L3Protocol::GetSendIcmpv6Redirect),
                           MakeBooleanChecker())
-            .AddAttribute("StrongEndSystemModel",
-                          "Reject packets for an address not configured on the interface they're "
-                          "coming from (RFC1122, section 3.3.4.2).",
-                          BooleanValue(true),
-                          MakeBooleanAccessor(&Ipv6L3Protocol::m_strongEndSystemModel),
-                          MakeBooleanChecker())
             .AddTraceSource("Tx",
                             "Send IPv6 packet to outgoing interface.",
                             MakeTraceSourceAccessor(&Ipv6L3Protocol::m_txTrace),
@@ -1169,20 +1163,22 @@
                     LocalDeliver(packet, hdr, interface);
                     return;
                 }
-                else if (!m_strongEndSystemModel)
+                else if (!GetStrongEndSystemModel())
                 {
                     NS_LOG_LOGIC("For me (destination "
-                                 << addr << " match) on another interface with Weak ES Model"
+                                 << addr
+                                 << " match) on another interface with Weak End System Model"
                                  << hdr.GetDestination());
                     LocalDeliver(packet, hdr, interface);
                     return;
                 }
                 else
                 {
-                    NS_LOG_LOGIC("For me (destination "
-                                 << addr
-                                 << " match) on another interface with Strong ES Model - discarding"
-                                 << hdr.GetDestination());
+                    NS_LOG_LOGIC(
+                        "For me (destination "
+                        << addr
+                        << " match) on another interface with Strong End System Model - discarding"
+                        << hdr.GetDestination());
                     m_dropTrace(hdr, packet, DROP_NO_ROUTE, this, interface);
                     return;
                 }
@@ -1839,4 +1835,18 @@
     return true;
 }
 
+void
+Ipv6L3Protocol::SetStrongEndSystemModel(bool model)
+{
+    NS_LOG_FUNCTION(this << model);
+    m_strongEndSystemModel = model;
+}
+
+bool
+Ipv6L3Protocol::GetStrongEndSystemModel() const
+{
+    NS_LOG_FUNCTION(this);
+    return m_strongEndSystemModel;
+}
+
 } /* namespace ns3 */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6-l3-protocol.h ns-3.41/src/internet/model/ipv6-l3-protocol.h
--- ns-3.40/src/internet/model/ipv6-l3-protocol.h	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6-l3-protocol.h	2024-02-09 14:44:07.933790200 -0800
@@ -707,6 +707,9 @@
      */
     virtual bool GetSendIcmpv6Redirect() const;
 
+    void SetStrongEndSystemModel(bool model) override;
+    bool GetStrongEndSystemModel() const override;
+
     /**
      * \brief Node attached to stack.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6-raw-socket-impl.cc ns-3.41/src/internet/model/ipv6-raw-socket-impl.cc
--- ns-3.40/src/internet/model/ipv6-raw-socket-impl.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6-raw-socket-impl.cc	2024-02-09 14:44:07.933790200 -0800
@@ -139,7 +139,7 @@
 int
 Ipv6RawSocketImpl::Bind6()
 {
-    return (Bind());
+    return Bind();
 }
 
 int
@@ -534,25 +534,25 @@
 void
 Ipv6RawSocketImpl::Icmpv6FilterSetPass(uint8_t type)
 {
-    (m_icmpFilter.icmpv6Filt[(type) >> 5]) |= (uint32_t(1) << ((type)&31));
+    m_icmpFilter.icmpv6Filt[type >> 5] |= (uint32_t(1) << (type & 31));
 }
 
 void
 Ipv6RawSocketImpl::Icmpv6FilterSetBlock(uint8_t type)
 {
-    (m_icmpFilter.icmpv6Filt[(type) >> 5]) &= ~(uint32_t(1) << ((type)&31));
+    m_icmpFilter.icmpv6Filt[(type) >> 5] &= ~(uint32_t(1) << (type & 31));
 }
 
 bool
 Ipv6RawSocketImpl::Icmpv6FilterWillPass(uint8_t type)
 {
-    return (((m_icmpFilter.icmpv6Filt[(type) >> 5]) & (uint32_t(1) << ((type)&31))) != 0);
+    return ((m_icmpFilter.icmpv6Filt[type >> 5]) & (uint32_t(1) << (type & 31))) != 0;
 }
 
 bool
 Ipv6RawSocketImpl::Icmpv6FilterWillBlock(uint8_t type)
 {
-    return (((m_icmpFilter.icmpv6Filt[(type) >> 5]) & (uint32_t(1) << ((type)&31))) == 0);
+    return ((m_icmpFilter.icmpv6Filt[type >> 5]) & (uint32_t(1) << (type & 31))) == 0;
 }
 
 } /* namespace ns3 */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ipv6-route.cc ns-3.41/src/internet/model/ipv6-route.cc
--- ns-3.40/src/internet/model/ipv6-route.cc	2023-09-27 13:30:37.658839200 -0700
+++ ns-3.41/src/internet/model/ipv6-route.cc	2024-02-09 14:44:07.933790200 -0800
@@ -156,7 +156,7 @@
 std::map<uint32_t, uint32_t>
 Ipv6MulticastRoute::GetOutputTtlMap() const
 {
-    return (m_ttls);
+    return m_ttls;
 }
 
 std::ostream&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/rip.cc ns-3.41/src/internet/model/rip.cc
--- ns-3.40/src/internet/model/rip.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/rip.cc	2024-02-09 14:44:07.937790400 -0800
@@ -99,7 +99,7 @@
             .AddAttribute("SplitHorizon",
                           "Split Horizon strategy.",
                           EnumValue(Rip::POISON_REVERSE),
-                          MakeEnumAccessor(&Rip::m_splitHorizonStrategy),
+                          MakeEnumAccessor<SplitHorizonType_e>(&Rip::m_splitHorizonStrategy),
                           MakeEnumChecker(Rip::NO_SPLIT_HORIZON,
                                           "NoSplitHorizon",
                                           Rip::SPLIT_HORIZON,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/ripng.cc ns-3.41/src/internet/model/ripng.cc
--- ns-3.40/src/internet/model/ripng.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/ripng.cc	2024-02-09 14:44:07.937790400 -0800
@@ -99,7 +99,7 @@
             .AddAttribute("SplitHorizon",
                           "Split Horizon strategy.",
                           EnumValue(RipNg::POISON_REVERSE),
-                          MakeEnumAccessor(&RipNg::m_splitHorizonStrategy),
+                          MakeEnumAccessor<SplitHorizonType_e>(&RipNg::m_splitHorizonStrategy),
                           MakeEnumChecker(RipNg::NO_SPLIT_HORIZON,
                                           "NoSplitHorizon",
                                           RipNg::SPLIT_HORIZON,
@@ -657,22 +657,23 @@
 
                     if (setSource)
                     {
-                        if (route->GetGateway().IsAny())
+                        // GetGateway().IsAny() means that the destination is reachable without a
+                        // gateway (is on-link). GetDest().IsAny() means that the route is the
+                        // default route. Having both true is very strange, but possible.
+                        // If the RT entry is specific for a destination, use that as a hint for the
+                        // source address to be used. Else, use the destination or the prefix to be
+                        // used stated in the RT entry.
+                        if (!route->GetDest().IsAny())
                         {
                             rtentry->SetSource(
                                 m_ipv6->SourceAddressSelection(interfaceIdx, route->GetDest()));
                         }
-                        else if (route->GetDest().IsAny()) /* default route */
+                        else
                         {
                             rtentry->SetSource(m_ipv6->SourceAddressSelection(
                                 interfaceIdx,
                                 route->GetPrefixToUse().IsAny() ? dst : route->GetPrefixToUse()));
                         }
-                        else
-                        {
-                            rtentry->SetSource(
-                                m_ipv6->SourceAddressSelection(interfaceIdx, route->GetDest()));
-                        }
                     }
 
                     rtentry->SetDestination(route->GetDest());
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-cubic.cc ns-3.41/src/internet/model/tcp-cubic.cc
--- ns-3.40/src/internet/model/tcp-cubic.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-cubic.cc	2024-02-09 14:44:07.937790400 -0800
@@ -45,6 +45,11 @@
                           BooleanValue(true),
                           MakeBooleanAccessor(&TcpCubic::m_fastConvergence),
                           MakeBooleanChecker())
+            .AddAttribute("TcpFriendliness",
+                          "Enable (true) or disable (false) TCP friendliness",
+                          BooleanValue(true),
+                          MakeBooleanAccessor(&TcpCubic::m_tcpFriendliness),
+                          MakeBooleanChecker())
             .AddAttribute("Beta",
                           "Beta for multiplicative decrease",
                           DoubleValue(0.7),
@@ -64,7 +69,7 @@
                           "Hybrid Slow Start detection mechanisms:"
                           "packet train, delay, both",
                           EnumValue(HybridSSDetectionMode::BOTH),
-                          MakeEnumAccessor(&TcpCubic::m_hystartDetect),
+                          MakeEnumAccessor<HybridSSDetectionMode>(&TcpCubic::m_hystartDetect),
                           MakeEnumChecker(HybridSSDetectionMode::PACKET_TRAIN,
                                           "PACKET_TRAIN",
                                           HybridSSDetectionMode::DELAY,
@@ -209,7 +214,7 @@
     if (tcb->m_cWnd >= tcb->m_ssThresh && segmentsAcked > 0)
     {
         m_cWndCnt += segmentsAcked;
-        uint32_t cnt = Update(tcb);
+        uint32_t cnt = Update(tcb, segmentsAcked);
 
         /* According to RFC 6356 even once the new cwnd is
          * calculated you must compare this to the number of ACKs received since
@@ -232,19 +237,24 @@
 }
 
 uint32_t
-TcpCubic::Update(Ptr<TcpSocketState> tcb)
+TcpCubic::Update(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
 {
     NS_LOG_FUNCTION(this);
     Time t;
     uint32_t delta;
     uint32_t bicTarget;
     uint32_t cnt = 0;
+    uint32_t maxCnt;
     double offs;
     uint32_t segCwnd = tcb->GetCwndInSegments();
 
+    m_ackCnt += segmentsAcked;
+
     if (m_epochStart == Time::Min())
     {
         m_epochStart = Simulator::Now(); // record the beginning of an epoch
+        m_ackCnt = segmentsAcked;
+        m_tcpCwnd = segCwnd;
 
         if (m_lastMaxCwnd <= segCwnd)
         {
@@ -311,6 +321,26 @@
         cnt = m_cntClamp;
     }
 
+    if (m_tcpFriendliness)
+    {
+        auto scale = static_cast<uint32_t>(8 * (1024 + m_beta * 1024) / 3 / (1024 - m_beta * 1024));
+        delta = (segCwnd * scale) >> 3;
+        while (m_ackCnt > delta)
+        {
+            m_ackCnt -= delta;
+            m_tcpCwnd++;
+        }
+        if (m_tcpCwnd > segCwnd)
+        {
+            delta = m_tcpCwnd - segCwnd;
+            maxCnt = segCwnd / delta;
+            if (cnt > maxCnt)
+            {
+                cnt = maxCnt;
+            }
+        }
+    }
+
     // The maximum rate of cwnd increase CUBIC allows is 1 packet per
     // 2 packets ACKed, meaning cwnd grows at 1.5x per RTT.
     return std::max(cnt, 2U);
@@ -463,6 +493,8 @@
     m_lastMaxCwnd = 0;
     m_bicOriginPoint = 0;
     m_bicK = 0;
+    m_ackCnt = 0;
+    m_tcpCwnd = 0;
     m_delayMin = Time::Min();
     m_found = false;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-cubic.h ns-3.41/src/internet/model/tcp-cubic.h
--- ns-3.40/src/internet/model/tcp-cubic.h	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-cubic.h	2024-02-09 14:44:07.937790400 -0800
@@ -104,6 +104,7 @@
 
   private:
     bool m_fastConvergence; //!< Enable or disable fast convergence algorithm
+    bool m_tcpFriendliness; //!< Enable or disable TCP-friendliness heuristic
     double m_beta;          //!< Beta for cubic multiplicative increase
 
     bool m_hystart;                        //!< Enable or disable HyStart algorithm
@@ -134,6 +135,8 @@
     Time m_cubicDelta;         //!<  Time to wait after recovery before update
     Time m_currRtt;            //!<  Current Rtt
     uint32_t m_sampleCnt;      //!<  Count of samples for HyStart
+    uint32_t m_ackCnt;         //!<  Count the number of ACKed packets
+    uint32_t m_tcpCwnd;        //!<  Estimated tcp cwnd (for Reno-friendliness)
 
   private:
     /**
@@ -151,9 +154,10 @@
     /**
      * \brief Cubic window update after a new ack received
      * \param tcb Transmission Control Block of the connection
+     * \param segmentsAcked Segments acked
      * \returns the congestion window update counter
      */
-    uint32_t Update(Ptr<TcpSocketState> tcb);
+    uint32_t Update(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked);
 
     /**
      * \brief Update HyStart parameters
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-dctcp.h ns-3.41/src/internet/model/tcp-dctcp.h
--- ns-3.40/src/internet/model/tcp-dctcp.h	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-dctcp.h	2024-02-09 14:44:07.937790400 -0800
@@ -33,7 +33,10 @@
  * \ingroup tcp
  *
  * \brief An implementation of DCTCP. This model implements all of the
- * endpoint capabilities mentioned in the DCTCP SIGCOMM paper.
+ * endpoint capabilities mentioned in the DCTCP SIGCOMM paper.  Note that
+ * this model presently will not do any fallback to RFC 5681 congestion
+ * avoidance as specified in RFC 8257 Section 3.5, so it should only be
+ * used in a simulation that does not involve packet loss.
  */
 
 class TcpDctcp : public TcpLinuxReno
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-ledbat.cc ns-3.41/src/internet/model/tcp-ledbat.cc
--- ns-3.40/src/internet/model/tcp-ledbat.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-ledbat.cc	2024-02-09 14:44:07.937790400 -0800
@@ -62,7 +62,7 @@
             .AddAttribute("SSParam",
                           "Possibility of Slow Start",
                           EnumValue(DO_SLOWSTART),
-                          MakeEnumAccessor(&TcpLedbat::SetDoSs),
+                          MakeEnumAccessor<SlowStartType>(&TcpLedbat::SetDoSs),
                           MakeEnumChecker(DO_SLOWSTART, "yes", DO_NOT_SLOWSTART, "no"))
             .AddAttribute("MinCwnd",
                           "Minimum cWnd for Ledbat",
@@ -102,7 +102,7 @@
     m_sndCwndCnt = 0;
     m_flag = LEDBAT_CAN_SS;
     m_minCwnd = 2;
-};
+}
 
 void
 TcpLedbat::InitCircBuf(OwdCircBuf& buffer)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-prr-recovery.cc ns-3.41/src/internet/model/tcp-prr-recovery.cc
--- ns-3.40/src/internet/model/tcp-prr-recovery.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-prr-recovery.cc	2024-02-09 14:44:07.941790300 -0800
@@ -35,15 +35,16 @@
 TypeId
 TcpPrrRecovery::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::TcpPrrRecovery")
-                            .SetParent<TcpClassicRecovery>()
-                            .AddConstructor<TcpPrrRecovery>()
-                            .SetGroupName("Internet")
-                            .AddAttribute("ReductionBound",
-                                          "Type of Reduction Bound",
-                                          EnumValue(SSRB),
-                                          MakeEnumAccessor(&TcpPrrRecovery::m_reductionBoundMode),
-                                          MakeEnumChecker(CRB, "CRB", SSRB, "SSRB"));
+    static TypeId tid =
+        TypeId("ns3::TcpPrrRecovery")
+            .SetParent<TcpClassicRecovery>()
+            .AddConstructor<TcpPrrRecovery>()
+            .SetGroupName("Internet")
+            .AddAttribute("ReductionBound",
+                          "Type of Reduction Bound",
+                          EnumValue(SSRB),
+                          MakeEnumAccessor<ReductionBound_t>(&TcpPrrRecovery::m_reductionBoundMode),
+                          MakeEnumChecker(CRB, "CRB", SSRB, "SSRB"));
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-rate-ops.cc ns-3.41/src/internet/model/tcp-rate-ops.cc
--- ns-3.40/src/internet/model/tcp-rate-ops.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-rate-ops.cc	2024-02-09 14:44:07.941790300 -0800
@@ -183,7 +183,7 @@
 
     if (m_rateSample.m_priorDelivered == 0 || skbInfo.m_delivered > m_rateSample.m_priorDelivered)
     {
-        m_rateSample.m_ackElapsed = Simulator::Now() - m_rateSample.m_priorTime;
+        m_rateSample.m_ackElapsed = Simulator::Now() - skbInfo.m_deliveredTime;
         m_rateSample.m_priorDelivered = skbInfo.m_delivered;
         m_rateSample.m_priorTime = skbInfo.m_deliveredTime;
         m_rateSample.m_isAppLimited = skbInfo.m_isAppLimited;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-socket-base.cc ns-3.41/src/internet/model/tcp-socket-base.cc
--- ns-3.40/src/internet/model/tcp-socket-base.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-socket-base.cc	2024-02-09 14:44:07.941790300 -0800
@@ -164,7 +164,7 @@
             .AddAttribute("UseEcn",
                           "Parameter to set ECN functionality",
                           EnumValue(TcpSocketState::Off),
-                          MakeEnumAccessor(&TcpSocketBase::SetUseEcn),
+                          MakeEnumAccessor<TcpSocketState::UseEcn_t>(&TcpSocketBase::SetUseEcn),
                           MakeEnumChecker(TcpSocketState::Off,
                                           "Off",
                                           TcpSocketState::On,
@@ -1689,7 +1689,8 @@
     }
 
     // (4.3) Retransmit the first data segment presumed dropped
-    DoRetransmit();
+    uint32_t sz = SendDataPacket(m_highRxAckMark, m_tcb->m_segmentSize, true);
+    NS_ASSERT_MSG(sz > 0, "SendDataPacket returned zero, indicating zero bytes were sent");
     // (4.4) Run SetPipe ()
     // (4.5) Proceed to step (C)
     // these steps are done after the ProcessAck function (SendPendingData)
@@ -1777,8 +1778,8 @@
         // (2) If DupAcks < DupThresh but IsLost (HighACK + 1) returns true
         // (indicating at least three segments have arrived above the current
         // cumulative acknowledgment point, which is taken to indicate loss)
-        // go to step (4).
-        else if (m_txBuffer->IsLost(m_highRxAckMark + m_tcb->m_segmentSize))
+        // go to step (4).  Note that m_highRxAckMark is (HighACK + 1)
+        else if (m_txBuffer->IsLost(m_highRxAckMark))
         {
             EnterRecovery(currentDelivered);
             NS_ASSERT(m_tcb->m_congState == TcpSocketState::CA_RECOVERY);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-tx-buffer.cc ns-3.41/src/internet/model/tcp-tx-buffer.cc
--- ns-3.40/src/internet/model/tcp-tx-buffer.cc	2023-09-27 13:30:37.662839200 -0700
+++ ns-3.41/src/internet/model/tcp-tx-buffer.cc	2024-02-09 14:44:07.941790300 -0800
@@ -926,8 +926,6 @@
 {
     NS_LOG_FUNCTION(this << seq);
 
-    SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
-
     if (seq >= m_highestSack.second)
     {
         return false;
@@ -938,7 +936,7 @@
     for (auto it = m_sentList.begin(); it != m_sentList.end(); ++it)
     {
         // Search for the right iterator before calling IsLost()
-        if (beginOfCurrentPacket >= seq)
+        if ((*it)->m_startSeq <= seq && seq < (*it)->m_startSeq + (*it)->m_packet->GetSize())
         {
             if ((*it)->m_lost)
             {
@@ -952,8 +950,6 @@
                 return false;
             }
         }
-
-        beginOfCurrentPacket += (*it)->m_packet->GetSize();
     }
 
     return false;
@@ -1016,7 +1012,7 @@
      */
     if (SizeFromSequence(m_firstByteSeq + m_sentSize) > 0)
     {
-        if (m_sentSize <= m_rWndCallback())
+        if (m_sentSize < m_rWndCallback())
         {
             NS_LOG_INFO("There is unsent data. Send it");
             *seq = m_firstByteSeq + m_sentSize;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/tcp-westwood-plus.cc ns-3.41/src/internet/model/tcp-westwood-plus.cc
--- ns-3.40/src/internet/model/tcp-westwood-plus.cc	2023-09-27 13:30:37.666839100 -0700
+++ ns-3.41/src/internet/model/tcp-westwood-plus.cc	2024-02-09 14:44:07.941790300 -0800
@@ -54,7 +54,7 @@
                 "FilterType",
                 "Use this to choose no filter or Tustin's approximation filter",
                 EnumValue(TcpWestwoodPlus::TUSTIN),
-                MakeEnumAccessor(&TcpWestwoodPlus::m_fType),
+                MakeEnumAccessor<FilterType>(&TcpWestwoodPlus::m_fType),
                 MakeEnumChecker(TcpWestwoodPlus::NONE, "None", TcpWestwoodPlus::TUSTIN, "Tustin"))
             .AddTraceSource("EstimatedBW",
                             "The estimated bandwidth",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/model/udp-socket-impl.cc ns-3.41/src/internet/model/udp-socket-impl.cc
--- ns-3.40/src/internet/model/udp-socket-impl.cc	2023-09-27 13:30:37.666839100 -0700
+++ ns-3.41/src/internet/model/udp-socket-impl.cc	2024-02-09 14:44:07.945790300 -0800
@@ -496,7 +496,7 @@
     }
 
     m_errno = ERROR_AFNOSUPPORT;
-    return (-1);
+    return -1;
 }
 
 int
@@ -695,7 +695,7 @@
 
     if (dest.IsIpv4MappedAddress())
     {
-        return (DoSendTo(p, dest.GetIpv4MappedAddress(), port, 0));
+        return DoSendTo(p, dest.GetIpv4MappedAddress(), port, 0);
     }
     if (m_boundnetdevice)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/test/neighbor-cache-test.cc ns-3.41/src/internet/test/neighbor-cache-test.cc
--- ns-3.40/src/internet/test/neighbor-cache-test.cc	2023-09-27 13:30:37.666839100 -0700
+++ ns-3.41/src/internet/test/neighbor-cache-test.cc	2024-02-09 14:44:07.945790300 -0800
@@ -872,7 +872,7 @@
     Ptr<Ipv4Interface> iface = DynamicCast<Ipv4L3Protocol>(v4)->GetInterface(index);
     Ptr<ArpCache> arpCache = iface->GetArpCache();
     ArpCache::Entry* arpCacheEntry = arpCache->Add(Ipv4Address("10.1.1.4"));
-    arpCacheEntry->SetMacAddress(Mac48Address("04-06-00:00:00:00:00:01"));
+    arpCacheEntry->SetMacAddress(Mac48Address("00:00:00:00:00:01"));
     arpCacheEntry->MarkPermanent();
 
     // Manually add an PERMANENT ndisc entry
@@ -882,7 +882,7 @@
     Ptr<Ipv6Interface> ifacev6 = DynamicCast<Ipv6L3Protocol>(v6)->GetInterface(index);
     Ptr<NdiscCache> ndiscCache = ifacev6->GetNdiscCache();
     NdiscCache::Entry* ndiscCacheEntry = ndiscCache->Add(Ipv6Address("2001::200:ff:fe00:4"));
-    ndiscCacheEntry->SetMacAddress(Mac48Address("04-06-00:00:00:00:00:01"));
+    ndiscCacheEntry->SetMacAddress(Mac48Address("00:00:00:00:00:01"));
     ndiscCacheEntry->MarkPermanent();
 
     // flush auto-generated cache
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/test/tcp-advertised-window-test.cc ns-3.41/src/internet/test/tcp-advertised-window-test.cc
--- ns-3.40/src/internet/test/tcp-advertised-window-test.cc	2023-09-27 13:30:37.666839100 -0700
+++ ns-3.41/src/internet/test/tcp-advertised-window-test.cc	2024-02-09 14:44:07.949790500 -0800
@@ -81,7 +81,7 @@
     void SetExpectedSegmentSize(uint16_t seg)
     {
         m_segmentSize = seg;
-    };
+    }
 
   protected:
     Ptr<TcpSocketBase> Fork() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet/test/tcp-rtt-estimation.cc ns-3.41/src/internet/test/tcp-rtt-estimation.cc
--- ns-3.40/src/internet/test/tcp-rtt-estimation.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/internet/test/tcp-rtt-estimation.cc	2024-02-09 14:44:07.949790500 -0800
@@ -234,7 +234,7 @@
 
     for (auto it = m_toDrop.begin(); it != m_toDrop.end(); ++it)
     {
-        errorModel->AddSeqToKill(SequenceNumber32((*it)));
+        errorModel->AddSeqToKill(SequenceNumber32(*it));
     }
 
     return errorModel;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/CMakeLists.txt ns-3.41/src/internet-apps/CMakeLists.txt
--- ns-3.40/src/internet-apps/CMakeLists.txt	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/CMakeLists.txt	2024-02-09 14:44:07.913790200 -0800
@@ -3,36 +3,28 @@
   SOURCE_FILES
     helper/dhcp-helper.cc
     helper/ping-helper.cc
-    helper/ping6-helper.cc
     helper/radvd-helper.cc
-    helper/v4ping-helper.cc
     helper/v4traceroute-helper.cc
     model/dhcp-client.cc
     model/dhcp-header.cc
     model/dhcp-server.cc
     model/ping.cc
-    model/ping6.cc
     model/radvd-interface.cc
     model/radvd-prefix.cc
     model/radvd.cc
-    model/v4ping.cc
     model/v4traceroute.cc
   HEADER_FILES
     helper/dhcp-helper.h
     helper/ping-helper.h
-    helper/ping6-helper.h
     helper/radvd-helper.h
-    helper/v4ping-helper.h
     helper/v4traceroute-helper.h
     model/dhcp-client.h
     model/dhcp-header.h
     model/dhcp-server.h
     model/ping.h
-    model/ping6.h
     model/radvd-interface.h
     model/radvd-prefix.h
     model/radvd.h
-    model/v4ping.h
     model/v4traceroute.h
   LIBRARIES_TO_LINK ${libinternet}
   TEST_SOURCES
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/helper/ping6-helper.cc ns-3.41/src/internet-apps/helper/ping6-helper.cc
--- ns-3.40/src/internet-apps/helper/ping6-helper.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/helper/ping6-helper.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2008-2009 Strasbourg University
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
- */
-
-#include "ping6-helper.h"
-
-#include "ns3/ping6.h"
-#include "ns3/uinteger.h"
-#include "ns3/warnings.h"
-
-namespace ns3
-{
-
-Ping6Helper::Ping6Helper()
-    : m_ifIndex(0)
-{
-    NS_WARNING_PUSH_DEPRECATED;
-    m_factory.SetTypeId(Ping6::GetTypeId());
-    NS_WARNING_POP;
-}
-
-void
-Ping6Helper::SetLocal(Ipv6Address ip)
-{
-    m_localIp = ip;
-}
-
-void
-Ping6Helper::SetRemote(Ipv6Address ip)
-{
-    m_remoteIp = ip;
-}
-
-void
-Ping6Helper::SetAttribute(std::string name, const AttributeValue& value)
-{
-    m_factory.Set(name, value);
-}
-
-ApplicationContainer
-Ping6Helper::Install(NodeContainer c)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-    ApplicationContainer apps;
-    for (auto i = c.Begin(); i != c.End(); ++i)
-    {
-        Ptr<Node> node = *i;
-        Ptr<Ping6> client = m_factory.Create<Ping6>();
-        client->SetLocal(m_localIp);
-        client->SetRemote(m_remoteIp);
-        client->SetIfIndex(m_ifIndex);
-        client->SetRouters(m_routers);
-        node->AddApplication(client);
-        apps.Add(client);
-    }
-    return apps;
-
-#pragma GCC diagnostic pop
-}
-
-void
-Ping6Helper::SetIfIndex(uint32_t ifIndex)
-{
-    m_ifIndex = ifIndex;
-}
-
-void
-Ping6Helper::SetRoutersAddress(std::vector<Ipv6Address> routers)
-{
-    m_routers = routers;
-}
-
-} /* namespace ns3 */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/helper/ping6-helper.h ns-3.41/src/internet-apps/helper/ping6-helper.h
--- ns-3.40/src/internet-apps/helper/ping6-helper.h	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/helper/ping6-helper.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,116 +0,0 @@
-/*
- * Copyright (c) 2008-2009 Strasbourg University
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
- */
-
-#ifndef PING6_HELPER_H
-#define PING6_HELPER_H
-
-#include "ns3/application-container.h"
-#include "ns3/ipv6-address.h"
-#include "ns3/node-container.h"
-#include "ns3/object-factory.h"
-
-#include <stdint.h>
-
-namespace ns3
-{
-
-/**
- * \ingroup ping6
- * \brief Ping6 application helper.
- */
-class Ping6Helper
-{
-  public:
-    /**
-     * \brief Constructor.
-     */
-    NS_DEPRECATED_3_38("Use PingHelper instead - the attributes might have been renamed.")
-    Ping6Helper();
-
-    /**
-     * \brief Set the local IPv6 address.
-     * \param ip local IPv6 address
-     */
-    void SetLocal(Ipv6Address ip);
-
-    /**
-     * \brief Set the remote IPv6 address.
-     * \param ip remote IPv6 address
-     */
-    void SetRemote(Ipv6Address ip);
-
-    /**
-     * \brief Set some attributes.
-     * \param name attribute name
-     * \param value attribute value
-     */
-    void SetAttribute(std::string name, const AttributeValue& value);
-
-    /**
-     * \brief Install the application in Nodes.
-     * \param c list of Nodes
-     * \return application container
-     */
-    ApplicationContainer Install(NodeContainer c);
-
-    /**
-     * \brief Set the out interface index.
-     * This is to send to link-local (unicast or multicast) address
-     * when a node has multiple interfaces.
-     * \param ifIndex interface index
-     */
-    NS_DEPRECATED_3_38("Use a source address")
-    void SetIfIndex(uint32_t ifIndex);
-
-    /**
-     * \brief Set routers addresses for routing type 0.
-     * \param routers routers addresses
-     */
-    void SetRoutersAddress(std::vector<Ipv6Address> routers);
-
-  private:
-    /**
-     * \brief An object factory.
-     */
-    ObjectFactory m_factory;
-
-    /**
-     * \brief The local IPv6 address.
-     */
-    Ipv6Address m_localIp;
-
-    /**
-     * \brief The remote IPv6 address.
-     */
-    Ipv6Address m_remoteIp;
-
-    /**
-     * \brief Out interface index.
-     */
-    uint32_t m_ifIndex;
-
-    /**
-     * \brief Routers addresses.
-     */
-    std::vector<Ipv6Address> m_routers;
-};
-
-} /* namespace ns3 */
-
-#endif /* PING6_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/helper/v4ping-helper.cc ns-3.41/src/internet-apps/helper/v4ping-helper.cc
--- ns-3.40/src/internet-apps/helper/v4ping-helper.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/helper/v4ping-helper.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
- */
-
-#include "v4ping-helper.h"
-
-#include "ns3/names.h"
-#include "ns3/v4ping.h"
-#include "ns3/warnings.h"
-
-namespace ns3
-{
-
-V4PingHelper::V4PingHelper(Ipv4Address remote)
-{
-    NS_WARNING_PUSH_DEPRECATED;
-    m_factory.SetTypeId("ns3::V4Ping");
-    m_factory.Set("Remote", Ipv4AddressValue(remote));
-    NS_WARNING_POP;
-}
-
-void
-V4PingHelper::SetAttribute(std::string name, const AttributeValue& value)
-{
-    m_factory.Set(name, value);
-}
-
-ApplicationContainer
-V4PingHelper::Install(Ptr<Node> node) const
-{
-    return ApplicationContainer(InstallPriv(node));
-}
-
-ApplicationContainer
-V4PingHelper::Install(std::string nodeName) const
-{
-    Ptr<Node> node = Names::Find<Node>(nodeName);
-    return ApplicationContainer(InstallPriv(node));
-}
-
-ApplicationContainer
-V4PingHelper::Install(NodeContainer c) const
-{
-    ApplicationContainer apps;
-    for (auto i = c.Begin(); i != c.End(); ++i)
-    {
-        apps.Add(InstallPriv(*i));
-    }
-
-    return apps;
-}
-
-Ptr<Application>
-V4PingHelper::InstallPriv(Ptr<Node> node) const
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-    Ptr<V4Ping> app = m_factory.Create<V4Ping>();
-    node->AddApplication(app);
-
-    return app;
-
-#pragma GCC diagnostic pop
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/helper/v4ping-helper.h ns-3.41/src/internet-apps/helper/v4ping-helper.h
--- ns-3.40/src/internet-apps/helper/v4ping-helper.h	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/helper/v4ping-helper.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
- */
-
-#ifndef V4PING_HELPER_H
-#define V4PING_HELPER_H
-
-#include "ns3/application-container.h"
-#include "ns3/node-container.h"
-#include "ns3/object-factory.h"
-
-namespace ns3
-{
-
-/**
- * \ingroup v4ping
- * \brief Create a IPv4 ping application and associate it to a node
- *
- * This class creates one or multiple instances of ns3::V4Ping and associates
- * it/them to one/multiple node(s).
- */
-class V4PingHelper
-{
-  public:
-    /**
-     * Create a V4PingHelper which is used to make life easier for people wanting
-     * to use ping Applications.
-     *
-     * \param remote The address which should be pinged
-     */
-    NS_DEPRECATED_3_38("Use PingHelper instead - the attributes might have been renamed.")
-    V4PingHelper(Ipv4Address remote);
-
-    /**
-     * Install a Ping application on each Node in the provided NodeContainer.
-     *
-     * \param nodes The NodeContainer containing all of the nodes to get a V4Ping
-     *              application.
-     *
-     * \returns A list of Ping applications, one for each input node
-     */
-    ApplicationContainer Install(NodeContainer nodes) const;
-
-    /**
-     * Install a Ping application on the provided Node.  The Node is specified
-     * directly by a Ptr<Node>
-     *
-     * \param node The node to install the V4PingApplication on.
-     *
-     * \returns An ApplicationContainer holding the Ping application created.
-     */
-    ApplicationContainer Install(Ptr<Node> node) const;
-
-    /**
-     * Install a Ping application on the provided Node.  The Node is specified
-     * by a string that must have previously been associated with a Node using the
-     * Object Name Service.
-     *
-     * \param nodeName The node to install the V4PingApplication on.
-     *
-     * \returns An ApplicationContainer holding the Ping application created.
-     */
-    ApplicationContainer Install(std::string nodeName) const;
-
-    /**
-     * \brief Configure ping applications attribute
-     * \param name   attribute's name
-     * \param value  attribute's value
-     */
-    void SetAttribute(std::string name, const AttributeValue& value);
-
-  private:
-    /**
-     * \brief Do the actual application installation in the node
-     * \param node the node
-     * \returns a Smart pointer to the installed application
-     */
-    Ptr<Application> InstallPriv(Ptr<Node> node) const;
-    /// Object factory
-    ObjectFactory m_factory;
-};
-
-} // namespace ns3
-
-#endif /* V4PING_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/model/dhcp-header.cc ns-3.41/src/internet-apps/model/dhcp-header.cc
--- ns-3.40/src/internet-apps/model/dhcp-header.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/model/dhcp-header.cc	2024-02-09 14:44:07.913790200 -0800
@@ -338,7 +338,7 @@
     {
         i.WriteU8(OP_MSGTYPE);
         i.WriteU8(1);
-        i.WriteU8((m_op + 1));
+        i.WriteU8(m_op + 1);
     }
     if (m_opt[OP_ADDREQ])
     {
@@ -383,8 +383,8 @@
 DhcpHeader::Deserialize(Buffer::Iterator start)
 {
     uint32_t len;
-    uint32_t clen = start.GetSize();
-    if (clen < 240)
+    uint32_t cLen = start.GetSize();
+    if (cLen < 240)
     {
         NS_LOG_WARN("Malformed Packet");
         return 0;
@@ -416,7 +416,7 @@
     bool loop = true;
     do
     {
-        if (len + 1 <= clen)
+        if (len + 1 <= cLen)
         {
             option = i.ReadU8();
             len += 1;
@@ -429,7 +429,7 @@
         switch (option)
         {
         case OP_MASK:
-            if (len + 5 < clen)
+            if (len + 5 < cLen)
             {
                 i.ReadU8();
                 m_mask = i.ReadNtohU32();
@@ -442,7 +442,7 @@
             }
             break;
         case OP_ROUTE:
-            if (len + 5 < clen)
+            if (len + 5 < cLen)
             {
                 i.ReadU8();
                 ReadFrom(i, m_route);
@@ -455,7 +455,7 @@
             }
             break;
         case OP_MSGTYPE:
-            if (len + 2 < clen)
+            if (len + 2 < cLen)
             {
                 i.ReadU8();
                 m_op = (i.ReadU8() - 1);
@@ -468,7 +468,7 @@
             }
             break;
         case OP_SERVID:
-            if (len + 5 < clen)
+            if (len + 5 < cLen)
             {
                 i.ReadU8();
                 ReadFrom(i, m_dhcps);
@@ -481,7 +481,7 @@
             }
             break;
         case OP_ADDREQ:
-            if (len + 5 < clen)
+            if (len + 5 < cLen)
             {
                 i.ReadU8();
                 ReadFrom(i, m_req);
@@ -494,7 +494,7 @@
             }
             break;
         case OP_LEASE:
-            if (len + 5 < clen)
+            if (len + 5 < cLen)
             {
                 i.ReadU8();
                 m_lease = i.ReadNtohU32();
@@ -507,7 +507,7 @@
             }
             break;
         case OP_RENEW:
-            if (len + 5 < clen)
+            if (len + 5 < cLen)
             {
                 i.ReadU8();
                 m_renew = i.ReadNtohU32();
@@ -520,7 +520,7 @@
             }
             break;
         case OP_REBIND:
-            if (len + 5 < clen)
+            if (len + 5 < cLen)
             {
                 i.ReadU8();
                 m_rebind = i.ReadNtohU32();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/model/ping6.cc ns-3.41/src/internet-apps/model/ping6.cc
--- ns-3.40/src/internet-apps/model/ping6.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/model/ping6.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,319 +0,0 @@
-/*
- * Copyright (c) 2007-2009 Strasbourg University
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
- */
-
-#include "ping6.h"
-
-#include "ns3/icmpv6-header.h"
-#include "ns3/inet6-socket-address.h"
-#include "ns3/ipv6-extension-header.h"
-#include "ns3/ipv6-header.h"
-#include "ns3/ipv6-raw-socket-factory.h"
-#include "ns3/log.h"
-#include "ns3/nstime.h"
-#include "ns3/packet.h"
-#include "ns3/simulator.h"
-#include "ns3/socket-factory.h"
-#include "ns3/socket.h"
-#include "ns3/uinteger.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("Ping6Application");
-
-NS_OBJECT_ENSURE_REGISTERED(Ping6);
-
-TypeId
-Ping6::GetTypeId()
-{
-    static TypeId tid =
-        TypeId("ns3::Ping6")
-            .SetParent<Application>()
-            .SetGroupName("Internet-Apps")
-            .AddConstructor<Ping6>()
-            .AddAttribute(
-                "MaxPackets",
-                "The maximum number of packets the application will send (zero means infinite)",
-                UintegerValue(100),
-                MakeUintegerAccessor(&Ping6::m_count),
-                MakeUintegerChecker<uint32_t>())
-            .AddAttribute("Interval",
-                          "The time to wait between packets",
-                          TimeValue(Seconds(1.0)),
-                          MakeTimeAccessor(&Ping6::m_interval),
-                          MakeTimeChecker())
-            .AddAttribute("RemoteIpv6",
-                          "The Ipv6Address of the outbound packets",
-                          Ipv6AddressValue(),
-                          MakeIpv6AddressAccessor(&Ping6::m_peerAddress),
-                          MakeIpv6AddressChecker())
-            .AddAttribute("LocalIpv6",
-                          "Local Ipv6Address of the sender",
-                          Ipv6AddressValue(),
-                          MakeIpv6AddressAccessor(&Ping6::m_localAddress),
-                          MakeIpv6AddressChecker())
-            .AddAttribute("PacketSize",
-                          "Size of packets generated",
-                          UintegerValue(100),
-                          MakeUintegerAccessor(&Ping6::m_size),
-                          MakeUintegerChecker<uint32_t>());
-    return tid;
-}
-
-Ping6::Ping6()
-{
-    NS_LOG_FUNCTION(this);
-    m_sent = 0;
-    m_socket = nullptr;
-    m_seq = 0;
-    m_ipInterfaceIndex = 0;
-    m_ifIndex = 0;
-    m_sendEvent = EventId();
-}
-
-Ping6::~Ping6()
-{
-    NS_LOG_FUNCTION(this);
-    m_socket = nullptr;
-}
-
-void
-Ping6::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    Application::DoDispose();
-}
-
-void
-Ping6::StartApplication()
-{
-    NS_LOG_FUNCTION(this);
-
-    if (!m_socket)
-    {
-        TypeId tid = TypeId::LookupByName("ns3::Ipv6RawSocketFactory");
-        m_socket = Socket::CreateSocket(GetNode(), tid);
-
-        NS_ASSERT(m_socket);
-
-        m_socket->Bind(Inet6SocketAddress(m_localAddress, 0));
-        m_socket->SetAttribute("Protocol", UintegerValue(Ipv6Header::IPV6_ICMPV6));
-        m_socket->SetRecvCallback(MakeCallback(&Ping6::HandleRead, this));
-        m_ipv6Protocol = m_node->GetObject<Ipv6L3Protocol>();
-
-        if (!m_localAddress.IsAny())
-        {
-            m_ipInterfaceIndex = m_ipv6Protocol->GetInterfaceForAddress(m_localAddress);
-        }
-    }
-
-    ScheduleTransmit(Seconds(0.));
-}
-
-void
-Ping6::SetLocal(Ipv6Address ipv6)
-{
-    NS_LOG_FUNCTION(this << ipv6);
-    m_localAddress = ipv6;
-}
-
-void
-Ping6::SetRemote(Ipv6Address ipv6)
-{
-    NS_LOG_FUNCTION(this << ipv6);
-    m_peerAddress = ipv6;
-}
-
-void
-Ping6::StopApplication()
-{
-    NS_LOG_FUNCTION(this);
-
-    if (m_socket)
-    {
-        m_socket->SetRecvCallback(MakeNullCallback<void, Ptr<Socket>>());
-    }
-
-    Simulator::Cancel(m_sendEvent);
-}
-
-void
-Ping6::SetIfIndex(uint32_t ifIndex)
-{
-    m_ifIndex = ifIndex;
-}
-
-void
-Ping6::ScheduleTransmit(Time dt)
-{
-    NS_LOG_FUNCTION(this << dt);
-    m_sendEvent = Simulator::Schedule(dt, &Ping6::Send, this);
-}
-
-void
-Ping6::SetRouters(std::vector<Ipv6Address> routers)
-{
-    m_routers = routers;
-}
-
-void
-Ping6::Send()
-{
-    NS_LOG_FUNCTION(this);
-    NS_ASSERT(m_sendEvent.IsExpired());
-
-    Ipv6Address src;
-    Ptr<Ipv6> ipv6 = GetNode()->GetObject<Ipv6>();
-
-    if (m_ifIndex > 0)
-    {
-        /* hack to have ifIndex in Ipv6RawSocketImpl
-         * maybe add a SetIfIndex in Ipv6RawSocketImpl directly
-         */
-        for (uint32_t i = 0; i < GetNode()->GetObject<Ipv6>()->GetNAddresses(m_ifIndex); i++)
-        {
-            Ipv6InterfaceAddress srcIa;
-            srcIa = GetNode()->GetObject<Ipv6>()->GetAddress(m_ifIndex, i);
-
-            if (srcIa.IsInSameSubnet(m_peerAddress))
-            {
-                src = srcIa.GetAddress();
-                break;
-            }
-        }
-    }
-    else
-    {
-        src = m_localAddress;
-    }
-
-    uint32_t size = m_size;
-    if (m_size < 4)
-    {
-        NS_LOG_WARN("ICMPv6 echo request payload size must be >= 4");
-        size = 4;
-    }
-
-    auto data = new uint8_t[size];
-    memset(data, 0, size);
-    data[0] = 0xDE;
-    data[1] = 0xAD;
-    data[2] = 0xBE;
-    data[3] = 0xEF;
-
-    Ptr<Packet> p = Create<Packet>(data, size);
-    Icmpv6Echo req(true);
-
-    req.SetId(0xBEEF);
-    req.SetSeq(m_seq);
-    m_seq++;
-
-    /* we do not calculate pseudo header checksum here, because we are not sure about
-     * source IPv6 address. Checksum is calculated in Ipv6RawSocketImpl.
-     */
-
-    p->AddHeader(req);
-    m_socket->Bind(Inet6SocketAddress(src, 0));
-
-    /* use Loose Routing (routing type 0) */
-    if (!m_routers.empty())
-    {
-        Ipv6ExtensionLooseRoutingHeader routingHeader;
-        routingHeader.SetNextHeader(Ipv6Header::IPV6_ICMPV6);
-        routingHeader.SetTypeRouting(0);
-        routingHeader.SetSegmentsLeft(m_routers.size());
-        routingHeader.SetRoutersAddress(m_routers);
-        p->AddHeader(routingHeader);
-        m_socket->SetAttribute("Protocol", UintegerValue(Ipv6Header::IPV6_EXT_ROUTING));
-    }
-
-    m_socket->SendTo(p, 0, Inet6SocketAddress(m_peerAddress, 0));
-    ++m_sent;
-
-    NS_LOG_INFO("Sent " << p->GetSize() << " bytes to " << m_peerAddress);
-
-    if (m_sent < m_count || m_count == 0)
-    {
-        ScheduleTransmit(m_interval);
-    }
-
-    delete[] data;
-}
-
-void
-Ping6::HandleRead(Ptr<Socket> socket)
-{
-    NS_LOG_FUNCTION(this << socket);
-
-    Ptr<Packet> packet = nullptr;
-    Address from;
-    while ((packet = socket->RecvFrom(from)))
-    {
-        if (Inet6SocketAddress::IsMatchingType(from))
-        {
-            Ipv6Header hdr;
-            Icmpv6Echo reply(false);
-            Icmpv6DestinationUnreachable destUnreach;
-            Icmpv6TimeExceeded timeExceeded;
-            Inet6SocketAddress address = Inet6SocketAddress::ConvertFrom(from);
-
-            packet->RemoveHeader(hdr);
-
-            uint8_t type;
-            packet->CopyData(&type, sizeof(type));
-
-            switch (type)
-            {
-            case Icmpv6Header::ICMPV6_ECHO_REPLY:
-                packet->RemoveHeader(reply);
-
-                NS_LOG_INFO("Received Echo Reply size  = "
-                            << std::dec << packet->GetSize() << " bytes from " << address.GetIpv6()
-                            << " id =  " << (uint16_t)reply.GetId()
-                            << " seq = " << (uint16_t)reply.GetSeq()
-                            << " Hop Count = " << (uint16_t)(64 - hdr.GetHopLimit()));
-
-                if (m_ifIndex)
-                {
-                    m_ipv6Protocol->ReachabilityHint(m_ifIndex, address.GetIpv6());
-                }
-                else
-                {
-                    m_ipv6Protocol->ReachabilityHint(m_ipInterfaceIndex, address.GetIpv6());
-                }
-
-                break;
-            case Icmpv6Header::ICMPV6_ERROR_DESTINATION_UNREACHABLE:
-                packet->RemoveHeader(destUnreach);
-
-                NS_LOG_INFO("Received Destination Unreachable from " << address.GetIpv6());
-                break;
-            case Icmpv6Header::ICMPV6_ERROR_TIME_EXCEEDED:
-                packet->RemoveHeader(timeExceeded);
-
-                NS_LOG_INFO("Received Time Exceeded from " << address.GetIpv6());
-                break;
-            default:
-                break;
-            }
-        }
-    }
-}
-
-} /* namespace ns3 */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/model/ping6.h ns-3.41/src/internet-apps/model/ping6.h
--- ns-3.40/src/internet-apps/model/ping6.h	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/model/ping6.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,195 +0,0 @@
-/*
- * Copyright (c) 2007-2009 Strasbourg University
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Sebastien Vincent <vincent@clarinet.u-strasbg.fr>
- */
-
-#ifndef PING6_H
-#define PING6_H
-
-#include "ns3/application.h"
-#include "ns3/ipv6-address.h"
-#include "ns3/ipv6-l3-protocol.h"
-
-namespace ns3
-{
-
-class Packet;
-class Socket;
-
-/**
- * \ingroup internet-apps
- * \defgroup ping6 Ping6
- */
-
-/**
- * \ingroup ping6
- * \brief A ping6 application.
- */
-class NS_DEPRECATED_3_38("Use Ping instead - the attributes might have been renamed.") Ping6
-    : public Application
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return type ID
-     */
-    static TypeId GetTypeId();
-
-    /**
-     * \brief Constructor.
-     */
-    Ping6();
-
-    /**
-     * \brief Destructor.
-     */
-    ~Ping6() override;
-
-    /**
-     * \brief Set the local address.
-     * \param ipv6 the local IPv6 address
-     */
-    void SetLocal(Ipv6Address ipv6);
-
-    /**
-     * \brief Set the remote peer.
-     * \param ipv6 IPv6 address of the peer
-     */
-    void SetRemote(Ipv6Address ipv6);
-
-    /**
-     * \brief Set the out interface index.
-     * This is to send to link-local (unicast or multicast) address
-     * when a node has multiple interfaces.
-     * \param ifIndex interface index
-     */
-    void SetIfIndex(uint32_t ifIndex);
-
-    /**
-     * \brief Set routers for routing type 0 (loose routing).
-     * \param routers routers addresses
-     */
-    void SetRouters(std::vector<Ipv6Address> routers);
-
-  protected:
-    /**
-     * \brief Dispose this object;
-     */
-    void DoDispose() override;
-
-  private:
-    /**
-     * \brief Start the application.
-     */
-    void StartApplication() override;
-
-    /**
-     * \brief Stop the application.
-     */
-    void StopApplication() override;
-
-    /**
-     * \brief Schedule sending a packet.
-     * \param dt interval between packet
-     */
-    void ScheduleTransmit(Time dt);
-
-    /**
-     * \brief Send a packet.
-     */
-    void Send();
-
-    /**
-     * \brief Receive method.
-     * \param socket socket that receive a packet
-     */
-    void HandleRead(Ptr<Socket> socket);
-
-    /**
-     * \brief Peer IPv6 address.
-     */
-    Ipv6Address m_address;
-
-    /**
-     * \brief Number of "Echo request" packets that will be sent.
-     */
-    uint32_t m_count;
-
-    /**
-     * \brief Number of packets sent.
-     */
-    uint32_t m_sent;
-
-    /**
-     * \brief Size of the packet.
-     */
-    uint32_t m_size;
-
-    /**
-     * \brief Interval between packets sent.
-     */
-    Time m_interval;
-
-    /**
-     * \brief Local address.
-     */
-    Ipv6Address m_localAddress;
-
-    /**
-     * \brief IP interface index relative to the local address.
-     */
-    uint32_t m_ipInterfaceIndex;
-
-    /**
-     * \brief IP interface index relative to the local address.
-     */
-    Ptr<Ipv6L3Protocol> m_ipv6Protocol;
-
-    /**
-     * \brief Peer address.
-     */
-    Ipv6Address m_peerAddress;
-
-    /**
-     * \brief Local socket.
-     */
-    Ptr<Socket> m_socket;
-
-    /**
-     * \brief Sequence number.
-     */
-    uint16_t m_seq;
-
-    /**
-     * \brief Event ID.
-     */
-    EventId m_sendEvent;
-
-    /**
-     * \brief Out interface (i.e. for link-local communication).
-     */
-    uint32_t m_ifIndex;
-
-    /**
-     * \brief Routers addresses for routing type 0.
-     */
-    std::vector<Ipv6Address> m_routers;
-};
-
-} /* namespace ns3 */
-
-#endif /* PING6_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/model/ping.cc ns-3.41/src/internet-apps/model/ping.cc
--- ns-3.40/src/internet-apps/model/ping.cc	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/model/ping.cc	2024-02-09 14:44:07.913790200 -0800
@@ -69,7 +69,7 @@
             .AddAttribute("VerboseMode",
                           "Configure verbose, quiet, or silent output",
                           EnumValue(VerboseMode::VERBOSE),
-                          MakeEnumAccessor(&Ping::m_verbose),
+                          MakeEnumAccessor<VerboseMode>(&Ping::m_verbose),
                           MakeEnumChecker(VerboseMode::VERBOSE,
                                           "Verbose",
                                           VerboseMode::QUIET,
@@ -103,6 +103,12 @@
                           TimeValue(Seconds(1)),
                           MakeTimeAccessor(&Ping::m_timeout),
                           MakeTimeChecker())
+            .AddAttribute("Tos",
+                          "The Type of Service used to send the ICMP Echo Requests. "
+                          "All 8 bits of the TOS byte are set (including ECN bits).",
+                          UintegerValue(0),
+                          MakeUintegerAccessor(&Ping::m_tos),
+                          MakeUintegerChecker<uint8_t>())
             .AddTraceSource("Tx",
                             "The sequence number and ICMP echo response packet.",
                             MakeTraceSourceAccessor(&Ping::m_txTrace),
@@ -455,8 +461,9 @@
             header.EnableChecksum();
         }
         p->AddHeader(header);
-        returnValue =
-            m_socket->SendTo(p, 0, InetSocketAddress(Ipv4Address::ConvertFrom(m_destination), 0));
+        auto dest = InetSocketAddress(Ipv4Address::ConvertFrom(m_destination), 0);
+        dest.SetTos(m_tos);
+        returnValue = m_socket->SendTo(p, 0, dest);
     }
     else
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/model/ping.h ns-3.41/src/internet-apps/model/ping.h
--- ns-3.40/src/internet-apps/model/ping.h	2023-09-27 13:30:37.646839100 -0700
+++ ns-3.41/src/internet-apps/model/ping.h	2024-02-09 14:44:07.913790200 -0800
@@ -213,6 +213,8 @@
     uint32_t m_size{56};
     /// The socket we send packets from
     Ptr<Socket> m_socket;
+    /// The Type of Service carried by ICMP ECHOs
+    uint8_t m_tos;
     /// ICMP ECHO sequence number
     uint16_t m_seq{0};
     /// Callbacks for tracing the packet Tx events
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/model/v4ping.cc ns-3.41/src/internet-apps/model/v4ping.cc
--- ns-3.40/src/internet-apps/model/v4ping.cc	2023-09-27 13:30:37.650839000 -0700
+++ ns-3.41/src/internet-apps/model/v4ping.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,307 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "v4ping.h"
-
-#include "ns3/assert.h"
-#include "ns3/boolean.h"
-#include "ns3/icmpv4.h"
-#include "ns3/inet-socket-address.h"
-#include "ns3/ipv4-address.h"
-#include "ns3/log.h"
-#include "ns3/packet.h"
-#include "ns3/socket.h"
-#include "ns3/trace-source-accessor.h"
-#include "ns3/uinteger.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("V4Ping");
-
-NS_OBJECT_ENSURE_REGISTERED(V4Ping);
-
-TypeId
-V4Ping::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::V4Ping")
-                            .SetParent<Application>()
-                            .SetGroupName("Internet-Apps")
-                            .AddConstructor<V4Ping>()
-                            .AddAttribute("Remote",
-                                          "The address of the machine we want to ping.",
-                                          Ipv4AddressValue(),
-                                          MakeIpv4AddressAccessor(&V4Ping::m_remote),
-                                          MakeIpv4AddressChecker())
-                            .AddAttribute("Verbose",
-                                          "Produce usual output.",
-                                          BooleanValue(false),
-                                          MakeBooleanAccessor(&V4Ping::m_verbose),
-                                          MakeBooleanChecker())
-                            .AddAttribute("Interval",
-                                          "Wait  interval  seconds between sending each packet.",
-                                          TimeValue(Seconds(1)),
-                                          MakeTimeAccessor(&V4Ping::m_interval),
-                                          MakeTimeChecker())
-                            .AddAttribute("Size",
-                                          "The number of data bytes to be sent, real packet will "
-                                          "be 8 (ICMP) + 20 (IP) bytes longer.",
-                                          UintegerValue(56),
-                                          MakeUintegerAccessor(&V4Ping::m_size),
-                                          MakeUintegerChecker<uint32_t>(16))
-                            .AddTraceSource("Rtt",
-                                            "The rtt calculated by the ping.",
-                                            MakeTraceSourceAccessor(&V4Ping::m_traceRtt),
-                                            "ns3::Time::TracedCallback");
-    ;
-    return tid;
-}
-
-V4Ping::V4Ping()
-    : m_interval(Seconds(1)),
-      m_size(56),
-      m_socket(nullptr),
-      m_seq(0),
-      m_verbose(false),
-      m_recv(0)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-V4Ping::~V4Ping()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-V4Ping::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-
-    if (m_next.IsRunning())
-    {
-        StopApplication();
-    }
-
-    m_socket = nullptr;
-    Application::DoDispose();
-}
-
-uint32_t
-V4Ping::GetApplicationId() const
-{
-    NS_LOG_FUNCTION(this);
-    Ptr<Node> node = GetNode();
-    for (uint32_t i = 0; i < node->GetNApplications(); ++i)
-    {
-        if (node->GetApplication(i) == this)
-        {
-            return i;
-        }
-    }
-    NS_ASSERT_MSG(false, "forgot to add application to node");
-    return 0; // quiet compiler
-}
-
-void
-V4Ping::Receive(Ptr<Socket> socket)
-{
-    NS_LOG_FUNCTION(this << socket);
-    while (m_socket->GetRxAvailable() > 0)
-    {
-        Address from;
-        Ptr<Packet> p = m_socket->RecvFrom(0xffffffff, 0, from);
-        NS_LOG_DEBUG("recv " << p->GetSize() << " bytes");
-        NS_ASSERT(InetSocketAddress::IsMatchingType(from));
-        InetSocketAddress realFrom = InetSocketAddress::ConvertFrom(from);
-        NS_ASSERT(realFrom.GetPort() == 1); // protocol should be icmp.
-        Ipv4Header ipv4;
-        p->RemoveHeader(ipv4);
-        uint32_t recvSize = p->GetSize();
-        NS_ASSERT(ipv4.GetProtocol() == 1); // protocol should be icmp.
-        Icmpv4Header icmp;
-        p->RemoveHeader(icmp);
-        if (icmp.GetType() == Icmpv4Header::ICMPV4_ECHO_REPLY)
-        {
-            Icmpv4Echo echo;
-            p->RemoveHeader(echo);
-            auto i = m_sent.find(echo.GetSequenceNumber());
-
-            if (i != m_sent.end() && echo.GetIdentifier() == 0)
-            {
-                auto buf = new uint32_t[m_size];
-                uint32_t dataSize = echo.GetDataSize();
-                uint32_t nodeId;
-                uint32_t appId;
-                if (dataSize == m_size)
-                {
-                    echo.GetData((uint8_t*)buf);
-                    Read32((const uint8_t*)&buf[0], nodeId);
-                    Read32((const uint8_t*)&buf[1], appId);
-
-                    if (nodeId == GetNode()->GetId() && appId == GetApplicationId())
-                    {
-                        Time sendTime = i->second;
-                        NS_ASSERT(Simulator::Now() >= sendTime);
-                        Time delta = Simulator::Now() - sendTime;
-
-                        m_sent.erase(i);
-                        m_avgRtt.Update(delta.GetMilliSeconds());
-                        m_recv++;
-                        m_traceRtt(delta);
-
-                        if (m_verbose)
-                        {
-                            std::cout << recvSize << " bytes from " << realFrom.GetIpv4() << ":"
-                                      << " icmp_seq=" << echo.GetSequenceNumber()
-                                      << " ttl=" << (unsigned)ipv4.GetTtl()
-                                      << " time=" << delta.As(Time::MS) << "\n";
-                        }
-                    }
-                }
-                delete[] buf;
-            }
-        }
-    }
-}
-
-// Writes data to buffer in little-endian format; least significant byte
-// of data is at lowest buffer address
-void
-V4Ping::Write32(uint8_t* buffer, const uint32_t data)
-{
-    NS_LOG_FUNCTION(this << (void*)buffer << data);
-    buffer[0] = (data >> 0) & 0xff;
-    buffer[1] = (data >> 8) & 0xff;
-    buffer[2] = (data >> 16) & 0xff;
-    buffer[3] = (data >> 24) & 0xff;
-}
-
-// Writes data from a little-endian formatted buffer to data
-void
-V4Ping::Read32(const uint8_t* buffer, uint32_t& data)
-{
-    NS_LOG_FUNCTION(this << (void*)buffer << data);
-    data = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0];
-}
-
-void
-V4Ping::Send()
-{
-    NS_LOG_FUNCTION(this);
-
-    NS_LOG_INFO("m_seq=" << m_seq);
-    Ptr<Packet> p = Create<Packet>();
-    Icmpv4Echo echo;
-    echo.SetSequenceNumber(m_seq);
-    m_seq++;
-    echo.SetIdentifier(0);
-
-    //
-    // We must write quantities out in some form of network order.  Since there
-    // isn't an htonl to work with we just follow the convention in pcap traces
-    // (where any difference would show up anyway) and borrow that code.  Don't
-    // be too surprised when you see that this is a little endian convention.
-    //
-    auto data = new uint8_t[m_size];
-    for (uint32_t i = 0; i < m_size; ++i)
-    {
-        data[i] = 0;
-    }
-    NS_ASSERT(m_size >= 16);
-
-    uint32_t tmp = GetNode()->GetId();
-    Write32(&data[0 * sizeof(uint32_t)], tmp);
-
-    tmp = GetApplicationId();
-    Write32(&data[1 * sizeof(uint32_t)], tmp);
-
-    Ptr<Packet> dataPacket = Create<Packet>((uint8_t*)data, m_size);
-    echo.SetData(dataPacket);
-    p->AddHeader(echo);
-    Icmpv4Header header;
-    header.SetType(Icmpv4Header::ICMPV4_ECHO);
-    header.SetCode(0);
-    if (Node::ChecksumEnabled())
-    {
-        header.EnableChecksum();
-    }
-    p->AddHeader(header);
-    m_sent.insert(std::make_pair(m_seq - 1, Simulator::Now()));
-    m_socket->Send(p, 0);
-    m_next = Simulator::Schedule(m_interval, &V4Ping::Send, this);
-    delete[] data;
-}
-
-void
-V4Ping::StartApplication()
-{
-    NS_LOG_FUNCTION(this);
-
-    m_started = Simulator::Now();
-    if (m_verbose)
-    {
-        std::cout << "PING " << m_remote << " - " << m_size << " bytes of data - " << m_size + 28
-                  << " bytes including ICMP and IPv4 headers.\n";
-    }
-
-    m_socket = Socket::CreateSocket(GetNode(), TypeId::LookupByName("ns3::Ipv4RawSocketFactory"));
-    NS_ASSERT(m_socket);
-    m_socket->SetAttribute("Protocol", UintegerValue(1)); // icmp
-    m_socket->SetRecvCallback(MakeCallback(&V4Ping::Receive, this));
-    InetSocketAddress src = InetSocketAddress(Ipv4Address::GetAny(), 0);
-    int status;
-    status = m_socket->Bind(src);
-    NS_ASSERT(status != -1);
-    InetSocketAddress dst = InetSocketAddress(m_remote, 0);
-    status = m_socket->Connect(dst);
-    NS_ASSERT(status != -1);
-
-    Send();
-}
-
-void
-V4Ping::StopApplication()
-{
-    NS_LOG_FUNCTION(this);
-
-    if (m_next.IsRunning())
-    {
-        m_next.Cancel();
-    }
-    if (m_socket)
-    {
-        m_socket->Close();
-    }
-
-    if (m_verbose)
-    {
-        std::ostringstream os;
-        os.precision(4);
-        os << "--- " << m_remote << " ping statistics ---\n"
-           << m_seq << " packets transmitted, " << m_recv << " received, "
-           << ((m_seq - m_recv) * 100 / m_seq) << "% packet loss, "
-           << "time " << (Simulator::Now() - m_started).As(Time::MS) << "\n";
-
-        if (m_avgRtt.Count() > 0)
-        {
-            os << "rtt min/avg/max/mdev = " << m_avgRtt.Min() << "/" << m_avgRtt.Avg() << "/"
-               << m_avgRtt.Max() << "/" << m_avgRtt.Stddev() << " ms\n";
-        }
-        std::cout << os.str();
-    }
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/internet-apps/model/v4ping.h ns-3.41/src/internet-apps/model/v4ping.h
--- ns-3.40/src/internet-apps/model/v4ping.h	2023-09-27 13:30:37.650839000 -0700
+++ ns-3.41/src/internet-apps/model/v4ping.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,129 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * 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; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#ifndef V4PING_H
-#define V4PING_H
-
-#include "ns3/application.h"
-#include "ns3/average.h"
-#include "ns3/traced-callback.h"
-
-#include <map>
-
-namespace ns3
-{
-
-class Socket;
-
-/**
- * \ingroup internet-apps
- * \defgroup v4ping V4Ping
- */
-
-/**
- * \ingroup v4ping
- * \brief an application which sends one ICMP ECHO request, waits for a REPLYs
- *        and reports the calculated RTT.
- *
- * Note: The RTT calculated is reported through a trace source.
- */
-class NS_DEPRECATED_3_38("Use Ping instead - the attributes might have been renamed.") V4Ping
-    : public Application
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-
-    /**
-     * create a pinger applications
-     */
-    V4Ping();
-    ~V4Ping() override;
-
-  private:
-    /**
-     * \brief Writes data to buffer in little-endian format.
-     *
-     * Least significant byte of data is at lowest buffer address
-     *
-     * \param buffer the buffer to write to
-     * \param data the data to write
-     */
-    void Write32(uint8_t* buffer, const uint32_t data);
-    /**
-     * \brief Writes data from a little-endian formatted buffer to data.
-     *
-     * \param buffer the buffer to read from
-     * \param data the read data
-     */
-    void Read32(const uint8_t* buffer, uint32_t& data);
-
-    // inherited from Application base class.
-    void StartApplication() override;
-    void StopApplication() override;
-    void DoDispose() override;
-    /**
-     * \brief Return the application ID in the node.
-     * \returns the application id
-     */
-    uint32_t GetApplicationId() const;
-    /**
-     * \brief Receive an ICMP Echo
-     * \param socket the receiving socket
-     *
-     * This function is called by lower layers through a callback.
-     */
-    void Receive(Ptr<Socket> socket);
-    /**
-     * \brief Send one Ping (ICMP ECHO) to the destination
-     */
-    void Send();
-
-    /// Remote address
-    Ipv4Address m_remote;
-    /// Wait  interval  seconds between sending each packet
-    Time m_interval;
-    /**
-     * Specifies  the number of data bytes to be sent.
-     * The default is 56, which translates into 64 ICMP data bytes when combined with the 8 bytes of
-     * ICMP header data.
-     */
-    uint32_t m_size;
-    /// The socket we send packets from
-    Ptr<Socket> m_socket;
-    /// ICMP ECHO sequence number
-    uint16_t m_seq;
-    /// TracedCallback for RTT measured by ICMP ECHOs
-    TracedCallback<Time> m_traceRtt;
-    /// produce ping-style output if true
-    bool m_verbose;
-    /// received packets counter
-    uint32_t m_recv;
-    /// Start time to report total ping time
-    Time m_started;
-    /// Average rtt is ms
-    Average<double> m_avgRtt;
-    /// Next packet will be sent
-    EventId m_next;
-    /// All sent but not answered packets. Map icmp seqno -> when sent
-    std::map<uint16_t, Time> m_sent;
-};
-
-} // namespace ns3
-
-#endif /* V4PING_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/CMakeLists.txt ns-3.41/src/lr-wpan/CMakeLists.txt
--- ns-3.40/src/lr-wpan/CMakeLists.txt	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/CMakeLists.txt	2024-02-09 14:44:07.953790400 -0800
@@ -10,6 +10,7 @@
     model/lr-wpan-mac-header.cc
     model/lr-wpan-mac-pl-headers.cc
     model/lr-wpan-mac-trailer.cc
+    model/lr-wpan-mac-base.cc
     model/lr-wpan-mac.cc
     model/lr-wpan-net-device.cc
     model/lr-wpan-phy.cc
@@ -26,17 +27,13 @@
     model/lr-wpan-mac-header.h
     model/lr-wpan-mac-pl-headers.h
     model/lr-wpan-mac-trailer.h
+    model/lr-wpan-mac-base.h
     model/lr-wpan-mac.h
     model/lr-wpan-net-device.h
     model/lr-wpan-phy.h
     model/lr-wpan-spectrum-signal-parameters.h
     model/lr-wpan-spectrum-value-helper.h
-  LIBRARIES_TO_LINK
-    ${libnetwork}
-    ${libcore}
-    ${libmobility}
-    ${libspectrum}
-    ${libpropagation}
+  LIBRARIES_TO_LINK ${libspectrum}
   TEST_SOURCES
     test/lr-wpan-ack-test.cc
     test/lr-wpan-cca-test.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/doc/lr-wpan.rst ns-3.41/src/lr-wpan/doc/lr-wpan.rst
--- ns-3.40/src/lr-wpan/doc/lr-wpan.rst	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/doc/lr-wpan.rst	2024-02-09 14:44:07.953790400 -0800
@@ -232,10 +232,10 @@
                               | <--------------------------------------->|
                                     Acceptable sensitivity range
 
-The example ``lr-wpan-per-plot.cc` shows that at given Rx sensitiviy, packets are dropped regardless of their theoretical error probability.
+The example ``lr-wpan-per-plot.cc` shows that at given Rx sensitivity, packets are dropped regardless of their theoretical error probability.
 This program outputs a file named ``802.15.4-per-vs-rxSignal.plt``.
 Loading this file into gnuplot yields a file ``802.15.4-per-vs-rsSignal.eps``, which can
-be converted to pdf or other formats. Packet payload size, Tx power and Rx sensitivity can be configurated.
+be converted to pdf or other formats. Packet payload size, Tx power and Rx sensitivity can be configured.
 The point where the blue line crosses with the PER indicates the Rx sensitivity. The default output is shown below.
 
 .. _fig-802-15-4-per-sens:
@@ -301,7 +301,7 @@
 the long address (64 bit) of the sender.
 
 Note also that RFC 4944 or RFC 6282 are the RFCs defining the IPv6 address compression formats
-(HC1 and IPHC respectively). It is defintely not a good idea to either mix devices using different
+(HC1 and IPHC respectively). It is definitely not a good idea to either mix devices using different
 pseudo-address format or compression types in the same network. This point is further discussed
 in the ``sixlowpan`` module documentation.
 
@@ -393,8 +393,8 @@
 Loading this file into gnuplot yields a file ``802.15.4-psr-distance.eps``, which can
 be converted to pdf or other formats.  The following image shows the output
 of multiple runs using different Rx sensitivity values. A higher Rx sensitivity (lower dBm) results
-in a increased communication distance but also makes the radio suceptible to more interference from
-surronding devices.
+in a increased communication distance but also makes the radio susceptible to more interference from
+surrounding devices.
 
 .. _fig-802-15-4-psr-distance:
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/examples/lr-wpan-active-scan.cc ns-3.41/src/lr-wpan/examples/lr-wpan-active-scan.cc
--- ns-3.40/src/lr-wpan/examples/lr-wpan-active-scan.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/examples/lr-wpan-active-scan.cc	2024-02-09 14:44:07.953790400 -0800
@@ -56,7 +56,7 @@
 static void
 ScanConfirm(Ptr<LrWpanNetDevice> device, MlmeScanConfirmParams params)
 {
-    if (params.m_status == MLMESCAN_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         std::cout << Simulator::Now().As(Time::S) << "| Active scan status SUCCESSFUL (Completed)"
                   << "\n";
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/examples/lr-wpan-bootstrap.cc ns-3.41/src/lr-wpan/examples/lr-wpan-bootstrap.cc
--- ns-3.40/src/lr-wpan/examples/lr-wpan-bootstrap.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/examples/lr-wpan-bootstrap.cc	2024-02-09 14:44:07.953790400 -0800
@@ -88,7 +88,7 @@
     // with the highest LQI value obtained from a passive scan and make
     // sure this coordinator allows association.
 
-    if (params.m_status == MLMESCAN_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         // Select the coordinator with the highest LQI from the PAN Descriptor List
         int maxLqi = 0;
@@ -105,7 +105,8 @@
             }
 
             // Only request association if the coordinator is permitting association at this moment.
-            if (params.m_panDescList[panDescIndex].m_superframeSpec.IsAssocPermit())
+            SuperframeField superframe(params.m_panDescList[panDescIndex].m_superframeSpec);
+            if (superframe.IsAssocPermit())
             {
                 std::string addressing;
                 if (params.m_panDescList[panDescIndex].m_coorAddrMode == SHORT_ADDR)
@@ -133,6 +134,7 @@
                     assocParams.m_chPage = params.m_panDescList[panDescIndex].m_logChPage;
                     assocParams.m_coordPanId = params.m_panDescList[panDescIndex].m_coorPanId;
                     assocParams.m_coordAddrMode = params.m_panDescList[panDescIndex].m_coorAddrMode;
+                    CapabilityField capability;
 
                     if (params.m_panDescList[panDescIndex].m_coorAddrMode ==
                         LrWpanAddressMode::SHORT_ADDR)
@@ -140,7 +142,7 @@
                         assocParams.m_coordAddrMode = LrWpanAddressMode::SHORT_ADDR;
                         assocParams.m_coordShortAddr =
                             params.m_panDescList[panDescIndex].m_coorShortAddr;
-                        assocParams.m_capabilityInfo.SetShortAddrAllocOn(true);
+                        capability.SetShortAddrAllocOn(true);
                     }
                     else if (assocParams.m_coordAddrMode == LrWpanAddressMode::EXT_ADDR)
                     {
@@ -148,8 +150,9 @@
                         assocParams.m_coordExtAddr =
                             params.m_panDescList[panDescIndex].m_coorExtAddr;
                         assocParams.m_coordShortAddr = Mac16Address("ff:fe");
-                        assocParams.m_capabilityInfo.SetShortAddrAllocOn(false);
+                        capability.SetShortAddrAllocOn(false);
                     }
+                    assocParams.m_capabilityInfo = capability.GetCapability();
 
                     Simulator::ScheduleNow(&LrWpanMac::MlmeAssociateRequest,
                                            device->GetMac(),
@@ -206,8 +209,11 @@
     MlmeAssociateResponseParams assocRespParams;
 
     assocRespParams.m_extDevAddr = params.m_extDevAddr;
-    assocRespParams.m_status = LrWpanAssociationStatus::ASSOCIATED;
-    if (params.capabilityInfo.IsShortAddrAllocOn())
+    assocRespParams.m_status = LrWpanMacStatus::SUCCESS;
+    CapabilityField capability;
+    capability.SetCapability(params.capabilityInfo);
+
+    if (capability.IsShortAddrAllocOn())
     {
         // Truncate the extended address and make an assigned
         // short address based on this. This mechanism is not described by the standard.
@@ -242,14 +248,14 @@
     // and is only here for demonstration purposes.
     switch (params.m_status)
     {
-    case LrWpanMlmeCommStatus::MLMECOMMSTATUS_TRANSACTION_EXPIRED:
+    case LrWpanMacStatus::TRANSACTION_EXPIRED:
         std::cout << Simulator::Now().As(Time::S) << " Coordinator " << device->GetNode()->GetId()
                   << " [" << device->GetMac()->GetShortAddress() << " | "
                   << device->GetMac()->GetExtendedAddress() << "]"
                   << " MLME-comm-status.indication: Transaction for device " << params.m_dstExtAddr
                   << " EXPIRED in pending transaction list\n";
         break;
-    case LrWpanMlmeCommStatus::MLMECOMMSTATUS_NO_ACK:
+    case LrWpanMacStatus::NO_ACK:
         std::cout << Simulator::Now().As(Time::S) << " Coordinator " << device->GetNode()->GetId()
                   << " [" << device->GetMac()->GetShortAddress() << " | "
                   << device->GetMac()->GetExtendedAddress() << "]"
@@ -257,7 +263,7 @@
                   << " device registered in the pending transaction list\n";
         break;
 
-    case LrWpanMlmeCommStatus::MLMECOMMSTATUS_CHANNEL_ACCESS_FAILURE:
+    case LrWpanMacStatus::CHANNEL_ACCESS_FAILURE:
         std::cout << Simulator::Now().As(Time::S) << " Coordinator " << device->GetNode()->GetId()
                   << " [" << device->GetMac()->GetShortAddress() << " | "
                   << device->GetMac()->GetExtendedAddress() << "]"
@@ -276,7 +282,7 @@
     // Used by device higher layer to inform the results of a
     // association procedure from its mac layer.This is implemented by other protocol stacks
     // and is only here for demonstration purposes.
-    if (params.m_status == LrWpanMlmeAssociateConfirmStatus::MLMEASSOC_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
                   << device->GetMac()->GetShortAddress() << " | "
@@ -286,7 +292,7 @@
                   << " | CoordShort: " << device->GetMac()->GetCoordShortAddress()
                   << " | CoordExt: " << device->GetMac()->GetCoordExtAddress() << ")\n";
     }
-    else if (params.m_status == LrWpanMlmeAssociateConfirmStatus::MLMEASSOC_NO_ACK)
+    else if (params.m_status == LrWpanMacStatus::NO_ACK)
     {
         std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
                   << device->GetMac()->GetShortAddress() << " | "
@@ -305,7 +311,7 @@
 static void
 PollConfirm(Ptr<LrWpanNetDevice> device, MlmePollConfirmParams params)
 {
-    if (params.m_status == LrWpanMlmePollConfirmStatus::MLMEPOLL_CHANNEL_ACCESS_FAILURE)
+    if (params.m_status == LrWpanMacStatus::CHANNEL_ACCESS_FAILURE)
     {
         std::cout
             << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
@@ -313,14 +319,14 @@
             << device->GetMac()->GetExtendedAddress() << "]"
             << " MLME-poll.confirm:  CHANNEL ACCESS problem when sending a data request command.\n";
     }
-    else if (params.m_status == LrWpanMlmePollConfirmStatus::MLMEPOLL_NO_ACK)
+    else if (params.m_status == LrWpanMacStatus::NO_ACK)
     {
         std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
                   << device->GetMac()->GetShortAddress() << " | "
                   << device->GetMac()->GetExtendedAddress() << "]"
                   << " MLME-poll.confirm: Data Request Command FAILED (NO ACK).\n";
     }
-    else if (params.m_status != LrWpanMlmePollConfirmStatus::MLMEPOLL_SUCCESS)
+    else if (params.m_status != LrWpanMacStatus::SUCCESS)
     {
         std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
                   << device->GetMac()->GetShortAddress() << " | "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/examples/lr-wpan-ed-scan.cc ns-3.41/src/lr-wpan/examples/lr-wpan-ed-scan.cc
--- ns-3.40/src/lr-wpan/examples/lr-wpan-ed-scan.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/examples/lr-wpan-ed-scan.cc	2024-02-09 14:44:07.953790400 -0800
@@ -56,7 +56,7 @@
 static void
 ScanConfirm(MlmeScanConfirmParams params)
 {
-    if (params.m_status == MLMESCAN_SUCCESS && params.m_scanType == MLMESCAN_ED)
+    if (params.m_status == LrWpanMacStatus::SUCCESS && params.m_scanType == MLMESCAN_ED)
     {
         std::cout << Simulator::Now().As(Time::S) << "| Scan status SUCCESSFUL\n";
         std::cout << "Results for Energy Scan:"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/examples/lr-wpan-mlme.cc ns-3.41/src/lr-wpan/examples/lr-wpan-mlme.cc
--- ns-3.40/src/lr-wpan/examples/lr-wpan-mlme.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/examples/lr-wpan-mlme.cc	2024-02-09 14:44:07.953790400 -0800
@@ -64,7 +64,7 @@
 {
     // In the case of transmissions with the Ack flag activated, the transaction is only
     // successful if the Ack was received.
-    if (params.m_status == LrWpanMcpsDataConfirmStatus::IEEE_802_15_4_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         NS_LOG_UNCOND(Simulator::Now().GetSeconds() << " secs | Transmission successfully sent");
     }
@@ -80,7 +80,7 @@
 void
 StartConfirm(MlmeStartConfirmParams params)
 {
-    if (params.m_status == MLMESTART_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "Beacon status SUCCESSFUL");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/examples/lr-wpan-orphan-scan.cc ns-3.41/src/lr-wpan/examples/lr-wpan-orphan-scan.cc
--- ns-3.40/src/lr-wpan/examples/lr-wpan-orphan-scan.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/examples/lr-wpan-orphan-scan.cc	2024-02-09 14:44:07.953790400 -0800
@@ -68,7 +68,7 @@
 static void
 ScanConfirm(Ptr<LrWpanNetDevice> device, MlmeScanConfirmParams params)
 {
-    if (params.m_status == MLMESCAN_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
                   << device->GetMac()->GetShortAddress() << " | "
@@ -76,7 +76,7 @@
                   << "] MLME-SCAN.confirm: Active scan status SUCCESSFUL "
                   << "(Coordinator found and address assigned) \n";
     }
-    else if (params.m_status == MLMESCAN_NO_BEACON)
+    else if (params.m_status == LrWpanMacStatus::NO_BEACON)
     {
         std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
                   << device->GetMac()->GetShortAddress() << " | "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-csmaca.cc ns-3.41/src/lr-wpan/model/lr-wpan-csmaca.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-csmaca.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-csmaca.cc	2024-02-09 14:44:07.953790400 -0800
@@ -115,14 +115,14 @@
 LrWpanCsmaCa::IsSlottedCsmaCa() const
 {
     NS_LOG_FUNCTION(this);
-    return (m_isSlotted);
+    return m_isSlotted;
 }
 
 bool
 LrWpanCsmaCa::IsUnSlottedCsmaCa() const
 {
     NS_LOG_FUNCTION(this);
-    return (!m_isSlotted);
+    return !m_isSlotted;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-fields.cc ns-3.41/src/lr-wpan/model/lr-wpan-fields.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-fields.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-fields.cc	2024-02-09 14:44:07.953790400 -0800
@@ -35,6 +35,11 @@
     SetAssocPermit(false);
 }
 
+SuperframeField::SuperframeField(uint16_t bitmap)
+{
+    SetSuperframe(bitmap);
+}
+
 void
 SuperframeField::SetSuperframe(uint16_t superFrmSpec)
 {
@@ -156,28 +161,6 @@
     return superframe;
 }
 
-uint32_t
-SuperframeField::GetSerializedSize() const
-{
-    return 2; // 2 Octets (superframeSpec)
-}
-
-Buffer::Iterator
-SuperframeField::Serialize(Buffer::Iterator i) const
-{
-    i.WriteHtolsbU16(GetSuperframe());
-    return i;
-}
-
-Buffer::Iterator
-SuperframeField::Deserialize(Buffer::Iterator i)
-{
-    uint16_t superframe = i.ReadLsbtohU16();
-    SetSuperframe(superframe);
-
-    return i;
-}
-
 std::ostream&
 operator<<(std::ostream& os, const SuperframeField& superframeField)
 {
@@ -525,27 +508,6 @@
     m_allocAddr = (bitmap >> 7) & (0x01);          //!< Bit 7
 }
 
-uint32_t
-CapabilityField::GetSerializedSize() const
-{
-    return 1;
-}
-
-Buffer::Iterator
-CapabilityField::Serialize(Buffer::Iterator i) const
-{
-    i.WriteU8(GetCapability());
-    return i;
-}
-
-Buffer::Iterator
-CapabilityField::Deserialize(Buffer::Iterator i)
-{
-    uint8_t capability = i.ReadU8();
-    SetCapability(capability);
-    return i;
-}
-
 bool
 CapabilityField::IsDeviceTypeFfd() const
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-fields.h ns-3.41/src/lr-wpan/model/lr-wpan-fields.h
--- ns-3.40/src/lr-wpan/model/lr-wpan-fields.h	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-fields.h	2024-02-09 14:44:07.953790400 -0800
@@ -54,6 +54,14 @@
 {
   public:
     SuperframeField();
+
+    /**
+     * Create a superframe Specification Information field with
+     * the information specified in the bitmap.
+     *
+     * \param bitmap The superframe in bitmap form
+     */
+    SuperframeField(uint16_t bitmap);
     /**
      * Set the whole Superframe Specification Information field.
      * \param superFrm The Superframe Specification information field.
@@ -127,24 +135,6 @@
      * \return the Superframe Specification Information field bits.
      */
     uint16_t GetSuperframe() const;
-    /**
-     * Get the size of the serialized Superframe specification information field.
-     * \return the size of the serialized field.
-     */
-    uint32_t GetSerializedSize() const;
-    /**
-     * Serialize the entire superframe specification field.
-     * \param i an iterator which points to where the superframe specification field should be
-     * written.
-     * \return an iterator.
-     */
-    Buffer::Iterator Serialize(Buffer::Iterator i) const;
-    /**
-     * Deserialize the entire superframe specification field.
-     * \param i an iterator which points to where the superframe specification field should be read.
-     * \return an iterator.
-     */
-    Buffer::Iterator Deserialize(Buffer::Iterator i);
 
   private:
     // Superframe Specification field
@@ -378,30 +368,6 @@
     void SetCapability(uint8_t bitmap);
 
     /**
-     * Get the size of the serialized Capability Information Field.
-     *
-     * \return the size of the serialized field.
-     */
-    uint32_t GetSerializedSize() const;
-
-    /**
-     * Serialize the entire Capability Information Field.
-     *
-     * \param i an iterator which points to where the Capability information field
-     * should be written.
-     * \return an iterator.
-     */
-    Buffer::Iterator Serialize(Buffer::Iterator i) const;
-
-    /**
-     * Deserialize the entire Capability Information Field.
-     *
-     * \param i an iterator which points to where the Capability information field should be read.
-     * \return an iterator.
-     */
-    Buffer::Iterator Deserialize(Buffer::Iterator i);
-
-    /**
      * True if the device type is a Full Functional Device (FFD) false if is a Reduced Functional
      * Device (RFD).
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-mac-base.cc ns-3.41/src/lr-wpan/model/lr-wpan-mac-base.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-mac-base.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/lr-wpan/model/lr-wpan-mac-base.cc	2024-02-09 14:44:07.953790400 -0800
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2023 Tokushima University, Japan.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author: Alberto Gallegos Ramonet <alramonet@is.tokushima-u.ac.jp>
+ */
+
+#include "lr-wpan-mac-base.h"
+
+#include <ns3/log.h>
+#include <ns3/uinteger.h>
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("LrWpanMacBase");
+NS_OBJECT_ENSURE_REGISTERED(LrWpanMacBase);
+
+TypeId
+LrWpanMacBase::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::LrWpanMacBase").SetParent<Object>().SetGroupName("LrWpan");
+    return tid;
+}
+
+LrWpanMacBase::~LrWpanMacBase()
+{
+}
+
+void
+LrWpanMacBase::SetMcpsDataConfirmCallback(McpsDataConfirmCallback c)
+{
+    m_mcpsDataConfirmCallback = c;
+}
+
+void
+LrWpanMacBase::SetMcpsDataIndicationCallback(McpsDataIndicationCallback c)
+{
+    m_mcpsDataIndicationCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeAssociateIndicationCallback(MlmeAssociateIndicationCallback c)
+{
+    m_mlmeAssociateIndicationCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeCommStatusIndicationCallback(MlmeCommStatusIndicationCallback c)
+{
+    m_mlmeCommStatusIndicationCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeOrphanIndicationCallback(MlmeOrphanIndicationCallback c)
+{
+    m_mlmeOrphanIndicationCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeStartConfirmCallback(MlmeStartConfirmCallback c)
+{
+    m_mlmeStartConfirmCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeScanConfirmCallback(MlmeScanConfirmCallback c)
+{
+    m_mlmeScanConfirmCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeAssociateConfirmCallback(MlmeAssociateConfirmCallback c)
+{
+    m_mlmeAssociateConfirmCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeBeaconNotifyIndicationCallback(MlmeBeaconNotifyIndicationCallback c)
+{
+    m_mlmeBeaconNotifyIndicationCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeSyncLossIndicationCallback(MlmeSyncLossIndicationCallback c)
+{
+    m_mlmeSyncLossIndicationCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeSetConfirmCallback(MlmeSetConfirmCallback c)
+{
+    m_mlmeSetConfirmCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmeGetConfirmCallback(MlmeGetConfirmCallback c)
+{
+    m_mlmeGetConfirmCallback = c;
+}
+
+void
+LrWpanMacBase::SetMlmePollConfirmCallback(MlmePollConfirmCallback c)
+{
+    m_mlmePollConfirmCallback = c;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-mac-base.h ns-3.41/src/lr-wpan/model/lr-wpan-mac-base.h
--- ns-3.40/src/lr-wpan/model/lr-wpan-mac-base.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/lr-wpan/model/lr-wpan-mac-base.h	2024-02-09 14:44:07.953790400 -0800
@@ -0,0 +1,940 @@
+/*
+ * Copyright (c) 2023 Tokushima University, Japan.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author: Alberto Gallegos Ramonet <alramonet@is.tokushima-u.ac.jp>
+ */
+
+#ifndef LR_WPAN_MAC_BASE_H
+#define LR_WPAN_MAC_BASE_H
+
+#include <ns3/callback.h>
+#include <ns3/mac16-address.h>
+#include <ns3/mac64-address.h>
+#include <ns3/nstime.h>
+#include <ns3/object.h>
+#include <ns3/packet.h>
+#include <ns3/ptr.h>
+
+#include <cstdint>
+
+namespace ns3
+{
+
+/**
+ * \ingroup lr-wpan
+ *
+ * The status of a confirm or an indication primitive as a result of a previous request.
+ * Represent the value of status in IEEE 802.15.4-2011 primitives.
+ * (Tables 6, 12, 18, 20, 31, 33, 35, 37, 39, 47)
+ *
+ * Status codes values only appear in IEEE 802.15.4-2006, Table 78
+ * See also NXP JN5169 IEEE 802.15.4 Stack User Guide
+ * (Table 6: Status Enumerations and Section 6.1.23)
+ *
+ */
+enum class LrWpanMacStatus : std::uint8_t
+{
+    SUCCESS = 0,              //!< The operation was completed successfully.
+    FULL_CAPACITY = 0x01,     //!< PAN at capacity. Association Status field (std. 2006, Table 83)
+    ACCESS_DENIED = 0x02,     //!< PAN access denied. Association Status field (std. 2006, Table 83)
+    COUNTER_ERROR = 0xdb,     //!< The frame counter of the received frame is invalid.
+    IMPROPER_KEY_TYPE = 0xdc, //!< The key is not allowed to be used with that frame type.
+    IMPROPER_SECURITY_LEVEL = 0xdd, //!< Insufficient security level expected by the recipient.
+    UNSUPPORTED_LEGACY = 0xde,      //!< Deprecated security used in IEEE 802.15.4-2003
+    UNSUPPORTED_SECURITY = 0xdf,    //!< The security applied is not supported.
+    BEACON_LOSS = 0xe0,             //!< The beacon was lost following a synchronization request.
+    CHANNEL_ACCESS_FAILURE = 0xe1,  //!< A Tx could not take place due to activity in the CH.
+    DENIED = 0xe2,                  //!< The GTS request has been denied by the PAN coordinator.
+    DISABLE_TRX_FAILURE = 0xe3,     //!< The attempt to disable the transceier has failed.
+    SECURITY_ERROR = 0xe4,      // Cryptographic process of the frame failed(FAILED_SECURITY_CHECK).
+    FRAME_TOO_LONG = 0xe5,      //!< Frame more than aMaxPHYPacketSize or too large for CAP or GTS.
+    INVALID_GTS = 0xe6,         //!< Missing GTS transmit or undefined direction.
+    INVALID_HANDLE = 0xe7,      //!< When purging from TX queue handle was not found.
+    INVALID_PARAMETER = 0xe8,   //!< Primitive parameter not supported or out of range.
+    NO_ACK = 0xe9,              //!< No acknowledgment was received after macMaxFrameRetries.
+    NO_BEACON = 0xea,           //!< A scan operation failed to find any network beacons.
+    NO_DATA = 0xeb,             //!<  No response data were available following a request.
+    NO_SHORT_ADDRESS = 0xec,    //!< Failure due to unallocated 16-bit short address.
+    OUT_OF_CAP = 0xed,          //!< (Deprecated) See IEEE 802.15.4-2003
+    PAN_ID_CONFLICT = 0xee,     //!<  PAN id conflict detected and informed to the coordinator.
+    REALIGMENT = 0xef,          //!< A coordinator realigment command has been received.
+    TRANSACTION_EXPIRED = 0xf0, //!< The transaction expired and its information discarded.
+    TRANSACTION_OVERFLOW = 0xf1,  //!< There is no capacity to store the transaction.
+    TX_ACTIVE = 0xf2,             //!< The transceiver was already enabled.
+    UNAVAILABLE_KEY = 0xf3,       //!< Unavailable key, unknown or blacklisted.
+    UNSUPPORTED_ATTRIBUTE = 0xf4, //!< SET/GET request issued with a non supported ID.
+    INVALID_ADDRESS = 0xf5,       //!< Invalid source or destination address.
+    ON_TIME_TOO_LONG = 0xf6,      //!< RX enable request fail due to syms. longer than Bcn. interval
+    PAST_TIME = 0xf7,             //!< Rx enable request fail due to lack of time in superframe.
+    TRACKING_OFF = 0xf8,          //!< This device is currently not tracking beacons.
+    INVALID_INDEX = 0xf9,     //!< A MAC PIB write failed because specified index is out of range.
+    LIMIT_REACHED = 0xfa,     //!< PAN descriptors stored reached max capacity.
+    READ_ONLY = 0xfb,         //!< SET/GET request issued for a read only attribute.
+    SCAN_IN_PROGRESS = 0xfc,  //!< Scan failed because already performing another scan.
+    SUPERFRAME_OVERLAP = 0xfd //!< Coordinator sperframe and this device superframe tx overlap.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * table 80 of 802.15.4
+ */
+enum LrWpanAddressMode
+{
+    NO_PANID_ADDR = 0,
+    ADDR_MODE_RESERVED = 1,
+    SHORT_ADDR = 2,
+    EXT_ADDR = 3
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * Table 30 of IEEE 802.15.4-2011
+ */
+enum LrWpanMlmeScanType
+{
+    MLMESCAN_ED = 0x00,
+    MLMESCAN_ACTIVE = 0x01,
+    MLMESCAN_PASSIVE = 0x02,
+    MLMESCAN_ORPHAN = 0x03
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MCPS-DATA.request params. See 7.1.1.1
+ */
+struct McpsDataRequestParams
+{
+    LrWpanAddressMode m_srcAddrMode{SHORT_ADDR}; //!< Source address mode
+    LrWpanAddressMode m_dstAddrMode{SHORT_ADDR}; //!< Destination address mode
+    uint16_t m_dstPanId{0};                      //!< Destination PAN identifier
+    Mac16Address m_dstAddr;                      //!< Destination address
+    Mac64Address m_dstExtAddr;                   //!< Destination extended address
+    uint8_t m_msduHandle{0};                     //!< MSDU handle
+    uint8_t m_txOptions{0};                      //!< Tx Options (bitfield)
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-START.request params. See 802.15.4-2011  Section 6.2.12.1
+ */
+struct MlmeStartRequestParams
+{
+    uint16_t m_PanId{0};     //!< Pan Identifier used by the device.
+    uint8_t m_logCh{11};     //!< Logical channel on which to start using the
+                             //!< new superframe configuration.
+    uint32_t m_logChPage{0}; //!< Logical channel page on which to start using the
+                             //!< new superframe configuration.
+    uint32_t m_startTime{0}; //!< Time at which to begin transmitting beacons (Used by Coordinator
+                             //!< not PAN Coordinators). The time is specified in symbols.
+    uint8_t m_bcnOrd{15};    //!< Beacon Order, Used to calculate the beacon interval, a value of 15
+                             //!< indicates no periodic beacons will be transmitted.
+    uint8_t m_sfrmOrd{15};   //!< Superframe Order, indicates the length of the CAP in time slots.
+    bool m_panCoor{false};   //!< On true this device will become coordinator.
+    bool m_battLifeExt{false}; //!< Flag indicating whether or not the Battery life extension (BLE)
+                               //!< features are used.
+    bool m_coorRealgn{false};  //!< True if a realignment request command is to be transmitted prior
+                               //!< changing the superframe.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-SCAN.request params. See IEEE 802.15.4-2011  Section 6.2.10.1 Table 30
+ */
+struct MlmeScanRequestParams
+{
+    LrWpanMlmeScanType m_scanType{MLMESCAN_PASSIVE}; //!< Indicates the type of scan performed as
+                                                     //!< described in IEEE 802.15.4-2011 (5.1.2.1).
+    uint32_t m_scanChannels{0x7FFF800};              //!< The channel numbers to be scanned.
+                                                     //!< Default: (0x7FFF800 = Ch11-Ch26)
+                                                     //!< 27 LSB (b0,b1,...,b26) = channels
+    uint8_t m_scanDuration{14}; //!< The factor (0-14) used to calculate the length of time
+                                //!< to spend scanning.
+                                //!< scanDurationSymbols =
+                                //!< [aBaseSuperframeDuration * (2^m_scanDuration + 1)].
+    uint32_t m_chPage{0};       //!< The channel page on which to perform scan.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-ASSOCIATE.request params. See 802.15.4-2011  Section 6.2.2.1
+ */
+struct MlmeAssociateRequestParams
+{
+    uint8_t m_chNum{11};                 //!< The channel number on which to attempt association.
+    uint32_t m_chPage{0};                //!< The channel page on which to attempt association.
+    uint8_t m_coordAddrMode{SHORT_ADDR}; //!< The coordinator addressing mode for this
+                                         //!< primitive and subsequent MPDU.
+    uint16_t m_coordPanId{0};            //!< The identifier of the PAN with which to associate.
+    Mac16Address m_coordShortAddr;       //!< The short address of the coordinator
+                                         //!< with which to associate.
+    Mac64Address m_coordExtAddr;         //!< The extended address of the coordinator
+                                         //!< with which to associate.
+    uint8_t m_capabilityInfo{0};         //!< Specifies the operational capabilities
+                                         //!< of the associating device (bitmap).
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-ASSOCIATE.response params. See 802.15.4-2011 6.2.2.3.
+ */
+struct MlmeAssociateResponseParams
+{
+    Mac64Address m_extDevAddr;     //!< The extended address of the device requesting association
+    Mac16Address m_assocShortAddr; //!< The short address allocated by the coordinator on successful
+                                   //!< assoc. FF:FF = Unsuccessful
+    LrWpanMacStatus m_status{
+        LrWpanMacStatus::ACCESS_DENIED}; //!< The status of the association
+                                         //!< attempt (As defined on Table 83 IEEE 802.15.4-2006)
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-ORPHAN.response params. See  802.15.4-2011   Section 6.2.7.2
+ */
+struct MlmeOrphanResponseParams
+{
+    Mac64Address m_orphanAddr; //!< The address of the orphaned device.
+    Mac16Address m_shortAddr;  //!< The short address allocated.
+    bool m_assocMember{false}; //!< T = allocated with this coord |  F = otherwise
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-SYNC.request params. See 802.15.4-2011  Section 6.2.13.1
+ */
+struct MlmeSyncRequestParams
+{
+    uint8_t m_logCh{11};    //!< The channel number on which to attempt coordinator synchronization.
+    bool m_trackBcn{false}; //!< True if the mlme sync with the next beacon and attempts to track
+                            //!< future beacons. False if mlme sync only the next beacon.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-POLL.request params. See 802.15.4-2011  Section 6.2.14.1
+ */
+struct MlmePollRequestParams
+{
+    LrWpanAddressMode m_coorAddrMode{SHORT_ADDR}; //!< The addressing mode of the coordinator
+                                                  //!< to which the pool is intended.
+    uint16_t m_coorPanId{0};      //!< The PAN id of the coordinator to which the poll is intended.
+    Mac16Address m_coorShortAddr; //!< Coordinator short address.
+    Mac64Address m_coorExtAddr;   //!< Coordinator extended address.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * IEEE802.15.4-2011 MAC PIB Attribute Identifiers Table 52 in section 6.4.2
+ *
+ */
+enum LrWpanMacPibAttributeIdentifier
+{
+    macBeaconPayload = 0,
+    macBeaconPayloadLength = 1,
+    macShortAddress = 2,
+    macExtendedAddress = 3,
+    macPanId = 4,
+    pCurrentChannel = 100,
+    pCurrentPage = 101,
+    unsupported = 255
+    // TODO: complete other MAC pib attributes
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * IEEE802.15.4-2011 PHY PIB Attributes Table 52 in section 6.4.2
+ */
+struct LrWpanMacPibAttributes : public SimpleRefCount<LrWpanMacPibAttributes>
+{
+    Ptr<Packet> macBeaconPayload;      //!< The contents of the beacon payload.
+    uint8_t macBeaconPayloadLength{0}; //!< The length in octets of the beacon payload.
+    Mac16Address macShortAddress;      //!< The 16 bit mac short address
+    Mac64Address macExtendedAddress;   //!< The EUI-64 bit address
+    uint16_t macPanId{0xffff};         //!< The identifier of the PAN
+    uint8_t pCurrentChannel{11};       //!< The current logical channel in used in the PHY
+    uint8_t pCurrentPage{0};           //!< The current logical page in use in the PHY
+    // TODO: complete other MAC pib attributes
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MCPS-DATA.confirm params. See 7.1.1.2
+ */
+struct McpsDataConfirmParams
+{
+    uint8_t m_msduHandle{0};                                      //!< MSDU handle
+    LrWpanMacStatus m_status{LrWpanMacStatus::INVALID_PARAMETER}; //!< The status
+                                                                  //!< of the last MSDU transmission
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MCPS-DATA.indication params. See 7.1.1.3
+ */
+struct McpsDataIndicationParams
+{
+    uint8_t m_srcAddrMode{SHORT_ADDR}; //!< Source address mode
+    uint16_t m_srcPanId{0};            //!< Source PAN identifier
+    Mac16Address m_srcAddr;            //!< Source address
+    Mac64Address m_srcExtAddr;         //!< Source extended address
+    uint8_t m_dstAddrMode{SHORT_ADDR}; //!< Destination address mode
+    uint16_t m_dstPanId{0};            //!< Destination PAN identifier
+    Mac16Address m_dstAddr;            //!< Destination address
+    Mac64Address m_dstExtAddr;         //!< Destination extended address
+    uint8_t m_mpduLinkQuality{0};      //!< LQI value measured during reception of the MPDU
+    uint8_t m_dsn{0};                  //!< The DSN of the received data frame
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-ASSOCIATE.indication params. See 802.15.4-2011 6.2.2.2.
+ */
+struct MlmeAssociateIndicationParams
+{
+    Mac64Address m_extDevAddr; //!< The extended address of the device requesting association
+    uint8_t capabilityInfo{0}; //!< The operational capabilities of
+                               //!< the device requesting association.
+    uint8_t lqi{0}; //!< The link quality indicator of the received associate request command
+                    //!< (Not officially supported in the standard but found in implementations)
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-COMM-STATUS.indication params. See  802.15.4-2011   Section 6.2.4.2 Table 18
+ */
+struct MlmeCommStatusIndicationParams
+{
+    uint16_t m_panId{0}; //!< The PAN identifier of the device from which the frame was received or
+                         //!< to which the frame was being sent.
+    uint8_t m_srcAddrMode{SHORT_ADDR}; //!< The source addressing mode for this primitive
+    Mac16Address m_srcShortAddr; //!< The short address of the entity from which the frame causing
+                                 //!< the error originated.
+    Mac64Address m_srcExtAddr; //!< The extended address of the entity from which the frame causing
+                               //!< the error originated.
+    uint8_t m_dstAddrMode{SHORT_ADDR}; //!< The destination addressing mode for this primitive.
+    Mac16Address m_dstShortAddr;       //!< The short address of the device for
+                                       //!< which the frame was intended.
+    Mac64Address m_dstExtAddr;         //!< The extended address of the device for
+                                       //!< which the frame was intended.
+    LrWpanMacStatus m_status{LrWpanMacStatus::INVALID_PARAMETER}; //!< The communication status
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-ORPHAN.indication params. See  802.15.4-2011   Section 6.2.7.1
+ */
+struct MlmeOrphanIndicationParams
+{
+    Mac64Address m_orphanAddr; //!< The address of the orphaned device.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-START.confirm params. See  802.15.4-2011   Section 6.2.12.2
+ */
+struct MlmeStartConfirmParams
+{
+    LrWpanMacStatus m_status{LrWpanMacStatus::INVALID_PARAMETER}; //!< The status of
+                                                                  //!< a MLME-start.request
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * PAN Descriptor, Table 17 IEEE 802.15.4-2011
+ */
+struct PanDescriptor
+{
+    LrWpanAddressMode m_coorAddrMode{SHORT_ADDR}; //!< The coordinator addressing mode corresponding
+                                                  //!< to the received beacon frame.
+    uint16_t m_coorPanId{0xffff};                 //!< The PAN ID of the coordinator as specified in
+                                                  //!<  the received beacon frame.
+    Mac16Address m_coorShortAddr; //!< The coordinator short address as specified in the coordinator
+                                  //!< address mode.
+    Mac64Address m_coorExtAddr;   //!< The coordinator extended address as specified in the
+                                  //!< coordinator address mode.
+    uint8_t m_logCh{11};          //!< The current channel number occupied by the network.
+    uint8_t m_logChPage{0};       //!< The current channel page occupied by the network.
+    uint16_t m_superframeSpec{0}; //!< The superframe specification as specified in the received
+                                  //!< beacon frame.
+    bool m_gtsPermit{false};      //!< TRUE if the beacon is from the PAN coordinator
+                                  //!< that is accepting GTS requests.
+    uint8_t m_linkQuality{0};     //!< The LQI at which the network beacon was received.
+                                  //!< Lower values represent lower LQI.
+    Time m_timeStamp; //!< Beacon frame reception time. Used as Time data type in ns-3 to avoid
+                      //!< precision problems.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-SCAN.confirm params. See IEEE 802.15.4-2011 Section 6.2.10.2
+ */
+struct MlmeScanConfirmParams
+{
+    LrWpanMacStatus m_status{
+        LrWpanMacStatus::INVALID_PARAMETER}; //!< The status of the scan request.
+    uint8_t m_scanType{MLMESCAN_PASSIVE};    //!< Indicates the type of scan
+                                             //!<  performed (ED,ACTIVE,PASSIVE,ORPHAN).
+    uint32_t m_chPage{0};                    //!< The channel page on which the scan was performed.
+    std::vector<uint8_t> m_unscannedCh;      //!< A list of channels given in the request which
+                                             //!<  were not scanned (Not valid for ED scans).
+    uint8_t m_resultListSize{0};             //!< The number of elements returned in the appropriate
+                                             //!<  result list. (Not valid for Orphan scan).
+    std::vector<uint8_t> m_energyDetList;    //!< A list of energy measurements, one for each
+                                             //!< channel searched during ED scan
+                                             //!< (Not valid for Active, Passive or Orphan Scans)
+    std::vector<PanDescriptor> m_panDescList; //!< A list of PAN descriptor, one for each beacon
+                                              //!< found (Not valid for ED and Orphan scans).
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-ASSOCIATE.confirm params. See 802.15.4-2011  Section 6.2.2.4
+ */
+struct MlmeAssociateConfirmParams
+{
+    Mac16Address m_assocShortAddr; //!< The short address used in the association request
+    LrWpanMacStatus m_status{LrWpanMacStatus::INVALID_PARAMETER}; //!< The status of
+                                                                  //!< a MLME-associate.request
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-BEACON-NOTIFY.indication params. See  802.15.4-2011   Section 6.2.4.1, Table 16
+ */
+struct MlmeBeaconNotifyIndicationParams
+{
+    uint8_t m_bsn{0};              //!< The beacon sequence number.
+    PanDescriptor m_panDescriptor; //!< The PAN descriptor for the received beacon.
+    uint32_t m_sduLength{0};       //!< The number of octets contained in the beacon payload.
+    Ptr<Packet> m_sdu;             //!< The set of octets comprising the beacon payload.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-SYNC-LOSS.indication params. See  802.15.4-2011   Section 6.2.13.2, Table 37
+ */
+struct MlmeSyncLossIndicationParams
+{
+    LrWpanMacStatus m_lossReason{LrWpanMacStatus::PAN_ID_CONFLICT}; //!< The reason for the lost
+                                                                    //!< of synchronization.
+    uint16_t m_panId{0}; //!< The PAN identifier with which the device lost synchronization or to
+                         //!< which it was realigned.
+    uint8_t m_logCh{11}; //!< The channel number on which the device lost synchronization or to
+                         //!< which it was realigned.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-SET.confirm params. See  802.15.4-2011   Section 6.2.11.2
+ */
+struct MlmeSetConfirmParams
+{
+    LrWpanMacStatus m_status{LrWpanMacStatus::UNSUPPORTED_ATTRIBUTE}; //!< The result of
+                                                                      //!< the request to write
+                                                                      //!< the PIB attribute.
+    LrWpanMacPibAttributeIdentifier id; //!< The id of the PIB attribute that was written.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * MLME-START.confirm params. See  802.15.4-2011   Section 6.2.14.2
+ */
+struct MlmePollConfirmParams
+{
+    LrWpanMacStatus m_status{LrWpanMacStatus::INVALID_PARAMETER}; //!< The confirmation
+                                                                  //!< status resulting from a
+                                                                  //!< MLME-poll.request.
+};
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a McpsDataRequest has been called from
+ * the higher layer.  It returns a status of the outcome of the
+ * transmission request
+ */
+using McpsDataConfirmCallback = Callback<void, McpsDataConfirmParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a Mcps has successfully received a
+ *  frame and wants to deliver it to the higher layer.
+ *
+ *  \todo for now, we do not deliver all of the parameters in section
+ *  802.15.4-2006 7.1.1.3.1 but just send up the packet.
+ */
+using McpsDataIndicationCallback = Callback<void, McpsDataIndicationParams, Ptr<Packet>>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a Mlme has successfully received a command
+ *  frame and wants to deliver it to the higher layer.
+ *
+ *  Security related parameters and not handle.
+ *  See 802.15.4-2011 6.2.2.2.
+ */
+using MlmeAssociateIndicationCallback = Callback<void, MlmeAssociateIndicationParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called by the MLME and issued to its next higher layer following
+ * a transmission instigated through a response primitive.
+ *
+ *  Security related parameters and not handle.
+ *  See 802.15.4-2011 6.2.4.2
+ */
+using MlmeCommStatusIndicationCallback = Callback<void, MlmeCommStatusIndicationParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called by the MLME and issued to its next higher layer following
+ * the reception of a orphan notification.
+ *
+ *  Security related parameters and not handle.
+ *  See 802.15.4-2011 6.2.7.1
+ */
+using MlmeOrphanIndicationCallback = Callback<void, MlmeOrphanIndicationParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a MlmeStartRequest has been called from
+ * the higher layer.  It returns a status of the outcome of the
+ * transmission request
+ */
+using MlmeStartConfirmCallback = Callback<void, MlmeStartConfirmParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a MlmeScanRequest has been called from
+ * the higher layer.  It returns a status of the outcome of the scan.
+ */
+using MlmeScanConfirmCallback = Callback<void, MlmeScanConfirmParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a MlmeAssociateRequest has been called from
+ * the higher layer. It returns a status of the outcome of the
+ * association request
+ */
+using MlmeAssociateConfirmCallback = Callback<void, MlmeAssociateConfirmParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a Mlme has successfully received a
+ *  beacon frame and wants to deliver it to the higher layer.
+ *
+ *  \todo for now, we do not deliver all of the parameters in section
+ *  802.15.4-2006 6.2.4.1 but just send up the packet.
+ */
+using MlmeBeaconNotifyIndicationCallback = Callback<void, MlmeBeaconNotifyIndicationParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called to indicate the loss of synchronization with
+ * a coordinator.
+ *
+ * \todo for now, we do not deliver all of the parameters in section
+ *  See IEEE 802.15.4-2011 6.2.13.2.
+ */
+using MlmeSyncLossIndicationCallback = Callback<void, MlmeSyncLossIndicationParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a MlmeSetRequest has been called from
+ * the higher layer to set a PIB. It returns a status of the outcome of the
+ * write attempt.
+ */
+using MlmeSetConfirmCallback = Callback<void, MlmeSetConfirmParams>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a MlmeGetRequest has been called from
+ * the higher layer to get a PIB. It returns a status of the outcome of the
+ * write attempt.
+ */
+using MlmeGetConfirmCallback =
+    Callback<void, LrWpanMacStatus, LrWpanMacPibAttributeIdentifier, Ptr<LrWpanMacPibAttributes>>;
+
+/**
+ * \ingroup lr-wpan
+ *
+ * This callback is called after a Mlme-Poll.Request has been called from
+ * the higher layer.  It returns a status of the outcome of the
+ * transmission request
+ */
+using MlmePollConfirmCallback = Callback<void, MlmePollConfirmParams>;
+
+/**
+ * \ingroup netdevice
+ *
+ * \brief Lr-wpan MAC layer abstraction
+ *
+ * This class defines the interface functions (primitives) used by a IEEE 802.15.4-2011 compliant
+ * MAC layer. Any lr-wpan MAC should extend from this class and implement the
+ * behavior of the basic MAC interfaces (primitives).
+ *
+ */
+class LrWpanMacBase : public Object
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+    ~LrWpanMacBase() override;
+
+    /**
+     * IEEE 802.15.4-2006, section 7.1.1.1
+     * MCPS-DATA.request
+     * Request to transfer a MSDU.
+     *
+     * \param params the request parameters
+     * \param p the packet to be transmitted
+     */
+    virtual void McpsDataRequest(McpsDataRequestParams params, Ptr<Packet> p) = 0;
+
+    /**
+     * IEEE 802.15.4-2006, section 7.1.14.1
+     * MLME-START.request
+     * Request to allow a PAN coordinator to initiate
+     * a new PAN or beginning a new superframe configuration.
+     *
+     * \param params the request parameters
+     */
+    virtual void MlmeStartRequest(MlmeStartRequestParams params) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.10.1
+     * MLME-SCAN.request
+     * Request primitive used to initiate a channel scan over a given list of channels.
+     *
+     * \param params the scan request parameters
+     */
+    virtual void MlmeScanRequest(MlmeScanRequestParams params) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.2.1
+     * MLME-ASSOCIATE.request
+     * Request primitive used by a device to request an association with
+     * a coordinator.
+     *
+     * \param params the request parameters
+     */
+    virtual void MlmeAssociateRequest(MlmeAssociateRequestParams params) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.2.3
+     * MLME-ASSOCIATE.response
+     * Primitive used to initiate a response to an MLME-ASSOCIATE.indication
+     * primitive.
+     *
+     * \param params the associate response parameters
+     */
+    virtual void MlmeAssociateResponse(MlmeAssociateResponseParams params) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.13.1
+     * MLME-SYNC.request
+     * Request to synchronize with the coordinator by acquiring and,
+     * if specified, tracking beacons.
+     *
+     * \param params the request parameters
+     */
+    virtual void MlmeSyncRequest(MlmeSyncRequestParams params) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.14.2
+     * MLME-POLL.request
+     * Prompts the device to request data from the coordinator.
+     *
+     * \param params the request parameters
+     */
+    virtual void MlmePollRequest(MlmePollRequestParams params) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.7.2
+     * MLME-ORPHAN.response
+     * Primitive used to initiatte a response to an MLME-ORPHAN.indication
+     * primitive.
+     *
+     * \param params the orphan response parameters
+     */
+    virtual void MlmeOrphanResponse(MlmeOrphanResponseParams params) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.11.1
+     * MLME-SET.request
+     * Attempts to write the given value to the indicated PIB attribute.
+     *
+     * \param id the attributed identifier
+     * \param attribute the attribute value
+     */
+    virtual void MlmeSetRequest(LrWpanMacPibAttributeIdentifier id,
+                                Ptr<LrWpanMacPibAttributes> attribute) = 0;
+
+    /**
+     * IEEE 802.15.4-2011, section 6.2.5.1
+     * MLME-GET.request
+     * Request information about a given PIB attribute.
+     *
+     * \param id the attribute identifier
+     */
+    virtual void MlmeGetRequest(LrWpanMacPibAttributeIdentifier id) = 0;
+
+    /**
+     * Set the callback for the confirmation of a data transmission request.
+     * The callback implements MCPS-DATA.confirm SAP of IEEE 802.15.4-2006,
+     * section 7.1.1.2.
+     *
+     * \param c the callback
+     */
+    void SetMcpsDataConfirmCallback(McpsDataConfirmCallback c);
+
+    /**
+     * Set the callback for the indication of an incoming data packet.
+     * The callback implements MCPS-DATA.indication SAP of IEEE 802.15.4-2006,
+     * section 7.1.1.3.
+     *
+     * \param c the callback
+     */
+    void SetMcpsDataIndicationCallback(McpsDataIndicationCallback c);
+
+    /**
+     * Set the callback for the indication of an incoming associate request command.
+     * The callback implements MLME-ASSOCIATE.indication SAP of IEEE 802.15.4-2011,
+     * section 6.2.2.2.
+     *
+     * \param c the callback
+     */
+    void SetMlmeAssociateIndicationCallback(MlmeAssociateIndicationCallback c);
+
+    /**
+     * Set the callback for the indication to a response primitive.
+     * The callback implements MLME-COMM-STATUS.indication SAP of IEEE 802.15.4-2011,
+     * section 6.2.4.2.
+     *
+     * \param c the callback
+     */
+    void SetMlmeCommStatusIndicationCallback(MlmeCommStatusIndicationCallback c);
+
+    /**
+     * Set the callback for the indication to the reception of an orphan notification.
+     * The callback implements MLME-ORPHAN.indication SAP of IEEE 802.15.4-2011,
+     * section 6.2.7.1.
+     *
+     * \param c the callback
+     */
+    void SetMlmeOrphanIndicationCallback(MlmeOrphanIndicationCallback c);
+
+    /**
+     * Set the callback for the confirmation of a data transmission request.
+     * The callback implements MLME-START.confirm SAP of IEEE 802.15.4-2006,
+     * section 7.1.14.2.
+     *
+     * \param c the callback
+     */
+    void SetMlmeStartConfirmCallback(MlmeStartConfirmCallback c);
+
+    /**
+     * Set the callback for the confirmation of a data transmission request.
+     * The callback implements MLME-SCAN.confirm SAP of IEEE 802.15.4-2011,
+     * section 6.2.10.2.
+     *
+     * \param c the callback
+     */
+    void SetMlmeScanConfirmCallback(MlmeScanConfirmCallback c);
+
+    /**
+     * Set the callback for the confirmation of a data transmission request.
+     * The callback implements MLME-ASSOCIATE.confirm SAP of IEEE 802.15.4-2011,
+     * section 6.2.2.4.
+     *
+     * \param c the callback
+     */
+    void SetMlmeAssociateConfirmCallback(MlmeAssociateConfirmCallback c);
+
+    /**
+     * Set the callback for the indication of an incoming beacon packet.
+     * The callback implements MLME-BEACON-NOTIFY.indication SAP of IEEE 802.15.4-2011,
+     * section 6.2.4.1.
+     *
+     * \param c the callback
+     */
+    void SetMlmeBeaconNotifyIndicationCallback(MlmeBeaconNotifyIndicationCallback c);
+
+    /**
+     * Set the callback for the loss of synchronization with a coordinator.
+     * The callback implements MLME-SYNC-LOSS.indication SAP of IEEE 802.15.4-2011,
+     * section 6.2.13.2.
+     *
+     * \param c the callback
+     */
+    void SetMlmeSyncLossIndicationCallback(MlmeSyncLossIndicationCallback c);
+
+    /**
+     * Set the callback for the confirmation of an attempt to write an attribute.
+     * The callback implements MLME-SET.confirm SAP of IEEE 802.15.4-2011,
+     * section 6.2.11.2
+     *
+     * \param c the callback
+     */
+    void SetMlmeSetConfirmCallback(MlmeSetConfirmCallback c);
+
+    /**
+     * Set the callback for the confirmation of an attempt to read an attribute.
+     * The callback implements MLME-GET.confirm SAP of IEEE 802.15.4-2011,
+     * section 6.2.5.2
+     *
+     *\param c the callback
+     */
+    void SetMlmeGetConfirmCallback(MlmeGetConfirmCallback c);
+
+    /**
+     * Set the callback for the confirmation of a data transmission request.
+     * The callback implements MLME-POLL.confirm SAP of IEEE 802.15.4-2011,
+     * section 6.2.14.2
+     *
+     * \param c the callback
+     */
+    void SetMlmePollConfirmCallback(MlmePollConfirmCallback c);
+
+  protected:
+    /**
+     * This callback is used to report data transmission request status to the
+     * upper layers.
+     * See IEEE 802.15.4-2006, section 7.1.1.2.
+     */
+    McpsDataConfirmCallback m_mcpsDataConfirmCallback;
+
+    /**
+     * This callback is used to notify incoming packets to the upper layers.
+     * See IEEE 802.15.4-2006, section 7.1.1.3.
+     */
+    McpsDataIndicationCallback m_mcpsDataIndicationCallback;
+
+    /**
+     * This callback is used to indicate the reception of an association request command.
+     * See IEEE 802.15.4-2011, section 6.2.2.2
+     */
+    MlmeAssociateIndicationCallback m_mlmeAssociateIndicationCallback;
+
+    /**
+     * This callback is instigated through a response primitive.
+     * See IEEE 802.15.4-2011, section 6.2.4.2
+     */
+    MlmeCommStatusIndicationCallback m_mlmeCommStatusIndicationCallback;
+
+    /**
+     * This callback is used to indicate the reception of a orphan notification command.
+     * See IEEE 802.15.4-2011, section 6.2.7.1
+     */
+    MlmeOrphanIndicationCallback m_mlmeOrphanIndicationCallback;
+
+    /**
+     * This callback is used to report the start of a new PAN or
+     * the begin of a new superframe configuration.
+     * See IEEE 802.15.4-2006, section 7.1.14.2.
+     */
+    MlmeStartConfirmCallback m_mlmeStartConfirmCallback;
+
+    /**
+     * This callback is used to report the result of a scan on a group of channels for the
+     * selected channel page.
+     * See IEEE 802.15.4-2011, section 6.2.10.2.
+     */
+    MlmeScanConfirmCallback m_mlmeScanConfirmCallback;
+
+    /**
+     * This callback is used to report the status after a device request an association with
+     * a coordinator.
+     * See IEEE 802.15.4-2011, section 6.2.2.4.
+     */
+    MlmeAssociateConfirmCallback m_mlmeAssociateConfirmCallback;
+
+    /**
+     * This callback is used to notify incoming beacon packets to the upper layers.
+     * See IEEE 802.15.4-2011, section 6.2.4.1.
+     */
+    MlmeBeaconNotifyIndicationCallback m_mlmeBeaconNotifyIndicationCallback;
+
+    /**
+     * This callback is used to indicate the loss of synchronization with a coordinator.
+     * See IEEE 802.15.4-2011, section 6.2.13.2.
+     */
+    MlmeSyncLossIndicationCallback m_mlmeSyncLossIndicationCallback;
+
+    /**
+     * This callback is used to report the result of an attribute writing request
+     * to the upper layers.
+     * See IEEE 802.15.4-2011, section 6.2.11.2.
+     */
+    MlmeSetConfirmCallback m_mlmeSetConfirmCallback;
+
+    /**
+     * This callback is used to report the result of an attribute read request
+     * to the upper layers.
+     * See IEEE 802.15.4-2011, section 6.2.5.2
+     */
+    MlmeGetConfirmCallback m_mlmeGetConfirmCallback;
+
+    /**
+     * This callback is used to report the status after a device send data command request to
+     * the coordinator to transmit data.
+     * See IEEE 802.15.4-2011, section 6.2.14.2.
+     */
+    MlmePollConfirmCallback m_mlmePollConfirmCallback;
+};
+
+} // namespace ns3
+
+#endif /* LR_WPAN_MAC_BASE_H*/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-mac.cc ns-3.41/src/lr-wpan/model/lr-wpan-mac.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-mac.cc	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-mac.cc	2024-02-09 14:44:07.957790400 -0800
@@ -48,12 +48,51 @@
 NS_LOG_COMPONENT_DEFINE("LrWpanMac");
 NS_OBJECT_ENSURE_REGISTERED(LrWpanMac);
 
+std::ostream&
+operator<<(std::ostream& os, const LrWpanMacState& state)
+{
+    switch (state)
+    {
+    case LrWpanMacState::MAC_IDLE:
+        os << "MAC IDLE";
+        break;
+    case LrWpanMacState::MAC_CSMA:
+        os << "CSMA";
+        break;
+    case LrWpanMacState::MAC_SENDING:
+        os << "SENDING";
+        break;
+    case LrWpanMacState::MAC_ACK_PENDING:
+        os << "ACK PENDING";
+        break;
+    case LrWpanMacState::CHANNEL_ACCESS_FAILURE:
+        os << "CHANNEL_ACCESS_FAILURE";
+        break;
+    case LrWpanMacState::CHANNEL_IDLE:
+        os << "CHANNEL IDLE";
+        break;
+    case LrWpanMacState::SET_PHY_TX_ON:
+        os << "SET PHY to TX ON";
+        break;
+    case LrWpanMacState::MAC_GTS:
+        os << "MAC GTS PERIOD";
+        break;
+    case LrWpanMacState::MAC_INACTIVE:
+        os << "SUPERFRAME INACTIVE PERIOD";
+        break;
+    case LrWpanMacState::MAC_CSMA_DEFERRED:
+        os << "CSMA DEFERRED TO NEXT PERIOD";
+        break;
+    }
+    return os;
+};
+
 TypeId
 LrWpanMac::GetTypeId()
 {
     static TypeId tid =
         TypeId("ns3::LrWpanMac")
-            .SetParent<Object>()
+            .SetParent<LrWpanMacBase>()
             .SetGroupName("LrWpan")
             .AddConstructor<LrWpanMac>()
             .AddAttribute("PanId",
@@ -175,7 +214,6 @@
     m_macCoordShortAddress = Mac16Address("ff:ff");
     m_macCoordExtendedAddress = Mac64Address("ff:ff:ff:ff:ff:ff:ff:ed");
     m_deviceCapability = DeviceType::FFD;
-    m_associationStatus = ASSOCIATED;
     m_selfExt = Mac64Address::Allocate();
     m_macPromiscuousMode = false;
     m_macMaxFrameRetries = 3;
@@ -254,16 +292,12 @@
     for (uint32_t i = 0; i < m_txQueue.size(); i++)
     {
         m_txQueue[i]->txQPkt = nullptr;
-        m_txQueue[i]->txQMsduHandle = 0;
     }
     m_txQueue.clear();
 
     for (uint32_t i = 0; i < m_indTxQueue.size(); i++)
     {
         m_indTxQueue[i]->txQPkt = nullptr;
-        m_indTxQueue[i]->seqNum = 0;
-        m_indTxQueue[i]->dstExtAddress = nullptr;
-        m_indTxQueue[i]->dstShortAddress = nullptr;
     }
     m_indTxQueue.clear();
 
@@ -368,7 +402,7 @@
         // The frame could still be too large once headers are put on
         // in which case the phy will reject it instead
         NS_LOG_ERROR(this << " packet too big: " << p->GetSize());
-        confirmParams.m_status = IEEE_802_15_4_FRAME_TOO_LONG;
+        confirmParams.m_status = LrWpanMacStatus::FRAME_TOO_LONG;
         if (!m_mcpsDataConfirmCallback.IsNull())
         {
             m_mcpsDataConfirmCallback(confirmParams);
@@ -379,7 +413,7 @@
     if ((params.m_srcAddrMode == NO_PANID_ADDR) && (params.m_dstAddrMode == NO_PANID_ADDR))
     {
         NS_LOG_ERROR(this << " Can not send packet with no Address field");
-        confirmParams.m_status = IEEE_802_15_4_INVALID_ADDRESS;
+        confirmParams.m_status = LrWpanMacStatus::INVALID_ADDRESS;
         if (!m_mcpsDataConfirmCallback.IsNull())
         {
             m_mcpsDataConfirmCallback(confirmParams);
@@ -406,7 +440,7 @@
     default:
         NS_LOG_ERROR(this << " Can not send packet with incorrect Source Address mode = "
                           << params.m_srcAddrMode);
-        confirmParams.m_status = IEEE_802_15_4_INVALID_ADDRESS;
+        confirmParams.m_status = LrWpanMacStatus::INVALID_ADDRESS;
         if (!m_mcpsDataConfirmCallback.IsNull())
         {
             m_mcpsDataConfirmCallback(confirmParams);
@@ -433,7 +467,7 @@
     default:
         NS_LOG_ERROR(this << " Can not send packet with incorrect Destination Address mode = "
                           << params.m_dstAddrMode);
-        confirmParams.m_status = IEEE_802_15_4_INVALID_ADDRESS;
+        confirmParams.m_status = LrWpanMacStatus::INVALID_ADDRESS;
         if (!m_mcpsDataConfirmCallback.IsNull())
         {
             m_mcpsDataConfirmCallback(confirmParams);
@@ -557,7 +591,7 @@
     if (GetShortAddress() == Mac16Address("ff:ff"))
     {
         NS_LOG_ERROR(this << " Invalid MAC short address");
-        confirmParams.m_status = MLMESTART_NO_SHORT_ADDRESS;
+        confirmParams.m_status = LrWpanMacStatus::NO_SHORT_ADDRESS;
         if (!m_mlmeStartConfirmCallback.IsNull())
         {
             m_mlmeStartConfirmCallback(confirmParams);
@@ -567,7 +601,7 @@
 
     if ((params.m_bcnOrd > 15) || (params.m_sfrmOrd > params.m_bcnOrd))
     {
-        confirmParams.m_status = MLMESTART_INVALID_PARAMETER;
+        confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
         if (!m_mlmeStartConfirmCallback.IsNull())
         {
             m_mlmeStartConfirmCallback(confirmParams);
@@ -598,7 +632,7 @@
     {
         if (!m_mlmeScanConfirmCallback.IsNull())
         {
-            confirmParams.m_status = MLMESCAN_SCAN_IN_PROGRESS;
+            confirmParams.m_status = LrWpanMacStatus::SCAN_IN_PROGRESS;
             m_mlmeScanConfirmCallback(confirmParams);
         }
         NS_LOG_ERROR(this << " A channel scan is already in progress");
@@ -609,7 +643,7 @@
     {
         if (!m_mlmeScanConfirmCallback.IsNull())
         {
-            confirmParams.m_status = MLMESCAN_INVALID_PARAMETER;
+            confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
             m_mlmeScanConfirmCallback(confirmParams);
         }
         NS_LOG_ERROR(this << "Invalid scan duration or unsupported scan type");
@@ -688,7 +722,7 @@
         {
             MlmeAssociateConfirmParams confirmParams;
             confirmParams.m_assocShortAddr = Mac16Address("FF:FF");
-            confirmParams.m_status = MLMEASSOC_INVALID_PARAMETER;
+            confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
             m_mlmeAssociateConfirmCallback(confirmParams);
         }
     }
@@ -744,24 +778,7 @@
 
     CommandPayloadHeader macPayload(CommandPayloadHeader::ASSOCIATION_RESP);
     macPayload.SetShortAddr(params.m_assocShortAddr);
-    switch (params.m_status)
-    {
-    case LrWpanAssociationStatus::ASSOCIATED:
-        macPayload.SetAssociationStatus(CommandPayloadHeader::SUCCESSFUL);
-        break;
-    case LrWpanAssociationStatus::PAN_AT_CAPACITY:
-        macPayload.SetAssociationStatus(CommandPayloadHeader::FULL_CAPACITY);
-        break;
-    case LrWpanAssociationStatus::PAN_ACCESS_DENIED:
-        macPayload.SetAssociationStatus(CommandPayloadHeader::ACCESS_DENIED);
-        break;
-    case LrWpanAssociationStatus::ASSOCIATED_WITHOUT_ADDRESS:
-        NS_LOG_ERROR("Error, Associated without address");
-        break;
-    case LrWpanAssociationStatus::DISASSOCIATED:
-        NS_LOG_ERROR("Error, device not associated");
-        break;
-    }
+    macPayload.SetAssociationStatus(static_cast<uint8_t>(params.m_status));
 
     macHdr.SetSecDisable();
     macHdr.SetAckReq();
@@ -901,14 +918,14 @@
 LrWpanMac::MlmeSetRequest(LrWpanMacPibAttributeIdentifier id, Ptr<LrWpanMacPibAttributes> attribute)
 {
     MlmeSetConfirmParams confirmParams;
-    confirmParams.m_status = MLMESET_SUCCESS;
+    confirmParams.m_status = LrWpanMacStatus::SUCCESS;
 
     switch (id)
     {
     case macBeaconPayload:
         if (attribute->macBeaconPayload->GetSize() > lrwpan::aMaxBeaconPayloadLength)
         {
-            confirmParams.m_status = MLMESET_INVALID_PARAMETER;
+            confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
         }
         else
         {
@@ -917,20 +934,20 @@
         }
         break;
     case macBeaconPayloadLength:
-        confirmParams.m_status = MLMESET_INVALID_PARAMETER;
+        confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
         break;
     case macShortAddress:
         m_shortAddress = attribute->macShortAddress;
         break;
     case macExtendedAddress:
-        confirmParams.m_status = MLMESET_READ_ONLY;
+        confirmParams.m_status = LrWpanMacStatus::READ_ONLY;
         break;
     case macPanId:
         m_macPanId = macPanId;
         break;
     default:
         // TODO: Add support for setting other attributes
-        confirmParams.m_status = MLMESET_UNSUPPORTED_ATTRIBUTE;
+        confirmParams.m_status = LrWpanMacStatus::UNSUPPORTED_ATTRIBUTE;
         break;
     }
 
@@ -944,7 +961,7 @@
 void
 LrWpanMac::MlmeGetRequest(LrWpanMacPibAttributeIdentifier id)
 {
-    LrWpanMlmeGetConfirmStatus status = MLMEGET_SUCCESS;
+    LrWpanMacStatus status = LrWpanMacStatus::SUCCESS;
     Ptr<LrWpanMacPibAttributes> attributes = Create<LrWpanMacPibAttributes>();
 
     switch (id)
@@ -964,8 +981,14 @@
     case macPanId:
         attributes->macPanId = m_macPanId;
         break;
+    case pCurrentChannel:
+        attributes->pCurrentChannel = m_phy->GetCurrentChannelNum();
+        break;
+    case pCurrentPage:
+        attributes->pCurrentPage = m_phy->GetCurrentPage();
+        break;
     default:
-        status = MLMEGET_UNSUPPORTED_ATTRIBUTE;
+        status = LrWpanMacStatus::UNSUPPORTED_ATTRIBUTE;
         break;
     }
 
@@ -1272,7 +1295,7 @@
     {
         MlmeAssociateConfirmParams confirmParams;
         confirmParams.m_assocShortAddr = Mac16Address("FF:FF");
-        confirmParams.m_status = MLMEASSOC_NO_DATA;
+        confirmParams.m_status = LrWpanMacStatus::NO_DATA;
         m_mlmeAssociateConfirmCallback(confirmParams);
     }
 }
@@ -1326,7 +1349,7 @@
             if (!m_mlmeStartConfirmCallback.IsNull())
             {
                 MlmeStartConfirmParams confirmParams;
-                confirmParams.m_status = MLMESTART_SUCCESS;
+                confirmParams.m_status = LrWpanMacStatus::SUCCESS;
                 m_mlmeStartConfirmCallback(confirmParams);
             }
 
@@ -1409,12 +1432,12 @@
             {
                 confirmParams.m_panDescList = m_panDescriptorList;
             }
-            confirmParams.m_status = MLMESCAN_SUCCESS;
+            confirmParams.m_status = LrWpanMacStatus::SUCCESS;
             break;
         case MLMESCAN_ACTIVE:
             if (m_panDescriptorList.empty())
             {
-                confirmParams.m_status = MLMESCAN_NO_BEACON;
+                confirmParams.m_status = LrWpanMacStatus::NO_BEACON;
             }
             else
             {
@@ -1422,12 +1445,12 @@
                 {
                     confirmParams.m_panDescList = m_panDescriptorList;
                 }
-                confirmParams.m_status = MLMESCAN_SUCCESS;
+                confirmParams.m_status = LrWpanMacStatus::SUCCESS;
             }
             break;
         case MLMESCAN_ORPHAN:
             confirmParams.m_panDescList = {};
-            confirmParams.m_status = MLMESCAN_NO_BEACON;
+            confirmParams.m_status = LrWpanMacStatus::NO_BEACON;
             confirmParams.m_resultListSize = 0;
             // The device lost track of the coordinator and was unable
             // to locate it, disassociate from the network.
@@ -1491,7 +1514,7 @@
 
         // All channels scanned, report success
         MlmeScanConfirmParams confirmParams;
-        confirmParams.m_status = MLMESCAN_SUCCESS;
+        confirmParams.m_status = LrWpanMacStatus::SUCCESS;
         confirmParams.m_chPage = m_phy->GetCurrentPage();
         confirmParams.m_scanType = m_scanParams.m_scanType;
         confirmParams.m_energyDetList = m_energyDetectList;
@@ -1666,7 +1689,7 @@
     {
         MlmeSyncLossIndicationParams syncLossParams;
         // syncLossParams.m_logCh =
-        syncLossParams.m_lossReason = MLMESYNCLOSS_BEACON_LOST;
+        syncLossParams.m_lossReason = LrWpanMacStatus::BEACON_LOSS;
         syncLossParams.m_panId = m_macPanId;
         m_mlmeSyncLossIndicationCallback(syncLossParams);
 
@@ -1711,7 +1734,7 @@
     }
 }
 
-SuperframeField
+uint16_t
 LrWpanMac::GetSuperframeField()
 {
     SuperframeField sfrmSpec;
@@ -1736,7 +1759,7 @@
         sfrmSpec.SetAssocPermit(true);
     }
 
-    return sfrmSpec;
+    return sfrmSpec.GetSuperframe();
 }
 
 GtsFields
@@ -1777,84 +1800,6 @@
 }
 
 void
-LrWpanMac::SetMcpsDataIndicationCallback(McpsDataIndicationCallback c)
-{
-    m_mcpsDataIndicationCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeAssociateIndicationCallback(MlmeAssociateIndicationCallback c)
-{
-    m_mlmeAssociateIndicationCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeCommStatusIndicationCallback(MlmeCommStatusIndicationCallback c)
-{
-    m_mlmeCommStatusIndicationCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeOrphanIndicationCallback(MlmeOrphanIndicationCallback c)
-{
-    m_mlmeOrphanIndicationCallback = c;
-}
-
-void
-LrWpanMac::SetMcpsDataConfirmCallback(McpsDataConfirmCallback c)
-{
-    m_mcpsDataConfirmCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeStartConfirmCallback(MlmeStartConfirmCallback c)
-{
-    m_mlmeStartConfirmCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeScanConfirmCallback(MlmeScanConfirmCallback c)
-{
-    m_mlmeScanConfirmCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeAssociateConfirmCallback(MlmeAssociateConfirmCallback c)
-{
-    m_mlmeAssociateConfirmCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeBeaconNotifyIndicationCallback(MlmeBeaconNotifyIndicationCallback c)
-{
-    m_mlmeBeaconNotifyIndicationCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeSyncLossIndicationCallback(MlmeSyncLossIndicationCallback c)
-{
-    m_mlmeSyncLossIndicationCallback = c;
-}
-
-void
-LrWpanMac::SetMlmePollConfirmCallback(MlmePollConfirmCallback c)
-{
-    m_mlmePollConfirmCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeSetConfirmCallback(MlmeSetConfirmCallback c)
-{
-    m_mlmeSetConfirmCallback = c;
-}
-
-void
-LrWpanMac::SetMlmeGetConfirmCallback(MlmeGetConfirmCallback c)
-{
-    m_mlmeGetConfirmCallback = c;
-}
-
-void
 LrWpanMac::PdDataIndication(uint32_t psduLength, Ptr<Packet> p, uint8_t lqi)
 {
     NS_ASSERT(m_lrWpanMacState == MAC_IDLE || m_lrWpanMacState == MAC_ACK_PENDING ||
@@ -2184,42 +2129,51 @@
                         // beginning of an Association).
                         m_csmaCa->Cancel();
 
-                        SuperframeField incomingSuperframe;
-                        incomingSuperframe = receivedMacPayload.GetSuperframeSpecField();
+                        SuperframeField incomingSuperframe(
+                            receivedMacPayload.GetSuperframeSpecField());
 
                         m_incomingBeaconOrder = incomingSuperframe.GetBeaconOrder();
                         m_incomingSuperframeOrder = incomingSuperframe.GetFrameOrder();
                         m_incomingFnlCapSlot = incomingSuperframe.GetFinalCapSlot();
 
-                        m_incomingBeaconInterval =
-                            (static_cast<uint32_t>(1 << m_incomingBeaconOrder)) *
-                            lrwpan::aBaseSuperframeDuration;
-                        m_incomingSuperframeDuration =
-                            lrwpan::aBaseSuperframeDuration *
-                            (static_cast<uint32_t>(1 << m_incomingSuperframeOrder));
-
-                        if (incomingSuperframe.IsBattLifeExt())
+                        if (m_incomingBeaconOrder < 15)
                         {
-                            m_csmaCa->SetBatteryLifeExtension(true);
+                            // Start Beacon-enabled mode
+                            m_csmaCa->SetSlottedCsmaCa();
+                            m_incomingBeaconInterval =
+                                (static_cast<uint32_t>(1 << m_incomingBeaconOrder)) *
+                                lrwpan::aBaseSuperframeDuration;
+                            m_incomingSuperframeDuration =
+                                lrwpan::aBaseSuperframeDuration *
+                                (static_cast<uint32_t>(1 << m_incomingSuperframeOrder));
+
+                            if (incomingSuperframe.IsBattLifeExt())
+                            {
+                                m_csmaCa->SetBatteryLifeExtension(true);
+                            }
+                            else
+                            {
+                                m_csmaCa->SetBatteryLifeExtension(false);
+                            }
+
+                            // TODO: get Incoming frame GTS Fields here
+
+                            // Begin CAP on the current device using info from
+                            // the Incoming superframe
+                            NS_LOG_DEBUG("Incoming superframe Active Portion "
+                                         << "(Beacon + CAP + CFP): " << m_incomingSuperframeDuration
+                                         << " symbols");
+
+                            m_incCapEvent = Simulator::ScheduleNow(&LrWpanMac::StartCAP,
+                                                                   this,
+                                                                   SuperframeType::INCOMING);
                         }
                         else
                         {
-                            m_csmaCa->SetBatteryLifeExtension(false);
-                        }
-
-                        if (m_incomingBeaconOrder < 15 && !m_csmaCa->IsSlottedCsmaCa())
-                        {
-                            m_csmaCa->SetSlottedCsmaCa();
+                            // Start non-beacon enabled mode
+                            m_csmaCa->SetUnSlottedCsmaCa();
                         }
 
-                        // TODO: get Incoming frame GTS Fields here
-
-                        // Begin CAP on the current device using info from the Incoming superframe
-                        NS_LOG_DEBUG("Incoming superframe Active Portion (Beacon + CAP + CFP): "
-                                     << m_incomingSuperframeDuration << " symbols");
-                        m_incCapEvent = Simulator::ScheduleNow(&LrWpanMac::StartCAP,
-                                                               this,
-                                                               SuperframeType::INCOMING);
                         m_setMacState =
                             Simulator::ScheduleNow(&LrWpanMac::SetLrWpanMacState, this, MAC_IDLE);
                     }
@@ -2384,7 +2338,7 @@
                                 MlmeScanConfirmParams confirmParams;
                                 confirmParams.m_scanType = m_scanParams.m_scanType;
                                 confirmParams.m_chPage = m_scanParams.m_chPage;
-                                confirmParams.m_status = MLMESCAN_SUCCESS;
+                                confirmParams.m_status = LrWpanMacStatus::SUCCESS;
                                 m_mlmeScanConfirmCallback(confirmParams);
                             }
                             m_scanParams = {};
@@ -2464,8 +2418,7 @@
                                     commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
                                     commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
                                     commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
-                                    commStatusParams.m_status =
-                                        LrWpanMlmeCommStatus::MLMECOMMSTATUS_SUCCESS;
+                                    commStatusParams.m_status = LrWpanMacStatus::SUCCESS;
                                     m_mlmeCommStatusIndicationCallback(commStatusParams);
                                 }
                                 // Remove element from Pending Transaction List
@@ -2487,8 +2440,7 @@
                                 if (!m_mlmePollConfirmCallback.IsNull())
                                 {
                                     MlmePollConfirmParams pollConfirmParams;
-                                    pollConfirmParams.m_status =
-                                        LrWpanMlmePollConfirmStatus::MLMEPOLL_SUCCESS;
+                                    pollConfirmParams.m_status = LrWpanMacStatus::SUCCESS;
                                     m_mlmePollConfirmCallback(pollConfirmParams);
                                 }
                                 break;
@@ -2506,8 +2458,7 @@
                                     commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
                                     commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
                                     commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
-                                    commStatusParams.m_status =
-                                        LrWpanMlmeCommStatus::MLMECOMMSTATUS_SUCCESS;
+                                    commStatusParams.m_status = LrWpanMacStatus::SUCCESS;
                                     m_mlmeCommStatusIndicationCallback(commStatusParams);
                                 }
                             }
@@ -2525,7 +2476,7 @@
                                 Ptr<TxQueueElement> txQElement = m_txQueue.front();
                                 McpsDataConfirmParams confirmParams;
                                 confirmParams.m_msduHandle = txQElement->txQMsduHandle;
-                                confirmParams.m_status = IEEE_802_15_4_SUCCESS;
+                                confirmParams.m_status = LrWpanMacStatus::SUCCESS;
                                 m_mcpsDataConfirmCallback(confirmParams);
                             }
                         }
@@ -2614,7 +2565,7 @@
         {
             McpsDataConfirmParams confirmParams;
             confirmParams.m_msduHandle = txQElement->txQMsduHandle;
-            confirmParams.m_status = IEEE_802_15_4_TRANSACTION_OVERFLOW;
+            confirmParams.m_status = LrWpanMacStatus::TRANSACTION_OVERFLOW;
             m_mcpsDataConfirmCallback(confirmParams);
         }
         NS_LOG_DEBUG("TX Queue with size " << m_txQueue.size() << " is full, dropping packet");
@@ -2729,7 +2680,7 @@
                 {
                     MlmeAssociateConfirmParams confirmParams;
                     confirmParams.m_assocShortAddr = Mac16Address("FF:FF");
-                    confirmParams.m_status = MLMEASSOC_NO_ACK;
+                    confirmParams.m_status = LrWpanMacStatus::NO_ACK;
                     m_mlmeAssociateConfirmCallback(confirmParams);
                 }
                 break;
@@ -2744,7 +2695,7 @@
                     commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
                     commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
                     commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
-                    commStatusParams.m_status = LrWpanMlmeCommStatus::MLMECOMMSTATUS_NO_ACK;
+                    commStatusParams.m_status = LrWpanMacStatus::NO_ACK;
                     m_mlmeCommStatusIndicationCallback(commStatusParams);
                 }
                 RemovePendTxQElement(m_txPkt->Copy());
@@ -2764,7 +2715,7 @@
                 if (!m_mlmePollConfirmCallback.IsNull())
                 {
                     MlmePollConfirmParams pollConfirmParams;
-                    pollConfirmParams.m_status = LrWpanMlmePollConfirmStatus::MLMEPOLL_NO_ACK;
+                    pollConfirmParams.m_status = LrWpanMacStatus::NO_ACK;
                     m_mlmePollConfirmCallback(pollConfirmParams);
                 }
                 break;
@@ -2785,7 +2736,7 @@
             {
                 McpsDataConfirmParams confirmParams;
                 confirmParams.m_msduHandle = txQElement->txQMsduHandle;
-                confirmParams.m_status = IEEE_802_15_4_NO_ACK;
+                confirmParams.m_status = LrWpanMacStatus::NO_ACK;
                 m_mcpsDataConfirmCallback(confirmParams);
             }
         }
@@ -2861,7 +2812,7 @@
             commStatusParams.m_srcExtAddr = peekedMacHdr.GetExtSrcAddr();
             commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
             commStatusParams.m_dstExtAddr = peekedMacHdr.GetExtDstAddr();
-            commStatusParams.m_status = MLMECOMMSTATUS_TRANSACTION_OVERFLOW;
+            commStatusParams.m_status = LrWpanMacStatus::TRANSACTION_OVERFLOW;
             m_mlmeCommStatusIndicationCallback(commStatusParams);
         }
         m_macIndTxDropTrace(p);
@@ -2908,8 +2859,7 @@
                     commStatusParams.m_srcExtAddr = peekedMacHdr.GetExtSrcAddr();
                     commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
                     commStatusParams.m_dstExtAddr = peekedMacHdr.GetExtDstAddr();
-                    commStatusParams.m_status =
-                        LrWpanMlmeCommStatus::MLMECOMMSTATUS_TRANSACTION_EXPIRED;
+                    commStatusParams.m_status = LrWpanMacStatus::TRANSACTION_EXPIRED;
                     m_mlmeCommStatusIndicationCallback(commStatusParams);
                 }
             }
@@ -2919,7 +2869,7 @@
                 if (!m_mcpsDataConfirmCallback.IsNull())
                 {
                     McpsDataConfirmParams confParams;
-                    confParams.m_status = IEEE_802_15_4_TRANSACTION_EXPIRED;
+                    confParams.m_status = LrWpanMacStatus::TRANSACTION_EXPIRED;
                     m_mcpsDataConfirmCallback(confParams);
                 }
             }
@@ -3083,7 +3033,7 @@
                     if (!m_mlmeStartConfirmCallback.IsNull())
                     {
                         MlmeStartConfirmParams mlmeConfirmParams;
-                        mlmeConfirmParams.m_status = MLMESTART_SUCCESS;
+                        mlmeConfirmParams.m_status = LrWpanMacStatus::SUCCESS;
                         m_mlmeStartConfirmCallback(mlmeConfirmParams);
                     }
                 }
@@ -3133,7 +3083,7 @@
                         commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
                         commStatusParams.m_dstShortAddr = macHdr.GetShortDstAddr();
 
-                        commStatusParams.m_status = LrWpanMlmeCommStatus::MLMECOMMSTATUS_SUCCESS;
+                        commStatusParams.m_status = LrWpanMacStatus::SUCCESS;
                         m_mlmeCommStatusIndicationCallback(commStatusParams);
                     }
                 }
@@ -3151,7 +3101,7 @@
                     NS_ASSERT_MSG(!m_txQueue.empty(), "TxQsize = 0");
                     Ptr<TxQueueElement> txQElement = m_txQueue.front();
                     confirmParams.m_msduHandle = txQElement->txQMsduHandle;
-                    confirmParams.m_status = IEEE_802_15_4_SUCCESS;
+                    confirmParams.m_status = LrWpanMacStatus::SUCCESS;
                     m_mcpsDataConfirmCallback(confirmParams);
                 }
                 ifsWaitTime = Seconds(static_cast<double>(GetIfsSize()) / symbolRate);
@@ -3196,20 +3146,18 @@
                 {
                     MlmeAssociateConfirmParams confirmParams;
 
-                    switch (receivedMacPayload.GetAssociationStatus())
+                    switch (static_cast<LrWpanMacStatus>(receivedMacPayload.GetAssociationStatus()))
                     {
-                    case CommandPayloadHeader::SUCCESSFUL:
+                    case LrWpanMacStatus::SUCCESS:
                         // The assigned short address by the coordinator
                         SetShortAddress(receivedMacPayload.GetShortAddr());
                         m_macPanId = receivedMacHdr.GetSrcPanId();
 
-                        confirmParams.m_status =
-                            LrWpanMlmeAssociateConfirmStatus::MLMEASSOC_SUCCESS;
+                        confirmParams.m_status = LrWpanMacStatus::SUCCESS;
                         confirmParams.m_assocShortAddr = GetShortAddress();
                         break;
-                    case CommandPayloadHeader::FULL_CAPACITY:
-                        confirmParams.m_status =
-                            LrWpanMlmeAssociateConfirmStatus::MLMEASSOC_FULL_CAPACITY;
+                    case LrWpanMacStatus::FULL_CAPACITY:
+                        confirmParams.m_status = LrWpanMacStatus::FULL_CAPACITY;
                         m_macPanId = 0xffff;
                         m_macCoordShortAddress = Mac16Address("FF:FF");
                         m_macCoordExtendedAddress = Mac64Address("ff:ff:ff:ff:ff:ff:ff:ed");
@@ -3219,9 +3167,9 @@
                         m_incomingBeaconOrder = 15;
                         m_incomingSuperframeOrder = 15;
                         break;
-                    case CommandPayloadHeader::ACCESS_DENIED:
-                        confirmParams.m_status =
-                            LrWpanMlmeAssociateConfirmStatus::MLMEASSOC_ACCESS_DENIED;
+                    case LrWpanMacStatus::ACCESS_DENIED:
+                    default:
+                        confirmParams.m_status = LrWpanMacStatus::ACCESS_DENIED;
                         m_macPanId = 0xffff;
                         m_macCoordShortAddress = Mac16Address("FF:FF");
                         m_macCoordExtendedAddress = Mac64Address("ff:ff:ff:ff:ff:ff:ff:ed");
@@ -3262,7 +3210,7 @@
             {
                 McpsDataConfirmParams confirmParams;
                 confirmParams.m_msduHandle = txQElement->txQMsduHandle;
-                confirmParams.m_status = IEEE_802_15_4_FRAME_TOO_LONG;
+                confirmParams.m_status = LrWpanMacStatus::FRAME_TOO_LONG;
                 m_mcpsDataConfirmCallback(confirmParams);
             }
             RemoveFirstTxQElement();
@@ -3408,7 +3356,7 @@
                 MlmeScanConfirmParams confirmParams;
                 confirmParams.m_scanType = m_scanParams.m_scanType;
                 confirmParams.m_chPage = m_scanParams.m_chPage;
-                confirmParams.m_status = MLMESCAN_INVALID_PARAMETER;
+                confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
                 m_mlmeScanConfirmCallback(confirmParams);
             }
             NS_LOG_ERROR(this << "Channel Scan: Invalid channel page");
@@ -3462,7 +3410,7 @@
                 MlmeScanConfirmParams confirmParams;
                 confirmParams.m_scanType = m_scanParams.m_scanType;
                 confirmParams.m_chPage = m_scanParams.m_chPage;
-                confirmParams.m_status = MLMESCAN_INVALID_PARAMETER;
+                confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
                 if (!m_mlmeScanConfirmCallback.IsNull())
                 {
                     m_mlmeScanConfirmCallback(confirmParams);
@@ -3478,7 +3426,7 @@
                 MlmeScanConfirmParams confirmParams;
                 confirmParams.m_scanType = m_scanParams.m_scanType;
                 confirmParams.m_chPage = m_scanParams.m_chPage;
-                confirmParams.m_status = MLMESCAN_INVALID_PARAMETER;
+                confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
                 m_mlmeScanConfirmCallback(confirmParams);
             }
             NS_LOG_ERROR("Channel " << m_channelScanIndex
@@ -3500,7 +3448,7 @@
             if (!m_mlmeStartConfirmCallback.IsNull())
             {
                 MlmeStartConfirmParams confirmParams;
-                confirmParams.m_status = MLMESTART_INVALID_PARAMETER;
+                confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
                 m_mlmeStartConfirmCallback(confirmParams);
             }
             NS_LOG_ERROR("Invalid page parameter in MLME-start");
@@ -3518,7 +3466,7 @@
             if (!m_mlmeStartConfirmCallback.IsNull())
             {
                 MlmeStartConfirmParams confirmParams;
-                confirmParams.m_status = MLMESTART_INVALID_PARAMETER;
+                confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
                 m_mlmeStartConfirmCallback(confirmParams);
             }
             NS_LOG_ERROR("Invalid channel parameter in MLME-start");
@@ -3549,7 +3497,7 @@
             {
                 MlmeAssociateConfirmParams confirmParams;
                 confirmParams.m_assocShortAddr = Mac16Address("FF:FF");
-                confirmParams.m_status = MLMEASSOC_INVALID_PARAMETER;
+                confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
                 m_mlmeAssociateConfirmCallback(confirmParams);
             }
             NS_LOG_ERROR("Invalid page parameter in MLME-associate");
@@ -3577,7 +3525,7 @@
             {
                 MlmeAssociateConfirmParams confirmParams;
                 confirmParams.m_assocShortAddr = Mac16Address("FF:FF");
-                confirmParams.m_status = MLMEASSOC_INVALID_PARAMETER;
+                confirmParams.m_status = LrWpanMacStatus::INVALID_PARAMETER;
                 m_mlmeAssociateConfirmCallback(confirmParams);
             }
             NS_LOG_ERROR("Invalid channel parameter in MLME-associate");
@@ -3654,7 +3602,7 @@
                 {
                     MlmeAssociateConfirmParams confirmParams;
                     confirmParams.m_assocShortAddr = Mac16Address("FF:FF");
-                    confirmParams.m_status = MLMEASSOC_CHANNEL_ACCESS_FAILURE;
+                    confirmParams.m_status = LrWpanMacStatus::CHANNEL_ACCESS_FAILURE;
                     m_mlmeAssociateConfirmCallback(confirmParams);
                 }
                 break;
@@ -3668,8 +3616,7 @@
                     commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
                     commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
                     commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
-                    commStatusParams.m_status =
-                        LrWpanMlmeCommStatus::MLMECOMMSTATUS_CHANNEL_ACCESS_FAILURE;
+                    commStatusParams.m_status = LrWpanMacStatus::CHANNEL_ACCESS_FAILURE;
                     m_mlmeCommStatusIndicationCallback(commStatusParams);
                 }
                 RemovePendTxQElement(m_txPkt->Copy());
@@ -3688,8 +3635,7 @@
                 if (!m_mlmePollConfirmCallback.IsNull())
                 {
                     MlmePollConfirmParams pollConfirmParams;
-                    pollConfirmParams.m_status =
-                        LrWpanMlmePollConfirmStatus::MLMEPOLL_CHANNEL_ACCESS_FAILURE;
+                    pollConfirmParams.m_status = LrWpanMacStatus::CHANNEL_ACCESS_FAILURE;
                     m_mlmePollConfirmCallback(pollConfirmParams);
                 }
                 break;
@@ -3703,8 +3649,7 @@
                     commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
                     commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
                     commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
-                    commStatusParams.m_status =
-                        LrWpanMlmeCommStatus::MLMECOMMSTATUS_CHANNEL_ACCESS_FAILURE;
+                    commStatusParams.m_status = LrWpanMacStatus::CHANNEL_ACCESS_FAILURE;
                     m_mlmeCommStatusIndicationCallback(commStatusParams);
                 }
                 break;
@@ -3740,7 +3685,7 @@
             {
                 McpsDataConfirmParams confirmParams;
                 confirmParams.m_msduHandle = m_txQueue.front()->txQMsduHandle;
-                confirmParams.m_status = IEEE_802_15_4_CHANNEL_ACCESS_FAILURE;
+                confirmParams.m_status = LrWpanMacStatus::CHANNEL_ACCESS_FAILURE;
                 m_mcpsDataConfirmCallback(confirmParams);
             }
             // remove the copy of the packet that was just sent
@@ -3777,18 +3722,6 @@
     }
 }
 
-LrWpanAssociationStatus
-LrWpanMac::GetAssociationStatus() const
-{
-    return m_associationStatus;
-}
-
-void
-LrWpanMac::SetAssociationStatus(LrWpanAssociationStatus status)
-{
-    m_associationStatus = status;
-}
-
 void
 LrWpanMac::SetTxQMaxSize(uint32_t queueSize)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-mac.h ns-3.41/src/lr-wpan/model/lr-wpan-mac.h
--- ns-3.40/src/lr-wpan/model/lr-wpan-mac.h	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-mac.h	2024-02-09 14:44:07.957790400 -0800
@@ -26,12 +26,10 @@
 #define LR_WPAN_MAC_H
 
 #include "lr-wpan-fields.h"
+#include "lr-wpan-mac-base.h"
 #include "lr-wpan-phy.h"
 
 #include <ns3/event-id.h>
-#include <ns3/mac16-address.h>
-#include <ns3/mac64-address.h>
-#include <ns3/object.h>
 #include <ns3/sequence-number.h>
 #include <ns3/traced-callback.h>
 #include <ns3/traced-value.h>
@@ -85,6 +83,15 @@
 };
 
 /**
+ *  Overloaded operator to print the value of a LrWpanMacState.
+ *
+ *  \param os The output stream
+ *  \param state The text value of the PHY state
+ *  \return The output stream with text value of the MAC state
+ */
+std::ostream& operator<<(std::ostream& os, const LrWpanMacState& state);
+
+/**
  * \ingroup lr-wpan
  *
  * Superframe status
@@ -148,696 +155,9 @@
 /**
  * \ingroup lr-wpan
  *
- * table 80 of 802.15.4
- */
-enum LrWpanAddressMode
-{
-    NO_PANID_ADDR = 0,
-    ADDR_MODE_RESERVED = 1,
-    SHORT_ADDR = 2,
-    EXT_ADDR = 3
-};
-
-/**
- * \ingroup lr-wpan
- *
- * table 83 of 802.15.4
- */
-enum LrWpanAssociationStatus
-{
-    ASSOCIATED = 0,
-    PAN_AT_CAPACITY = 1,
-    PAN_ACCESS_DENIED = 2,
-    ASSOCIATED_WITHOUT_ADDRESS = 0xfe,
-    DISASSOCIATED = 0xff
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 30 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmeScanType
-{
-    MLMESCAN_ED = 0x00,
-    MLMESCAN_ACTIVE = 0x01,
-    MLMESCAN_PASSIVE = 0x02,
-    MLMESCAN_ORPHAN = 0x03
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 42 of 802.15.4-2006
- */
-enum LrWpanMcpsDataConfirmStatus
-{
-    IEEE_802_15_4_SUCCESS = 0,
-    IEEE_802_15_4_TRANSACTION_OVERFLOW = 1,
-    IEEE_802_15_4_TRANSACTION_EXPIRED = 2,
-    IEEE_802_15_4_CHANNEL_ACCESS_FAILURE = 3,
-    IEEE_802_15_4_INVALID_ADDRESS = 4,
-    IEEE_802_15_4_INVALID_GTS = 5,
-    IEEE_802_15_4_NO_ACK = 6,
-    IEEE_802_15_4_COUNTER_ERROR = 7,
-    IEEE_802_15_4_FRAME_TOO_LONG = 8,
-    IEEE_802_15_4_UNAVAILABLE_KEY = 9,
-    IEEE_802_15_4_UNSUPPORTED_SECURITY = 10,
-    IEEE_802_15_4_INVALID_PARAMETER = 11
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 35 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmeStartConfirmStatus
-{
-    MLMESTART_SUCCESS = 0,
-    MLMESTART_NO_SHORT_ADDRESS = 1,
-    MLMESTART_SUPERFRAME_OVERLAP = 2,
-    MLMESTART_TRACKING_OFF = 3,
-    MLMESTART_INVALID_PARAMETER = 4,
-    MLMESTART_COUNTER_ERROR = 5,
-    MLMESTART_FRAME_TOO_LONG = 6,
-    MLMESTART_UNAVAILABLE_KEY = 7,
-    MLMESTART_UNSUPPORTED_SECURITY = 8,
-    MLMESTART_CHANNEL_ACCESS_FAILURE = 9
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 31 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmeScanConfirmStatus
-{
-    MLMESCAN_SUCCESS = 0,
-    MLMESCAN_LIMIT_REACHED = 1,
-    MLMESCAN_NO_BEACON = 2,
-    MLMESCAN_SCAN_IN_PROGRESS = 3,
-    MLMESCAN_COUNTER_ERROR = 4,
-    MLMESCAN_FRAME_TOO_LONG = 5,
-    MLMESCAN_UNAVAILABLE_KEY = 6,
-    MLMESCAN_UNSUPPORTED_SECURITY = 7,
-    MLMESCAN_INVALID_PARAMETER = 8
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 12 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmeAssociateConfirmStatus
-{
-    MLMEASSOC_SUCCESS = 0,
-    MLMEASSOC_FULL_CAPACITY = 1,
-    MLMEASSOC_ACCESS_DENIED = 2,
-    MLMEASSOC_CHANNEL_ACCESS_FAILURE = 3,
-    MLMEASSOC_NO_ACK = 4,
-    MLMEASSOC_NO_DATA = 5,
-    MLMEASSOC_COUNTER_ERROR = 6,
-    MLMEASSOC_FRAME_TOO_LONG = 7,
-    MLMEASSOC_UNSUPPORTED_LEGACY = 8,
-    MLMEASSOC_INVALID_PARAMETER = 9
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 37 of IEEE 802.15.4-2011
- */
-enum LrWpanSyncLossReason
-{
-    MLMESYNCLOSS_PAN_ID_CONFLICT = 0,
-    MLMESYNCLOSS_REALIGMENT = 1,
-    MLMESYNCLOSS_BEACON_LOST = 2,
-    MLMESYNCLOSS_SUPERFRAME_OVERLAP = 3
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 18 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmeCommStatus
-{
-    MLMECOMMSTATUS_SUCCESS = 0,
-    MLMECOMMSTATUS_TRANSACTION_OVERFLOW = 1,
-    MLMECOMMSTATUS_TRANSACTION_EXPIRED = 2,
-    MLMECOMMSTATUS_CHANNEL_ACCESS_FAILURE = 3,
-    MLMECOMMSTATUS_NO_ACK = 4,
-    MLMECOMMSTATUS_COUNTER_ERROR = 5,
-    MLMECOMMSTATUS_FRAME_TOO_LONG = 6,
-    MLMECOMMSTATUS_INVALID_PARAMETER = 7
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 39 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmePollConfirmStatus
-{
-    MLMEPOLL_SUCCESS = 0,
-    MLMEPOLL_CHANNEL_ACCESS_FAILURE = 2,
-    MLMEPOLL_NO_ACK = 3,
-    MLMEPOLL_NO_DATA = 4,
-    MLMEPOLL_COUNTER_ERROR = 5,
-    MLMEPOLL_FRAME_TOO_LONG = 6,
-    MLMEPOLL_UNAVAILABLE_KEY = 7,
-    MLMEPOLL_UNSUPPORTED_SECURITY = 8,
-    MLMEPOLL_INVALID_PARAMETER = 9
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 33 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmeSetConfirmStatus
-{
-    MLMESET_SUCCESS = 0,
-    MLMESET_READ_ONLY = 1,
-    MLMESET_UNSUPPORTED_ATTRIBUTE = 2,
-    MLMESET_INVALID_INDEX = 3,
-    MLMESET_INVALID_PARAMETER = 4
-};
-
-/**
- * \ingroup lr-wpan
- *
- * Table 20 of IEEE 802.15.4-2011
- */
-enum LrWpanMlmeGetConfirmStatus
-{
-    MLMEGET_SUCCESS = 0,
-    MLMEGET_UNSUPPORTED_ATTRIBUTE = 1
-};
-
-/**
- * \ingroup lr-wpan
- *
- * IEEE802.15.4-2011 MAC PIB Attribute Identifiers Table 52 in section 6.4.2
- *
- */
-enum LrWpanMacPibAttributeIdentifier
-{
-    macBeaconPayload = 0,
-    macBeaconPayloadLength = 1,
-    macShortAddress = 2,
-    macExtendedAddress = 3,
-    macPanId = 4,
-    unsupported = 255
-    // TODO: complete other MAC pib attributes
-};
-
-/**
- * \ingroup lr-wpan
- *
- * IEEE802.15.4-2011 PHY PIB Attributes Table 52 in section 6.4.2
- */
-struct LrWpanMacPibAttributes : public SimpleRefCount<LrWpanMacPibAttributes>
-{
-    Ptr<Packet> macBeaconPayload;      //!< The contents of the beacon payload.
-    uint8_t macBeaconPayloadLength{0}; //!< The length in octets of the beacon payload.
-    Mac16Address macShortAddress;      //!< The 16 bit mac short address
-    Mac64Address macExtendedAddress;   //!< The EUI-64 bit address
-    uint16_t macPanId;                 //!< The identifier of the PAN
-    // TODO: complete other MAC pib attributes
-};
-
-/**
- * \ingroup lr-wpan
- *
- * PAN Descriptor, Table 17 IEEE 802.15.4-2011
- */
-struct PanDescriptor
-{
-    LrWpanAddressMode m_coorAddrMode{SHORT_ADDR}; //!< The coordinator addressing mode corresponding
-                                                  //!< to the received beacon frame.
-    uint16_t m_coorPanId{0xffff};                 //!< The PAN ID of the coordinator as specified in
-                                                  //!<  the received beacon frame.
-    Mac16Address m_coorShortAddr; //!< The coordinator short address as specified in the coordinator
-                                  //!< address mode.
-    Mac64Address m_coorExtAddr;   //!< The coordinator extended address as specified in the
-                                  //!< coordinator address mode.
-    uint8_t m_logCh{11};          //!< The current channel number occupied by the network.
-    uint8_t m_logChPage{0};       //!< The current channel page occupied by the network.
-    SuperframeField m_superframeSpec; //!< The superframe specification as specified in the received
-                                      //!< beacon frame.
-    bool m_gtsPermit{false};          //!< TRUE if the beacon is from the PAN coordinator
-                                      //!< that is accepting GTS requests.
-    uint8_t m_linkQuality{0};         //!< The LQI at which the network beacon was received.
-                                      //!< Lower values represent lower LQI.
-    Time m_timeStamp; //!< Beacon frame reception time. Used as Time data type in ns-3 to avoid
-                      //!< precision problems.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MCPS-DATA.request params. See 7.1.1.1
- */
-struct McpsDataRequestParams
-{
-    LrWpanAddressMode m_srcAddrMode{SHORT_ADDR}; //!< Source address mode
-    LrWpanAddressMode m_dstAddrMode{SHORT_ADDR}; //!< Destination address mode
-    uint16_t m_dstPanId{0};                      //!< Destination PAN identifier
-    Mac16Address m_dstAddr;                      //!< Destination address
-    Mac64Address m_dstExtAddr;                   //!< Destination extended address
-    uint8_t m_msduHandle{0};                     //!< MSDU handle
-    uint8_t m_txOptions{0};                      //!< Tx Options (bitfield)
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MCPS-DATA.confirm params. See 7.1.1.2
- */
-struct McpsDataConfirmParams
-{
-    uint8_t m_msduHandle{0}; //!< MSDU handle
-    LrWpanMcpsDataConfirmStatus m_status{
-        IEEE_802_15_4_INVALID_PARAMETER}; //!< The status of the last MSDU transmission
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MCPS-DATA.indication params. See 7.1.1.3
- */
-struct McpsDataIndicationParams
-{
-    uint8_t m_srcAddrMode{SHORT_ADDR}; //!< Source address mode
-    uint16_t m_srcPanId{0};            //!< Source PAN identifier
-    Mac16Address m_srcAddr;            //!< Source address
-    Mac64Address m_srcExtAddr;         //!< Source extended address
-    uint8_t m_dstAddrMode{SHORT_ADDR}; //!< Destination address mode
-    uint16_t m_dstPanId{0};            //!< Destination PAN identifier
-    Mac16Address m_dstAddr;            //!< Destination address
-    Mac64Address m_dstExtAddr;         //!< Destination extended address
-    uint8_t m_mpduLinkQuality{0};      //!< LQI value measured during reception of the MPDU
-    uint8_t m_dsn{0};                  //!< The DSN of the received data frame
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-ASSOCIATE.indication params. See 802.15.4-2011 6.2.2.2.
- */
-struct MlmeAssociateIndicationParams
-{
-    Mac64Address m_extDevAddr;      //!< The extended address of the device requesting association
-    CapabilityField capabilityInfo; //!< The operational capabilities of
-                                    //!< the device requesting association.
-    uint8_t lqi{0}; //!< The link quality indicator of the received associate request command
-                    //!< (Not officially supported in the standard but found in implementations)
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-ASSOCIATE.response params. See 802.15.4-2011 6.2.2.3.
- */
-struct MlmeAssociateResponseParams
-{
-    Mac64Address m_extDevAddr;     //!< The extended address of the device requesting association
-    Mac16Address m_assocShortAddr; //!< The short address allocated by the coordinator on successful
-                                   //!< assoc. FF:FF = Unsuccessful
-    LrWpanAssociationStatus m_status{DISASSOCIATED}; //!< The status of the association attempt (As
-                                                     //!< defined on Table 83 IEEE 802.15.4-2006)
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-START.request params. See 802.15.4-2011  Section 6.2.12.1
- */
-struct MlmeStartRequestParams
-{
-    uint16_t m_PanId{0};     //!< Pan Identifier used by the device.
-    uint8_t m_logCh{11};     //!< Logical channel on which to start using the
-                             //!< new superframe configuration.
-    uint32_t m_logChPage{0}; //!< Logical channel page on which to start using the
-                             //!< new superframe configuration.
-    uint32_t m_startTime{0}; //!< Time at which to begin transmitting beacons (Used by Coordinator
-                             //!< not PAN Coordinators). The time is specified in symbols.
-    uint8_t m_bcnOrd{15};    //!< Beacon Order, Used to calculate the beacon interval, a value of 15
-                             //!< indicates no periodic beacons will be transmitted.
-    uint8_t m_sfrmOrd{15};   //!< Superframe Order, indicates the length of the CAP in time slots.
-    bool m_panCoor{false};   //!< On true this device will become coordinator.
-    bool m_battLifeExt{false}; //!< Flag indicating whether or not the Battery life extension (BLE)
-                               //!< features are used.
-    bool m_coorRealgn{false};  //!< True if a realignment request command is to be transmitted prior
-                               //!< changing the superframe.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-SYNC.request params. See 802.15.4-2011  Section 6.2.13.1
- */
-struct MlmeSyncRequestParams
-{
-    uint8_t m_logCh{11};    //!< The channel number on which to attempt coordinator synchronization.
-    bool m_trackBcn{false}; //!< True if the mlme sync with the next beacon and attempts to track
-                            //!< future beacons. False if mlme sync only the next beacon.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-POLL.request params. See 802.15.4-2011  Section 6.2.14.1
- */
-struct MlmePollRequestParams
-{
-    LrWpanAddressMode m_coorAddrMode{
-        SHORT_ADDR}; //!< The addressing mode of the coordinator to which the pool is intended.
-    uint16_t m_coorPanId{0};      //!< The PAN id of the coordinator to which the poll is intended.
-    Mac16Address m_coorShortAddr; //!< Coordinator short address.
-    Mac64Address m_coorExtAddr;   //!< Coordinator extended address.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-SCAN.request params. See IEEE 802.15.4-2011  Section 6.2.10.1 Table 30
- */
-struct MlmeScanRequestParams
-{
-    LrWpanMlmeScanType m_scanType{MLMESCAN_PASSIVE}; //!< Indicates the type of scan performed as
-                                                     //!< described in IEEE 802.15.4-2011 (5.1.2.1).
-    uint32_t m_scanChannels{0x7FFF800};              //!< The channel numbers to be scanned.
-                                                     //!< Default: (0x7FFF800 = Ch11-Ch26)
-                                                     //!< 27 LSB (b0,b1,...,b26) = channels
-    uint8_t m_scanDuration{14}; //!< The factor (0-14) used to calculate the length of time
-                                //!< to spend scanning.
-                                //!< scanDurationSymbols =
-                                //!< [aBaseSuperframeDuration * (2^m_scanDuration + 1)].
-    uint32_t m_chPage{0};       //!< The channel page on which to perform scan.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-SCAN.confirm params. See IEEE 802.15.4-2011 Section 6.2.10.2
- */
-struct MlmeScanConfirmParams
-{
-    LrWpanMlmeScanConfirmStatus m_status{MLMESCAN_INVALID_PARAMETER}; //!< The status the request.
-    LrWpanMlmeScanType m_scanType{MLMESCAN_PASSIVE}; //!< Indicates the type of scan
-                                                     //!<  performed (ED,ACTIVE,PASSIVE,ORPHAN).
-    uint32_t m_chPage{0};                 //!< The channel page on which the scan was performed.
-    std::vector<uint8_t> m_unscannedCh;   //!< A list of channels given in the request which
-                                          //!<  were not scanned (Not valid for ED scans).
-    uint8_t m_resultListSize{0};          //!< The number of elements returned in the appropriate
-                                          //!<  result list. (Not valid for Orphan scan).
-    std::vector<uint8_t> m_energyDetList; //!< A list of energy measurements, one for each
-                                          //!< channel searched during ED scan
-                                          //!< (Not valid for Active, Passive or Orphan Scans)
-    std::vector<PanDescriptor> m_panDescList; //!< A list of PAN descriptor, one for each beacon
-                                              //!< found (Not valid for ED and Orphan scans).
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-ASSOCIATE.request params. See 802.15.4-2011  Section 6.2.2.1
- */
-struct MlmeAssociateRequestParams
-{
-    uint8_t m_chNum{11};  //!< The channel number on which to attempt association.
-    uint32_t m_chPage{0}; //!< The channel page on which to attempt association.
-    uint8_t m_coordAddrMode{
-        SHORT_ADDR}; //!< The coordinator addressing mode for this primitive and subsequent MPDU.
-    uint16_t m_coordPanId{0}; //!< The identifier of the PAN with which to associate.
-    Mac16Address
-        m_coordShortAddr; //!< The short address of the coordinator with which to associate.
-    Mac64Address
-        m_coordExtAddr; //!< The extended address of the coordinator with which to associate.
-    CapabilityField
-        m_capabilityInfo; //!< Specifies the operational capabilities of the associating device.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-ASSOCIATE.confirm params. See 802.15.4-2011  Section 6.2.2.4
- */
-struct MlmeAssociateConfirmParams
-{
-    Mac16Address m_assocShortAddr; //!< The short address used in the association request
-    LrWpanMlmeAssociateConfirmStatus m_status{
-        MLMEASSOC_INVALID_PARAMETER}; //!< The status of a MLME-associate.request
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-START.confirm params. See  802.15.4-2011   Section 6.2.12.2
- */
-struct MlmeStartConfirmParams
-{
-    LrWpanMlmeStartConfirmStatus m_status{
-        MLMESTART_INVALID_PARAMETER}; //!< The status of a MLME-start.request
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-BEACON-NOTIFY.indication params. See  802.15.4-2011   Section 6.2.4.1, Table 16
- */
-struct MlmeBeaconNotifyIndicationParams
-{
-    uint8_t m_bsn{0};              //!< The beacon sequence number.
-    PanDescriptor m_panDescriptor; //!< The PAN descriptor for the received beacon.
-    uint32_t m_sduLength{0};       //!< The number of octets contained in the beacon payload.
-    Ptr<Packet> m_sdu;             //!< The set of octets comprising the beacon payload.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-SYNC-LOSS.indication params. See  802.15.4-2011   Section 6.2.13.2, Table 37
- */
-struct MlmeSyncLossIndicationParams
-{
-    LrWpanSyncLossReason m_lossReason{
-        MLMESYNCLOSS_PAN_ID_CONFLICT}; //!< The reason for the lost of synchronization.
-    uint16_t m_panId{0}; //!< The PAN identifier with which the device lost synchronization or to
-                         //!< which it was realigned.
-    uint8_t m_logCh{11}; //!< The channel number on which the device lost synchronization or to
-                         //!< which it was realigned.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-COMM-STATUS.indication params. See  802.15.4-2011   Section 6.2.4.2 Table 18
- */
-struct MlmeCommStatusIndicationParams
-{
-    uint16_t m_panId{0}; //!< The PAN identifier of the device from which the frame was received or
-                         //!< to which the frame was being sent.
-    uint8_t m_srcAddrMode{SHORT_ADDR}; //!< The source addressing mode for this primitive
-    Mac16Address m_srcShortAddr; //!< The short address of the entity from which the frame causing
-                                 //!< the error originated.
-    Mac64Address m_srcExtAddr; //!< The extended address of the entity from which the frame causing
-                               //!< the error originated.
-    uint8_t m_dstAddrMode{SHORT_ADDR}; //!< The destination addressing mode for this primitive.
-    Mac16Address m_dstShortAddr;       //!< The short address of the device for
-                                       //!< which the frame was intended.
-    Mac64Address m_dstExtAddr;         //!< The extended address of the device for
-                                       //!< which the frame was intended.
-    LrWpanMlmeCommStatus m_status{MLMECOMMSTATUS_INVALID_PARAMETER}; //!< The communication status
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-ORPHAN.indication params. See  802.15.4-2011   Section 6.2.7.1
- */
-struct MlmeOrphanIndicationParams
-{
-    Mac64Address m_orphanAddr; //!< The address of the orphaned device.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-ORPHAN.response params. See  802.15.4-2011   Section 6.2.7.2
- */
-struct MlmeOrphanResponseParams
-{
-    Mac64Address m_orphanAddr; //!< The address of the orphaned device.
-    Mac16Address m_shortAddr;  //!< The short address allocated.
-    bool m_assocMember{false}; //!< T = allocated with this coord |  F = otherwise
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-START.confirm params. See  802.15.4-2011   Section 6.2.14.2
- */
-struct MlmePollConfirmParams
-{
-    LrWpanMlmePollConfirmStatus m_status{
-        MLMEPOLL_INVALID_PARAMETER}; //!< The confirmation status resulting from a
-                                     //!< MLME-poll.request.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * MLME-SET.confirm params. See  802.15.4-2011   Section 6.2.11.2
- */
-struct MlmeSetConfirmParams
-{
-    LrWpanMlmeSetConfirmStatus m_status{MLMESET_UNSUPPORTED_ATTRIBUTE}; //!< The result of
-                                                                        //!< the request to write
-                                                                        //!< the PIB attribute.
-    LrWpanMacPibAttributeIdentifier id; //!< The id of the PIB attribute that was written.
-};
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a McpsDataRequest has been called from
- * the higher layer.  It returns a status of the outcome of the
- * transmission request
- */
-typedef Callback<void, McpsDataConfirmParams> McpsDataConfirmCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a Mcps has successfully received a
- *  frame and wants to deliver it to the higher layer.
- *
- *  \todo for now, we do not deliver all of the parameters in section
- *  802.15.4-2006 7.1.1.3.1 but just send up the packet.
- */
-typedef Callback<void, McpsDataIndicationParams, Ptr<Packet>> McpsDataIndicationCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a MlmeStartRequest has been called from
- * the higher layer.  It returns a status of the outcome of the
- * transmission request
- */
-typedef Callback<void, MlmeStartConfirmParams> MlmeStartConfirmCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a Mlme has successfully received a
- *  beacon frame and wants to deliver it to the higher layer.
- *
- *  \todo for now, we do not deliver all of the parameters in section
- *  802.15.4-2006 6.2.4.1 but just send up the packet.
- */
-typedef Callback<void, MlmeBeaconNotifyIndicationParams> MlmeBeaconNotifyIndicationCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called to indicate the loss of synchronization with
- * a coordinator.
- *
- * \todo for now, we do not deliver all of the parameters in section
- *  See IEEE 802.15.4-2011 6.2.13.2.
- */
-typedef Callback<void, MlmeSyncLossIndicationParams> MlmeSyncLossIndicationCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a Mlme-Poll.Request has been called from
- * the higher layer.  It returns a status of the outcome of the
- * transmission request
- */
-typedef Callback<void, MlmePollConfirmParams> MlmePollConfirmCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a MlmeScanRequest has been called from
- * the higher layer.  It returns a status of the outcome of the scan.
- */
-typedef Callback<void, MlmeScanConfirmParams> MlmeScanConfirmCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a MlmeAssociateRequest has been called from
- * the higher layer. It returns a status of the outcome of the
- * association request
- */
-typedef Callback<void, MlmeAssociateConfirmParams> MlmeAssociateConfirmCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a Mlme has successfully received a command
- *  frame and wants to deliver it to the higher layer.
- *
- *  Security related parameters and not handle.
- *  See 802.15.4-2011 6.2.2.2.
- */
-typedef Callback<void, MlmeAssociateIndicationParams> MlmeAssociateIndicationCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called by the MLME and issued to its next higher layer following
- * a transmission instigated through a response primitive.
- *
- *  Security related parameters and not handle.
- *  See 802.15.4-2011 6.2.4.2
- */
-typedef Callback<void, MlmeCommStatusIndicationParams> MlmeCommStatusIndicationCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called by the MLME and issued to its next higher layer following
- * the reception of a orphan notification.
- *
- *  Security related parameters and not handle.
- *  See 802.15.4-2011 6.2.7.1
- */
-typedef Callback<void, MlmeOrphanIndicationParams> MlmeOrphanIndicationCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a MlmeSetRequest has been called from
- * the higher layer to set a PIB. It returns a status of the outcome of the
- * write attempt.
- */
-typedef Callback<void, MlmeSetConfirmParams> MlmeSetConfirmCallback;
-
-/**
- * \ingroup lr-wpan
- *
- * This callback is called after a MlmeGetRequest has been called from
- * the higher layer to get a PIB. It returns a status of the outcome of the
- * write attempt.
- */
-typedef Callback<void,
-                 LrWpanMlmeGetConfirmStatus,
-                 LrWpanMacPibAttributeIdentifier,
-                 Ptr<LrWpanMacPibAttributes>>
-    MlmeGetConfirmCallback;
-
-/**
- * \ingroup lr-wpan
- *
  * Class that implements the LR-WPAN MAC state machine
  */
-class LrWpanMac : public Object
+class LrWpanMac : public LrWpanMacBase
 {
   public:
     /**
@@ -924,102 +244,26 @@
      */
     Mac64Address GetCoordExtAddress() const;
 
-    /**
-     * IEEE 802.15.4-2006, section 7.1.1.1
-     * MCPS-DATA.request
-     * Request to transfer a MSDU.
-     *
-     * \param params the request parameters
-     * \param p the packet to be transmitted
-     */
-    void McpsDataRequest(McpsDataRequestParams params, Ptr<Packet> p);
+    void McpsDataRequest(McpsDataRequestParams params, Ptr<Packet> p) override;
 
-    /**
-     * IEEE 802.15.4-2006, section 7.1.14.1
-     * MLME-START.request
-     * Request to allow a PAN coordinator to initiate
-     * a new PAN or beginning a new superframe configuration.
-     *
-     * \param params the request parameters
-     */
-    void MlmeStartRequest(MlmeStartRequestParams params);
+    void MlmeStartRequest(MlmeStartRequestParams params) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.10.1
-     * MLME-SCAN.request
-     * Request primitive used to initiate a channel scan over a given list of channels.
-     *
-     * \param params the scan request parameters
-     */
-    void MlmeScanRequest(MlmeScanRequestParams params);
+    void MlmeScanRequest(MlmeScanRequestParams params) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.2.1
-     * MLME-ASSOCIATE.request
-     * Request primitive used by a device to request an association with
-     * a coordinator.
-     *
-     * \param params the request parameters
-     */
-    void MlmeAssociateRequest(MlmeAssociateRequestParams params);
+    void MlmeAssociateRequest(MlmeAssociateRequestParams params) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.2.3
-     * MLME-ASSOCIATE.response
-     * Primitive used to initiate a response to an MLME-ASSOCIATE.indication
-     * primitive.
-     *
-     * \param params the associate response parameters
-     */
-    void MlmeAssociateResponse(MlmeAssociateResponseParams params);
+    void MlmeAssociateResponse(MlmeAssociateResponseParams params) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.7.2
-     * MLME-ORPHAN.response
-     * Primitive used to initiatte a response to an MLME-ORPHAN.indication
-     * primitive.
-     *
-     * \param params the orphan response parameters
-     */
-    void MlmeOrphanResponse(MlmeOrphanResponseParams params);
+    void MlmeOrphanResponse(MlmeOrphanResponseParams params) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.13.1
-     * MLME-SYNC.request
-     * Request to synchronize with the coordinator by acquiring and,
-     * if specified, tracking beacons.
-     *
-     * \param params the request parameters
-     */
-    void MlmeSyncRequest(MlmeSyncRequestParams params);
+    void MlmeSyncRequest(MlmeSyncRequestParams params) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.14.2
-     * MLME-POLL.request
-     * Prompts the device to request data from the coordinator.
-     *
-     * \param params the request parameters
-     */
-    void MlmePollRequest(MlmePollRequestParams params);
+    void MlmePollRequest(MlmePollRequestParams params) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.11.1
-     * MLME-SET.request
-     * Attempts to write the given value to the indicated PIB attribute.
-     *
-     * \param id the attributed identifier
-     * \param attribute the attribute value
-     */
-    void MlmeSetRequest(LrWpanMacPibAttributeIdentifier id, Ptr<LrWpanMacPibAttributes> attribute);
+    void MlmeSetRequest(LrWpanMacPibAttributeIdentifier id,
+                        Ptr<LrWpanMacPibAttributes> attribute) override;
 
-    /**
-     * IEEE 802.15.4-2011, section 6.2.5.1
-     * MLME-GET.request
-     * Request information about a given PIB attribute.
-     *
-     * \param id the attribute identifier
-     */
-    void MlmeGetRequest(LrWpanMacPibAttributeIdentifier id);
+    void MlmeGetRequest(LrWpanMacPibAttributeIdentifier id) override;
 
     /**
      * Set the CSMA/CA implementation to be used by the MAC.
@@ -1042,124 +286,9 @@
      */
     Ptr<LrWpanPhy> GetPhy();
 
-    /**
-     * Set the callback for the indication of an incoming data packet.
-     * The callback implements MCPS-DATA.indication SAP of IEEE 802.15.4-2006,
-     * section 7.1.1.3.
-     *
-     * \param c the callback
-     */
-    void SetMcpsDataIndicationCallback(McpsDataIndicationCallback c);
-
-    /**
-     * Set the callback for the indication of an incoming associate request command.
-     * The callback implements MLME-ASSOCIATE.indication SAP of IEEE 802.15.4-2011,
-     * section 6.2.2.2.
-     *
-     * \param c the callback
-     */
-    void SetMlmeAssociateIndicationCallback(MlmeAssociateIndicationCallback c);
-
-    /**
-     * Set the callback for the indication to a response primitive.
-     * The callback implements MLME-COMM-STATUS.indication SAP of IEEE 802.15.4-2011,
-     * section 6.2.4.2.
-     *
-     * \param c the callback
-     */
-    void SetMlmeCommStatusIndicationCallback(MlmeCommStatusIndicationCallback c);
-
-    /**
-     * Set the callback for the indication to the reception of an orphan notification.
-     * The callback implements MLME-ORPHAN.indication SAP of IEEE 802.15.4-2011,
-     * section 6.2.7.1.
-     *
-     * \param c the callback
-     */
-    void SetMlmeOrphanIndicationCallback(MlmeOrphanIndicationCallback c);
-
-    /**
-     * Set the callback for the confirmation of a data transmission request.
-     * The callback implements MCPS-DATA.confirm SAP of IEEE 802.15.4-2006,
-     * section 7.1.1.2.
-     *
-     * \param c the callback
-     */
-    void SetMcpsDataConfirmCallback(McpsDataConfirmCallback c);
-
-    /**
-     * Set the callback for the confirmation of a data transmission request.
-     * The callback implements MLME-START.confirm SAP of IEEE 802.15.4-2006,
-     * section 7.1.14.2.
-     *
-     * \param c the callback
-     */
-    void SetMlmeStartConfirmCallback(MlmeStartConfirmCallback c);
-
-    /**
-     * Set the callback for the confirmation of a data transmission request.
-     * The callback implements MLME-SCAN.confirm SAP of IEEE 802.15.4-2011,
-     * section 6.2.10.2.
-     *
-     * \param c the callback
-     */
-    void SetMlmeScanConfirmCallback(MlmeScanConfirmCallback c);
-
-    /**
-     * Set the callback for the confirmation of a data transmission request.
-     * The callback implements MLME-ASSOCIATE.confirm SAP of IEEE 802.15.4-2011,
-     * section 6.2.2.4.
-     *
-     * \param c the callback
-     */
-    void SetMlmeAssociateConfirmCallback(MlmeAssociateConfirmCallback c);
-
-    /**
-     * Set the callback for the indication of an incoming beacon packet.
-     * The callback implements MLME-BEACON-NOTIFY.indication SAP of IEEE 802.15.4-2011,
-     * section 6.2.4.1.
-     *
-     * \param c the callback
-     */
-    void SetMlmeBeaconNotifyIndicationCallback(MlmeBeaconNotifyIndicationCallback c);
-
-    /**
-     * Set the callback for the loss of synchronization with a coordinator.
-     * The callback implements MLME-BEACON-NOTIFY.indication SAP of IEEE 802.15.4-2011,
-     * section 6.2.13.2.
-     *
-     * \param c the callback
-     */
-    void SetMlmeSyncLossIndicationCallback(MlmeSyncLossIndicationCallback c);
-
-    /**
-     * Set the callback for the confirmation of a data transmission request.
-     * The callback implements MLME-POLL.confirm SAP of IEEE 802.15.4-2011,
-     * section 6.2.14.2
-     *
-     * \param c the callback
-     */
-    void SetMlmePollConfirmCallback(MlmePollConfirmCallback c);
-
-    /**
-     * Set the callback for the confirmation of an attempt to write an attribute.
-     * The callback implements MLME-SET.confirm SAP of IEEE 802.15.4-2011,
-     * section 6.2.11.2
-     *
-     * \param c the callback
-     */
-    void SetMlmeSetConfirmCallback(MlmeSetConfirmCallback c);
-
-    /**
-     * Set the callback for the confirmation of an attempt to read an attribute.
-     * The callback implements MLME-GET.confirm SAP of IEEE 802.15.4-2011,
-     * section 6.2.5.2
-     *
-     *\param c the callback
-     */
-    void SetMlmeGetConfirmCallback(MlmeGetConfirmCallback c);
-
-    // interfaces between MAC and PHY
+    ////////////////////////////////////
+    // Interfaces between MAC and PHY //
+    ////////////////////////////////////
 
     /**
      * IEEE 802.15.4-2006 section 6.2.1.3
@@ -1231,20 +360,6 @@
     void SetLrWpanMacState(LrWpanMacState macState);
 
     /**
-     * Get the current association status.
-     *
-     * \return current association status
-     */
-    LrWpanAssociationStatus GetAssociationStatus() const;
-
-    /**
-     * Set the current association status.
-     *
-     * \param status new association status
-     */
-    void SetAssociationStatus(LrWpanAssociationStatus status);
-
-    /**
      * Set the max size of the transmit queue.
      *
      * \param queueSize The transmit queue size.
@@ -1828,9 +943,9 @@
      * Constructs a Superframe specification field from the local information,
      * the superframe Specification field is necessary to create a beacon frame.
      *
-     * \returns the Superframe specification field
+     * \returns the Superframe specification field (bitmap)
      */
-    SuperframeField GetSuperframeField();
+    uint16_t GetSuperframeField();
 
     /**
      * Constructs the Guaranteed Time Slots (GTS) Fields from local information.
@@ -2017,91 +1132,6 @@
     Ptr<LrWpanCsmaCa> m_csmaCa;
 
     /**
-     * This callback is used to report the result of an attribute writing request
-     * to the upper layers.
-     * See IEEE 802.15.4-2011, section 6.2.11.2.
-     */
-    MlmeSetConfirmCallback m_mlmeSetConfirmCallback;
-
-    /**
-     * This callback is used to report the result of an attribute read request
-     * to the upper layers.
-     * See IEEE 802.15.4-2011, section 6.2.5.2
-     */
-    MlmeGetConfirmCallback m_mlmeGetConfirmCallback;
-
-    /**
-     * This callback is used to notify incoming beacon packets to the upper layers.
-     * See IEEE 802.15.4-2011, section 6.2.4.1.
-     */
-    MlmeBeaconNotifyIndicationCallback m_mlmeBeaconNotifyIndicationCallback;
-
-    /**
-     * This callback is used to indicate the loss of synchronization with a coordinator.
-     * See IEEE 802.15.4-2011, section 6.2.13.2.
-     */
-    MlmeSyncLossIndicationCallback m_mlmeSyncLossIndicationCallback;
-
-    /**
-     * This callback is used to report the result of a scan on a group of channels for the
-     * selected channel page.
-     * See IEEE 802.15.4-2011, section 6.2.10.2.
-     */
-    MlmeScanConfirmCallback m_mlmeScanConfirmCallback;
-
-    /**
-     * This callback is used to report the status after a device request an association with
-     * a coordinator.
-     * See IEEE 802.15.4-2011, section 6.2.2.4.
-     */
-    MlmeAssociateConfirmCallback m_mlmeAssociateConfirmCallback;
-
-    /**
-     * This callback is used to report the status after a device send data command request to
-     * the coordinator to transmit data.
-     * See IEEE 802.15.4-2011, section 6.2.14.2.
-     */
-    MlmePollConfirmCallback m_mlmePollConfirmCallback;
-
-    /**
-     * This callback is used to report the start of a new PAN or
-     * the begin of a new superframe configuration.
-     * See IEEE 802.15.4-2006, section 7.1.14.2.
-     */
-    MlmeStartConfirmCallback m_mlmeStartConfirmCallback;
-
-    /**
-     * This callback is used to notify incoming packets to the upper layers.
-     * See IEEE 802.15.4-2006, section 7.1.1.3.
-     */
-    McpsDataIndicationCallback m_mcpsDataIndicationCallback;
-
-    /**
-     * This callback is used to indicate the reception of an association request command.
-     * See IEEE 802.15.4-2011, section 6.2.2.2
-     */
-    MlmeAssociateIndicationCallback m_mlmeAssociateIndicationCallback;
-
-    /**
-     * This callback is instigated through a response primitive.
-     * See IEEE 802.15.4-2011, section 6.2.4.2
-     */
-    MlmeCommStatusIndicationCallback m_mlmeCommStatusIndicationCallback;
-
-    /**
-     * This callback is used to indicate the reception of a orphan notification command.
-     * See IEEE 802.15.4-2011, section 6.2.7.1
-     */
-    MlmeOrphanIndicationCallback m_mlmeOrphanIndicationCallback;
-
-    /**
-     * This callback is used to report data transmission request status to the
-     * upper layers.
-     * See IEEE 802.15.4-2006, section 7.1.1.2.
-     */
-    McpsDataConfirmCallback m_mcpsDataConfirmCallback;
-
-    /**
      * The current state of the MAC layer.
      */
     TracedValue<LrWpanMacState> m_lrWpanMacState;
@@ -2117,11 +1147,6 @@
     TracedValue<SuperframeStatus> m_outSuperframeStatus;
 
     /**
-     * The current association status of the MAC layer.
-     */
-    LrWpanAssociationStatus m_associationStatus;
-
-    /**
      * The packet which is currently being sent by the MAC layer.
      */
     Ptr<Packet> m_txPkt;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-mac-header.cc ns-3.41/src/lr-wpan/model/lr-wpan-mac-header.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-mac-header.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-mac-header.cc	2024-02-09 14:44:07.953790400 -0800
@@ -120,7 +120,7 @@
 uint8_t
 LrWpanMacHeader::GetFrmCtrlRes() const
 {
-    return (m_fctrlReserved);
+    return m_fctrlReserved;
 }
 
 uint8_t
@@ -144,43 +144,43 @@
 uint8_t
 LrWpanMacHeader::GetSeqNum() const
 {
-    return (m_SeqNum);
+    return m_SeqNum;
 }
 
 uint16_t
 LrWpanMacHeader::GetDstPanId() const
 {
-    return (m_addrDstPanId);
+    return m_addrDstPanId;
 }
 
 Mac16Address
 LrWpanMacHeader::GetShortDstAddr() const
 {
-    return (m_addrShortDstAddr);
+    return m_addrShortDstAddr;
 }
 
 Mac64Address
 LrWpanMacHeader::GetExtDstAddr() const
 {
-    return (m_addrExtDstAddr);
+    return m_addrExtDstAddr;
 }
 
 uint16_t
 LrWpanMacHeader::GetSrcPanId() const
 {
-    return (m_addrSrcPanId);
+    return m_addrSrcPanId;
 }
 
 Mac16Address
 LrWpanMacHeader::GetShortSrcAddr() const
 {
-    return (m_addrShortSrcAddr);
+    return m_addrShortSrcAddr;
 }
 
 Mac64Address
 LrWpanMacHeader::GetExtSrcAddr() const
 {
-    return (m_addrExtSrcAddr);
+    return m_addrExtSrcAddr;
 }
 
 uint8_t
@@ -192,49 +192,49 @@
     val |= (m_secctrlKeyIdMode << 3) & (0x3 << 3); // Bit 3-4
     val |= (m_secctrlReserved << 5) & (0x7 << 5);  // Bit 5-7
 
-    return (val);
+    return val;
 }
 
 uint32_t
 LrWpanMacHeader::GetFrmCounter() const
 {
-    return (m_auxFrmCntr);
+    return m_auxFrmCntr;
 }
 
 uint8_t
 LrWpanMacHeader::GetSecLevel() const
 {
-    return (m_secctrlSecLevel);
+    return m_secctrlSecLevel;
 }
 
 uint8_t
 LrWpanMacHeader::GetKeyIdMode() const
 {
-    return (m_secctrlKeyIdMode);
+    return m_secctrlKeyIdMode;
 }
 
 uint8_t
 LrWpanMacHeader::GetSecCtrlReserved() const
 {
-    return (m_secctrlReserved);
+    return m_secctrlReserved;
 }
 
 uint32_t
 LrWpanMacHeader::GetKeyIdSrc32() const
 {
-    return (m_auxKeyIdKeySrc32);
+    return m_auxKeyIdKeySrc32;
 }
 
 uint64_t
 LrWpanMacHeader::GetKeyIdSrc64() const
 {
-    return (m_auxKeyIdKeySrc64);
+    return m_auxKeyIdKeySrc64;
 }
 
 uint8_t
 LrWpanMacHeader::GetKeyIdIndex() const
 {
-    return (m_auxKeyIdKeyIndex);
+    return m_auxKeyIdKeyIndex;
 }
 
 bool
@@ -597,7 +597,7 @@
             break;
         }
     }
-    return (size);
+    return size;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc ns-3.41/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc	2024-02-09 14:44:07.953790400 -0800
@@ -55,7 +55,7 @@
 BeaconPayloadHeader::GetSerializedSize() const
 {
     uint32_t size = 0;
-    size += m_superframeField.GetSerializedSize();
+    size += sizeof(m_superframeField);
     size += m_gtsFields.GetSerializedSize();
     size += m_pndAddrFields.GetSerializedSize();
 
@@ -66,7 +66,7 @@
 BeaconPayloadHeader::Serialize(Buffer::Iterator start) const
 {
     Buffer::Iterator i = start;
-    i = m_superframeField.Serialize(i);
+    i.WriteU16(m_superframeField);
     i = m_gtsFields.Serialize(i);
     i = m_pndAddrFields.Serialize(i);
 }
@@ -75,7 +75,7 @@
 BeaconPayloadHeader::Deserialize(Buffer::Iterator start)
 {
     Buffer::Iterator i = start;
-    i = m_superframeField.Deserialize(i);
+    m_superframeField = i.ReadU16();
     i = m_gtsFields.Deserialize(i);
     i = m_pndAddrFields.Deserialize(i);
 
@@ -91,7 +91,7 @@
 }
 
 void
-BeaconPayloadHeader::SetSuperframeSpecField(SuperframeField sf)
+BeaconPayloadHeader::SetSuperframeSpecField(uint16_t sf)
 {
     m_superframeField = sf;
 }
@@ -108,7 +108,7 @@
     m_pndAddrFields = pndAddrFields;
 }
 
-SuperframeField
+uint16_t
 BeaconPayloadHeader::GetSuperframeSpecField() const
 {
     return m_superframeField;
@@ -166,7 +166,7 @@
     switch (m_cmdFrameId)
     {
     case ASSOCIATION_REQ:
-        size += m_capabilityInfo.GetSerializedSize();
+        size += 1; // (Capability field)
         break;
     case ASSOCIATION_RESP:
         size += 3; // (short address + Association Status)
@@ -201,7 +201,7 @@
     switch (m_cmdFrameId)
     {
     case ASSOCIATION_REQ:
-        i = m_capabilityInfo.Serialize(i);
+        i.WriteU8(m_capabilityInfo);
         break;
     case ASSOCIATION_RESP:
         WriteTo(i, m_shortAddr);
@@ -240,11 +240,11 @@
     switch (m_cmdFrameId)
     {
     case ASSOCIATION_REQ:
-        i = m_capabilityInfo.Deserialize(i);
+        m_capabilityInfo = i.ReadU8();
         break;
     case ASSOCIATION_RESP:
         ReadFrom(i, m_shortAddr);
-        m_assocStatus = static_cast<AssocStatus>(i.ReadU8());
+        m_assocStatus = i.ReadU8();
         break;
     case DISASSOCIATION_NOTIF:
         break;
@@ -278,13 +278,15 @@
     os << "| MAC Command Frame ID | = " << static_cast<uint32_t>(m_cmdFrameId);
     switch (m_cmdFrameId)
     {
-    case ASSOCIATION_REQ:
-        os << "| Device Type FFD | = " << m_capabilityInfo.IsDeviceTypeFfd()
-           << "| Alternative Power Source available | = " << m_capabilityInfo.IsPowSrcAvailable()
-           << "| Receiver on when Idle | = " << m_capabilityInfo.IsReceiverOnWhenIdle()
-           << "| Security capable | = " << m_capabilityInfo.IsSecurityCapability()
-           << "| Allocate address on | = " << m_capabilityInfo.IsShortAddrAllocOn();
+    case ASSOCIATION_REQ: {
+        CapabilityField capability(m_capabilityInfo);
+        os << "| Device Type FFD | = " << capability.IsDeviceTypeFfd()
+           << "| Alternative Power Source available | = " << capability.IsPowSrcAvailable()
+           << "| Receiver on when Idle | = " << capability.IsReceiverOnWhenIdle()
+           << "| Security capable | = " << capability.IsSecurityCapability()
+           << "| Allocate address on | = " << capability.IsShortAddrAllocOn();
         break;
+    }
     case ASSOCIATION_RESP:
         os << "| Assigned Short Address | = " << m_shortAddr
            << "| Status Response | = " << m_assocStatus;
@@ -310,6 +312,8 @@
         break;
     case CMD_RESERVED:
         break;
+    default:
+        break;
     }
 }
 
@@ -320,7 +324,7 @@
 }
 
 void
-CommandPayloadHeader::SetCapabilityField(CapabilityField cap)
+CommandPayloadHeader::SetCapabilityField(uint8_t cap)
 {
     NS_ASSERT(m_cmdFrameId == ASSOCIATION_REQ);
     m_capabilityInfo = cap;
@@ -390,7 +394,7 @@
 }
 
 void
-CommandPayloadHeader::SetAssociationStatus(AssocStatus status)
+CommandPayloadHeader::SetAssociationStatus(uint8_t status)
 {
     NS_ASSERT(m_cmdFrameId == ASSOCIATION_RESP);
     m_assocStatus = status;
@@ -402,14 +406,14 @@
     return m_shortAddr;
 }
 
-CommandPayloadHeader::AssocStatus
+uint8_t
 CommandPayloadHeader::GetAssociationStatus() const
 {
     NS_ASSERT(m_cmdFrameId == ASSOCIATION_RESP);
     return m_assocStatus;
 }
 
-CapabilityField
+uint8_t
 CommandPayloadHeader::GetCapabilityField() const
 {
     NS_ASSERT(m_cmdFrameId == ASSOCIATION_REQ);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-mac-pl-headers.h ns-3.41/src/lr-wpan/model/lr-wpan-mac-pl-headers.h
--- ns-3.40/src/lr-wpan/model/lr-wpan-mac-pl-headers.h	2023-09-27 13:30:37.670839000 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-mac-pl-headers.h	2024-02-09 14:44:07.957790400 -0800
@@ -50,9 +50,9 @@
     void Print(std::ostream& os) const override;
     /**
      * Set the superframe specification field to the beacon payload header.
-     * \param sfrmField The superframe specification field
+     * \param sfrmField The superframe specification field (bitmap)
      */
-    void SetSuperframeSpecField(SuperframeField sfrmField);
+    void SetSuperframeSpecField(uint16_t sfrmField);
     /**
      * Set the superframe Guaranteed Time Slot (GTS) fields to the beacon payload header.
      * \param gtsFields The GTS fields.
@@ -65,9 +65,9 @@
     void SetPndAddrFields(PendingAddrFields pndAddrFields);
     /**
      * Get the superframe specification field from the beacon payload header.
-     * \return The superframe specification field
+     * \return The superframe specification field (bitmap)
      */
-    SuperframeField GetSuperframeSpecField() const;
+    uint16_t GetSuperframeSpecField() const;
     /**
      * Get the Guaranteed Time Slots (GTS) fields from the beacon payload header.
      * \return The GTS fields.
@@ -83,7 +83,7 @@
     /**
      * Superframe Specification Field
      */
-    SuperframeField m_superframeField;
+    uint16_t m_superframeField;
     /**
      * GTS Fields
      */
@@ -123,17 +123,6 @@
         CMD_RESERVED = 0xff          //!< Reserved
     };
 
-    /**
-     *  Association Status Field values.
-     *  See IEEE 802.15.4-2011, Table 6
-     */
-    enum AssocStatus
-    {
-        SUCCESSFUL = 0x00,    //!< Association successful
-        FULL_CAPACITY = 0x01, //!< PAN at capacity
-        ACCESS_DENIED = 0x02  //!< PAN access denied
-    };
-
     CommandPayloadHeader();
     /**
      * Constructor
@@ -161,7 +150,7 @@
      * Command).
      * \param cap The capability Information field
      */
-    void SetCapabilityField(CapabilityField cap);
+    void SetCapabilityField(uint8_t cap);
     /**
      *  Set the coordinator short address (16 bit address).
      * \param addr The coordinator short address.
@@ -192,7 +181,7 @@
      * Set status resulting from the association attempt (Association Response Command).
      * \param status The status resulting from the association attempt
      */
-    void SetAssociationStatus(AssocStatus status);
+    void SetAssociationStatus(uint8_t status);
     /**
      * Get the Short address assigned by the coordinator
      * (Association Response and Coordinator Realigment commands).
@@ -203,7 +192,7 @@
      * Get the status resulting from an association request (Association Response Command).
      * \return The resulting status from an association request
      */
-    AssocStatus GetAssociationStatus() const;
+    uint8_t GetAssociationStatus() const;
     /**
      * Get the command frame type ID
      * \return The command type ID from the command payload header
@@ -212,9 +201,9 @@
     /**
      * Get the Capability Information Field from the command payload header.
      * (Association Request Command)
-     * \return The Capability Information Field
+     * \return The Capability Information Field (8 bit bitmap)
      */
-    CapabilityField GetCapabilityField() const;
+    uint8_t GetCapabilityField() const;
     /**
      *  Get the coordinator short address.
      * \return The coordinator short address (16 bit address)
@@ -237,17 +226,17 @@
     uint16_t GetPanId() const;
 
   private:
-    MacCommand m_cmdFrameId;          //!< The command Frame Identifier (Used by all commands)
-    CapabilityField m_capabilityInfo; //!< Capability Information Field
-                                      //!< (Association Request Command)
-    Mac16Address m_shortAddr;         //!< Contains the short address assigned by the coordinator
-                              //!< (Association Response and Coordinator Realiagment Command)
+    MacCommand m_cmdFrameId;       //!< The command Frame Identifier (Used by all commands)
+    uint8_t m_capabilityInfo;      //!< Capability Information Field
+                                   //!< (Association Request Command)
+    Mac16Address m_shortAddr;      //!< Contains the short address assigned by the coordinator
+                                   //!< (Association Response and Coordinator Realiagment Command)
     Mac16Address m_coordShortAddr; //!< The coordinator short address
                                    //!< (Coordinator realigment command)
     uint16_t m_panid;              //!< The PAN identifier (Coordinator realigment command)
     uint8_t m_logCh;               //!< The channel number (Coordinator realigment command)
     uint8_t m_logChPage;           //!< The channel page number (Coordinator realigment command)
-    AssocStatus m_assocStatus;     //!< Association Status (Association Response Command)
+    uint8_t m_assocStatus;         //!< Association Status (Association Response Command)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-net-device.cc ns-3.41/src/lr-wpan/model/lr-wpan-net-device.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-net-device.cc	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-net-device.cc	2024-02-09 14:44:07.957790400 -0800
@@ -68,15 +68,16 @@
                           BooleanValue(true),
                           MakeBooleanAccessor(&LrWpanNetDevice::m_useAcks),
                           MakeBooleanChecker())
-            .AddAttribute("PseudoMacAddressMode",
-                          "Build the pseudo-MAC Address according to RFC 4944 or RFC 6282 "
-                          "(default: RFC 6282).",
-                          EnumValue(LrWpanNetDevice::RFC6282),
-                          MakeEnumAccessor(&LrWpanNetDevice::m_pseudoMacMode),
-                          MakeEnumChecker(LrWpanNetDevice::RFC6282,
-                                          "RFC 6282 (don't use PanId)",
-                                          LrWpanNetDevice::RFC4944,
-                                          "RFC 4944 (use PanId)"));
+            .AddAttribute(
+                "PseudoMacAddressMode",
+                "Build the pseudo-MAC Address according to RFC 4944 or RFC 6282 "
+                "(default: RFC 6282).",
+                EnumValue(LrWpanNetDevice::RFC6282),
+                MakeEnumAccessor<PseudoMacAddressMode_e>(&LrWpanNetDevice::m_pseudoMacMode),
+                MakeEnumChecker(LrWpanNetDevice::RFC6282,
+                                "RFC 6282 (don't use PanId)",
+                                LrWpanNetDevice::RFC4944,
+                                "RFC 4944 (use PanId)"));
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-phy.cc ns-3.41/src/lr-wpan/model/lr-wpan-phy.cc
--- ns-3.40/src/lr-wpan/model/lr-wpan-phy.cc	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-phy.cc	2024-02-09 14:44:07.957790400 -0800
@@ -87,6 +87,61 @@
     {8.0, 2.0, 2.0},
 };
 
+std::ostream&
+operator<<(std::ostream& os, const LrWpanPhyEnumeration& state)
+{
+    switch (state)
+    {
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_BUSY:
+        os << "BUSY";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_BUSY_RX:
+        os << "BUSY_RX";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_BUSY_TX:
+        os << "BUSY_TX";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_FORCE_TRX_OFF:
+        os << "FORCE_TRX_OFF";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_IDLE:
+        os << "IDLE";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_INVALID_PARAMETER:
+        os << "INVALID_PARAMETER";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_RX_ON:
+        os << "RX_ON";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_SUCCESS:
+        os << "SUCCESS";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_TRX_OFF:
+        os << "TRX_OFF";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_TX_ON:
+        os << "TX_ON";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_UNSUPPORTED_ATTRIBUTE:
+        os << "UNSUPPORTED";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_READ_ONLY:
+        os << "READ_ONLY";
+        break;
+    case LrWpanPhyEnumeration::IEEE_802_15_4_PHY_UNSPECIFIED:
+        os << "UNSPECIFIED";
+        break;
+    }
+    return os;
+};
+
+std::ostream&
+operator<<(std::ostream& os, const TracedValue<LrWpanPhyEnumeration>& state)
+{
+    LrWpanPhyEnumeration s = state;
+    return os << s;
+};
+
 TypeId
 LrWpanPhy::GetTypeId()
 {
@@ -241,14 +296,12 @@
 Ptr<NetDevice>
 LrWpanPhy::GetDevice() const
 {
-    NS_LOG_FUNCTION(this);
     return m_device;
 }
 
 Ptr<MobilityModel>
 LrWpanPhy::GetMobility() const
 {
-    NS_LOG_FUNCTION(this);
     return m_mobility;
 }
 
@@ -297,7 +350,6 @@
 Ptr<Object>
 LrWpanPhy::GetAntenna() const
 {
-    NS_LOG_FUNCTION(this);
     return m_antenna;
 }
 
@@ -548,6 +600,10 @@
 
         if (!m_currentRxPacket.second)
         {
+            m_currentRxPacket = std::make_pair(nullptr, true);
+            ChangeTrxState(IEEE_802_15_4_PHY_RX_ON);
+            NS_LOG_DEBUG("Packet successfully received");
+
             // The packet was successfully received, push it up the stack.
             if (!m_pdDataIndicationCallback.IsNull())
             {
@@ -556,43 +612,22 @@
         }
         else
         {
-            // The packet was destroyed, drop it.
+            // The packet was destroyed due to interference, post-rx corruption or
+            // cancelled, therefore drop it.
             m_phyRxDropTrace(currentPacket);
-        }
-        Ptr<LrWpanSpectrumSignalParameters> none = nullptr;
-        m_currentRxPacket = std::make_pair(none, true);
+            m_currentRxPacket = std::make_pair(nullptr, true);
 
-        if (!m_isRxCanceled)
-        {
-            // We may be waiting to apply a pending state change.
-            if (m_trxStatePending != IEEE_802_15_4_PHY_IDLE)
+            if (!m_isRxCanceled)
             {
-                // Only change the state immediately, if the transceiver is not already
-                // switching the state.
-                if (!m_setTRXState.IsRunning())
-                {
-                    NS_LOG_LOGIC("Apply pending state change to " << m_trxStatePending);
-                    ChangeTrxState(m_trxStatePending);
-                    m_trxStatePending = IEEE_802_15_4_PHY_IDLE;
-                    if (!m_plmeSetTRXStateConfirmCallback.IsNull())
-                    {
-                        m_plmeSetTRXStateConfirmCallback(IEEE_802_15_4_PHY_SUCCESS);
-                    }
-                }
+                ChangeTrxState(IEEE_802_15_4_PHY_RX_ON);
             }
             else
             {
-                ChangeTrxState(IEEE_802_15_4_PHY_RX_ON);
+                // The state of The PHY was already changed when the packet was canceled
+                // due to a forced operation.
+                m_isRxCanceled = false;
             }
         }
-        else
-        {
-            // A TX event was forced during the reception of the frame.
-            // There is no need to change the PHY state after handling the signal,
-            // because the Forced TX already changed the PHY state.
-            // Return flag to default state
-            m_isRxCanceled = false;
-        }
     }
 }
 
@@ -776,6 +811,7 @@
                 (state != IEEE_802_15_4_PHY_FORCE_TRX_OFF) && (state != IEEE_802_15_4_PHY_TX_ON));
 
     NS_LOG_LOGIC("Trying to set m_trxState from " << m_trxState << " to " << state);
+
     // this method always overrides previous state setting attempts
     if (!m_setTRXState.IsExpired())
     {
@@ -1321,6 +1357,7 @@
 LrWpanPhy::ChangeTrxState(LrWpanPhyEnumeration newState)
 {
     NS_LOG_LOGIC(this << " state: " << m_trxState << " -> " << newState);
+
     m_trxStateLogger(Simulator::Now(), m_trxState, newState);
     m_trxState = newState;
 }
@@ -1560,8 +1597,6 @@
 double
 LrWpanPhy::GetDataOrSymbolRate(bool isData)
 {
-    NS_LOG_FUNCTION(this << isData);
-
     double rate = 0.0;
 
     NS_ASSERT(m_phyOption < IEEE_802_15_4_INVALID_PHY_OPTION);
@@ -1691,10 +1726,8 @@
     SetRxSensitivity(-106.58);
 
     m_rxLastUpdate = Seconds(0);
-    Ptr<Packet> none_packet = nullptr;
-    Ptr<LrWpanSpectrumSignalParameters> none_params = nullptr;
-    m_currentRxPacket = std::make_pair(none_params, true);
-    m_currentTxPacket = std::make_pair(none_packet, true);
+    m_currentRxPacket = std::make_pair(nullptr, true);
+    m_currentTxPacket = std::make_pair(nullptr, true);
     m_errorModel = nullptr;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/model/lr-wpan-phy.h ns-3.41/src/lr-wpan/model/lr-wpan-phy.h
--- ns-3.40/src/lr-wpan/model/lr-wpan-phy.h	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lr-wpan/model/lr-wpan-phy.h	2024-02-09 14:44:07.957790400 -0800
@@ -28,6 +28,8 @@
 #include <ns3/traced-callback.h>
 #include <ns3/traced-value.h>
 
+#include <iostream>
+
 namespace ns3
 {
 
@@ -122,6 +124,24 @@
     IEEE_802_15_4_PHY_UNSPECIFIED = 0xc // all cases not covered by ieee802.15.4
 };
 
+/**
+ *  Overloaded operator to print the value of a LrWpanPhyEnumeration.
+ *
+ *  \param os The output stream
+ *  \param state The text value of the PHY state
+ *  \return The output stream with text value of the PHY state
+ */
+std::ostream& operator<<(std::ostream& os, const LrWpanPhyEnumeration& state);
+
+/**
+ *  Overloaded operator to print the value of a TracedValue<LrWpanPhyEnumeration>.
+ *
+ *  \param os The output stream
+ *  \param state The text value of the PHY state
+ *  \return The output stream with text value of the PHY state
+ */
+std::ostream& operator<<(std::ostream& os, const TracedValue<LrWpanPhyEnumeration>& state);
+
 namespace TracedValueCallback
 {
 /**
@@ -704,7 +724,10 @@
 
     /**
      * The trace source fired when a packet ends the reception process from
-     * the medium.  Second quantity is received SINR.
+     * the medium. In essence, the notional event of receiving all the energy
+     * of a signal is traced. The received completed signal might represent
+     * a complete packet or a packet that is later on dropped because of interference,
+     * cancellation or post-rx corruption. Second quantity is the received SINR (LQI).
      *
      * \see class CallBackTraceSource
      */
@@ -712,6 +735,8 @@
 
     /**
      * The trace source fired when the phy layer drops a packet it has received.
+     * (Destruction of a packet due to interference, post-rx corruption or
+     *  cancellation of packet rx)
      *
      * \see class CallBackTraceSource
      */
@@ -894,18 +919,18 @@
     Time m_rxLastUpdate;
 
     /**
-     * Statusinformation of the currently received packet. The first parameter
-     * contains the frame, as well the signal power of the frame. The second
-     * parameter is set to false, if the frame is either invalid or destroyed
-     * due to interference.
+     * Status information of the currently received packet. The first parameter
+     * contains the frame, as well the signal power of the frame. If the second
+     * parameter is set to true, the frame is either invalid, destroyed
+     * due to interference or cancelled.
      */
     std::pair<Ptr<LrWpanSpectrumSignalParameters>, bool> m_currentRxPacket;
 
     /**
-     * Statusinformation of the currently transmitted packet. The first parameter
-     * contains the frame. The second parameter is set to false, if the frame not
-     * completely transmitted, in the event of a force transceiver switch, for
-     * example.
+     * Status information of the currently transmitted packet. The first parameter
+     * contains the frame. If the second parameter is set to true, the frame has not
+     * been completely transmitted (in the event of a force transceiver switch, for
+     * example).
      */
     PacketAndStatus m_currentTxPacket;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/test/lr-wpan-mac-test.cc ns-3.41/src/lr-wpan/test/lr-wpan-mac-test.cc
--- ns-3.40/src/lr-wpan/test/lr-wpan-mac-test.cc	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lr-wpan/test/lr-wpan-mac-test.cc	2024-02-09 14:44:07.957790400 -0800
@@ -103,11 +103,11 @@
 void
 TestRxOffWhenIdleAfterCsmaFailure::DataConfirm(McpsDataConfirmParams params)
 {
-    if (params.m_status == LrWpanMcpsDataConfirmStatus::IEEE_802_15_4_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         NS_LOG_DEBUG("LrWpanMcpsDataConfirmStatus = Success");
     }
-    else if (params.m_status == LrWpanMcpsDataConfirmStatus::IEEE_802_15_4_CHANNEL_ACCESS_FAILURE)
+    else if (params.m_status == LrWpanMacStatus::CHANNEL_ACCESS_FAILURE)
     {
         NS_LOG_DEBUG("LrWpanMcpsDataConfirmStatus =  Channel Access Failure");
     }
@@ -336,7 +336,7 @@
 void
 TestActiveScanPanDescriptors::ScanConfirm(MlmeScanConfirmParams params)
 {
-    if (params.m_status == MLMESCAN_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         m_panDescriptorList = params.m_panDescList;
     }
@@ -573,7 +573,7 @@
 void
 TestOrphanScan::ScanConfirm(MlmeScanConfirmParams params)
 {
-    if (params.m_status == MLMESCAN_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         m_orphanScanSuccess = true;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lr-wpan/test/lr-wpan-slotted-csmaca-test.cc ns-3.41/src/lr-wpan/test/lr-wpan-slotted-csmaca-test.cc
--- ns-3.40/src/lr-wpan/test/lr-wpan-slotted-csmaca-test.cc	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lr-wpan/test/lr-wpan-slotted-csmaca-test.cc	2024-02-09 14:44:07.957790400 -0800
@@ -125,7 +125,7 @@
 {
     // In the case of transmissions with the acknowledgment flag activated, the transmission is only
     // successful if the acknowledgment was received.
-    if (params.m_status == LrWpanMcpsDataConfirmStatus::IEEE_802_15_4_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "s Transmission successfully sent");
         testcase->m_sentTime = Simulator::Now();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/CMakeLists.txt ns-3.41/src/lte/CMakeLists.txt
--- ns-3.40/src/lte/CMakeLists.txt	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lte/CMakeLists.txt	2024-02-09 14:44:07.957790400 -0800
@@ -349,15 +349,11 @@
   HEADER_FILES ${header_files}
   LIBRARIES_TO_LINK
     ${emu_libraries}
-    ${libcore}
-    ${libnetwork}
     ${libspectrum}
-    ${libstats}
     ${libbuildings}
     ${libvirtual-net-device}
     ${libpoint-to-point}
     ${libapplications}
-    ${libinternet}
     ${libcsma}
     ${libconfig-store}
   TEST_SOURCES ${test_sources}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/doc/source/conf.py ns-3.41/src/lte/doc/source/conf.py
--- ns-3.40/src/lte/doc/source/conf.py	2023-09-27 13:30:37.674839300 -0700
+++ ns-3.41/src/lte/doc/source/conf.py	2024-02-09 14:44:07.957790400 -0800
@@ -11,202 +11,208 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'lte'
+master_doc = "lte"
 
 # General information about the project.
-project = u'LENA'
-copyright = u'CTTC'
+project = "LENA"
+copyright = "CTTC"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'v10'
+version = "v10"
 # The full version, including alpha/beta/rc tags.
-release = 'v10'
+release = "v10"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = "default"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-#htmlhelp_basename = 'ns-3doc'
+# htmlhelp_basename = 'ns-3doc'
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-#  ('lte-testing', 'lte-doc-testing.tex', u'LTE Simulator Testing Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
-#  ('lte-design', 'lte-doc-design.tex', u'LTE Simulator Design Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
-#  ('lte-user', 'lte-doc-user.tex', u'LTE Simulator User Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
-  ('lte', 'lena-lte-module-doc.tex', u'The LENA ns-3 LTE Module Documentation', u'Centre Tecnològic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
+    #  ('lte-testing', 'lte-doc-testing.tex', u'LTE Simulator Testing Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
+    #  ('lte-design', 'lte-doc-design.tex', u'LTE Simulator Design Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
+    #  ('lte-user', 'lte-doc-user.tex', u'LTE Simulator User Documentation', u'Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)', 'manual'),
+    (
+        "lte",
+        "lena-lte-module-doc.tex",
+        "The LENA ns-3 LTE Module Documentation",
+        "Centre Tecnològic de Telecomunicacions de Catalunya (CTTC)",
+        "manual",
+    ),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
-
+# latex_domain_indices = True
 
 
 # add page breaks in the pdf. Level 1 is for top-level sections, level 2 for subsections, and so on.
@@ -217,7 +223,4 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-model-library', u'ns-3 Model Library',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-model-library", "ns-3 Model Library", ["ns-3 project"], 1)]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/doc/source/lte-testing.rst ns-3.41/src/lte/doc/source/lte-testing.rst
--- ns-3.40/src/lte/doc/source/lte-testing.rst	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/doc/source/lte-testing.rst	2024-02-09 14:44:08.005790700 -0800
@@ -947,7 +947,7 @@
 
    \mathrm{T_{test-2}} = 0.0 \times 1 + 0.752 \times 2 + 0.248 \times 3 = 2.248
 
-The expected throughput is calculted by counting the number of transmission slots available during the simulation (e.g., the number of TTIs) and the size of the TB in the simulation, in detail:
+The expected throughput is calculated by counting the number of transmission slots available during the simulation (e.g., the number of TTIs) and the size of the TB in the simulation, in detail:
 
 .. math::
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/CMakeLists.txt ns-3.41/src/lte/examples/CMakeLists.txt
--- ns-3.40/src/lte/examples/CMakeLists.txt	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/CMakeLists.txt	2024-02-09 14:44:08.005790700 -0800
@@ -42,7 +42,7 @@
     SOURCE_FILES lena-simple-epc-emu.cc
     LIBRARIES_TO_LINK
       ${liblte}
-      ${fd-net-device}
+      ${libfd-net-device}
       ${libconfig-store}
   )
 endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-cc-helper.cc ns-3.41/src/lte/examples/lena-cc-helper.cc
--- ns-3.40/src/lte/examples/lena-cc-helper.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-cc-helper.cc	2024-02-09 14:44:08.005790700 -0800
@@ -19,9 +19,9 @@
 
 #include "ns3/cc-helper.h"
 #include "ns3/component-carrier.h"
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include <ns3/buildings-helper.h>
+// #include "ns3/config-store.h"
 
 using namespace ns3;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-distributed-ffr.cc ns-3.41/src/lte/examples/lena-distributed-ffr.cc
--- ns-3.40/src/lte/examples/lena-distributed-ffr.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-distributed-ffr.cc	2024-02-09 14:44:08.005790700 -0800
@@ -19,7 +19,6 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/log.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-frequency-reuse.cc ns-3.41/src/lte/examples/lena-frequency-reuse.cc
--- ns-3.40/src/lte/examples/lena-frequency-reuse.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-frequency-reuse.cc	2024-02-09 14:44:08.005790700 -0800
@@ -18,7 +18,6 @@
  *
  */
 
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/lte-module.h"
 #include "ns3/mobility-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-ipv6-addr-conf.cc ns-3.41/src/lte/examples/lena-ipv6-addr-conf.cc
--- ns-3.40/src/lte/examples/lena-ipv6-addr-conf.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-ipv6-addr-conf.cc	2024-02-09 14:44:08.005790700 -0800
@@ -18,7 +18,6 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/epc-helper.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-ipv6-ue-rh.cc ns-3.41/src/lte/examples/lena-ipv6-ue-rh.cc
--- ns-3.40/src/lte/examples/lena-ipv6-ue-rh.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-ipv6-ue-rh.cc	2024-02-09 14:44:08.005790700 -0800
@@ -18,7 +18,6 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/epc-helper.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-ipv6-ue-ue.cc ns-3.41/src/lte/examples/lena-ipv6-ue-ue.cc
--- ns-3.40/src/lte/examples/lena-ipv6-ue-ue.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-ipv6-ue-ue.cc	2024-02-09 14:44:08.005790700 -0800
@@ -18,7 +18,6 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/epc-helper.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-simple-epc-backhaul.cc ns-3.41/src/lte/examples/lena-simple-epc-backhaul.cc
--- ns-3.40/src/lte/examples/lena-simple-epc-backhaul.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-simple-epc-backhaul.cc	2024-02-09 14:44:08.005790700 -0800
@@ -14,11 +14,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Manuel Requena <manuel.requena@cttc.es>
+ * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
+#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/lte-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-simple-epc.cc ns-3.41/src/lte/examples/lena-simple-epc.cc
--- ns-3.40/src/lte/examples/lena-simple-epc.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-simple-epc.cc	2024-02-09 14:44:08.005790700 -0800
@@ -14,8 +14,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Jaume Nin <jaume.nin@cttc.cat>
- *          Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Jaume Nin <jaume.nin@cttc.cat>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #include "ns3/applications-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-simple-epc-emu.cc ns-3.41/src/lte/examples/lena-simple-epc-emu.cc
--- ns-3.40/src/lte/examples/lena-simple-epc-emu.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-simple-epc-emu.cc	2024-02-09 14:44:08.005790700 -0800
@@ -19,6 +19,7 @@
  */
 
 #include "ns3/applications-module.h"
+#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/epc-helper.h"
 #include "ns3/internet-module.h"
@@ -28,7 +29,6 @@
 #include "ns3/mobility-module.h"
 #include "ns3/network-module.h"
 #include "ns3/point-to-point-helper.h"
-#include <ns3/config-store-module.h>
 
 using namespace ns3;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-uplink-power-control.cc ns-3.41/src/lte/examples/lena-uplink-power-control.cc
--- ns-3.40/src/lte/examples/lena-uplink-power-control.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-uplink-power-control.cc	2024-02-09 14:44:08.005790700 -0800
@@ -18,7 +18,6 @@
  *
  */
 
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/lte-module.h"
 #include "ns3/mobility-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-x2-handover.cc ns-3.41/src/lte/examples/lena-x2-handover.cc
--- ns-3.40/src/lte/examples/lena-x2-handover.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-x2-handover.cc	2024-02-09 14:44:08.005790700 -0800
@@ -18,13 +18,13 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/lte-module.h"
 #include "ns3/mobility-module.h"
 #include "ns3/network-module.h"
 #include "ns3/point-to-point-module.h"
+// #include "ns3/gtk-config-store.h"
 
 using namespace ns3;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/examples/lena-x2-handover-measures.cc ns-3.41/src/lte/examples/lena-x2-handover-measures.cc
--- ns-3.40/src/lte/examples/lena-x2-handover-measures.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/examples/lena-x2-handover-measures.cc	2024-02-09 14:44:08.005790700 -0800
@@ -18,13 +18,13 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/lte-module.h"
 #include "ns3/mobility-module.h"
 #include "ns3/network-module.h"
 #include "ns3/point-to-point-module.h"
+// #include "ns3/config-store.h"
 
 using namespace ns3;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/emu-epc-helper.cc ns-3.41/src/lte/helper/emu-epc-helper.cc
--- ns-3.40/src/lte/helper/emu-epc-helper.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/emu-epc-helper.cc	2024-02-09 14:44:08.005790700 -0800
@@ -14,9 +14,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Jaume Nin <jnin@cttc.es>
- *         Nicola Baldo <nbaldo@cttc.es>
- *         Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Jaume Nin <jnin@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #include "emu-epc-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/emu-epc-helper.h ns-3.41/src/lte/helper/emu-epc-helper.h
--- ns-3.40/src/lte/helper/emu-epc-helper.h	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/emu-epc-helper.h	2024-02-09 14:44:08.005790700 -0800
@@ -14,9 +14,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Jaume Nin <jnin@cttc.es>
- *         Nicola Baldo <nbaldo@cttc.es>
- *         Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Jaume Nin <jnin@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #ifndef EMU_EPC_HELPER_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/epc-helper.cc ns-3.41/src/lte/helper/epc-helper.cc
--- ns-3.40/src/lte/helper/epc-helper.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/epc-helper.cc	2024-02-09 14:44:08.009791000 -0800
@@ -14,9 +14,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Jaume Nin <jnin@cttc.es>
- *         Nicola Baldo <nbaldo@cttc.es>
- *         Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Jaume Nin <jnin@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #include "epc-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/epc-helper.h ns-3.41/src/lte/helper/epc-helper.h
--- ns-3.40/src/lte/helper/epc-helper.h	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/epc-helper.h	2024-02-09 14:44:08.009791000 -0800
@@ -14,9 +14,10 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Jaume Nin <jnin@cttc.es>
- *         Nicola Baldo <nbaldo@cttc.es>
- *         Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Jaume Nin <jnin@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #ifndef EPC_HELPER_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/lte-helper.cc ns-3.41/src/lte/helper/lte-helper.cc
--- ns-3.40/src/lte/helper/lte-helper.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/lte-helper.cc	2024-02-09 14:44:08.009791000 -0800
@@ -632,7 +632,7 @@
 
     if (m_epcHelper)
     {
-        EnumValue epsBearerToRlcMapping;
+        EnumValue<LteEnbRrc::LteEpsBearerToRlcMapping_t> epsBearerToRlcMapping;
         rrc->GetAttribute("EpsBearerToRlcMapping", epsBearerToRlcMapping);
         // it does not make sense to use RLC/SM when also using the EPC
         if (epsBearerToRlcMapping.Get() == LteEnbRrc::RLC_SM_ALWAYS)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/point-to-point-epc-helper.cc ns-3.41/src/lte/helper/point-to-point-epc-helper.cc
--- ns-3.40/src/lte/helper/point-to-point-epc-helper.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/point-to-point-epc-helper.cc	2024-02-09 14:44:08.009791000 -0800
@@ -14,10 +14,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Jaume Nin <jnin@cttc.es>
- *         Nicola Baldo <nbaldo@cttc.es>
- *         Manuel Requena <manuel.requena@cttc.es>
- *         (most of the code refactored to no-backhaul-epc-helper.cc)
+ * Authors:
+ *   Jaume Nin <jnin@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
+ *   (most of the code refactored to no-backhaul-epc-helper.cc)
  */
 
 #include "point-to-point-epc-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/point-to-point-epc-helper.h ns-3.41/src/lte/helper/point-to-point-epc-helper.h
--- ns-3.40/src/lte/helper/point-to-point-epc-helper.h	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/point-to-point-epc-helper.h	2024-02-09 14:44:08.009791000 -0800
@@ -14,10 +14,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Jaume Nin <jnin@cttc.es>
- *         Nicola Baldo <nbaldo@cttc.es>
- *         Manuel Requena <manuel.requena@cttc.es>
- *         (most of the code refactored to no-backhaul-epc-helper.h)
+ * Authors:
+ *   Jaume Nin <jnin@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
+ *   (most of the code refactored to no-backhaul-epc-helper.h)
  */
 
 #ifndef POINT_TO_POINT_EPC_HELPER_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/radio-bearer-stats-connector.cc ns-3.41/src/lte/helper/radio-bearer-stats-connector.cc
--- ns-3.40/src/lte/helper/radio-bearer-stats-connector.cc	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/radio-bearer-stats-connector.cc	2024-02-09 14:44:08.009791000 -0800
@@ -14,8 +14,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Nicola Baldo <nbaldo@cttc.es>
- *          Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #include "radio-bearer-stats-connector.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/helper/radio-bearer-stats-connector.h ns-3.41/src/lte/helper/radio-bearer-stats-connector.h
--- ns-3.40/src/lte/helper/radio-bearer-stats-connector.h	2023-09-27 13:30:37.702839100 -0700
+++ ns-3.41/src/lte/helper/radio-bearer-stats-connector.h	2024-02-09 14:44:08.009791000 -0800
@@ -14,8 +14,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Nicola Baldo <nbaldo@cttc.es>
- *          Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #ifndef RADIO_BEARER_STATS_CONNECTOR_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/a2-a4-rsrq-handover-algorithm.cc ns-3.41/src/lte/model/a2-a4-rsrq-handover-algorithm.cc
--- ns-3.40/src/lte/model/a2-a4-rsrq-handover-algorithm.cc	2023-09-27 13:30:37.706839300 -0700
+++ ns-3.41/src/lte/model/a2-a4-rsrq-handover-algorithm.cc	2024-02-09 14:44:08.009791000 -0800
@@ -16,12 +16,12 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Original work authors (from lte-enb-rrc.cc):
- * - Nicola Baldo <nbaldo@cttc.es>
- * - Marco Miozzo <mmiozzo@cttc.es>
- * - Manuel Requena <manuel.requena@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Marco Miozzo <mmiozzo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  *
  * Converted to handover algorithm interface by:
- * - Budiarto Herman <budiarto.herman@magister.fi>
+ *   Budiarto Herman <budiarto.herman@magister.fi>
  */
 
 #include "a2-a4-rsrq-handover-algorithm.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/a2-a4-rsrq-handover-algorithm.h ns-3.41/src/lte/model/a2-a4-rsrq-handover-algorithm.h
--- ns-3.40/src/lte/model/a2-a4-rsrq-handover-algorithm.h	2023-09-27 13:30:37.706839300 -0700
+++ ns-3.41/src/lte/model/a2-a4-rsrq-handover-algorithm.h	2024-02-09 14:44:08.009791000 -0800
@@ -16,12 +16,12 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Original work authors (from lte-enb-rrc.cc):
- * - Nicola Baldo <nbaldo@cttc.es>
- * - Marco Miozzo <mmiozzo@cttc.es>
- * - Manuel Requena <manuel.requena@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Marco Miozzo <mmiozzo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  *
  * Converted to handover algorithm interface by:
- * - Budiarto Herman <budiarto.herman@magister.fi>
+ *   Budiarto Herman <budiarto.herman@magister.fi>
  */
 
 #ifndef A2_A4_RSRQ_HANDOVER_ALGORITHM_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/cqa-ff-mac-scheduler.cc ns-3.41/src/lte/model/cqa-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/cqa-ff-mac-scheduler.cc	2023-09-27 13:30:37.706839300 -0700
+++ ns-3.41/src/lte/model/cqa-ff-mac-scheduler.cc	2024-02-09 14:44:08.013790800 -0800
@@ -256,31 +256,25 @@
         m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
         m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -478,9 +472,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -511,11 +503,11 @@
     {
         if (dlbandwidth < CqaType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -535,7 +527,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -569,7 +561,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -598,7 +590,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -1758,9 +1750,8 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
                 m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
             }
@@ -1781,9 +1772,7 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
                 m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
             }
             else
@@ -1808,7 +1797,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1827,7 +1816,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1974,8 +1963,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -2104,8 +2092,7 @@
             //            {
             //              m_schedSapUser->SchedUlConfigInd (ret);
             //            }
-            //          m_allocationMaps.insert (std::pair <uint16_t, std::vector <uint16_t> >
-            //          (params.m_sfnSf, rbgAllocationMap)); return;
+            //          m_allocationMaps[params.m_sfnSf] = rbgAllocationMap; return;
             break;
         }
 
@@ -2248,8 +2235,7 @@
         NS_LOG_INFO(this << " UE average throughput " << (*itStats).second.lastAveragedThroughput);
         (*itStats).second.lastTtiBytesTransmitted = 0;
     }
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -2367,11 +2353,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/epc-enb-application.h ns-3.41/src/lte/model/epc-enb-application.h
--- ns-3.40/src/lte/model/epc-enb-application.h	2023-09-27 13:30:37.706839300 -0700
+++ ns-3.41/src/lte/model/epc-enb-application.h	2024-02-09 14:44:08.013790800 -0800
@@ -14,8 +14,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Jaume Nin <jnin@cttc.cat>
- *         Nicola Baldo <nbaldo@cttc.cat>
+ * Authors:
+ *   Jaume Nin <jnin@cttc.cat>
+ *   Nicola Baldo <nbaldo@cttc.cat>
  */
 
 #ifndef EPC_ENB_APPLICATION_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/epc-tft.cc ns-3.41/src/lte/model/epc-tft.cc
--- ns-3.40/src/lte/model/epc-tft.cc	2023-09-27 13:30:37.706839300 -0700
+++ ns-3.41/src/lte/model/epc-tft.cc	2024-02-09 14:44:08.013790800 -0800
@@ -310,6 +310,6 @@
 {
     NS_LOG_FUNCTION(this);
     return m_filters;
-};
+}
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/fdbet-ff-mac-scheduler.cc ns-3.41/src/lte/model/fdbet-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/fdbet-ff-mac-scheduler.cc	2023-09-27 13:30:37.814839600 -0700
+++ ns-3.41/src/lte/model/fdbet-ff-mac-scheduler.cc	2024-02-09 14:44:08.181792000 -0800
@@ -167,31 +167,25 @@
         m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
         m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -214,13 +208,13 @@
             fdbetsFlowPerf_t flowStatsDl;
             flowStatsDl.flowStart = Simulator::Now();
             flowStatsDl.totalBytesTransmitted = 0;
-            flowStatsDl.lastTtiBytesTrasmitted = 0;
+            flowStatsDl.lastTtiBytesTransmitted = 0;
             flowStatsDl.lastAveragedThroughput = 1;
             m_flowStatsDl.insert(std::pair<uint16_t, fdbetsFlowPerf_t>(params.m_rnti, flowStatsDl));
             fdbetsFlowPerf_t flowStatsUl;
             flowStatsUl.flowStart = Simulator::Now();
             flowStatsUl.totalBytesTransmitted = 0;
-            flowStatsUl.lastTtiBytesTrasmitted = 0;
+            flowStatsUl.lastTtiBytesTransmitted = 0;
             flowStatsUl.lastAveragedThroughput = 1;
             m_flowStatsUl.insert(std::pair<uint16_t, fdbetsFlowPerf_t>(params.m_rnti, flowStatsUl));
         }
@@ -303,9 +297,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -336,11 +328,11 @@
     {
         if (dlbandwidth < FdBetType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -360,7 +352,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -394,7 +386,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -423,7 +415,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -950,8 +942,7 @@
                 if (itMap == allocationMap.end())
                 {
                     tempMap.push_back(i);
-                    allocationMap.insert(
-                        std::pair<uint16_t, std::vector<uint16_t>>((*itMax).first, tempMap));
+                    allocationMap[(*itMax).first] = tempMap;
                 }
                 else
                 {
@@ -1023,7 +1014,7 @@
     // reset TTI stats of users
     for (auto itStats = m_flowStatsDl.begin(); itStats != m_flowStatsDl.end(); itStats++)
     {
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
 
     // generate the transmission opportunities by grouping the RBGs of the same RNTI and
@@ -1158,8 +1149,8 @@
         auto it = m_flowStatsDl.find((*itMap).first);
         if (it != m_flowStatsDl.end())
         {
-            (*it).second.lastTtiBytesTrasmitted = bytesTxed;
-            NS_LOG_INFO(this << " UE total bytes txed " << (*it).second.lastTtiBytesTrasmitted);
+            (*it).second.lastTtiBytesTransmitted = bytesTxed;
+            NS_LOG_INFO(this << " UE total bytes txed " << (*it).second.lastTtiBytesTransmitted);
         }
         else
         {
@@ -1174,15 +1165,15 @@
     NS_LOG_INFO(this << " Update UEs statistics");
     for (auto itStats = m_flowStatsDl.begin(); itStats != m_flowStatsDl.end(); itStats++)
     {
-        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTrasmitted;
+        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted;
         // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term
         // Evolution, Ed Wiley)
         (*itStats).second.lastAveragedThroughput =
             ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) +
-            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTrasmitted / 0.001));
+            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001));
         NS_LOG_INFO(this << " UE total bytes " << (*itStats).second.totalBytesTransmitted);
         NS_LOG_INFO(this << " UE average throughput " << (*itStats).second.lastAveragedThroughput);
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
 
     m_schedSapUser->SchedDlConfigInd(ret);
@@ -1214,9 +1205,8 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
                 m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
             }
@@ -1237,9 +1227,7 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
                 m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
             }
             else
@@ -1264,7 +1252,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1283,7 +1271,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1412,8 +1400,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1531,8 +1518,7 @@
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             return;
         }
 
@@ -1640,7 +1626,7 @@
         auto itStats = m_flowStatsUl.find((*it).first);
         if (itStats != m_flowStatsUl.end())
         {
-            (*itStats).second.lastTtiBytesTrasmitted = uldci.m_tbSize;
+            (*itStats).second.lastTtiBytesTransmitted = uldci.m_tbSize;
         }
         else
         {
@@ -1665,18 +1651,17 @@
     // update UEs stats
     for (auto itStats = m_flowStatsUl.begin(); itStats != m_flowStatsUl.end(); itStats++)
     {
-        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTrasmitted;
+        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted;
         // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term
         // Evolution, Ed Wiley)
         (*itStats).second.lastAveragedThroughput =
             ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) +
-            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTrasmitted / 0.001));
+            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001));
         NS_LOG_INFO(this << " UE total bytes " << (*itStats).second.totalBytesTransmitted);
         NS_LOG_INFO(this << " UE average throughput " << (*itStats).second.lastAveragedThroughput);
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -1794,11 +1779,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/fdbet-ff-mac-scheduler.h ns-3.41/src/lte/model/fdbet-ff-mac-scheduler.h
--- ns-3.40/src/lte/model/fdbet-ff-mac-scheduler.h	2023-09-27 13:30:37.814839600 -0700
+++ ns-3.41/src/lte/model/fdbet-ff-mac-scheduler.h	2024-02-09 14:44:08.181792000 -0800
@@ -39,10 +39,10 @@
 /// fdbetsFlowPerf_t structure
 struct fdbetsFlowPerf_t
 {
-    Time flowStart;                      ///< flow start time
-    unsigned long totalBytesTransmitted; ///< total bytes transmitted
-    unsigned int lastTtiBytesTrasmitted; ///< last total bytes transmitted
-    double lastAveragedThroughput;       ///< last averaged throughput
+    Time flowStart;                       ///< flow start time
+    unsigned long totalBytesTransmitted;  ///< total bytes transmitted
+    unsigned int lastTtiBytesTransmitted; ///< last total bytes transmitted
+    double lastAveragedThroughput;        ///< last averaged throughput
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/fdmt-ff-mac-scheduler.cc ns-3.41/src/lte/model/fdmt-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/fdmt-ff-mac-scheduler.cc	2023-09-27 13:30:37.814839600 -0700
+++ ns-3.41/src/lte/model/fdmt-ff-mac-scheduler.cc	2024-02-09 14:44:08.181792000 -0800
@@ -161,36 +161,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -292,9 +286,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -325,11 +317,11 @@
     {
         if (dlbandwidth < FdMtType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -349,7 +341,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -383,7 +375,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -412,7 +404,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -868,23 +860,23 @@
             double rcqiMax = 0.0;
             for (auto it = m_flowStatsDl.begin(); it != m_flowStatsDl.end(); it++)
             {
-                auto itRnti = rntiAllocated.find((*it));
-                if ((itRnti != rntiAllocated.end()) || (!HarqProcessAvailability((*it))))
+                auto itRnti = rntiAllocated.find(*it);
+                if (itRnti != rntiAllocated.end() || !HarqProcessAvailability(*it))
                 {
                     // UE already allocated for HARQ or without HARQ process available -> drop it
                     if (itRnti != rntiAllocated.end())
                     {
                         NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it));
                     }
-                    if (!HarqProcessAvailability((*it)))
+                    if (!HarqProcessAvailability(*it))
                     {
                         NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it));
                     }
                     continue;
                 }
 
-                auto itCqi = m_a30CqiRxed.find((*it));
-                auto itTxMode = m_uesTxMode.find((*it));
+                auto itCqi = m_a30CqiRxed.find(*it);
+                auto itTxMode = m_uesTxMode.find(*it);
                 if (itTxMode == m_uesTxMode.end())
                 {
                     NS_FATAL_ERROR("No Transmission Mode info on user " << (*it));
@@ -908,7 +900,7 @@
                 if ((cqi1 > 0) ||
                     (cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213)
                 {
-                    if (LcActivePerFlow((*it)) > 0)
+                    if (LcActivePerFlow(*it) > 0)
                     {
                         // this UE has data to transmit
                         double achievableRate = 0.0;
@@ -951,14 +943,13 @@
             else
             {
                 rbgMap.at(i) = true;
-                auto itMap = allocationMap.find((*itMax));
+                auto itMap = allocationMap.find(*itMax);
                 if (itMap == allocationMap.end())
                 {
                     // insert new element
                     std::vector<uint16_t> tempMap;
                     tempMap.push_back(i);
-                    allocationMap.insert(
-                        std::pair<uint16_t, std::vector<uint16_t>>((*itMax), tempMap));
+                    allocationMap[*itMax] = tempMap;
                 }
                 else
                 {
@@ -1178,11 +1169,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1201,10 +1191,8 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
-                m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
+                m_a30CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1228,7 +1216,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1247,7 +1235,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1376,8 +1364,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1495,8 +1482,7 @@
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             return;
         }
 
@@ -1614,8 +1600,7 @@
         }
     } while (((*it).first != m_nextRntiUl) && (rbPerFlow != 0));
 
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -1663,7 +1648,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
             }
             else
             {
@@ -1733,11 +1718,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1779,9 +1762,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/fdtbfq-ff-mac-scheduler.cc ns-3.41/src/lte/model/fdtbfq-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/fdtbfq-ff-mac-scheduler.cc	2023-09-27 13:30:37.814839600 -0700
+++ ns-3.41/src/lte/model/fdtbfq-ff-mac-scheduler.cc	2024-02-09 14:44:08.181792000 -0800
@@ -192,31 +192,25 @@
         m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
         m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -357,9 +351,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -390,11 +382,11 @@
     {
         if (dlbandwidth < FdTbfqType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -414,7 +406,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -448,7 +440,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -477,7 +469,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -1243,8 +1235,7 @@
                 // insert new element
                 std::vector<uint16_t> tempMap;
                 tempMap.push_back(rbgIndex);
-                allocationMap.insert(
-                    std::pair<uint16_t, std::vector<uint16_t>>((*itMax).first, tempMap));
+                allocationMap[(*itMax).first] = tempMap;
                 itMap = allocationMap.find(
                     (*itMax).first); // point itMap to the first RBGs assigned to this UE
             }
@@ -1572,9 +1563,8 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
                 m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
             }
@@ -1595,9 +1585,7 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
                 m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
             }
             else
@@ -1622,7 +1610,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1641,7 +1629,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1787,8 +1775,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1917,8 +1904,7 @@
             //            {
             //              m_schedSapUser->SchedUlConfigInd (ret);
             //            }
-            //          m_allocationMaps.insert (std::pair <uint16_t, std::vector <uint16_t> >
-            //          (params.m_sfnSf, rbgAllocationMap)); return;
+            //          m_allocationMaps[params.m_sfnSf] = rbgAllocationMap; return;
             break;
         }
 
@@ -2036,8 +2022,7 @@
         }
     } while (((*it).first != m_nextRntiUl) && (rbPerFlow != 0));
 
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -2155,11 +2140,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/ff-mac-scheduler.cc ns-3.41/src/lte/model/ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/ff-mac-scheduler.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/ff-mac-scheduler.cc	2024-02-09 14:44:08.181792000 -0800
@@ -49,17 +49,18 @@
 TypeId
 FfMacScheduler::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::FfMacScheduler")
-                            .SetParent<Object>()
-                            .SetGroupName("Lte")
-                            .AddAttribute("UlCqiFilter",
-                                          "The filter to apply on UL CQIs received",
-                                          EnumValue(FfMacScheduler::SRS_UL_CQI),
-                                          MakeEnumAccessor(&FfMacScheduler::m_ulCqiFilter),
-                                          MakeEnumChecker(FfMacScheduler::SRS_UL_CQI,
-                                                          "SRS_UL_CQI",
-                                                          FfMacScheduler::PUSCH_UL_CQI,
-                                                          "PUSCH_UL_CQI"));
+    static TypeId tid =
+        TypeId("ns3::FfMacScheduler")
+            .SetParent<Object>()
+            .SetGroupName("Lte")
+            .AddAttribute("UlCqiFilter",
+                          "The filter to apply on UL CQIs received",
+                          EnumValue(FfMacScheduler::SRS_UL_CQI),
+                          MakeEnumAccessor<UlCqiFilter_t>(&FfMacScheduler::m_ulCqiFilter),
+                          MakeEnumChecker(FfMacScheduler::SRS_UL_CQI,
+                                          "SRS_UL_CQI",
+                                          FfMacScheduler::PUSCH_UL_CQI,
+                                          "PUSCH_UL_CQI"));
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-amc.cc ns-3.41/src/lte/model/lte-amc.cc
--- ns-3.40/src/lte/model/lte-amc.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-amc.cc	2024-02-09 14:44:08.181792000 -0800
@@ -481,7 +481,7 @@
                 "AmcModel",
                 "AMC model used to assign CQI",
                 EnumValue(LteAmc::MiErrorModel),
-                MakeEnumAccessor(&LteAmc::m_amcModel),
+                MakeEnumAccessor<AmcModel>(&LteAmc::m_amcModel),
                 MakeEnumChecker(LteAmc::MiErrorModel, "Vienna", LteAmc::PiroEW2010, "PiroEW2010"));
     return tid;
 }
@@ -524,7 +524,7 @@
     NS_ASSERT_MSG(nprb > 0 && nprb < 111, "NPRB=" << nprb);
 
     int itbs = McsToItbsDl[mcs];
-    return (TransportBlockSizeTable[nprb - 1][itbs]);
+    return TransportBlockSizeTable[nprb - 1][itbs];
 }
 
 int
@@ -536,7 +536,7 @@
     NS_ASSERT_MSG(nprb > 0 && nprb < 111, "NPRB=" << nprb);
 
     int itbs = McsToItbsUl[mcs];
-    return (TransportBlockSizeTable[nprb - 1][itbs]);
+    return TransportBlockSizeTable[nprb - 1][itbs];
 }
 
 double
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-anr.cc ns-3.41/src/lte/model/lte-anr.cc
--- ns-3.40/src/lte/model/lte-anr.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-anr.cc	2024-02-09 14:44:08.181792000 -0800
@@ -16,12 +16,12 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Original work authors (from lte-enb-rrc.cc):
- * - Nicola Baldo <nbaldo@cttc.es>
- * - Marco Miozzo <mmiozzo@cttc.es>
- * - Manuel Requena <manuel.requena@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Marco Miozzo <mmiozzo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  *
  * Converted to ANR interface by:
- * - Budiarto Herman <budiarto.herman@magister.fi>
+ *   Budiarto Herman <budiarto.herman@magister.fi>
  */
 
 #include "lte-anr.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-anr.h ns-3.41/src/lte/model/lte-anr.h
--- ns-3.40/src/lte/model/lte-anr.h	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-anr.h	2024-02-09 14:44:08.181792000 -0800
@@ -16,12 +16,12 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Original work authors (from lte-enb-rrc.cc):
- * - Nicola Baldo <nbaldo@cttc.es>
- * - Marco Miozzo <mmiozzo@cttc.es>
- * - Manuel Requena <manuel.requena@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Marco Miozzo <mmiozzo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  *
  * Converted to ANR interface by:
- * - Budiarto Herman <budiarto.herman@magister.fi>
+ *   Budiarto Herman <budiarto.herman@magister.fi>
  */
 
 #ifndef LTE_ANR_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-common.cc ns-3.41/src/lte/model/lte-common.cc
--- ns-3.40/src/lte/model/lte-common.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-common.cc	2024-02-09 14:44:08.185792200 -0800
@@ -144,7 +144,7 @@
         val = -4096;
     }
     auto valFp = (int16_t)(val * 8);
-    return (valFp);
+    return valFp;
 }
 
 double
@@ -152,13 +152,13 @@
 {
     // convert from fixed point notation Sxxxxxxxxxxx.xxx to double
     double valD = ((int16_t)val) / 8.0;
-    return (valD);
+    return valD;
 }
 
 double
 LteFfConverter::getMinFpS11dot3Value()
 {
-    return (-4096); // -4096 = 0x8000 = 1000 0000 0000 0000 b
+    return -4096; // -4096 = 0x8000 = 1000 0000 0000 0000 b
 }
 
 // static double g_lowestFpS11dot3Value = -4096; // 0x8001 (1000 0000 0000 0000)
@@ -196,7 +196,7 @@
         }
     }
 
-    return (index);
+    return index;
 }
 
 uint8_t
@@ -227,7 +227,7 @@
         nLayer = 1;
         break;
     }
-    return (nLayer);
+    return nLayer;
 }
 
 double
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-enb-phy.cc ns-3.41/src/lte/model/lte-enb-phy.cc
--- ns-3.40/src/lte/model/lte-enb-phy.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-enb-phy.cc	2024-02-09 14:44:08.185792200 -0800
@@ -131,7 +131,7 @@
 uint8_t
 EnbMemberLteEnbPhySapProvider::GetMacChTtiDelay()
 {
-    return (m_phy->DoGetMacChTtiDelay());
+    return m_phy->DoGetMacChTtiDelay();
 }
 
 ////////////////////////////////////////
@@ -283,7 +283,7 @@
 LteEnbPhySapProvider*
 LteEnbPhy::GetLteEnbPhySapProvider()
 {
-    return (m_enbPhySapProvider);
+    return m_enbPhySapProvider;
 }
 
 void
@@ -297,7 +297,7 @@
 LteEnbPhy::GetLteEnbCphySapProvider()
 {
     NS_LOG_FUNCTION(this);
-    return (m_enbCphySapProvider);
+    return m_enbCphySapProvider;
 }
 
 void
@@ -359,7 +359,7 @@
 uint8_t
 LteEnbPhy::GetMacChDelay() const
 {
-    return (m_macChTtiDelay);
+    return m_macChTtiDelay;
 }
 
 Ptr<LteSpectrumPhy>
@@ -382,12 +382,12 @@
     if (it == m_ueAttached.end())
     {
         m_ueAttached.insert(rnti);
-        return (true);
+        return true;
     }
     else
     {
         NS_LOG_ERROR("UE already attached");
-        return (false);
+        return false;
     }
 }
 
@@ -399,12 +399,12 @@
     if (it == m_ueAttached.end())
     {
         NS_LOG_ERROR("UE not attached");
-        return (false);
+        return false;
     }
     else
     {
         m_ueAttached.erase(it);
-        return (true);
+        return true;
     }
 }
 
@@ -418,7 +418,7 @@
 uint8_t
 LteEnbPhy::DoGetMacChTtiDelay()
 {
-    return (m_macChTtiDelay);
+    return m_macChTtiDelay;
 }
 
 void
@@ -865,7 +865,7 @@
     ulcqi.m_ulCqi.m_type = UlCqi_s::PUSCH;
     for (auto it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
     {
-        double sinrdb = 10 * std::log10((*it));
+        double sinrdb = 10 * std::log10(*it);
         // NS_LOG_DEBUG ("ULCQI RB " << i << " value " << sinrdb);
         // convert from double to fixed point notation Sxxxxxxxxxxx.xxx
         int16_t sinrFp = LteFfConverter::double2fpS11dot3(sinrdb);
@@ -1012,7 +1012,7 @@
     double srsSum = 0.0;
     for (auto it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
     {
-        double sinrdb = 10 * log10((*it));
+        double sinrdb = 10 * log10(*it);
         //       NS_LOG_DEBUG ("ULCQI RB " << i << " value " << sinrdb);
         // convert from double to fixed point notation Sxxxxxxxxxxx.xxx
         int16_t sinrFp = LteFfConverter::double2fpS11dot3(sinrdb);
@@ -1030,7 +1030,7 @@
     ulcqi.m_vendorSpecificList.push_back(vsp);
     // call SRS tracing method
     CreateSrsReport(m_srsUeOffset.at(m_currentSrsOffset), (i > 0) ? (srsSum / i) : DBL_MAX);
-    return (ulcqi);
+    return ulcqi;
 }
 
 void
@@ -1076,7 +1076,7 @@
         m_ulDciQueue.erase(m_ulDciQueue.begin());
         std::list<UlDciLteControlMessage> l;
         m_ulDciQueue.push_back(l);
-        return (ret);
+        return ret;
     }
     else
     {
@@ -1084,7 +1084,7 @@
         std::list<UlDciLteControlMessage> l;
         m_ulDciQueue.push_back(l);
         std::list<UlDciLteControlMessage> emptylist;
-        return (emptylist);
+        return emptylist;
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-enb-rrc.cc ns-3.41/src/lte/model/lte-enb-rrc.cc
--- ns-3.40/src/lte/model/lte-enb-rrc.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-enb-rrc.cc	2024-02-09 14:44:08.185792200 -0800
@@ -15,12 +15,14 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Nicola Baldo <nbaldo@cttc.es>
- *          Marco Miozzo <mmiozzo@cttc.es>
- *          Manuel Requena <manuel.requena@cttc.es>
- * Modified by:  Danilo Abrignani <danilo.abrignani@unibo.it> (Carrier Aggregation - GSoC 2015),
- *               Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
- *               Vignesh Babu <ns3-dev@esk.fraunhofer.de> (RLF extensions)
+ * Authors:
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Marco Miozzo <mmiozzo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
+ * Modified by:
+ *   Danilo Abrignani <danilo.abrignani@unibo.it> (Carrier Aggregation - GSoC 2015),
+ *   Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
+ *   Vignesh Babu <ns3-dev@esk.fraunhofer.de> (RLF extensions)
  */
 
 #include "lte-enb-rrc.h"
@@ -140,7 +142,7 @@
 
 UeManager::UeManager()
 {
-    NS_FATAL_ERROR("this constructor is not espected to be used");
+    NS_FATAL_ERROR("this constructor is not expected to be used");
 }
 
 UeManager::UeManager(Ptr<LteEnbRrc> rrc, uint16_t rnti, State s, uint8_t componentCarrierId)
@@ -1938,18 +1940,19 @@
                           UintegerValue(0), // default tx-mode
                           MakeUintegerAccessor(&LteEnbRrc::m_defaultTransmissionMode),
                           MakeUintegerChecker<uint8_t>())
-            .AddAttribute("EpsBearerToRlcMapping",
-                          "Specify which type of RLC will be used for each type of EPS bearer.",
-                          EnumValue(RLC_SM_ALWAYS),
-                          MakeEnumAccessor(&LteEnbRrc::m_epsBearerToRlcMapping),
-                          MakeEnumChecker(RLC_SM_ALWAYS,
-                                          "RlcSmAlways",
-                                          RLC_UM_ALWAYS,
-                                          "RlcUmAlways",
-                                          RLC_AM_ALWAYS,
-                                          "RlcAmAlways",
-                                          PER_BASED,
-                                          "PacketErrorRateBased"))
+            .AddAttribute(
+                "EpsBearerToRlcMapping",
+                "Specify which type of RLC will be used for each type of EPS bearer.",
+                EnumValue(RLC_SM_ALWAYS),
+                MakeEnumAccessor<LteEpsBearerToRlcMapping_t>(&LteEnbRrc::m_epsBearerToRlcMapping),
+                MakeEnumChecker(RLC_SM_ALWAYS,
+                                "RlcSmAlways",
+                                RLC_UM_ALWAYS,
+                                "RlcUmAlways",
+                                RLC_AM_ALWAYS,
+                                "RlcAmAlways",
+                                PER_BASED,
+                                "PacketErrorRateBased"))
             .AddAttribute("SystemInformationPeriodicity",
                           "The interval for sending system information (Time value)",
                           TimeValue(MilliSeconds(80)),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-enb-rrc.h ns-3.41/src/lte/model/lte-enb-rrc.h
--- ns-3.40/src/lte/model/lte-enb-rrc.h	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-enb-rrc.h	2024-02-09 14:44:08.185792200 -0800
@@ -15,13 +15,14 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Nicola Baldo <nbaldo@cttc.es>
- *          Marco Miozzo <mmiozzo@cttc.es>
- *          Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Marco Miozzo <mmiozzo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  * Modified by:
- *          Danilo Abrignani <danilo.abrignani@unibo.it> (Carrier Aggregation - GSoC 2015)
- *          Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
- *          Vignesh Babu <ns3-dev@esk.fraunhofer.de> (RLF extensions)
+ *   Danilo Abrignani <danilo.abrignani@unibo.it> (Carrier Aggregation - GSoC 2015)
+ *   Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
+ *   Vignesh Babu <ns3-dev@esk.fraunhofer.de> (RLF extensions)
  */
 
 #ifndef LTE_ENB_RRC_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-ffr-algorithm.cc ns-3.41/src/lte/model/lte-ffr-algorithm.cc
--- ns-3.40/src/lte/model/lte-ffr-algorithm.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-ffr-algorithm.cc	2024-02-09 14:44:08.185792200 -0800
@@ -156,11 +156,11 @@
     {
         if (dlbandwidth < Type0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-ffr-distributed-algorithm.cc ns-3.41/src/lte/model/lte-ffr-distributed-algorithm.cc
--- ns-3.40/src/lte/model/lte-ffr-distributed-algorithm.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-ffr-distributed-algorithm.cc	2024-02-09 14:44:08.185792200 -0800
@@ -617,7 +617,7 @@
 
     for (auto ncIt = m_neighborCell.begin(); ncIt != m_neighborCell.end(); ncIt++)
     {
-        SendLoadInformation((*ncIt));
+        SendLoadInformation(*ncIt);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-ffr-enhanced-algorithm.cc ns-3.41/src/lte/model/lte-ffr-enhanced-algorithm.cc
--- ns-3.40/src/lte/model/lte-ffr-enhanced-algorithm.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-ffr-enhanced-algorithm.cc	2024-02-09 14:44:08.185792200 -0800
@@ -794,7 +794,7 @@
     if (itCqi == ulCqiMap.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -813,7 +813,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-harq-phy.cc ns-3.41/src/lte/model/lte-harq-phy.cc
--- ns-3.40/src/lte/model/lte-harq-phy.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-harq-phy.cc	2024-02-09 14:44:08.189792200 -0800
@@ -71,14 +71,14 @@
     {
         mi += list.at(i).m_mi;
     }
-    return (mi);
+    return mi;
 }
 
 HarqProcessInfoList_t
 LteHarqPhy::GetHarqProcessInfoDl(uint8_t harqProcId, uint8_t layer)
 {
     NS_LOG_FUNCTION(this << (uint32_t)harqProcId << (uint16_t)layer);
-    return (m_miDlHarqProcessesInfoMap.at(layer).at(harqProcId));
+    return m_miDlHarqProcessesInfoMap.at(layer).at(harqProcId);
 }
 
 double
@@ -94,7 +94,7 @@
     {
         mi += list.at(i).m_mi;
     }
-    return (mi);
+    return mi;
 }
 
 HarqProcessInfoList_t
@@ -109,11 +109,11 @@
         harqList.resize(8);
         m_miUlHarqProcessesInfoMap.insert(
             std::pair<uint16_t, std::vector<HarqProcessInfoList_t>>(rnti, harqList));
-        return (harqList.at(harqProcId));
+        return harqList.at(harqProcId);
     }
     else
     {
-        return ((*it).second.at(harqProcId));
+        return (*it).second.at(harqProcId);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-mi-error-model.cc ns-3.41/src/lte/model/lte-mi-error-model.cc
--- ns-3.40/src/lte/model/lte-mi-error-model.cc	2023-09-27 13:30:37.818839600 -0700
+++ ns-3.41/src/lte/model/lte-mi-error-model.cc	2024-02-09 14:44:08.189792200 -0800
@@ -1122,7 +1122,7 @@
         errorRate = PdcchPcfichBlerCurveYaxis[i];
     }
 
-    return (errorRate);
+    return errorRate;
 }
 
 TbStats_t
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-pdcp-header.cc ns-3.41/src/lte/model/lte-pdcp-header.cc
--- ns-3.40/src/lte/model/lte-pdcp-header.cc	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-pdcp-header.cc	2024-02-09 14:44:08.189792200 -0800
@@ -99,7 +99,7 @@
     Buffer::Iterator i = start;
 
     i.WriteU8((m_dcBit << 7) | (m_sequenceNumber & 0x0F00) >> 8);
-    i.WriteU8((m_sequenceNumber & 0x00FF));
+    i.WriteU8(m_sequenceNumber & 0x00FF);
 }
 
 uint32_t
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-phy.cc ns-3.41/src/lte/model/lte-phy.cc
--- ns-3.40/src/lte/model/lte-phy.cc	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-phy.cc	2024-02-09 14:44:08.189792200 -0800
@@ -196,13 +196,13 @@
         Ptr<PacketBurst> ret = m_packetBurstQueue.at(0)->Copy();
         m_packetBurstQueue.erase(m_packetBurstQueue.begin());
         m_packetBurstQueue.push_back(CreateObject<PacketBurst>());
-        return (ret);
+        return ret;
     }
     else
     {
         m_packetBurstQueue.erase(m_packetBurstQueue.begin());
         m_packetBurstQueue.push_back(CreateObject<PacketBurst>());
-        return (nullptr);
+        return nullptr;
     }
 }
 
@@ -224,7 +224,7 @@
         m_controlMessagesQueue.erase(m_controlMessagesQueue.begin());
         std::list<Ptr<LteControlMessage>> newlist;
         m_controlMessagesQueue.push_back(newlist);
-        return (ret);
+        return ret;
     }
     else
     {
@@ -232,7 +232,7 @@
         std::list<Ptr<LteControlMessage>> newlist;
         m_controlMessagesQueue.push_back(newlist);
         std::list<Ptr<LteControlMessage>> emptylist;
-        return (emptylist);
+        return emptylist;
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-radio-bearer-tag.h ns-3.41/src/lte/model/lte-radio-bearer-tag.h
--- ns-3.40/src/lte/model/lte-radio-bearer-tag.h	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-radio-bearer-tag.h	2024-02-09 14:44:08.189792200 -0800
@@ -27,7 +27,7 @@
 class Tag;
 
 /**
- * Tag used to define the RNTI and LC id for each MAC packet trasmitted
+ * Tag used to define the RNTI and LC id for each MAC packet transmitted
  */
 
 class LteRadioBearerTag : public Tag
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-rlc-am-header.cc ns-3.41/src/lte/model/lte-rlc-am-header.cc
--- ns-3.40/src/lte/model/lte-rlc-am-header.cc	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-rlc-am-header.cc	2024-02-09 14:44:08.189792200 -0800
@@ -404,7 +404,7 @@
             else
             {
                 i.WriteU8(((oddE << 7) & 0x80) | ((oddLi >> 4) & 0x007F));
-                i.WriteU8(((oddLi << 4) & 0x00F0)); // Padding is implicit
+                i.WriteU8((oddLi << 4) & 0x00F0); // Padding is implicit
             }
         }
     }
@@ -420,7 +420,7 @@
             NS_LOG_LOGIC(this << " no NACKs");
             // If there are no NACKs then this line adds the rest of the ACK
             // along with 0x00, indicating an E1 value of 0 or no NACKs follow.
-            i.WriteU8(((m_ackSn.GetValue() << 2) & 0xFC));
+            i.WriteU8((m_ackSn.GetValue() << 2) & 0xFC);
         }
         else
         {
@@ -440,7 +440,7 @@
                 // either the setup to enter this loop or the previous loop would
                 // have written the highest order bit to the previous octet.
                 // Write the next set of bits (2 - 9) into the next octet
-                i.WriteU8(((oddNack >> 1) & 0xFF));
+                i.WriteU8((oddNack >> 1) & 0xFF);
 
                 // Next check to see if there is going to be another NACK after
                 // this
@@ -466,13 +466,13 @@
                     else
                     {
                         // No, there are no more NACKs
-                        i.WriteU8(((evenNack << 3) & 0xF8));
+                        i.WriteU8((evenNack << 3) & 0xF8);
                     }
                 }
                 else
                 {
                     // No, this is the last NACK so E1 will be 0
-                    i.WriteU8(((oddNack << 7) & 0x80));
+                    i.WriteU8((oddNack << 7) & 0x80);
                 }
             }
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-rlc-am-header.h ns-3.41/src/lte/model/lte-rlc-am-header.h
--- ns-3.40/src/lte/model/lte-rlc-am-header.h	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-rlc-am-header.h	2024-02-09 14:44:08.189792200 -0800
@@ -107,16 +107,11 @@
      */
     uint8_t GetFramingInfo() const;
 
-    /// FramingInfoFirstByte_t enumeration
-    enum FramingInfoFirstByte_t
+    /// FramingInfoByte_t enumeration
+    enum FramingInfoByte_t
     {
         FIRST_BYTE = 0x00,
-        NO_FIRST_BYTE = 0x02
-    };
-
-    /// FramingInfoLastByte_t enumeration
-    enum FramingInfoLastByte_t
-    {
+        NO_FIRST_BYTE = 0x02,
         LAST_BYTE = 0x00,
         NO_LAST_BYTE = 0x01
     };
@@ -246,7 +241,7 @@
     /**
      * Get ack sn function
      *
-     * \returns sequence numbr
+     * \returns sequence number
      */
     SequenceNumber10 GetAckSn() const;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-rlc-header.cc ns-3.41/src/lte/model/lte-rlc-header.cc
--- ns-3.40/src/lte/model/lte-rlc-header.cc	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-rlc-header.cc	2024-02-09 14:44:08.189792200 -0800
@@ -204,7 +204,7 @@
         else
         {
             i.WriteU8(((oddE << 7) & 0x80) | ((oddLi >> 4) & 0x007F));
-            i.WriteU8(((oddLi << 4) & 0x00F0)); // Padding is implicit
+            i.WriteU8((oddLi << 4) & 0x00F0); // Padding is implicit
         }
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-rlc-header.h ns-3.41/src/lte/model/lte-rlc-header.h
--- ns-3.40/src/lte/model/lte-rlc-header.h	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-rlc-header.h	2024-02-09 14:44:08.281792900 -0800
@@ -107,16 +107,11 @@
         E_LI_FIELDS_FOLLOWS = 1
     };
 
-    /// FramingInfoFirstByte_t typedef
-    enum FramingInfoFirstByte_t
+    /// FramingInfoByte_t typedef
+    enum FramingInfoByte_t
     {
         FIRST_BYTE = 0x00,
-        NO_FIRST_BYTE = 0x02
-    };
-
-    /// FramingInfoLastByte_t tyedef
-    enum FramingInfoLastByte_t
-    {
+        NO_FIRST_BYTE = 0x02,
         LAST_BYTE = 0x00,
         NO_LAST_BYTE = 0x01
     };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-rlc-sequence-number.h ns-3.41/src/lte/model/lte-rlc-sequence-number.h
--- ns-3.40/src/lte/model/lte-rlc-sequence-number.h	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-rlc-sequence-number.h	2024-02-09 14:44:08.281792900 -0800
@@ -144,7 +144,7 @@
     uint16_t operator-(const SequenceNumber10& other) const
     {
         uint16_t diff = m_value - other.m_value;
-        return (diff);
+        return diff;
     }
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-ue-phy.cc ns-3.41/src/lte/model/lte-ue-phy.cc
--- ns-3.40/src/lte/model/lte-ue-phy.cc	2023-09-27 13:30:37.822839500 -0700
+++ ns-3.41/src/lte/model/lte-ue-phy.cc	2024-02-09 14:44:08.325793000 -0800
@@ -375,7 +375,7 @@
 LteUePhy::GetLteUePhySapProvider()
 {
     NS_LOG_FUNCTION(this);
-    return (m_uePhySapProvider);
+    return m_uePhySapProvider;
 }
 
 void
@@ -389,7 +389,7 @@
 LteUePhy::GetLteUeCphySapProvider()
 {
     NS_LOG_FUNCTION(this);
-    return (m_ueCphySapProvider);
+    return m_ueCphySapProvider;
 }
 
 void
@@ -431,7 +431,7 @@
 uint8_t
 LteUePhy::GetMacChDelay() const
 {
-    return (m_macChTtiDelay);
+    return m_macChTtiDelay;
 }
 
 Ptr<LteSpectrumPhy>
@@ -936,7 +936,7 @@
     NS_LOG_FUNCTION(this << Simulator::Now());
     NS_LOG_DEBUG(this << " Report UE Measurements ");
 
-    LteUeCphySapUser::UeMeasurementsParameters ret;
+    LteUeCphySapUser::UeMeasurementsParameters ret{};
 
     for (auto it = m_ueMeasurementsMap.begin(); it != m_ueMeasurementsMap.end(); it++)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-ue-rrc.h ns-3.41/src/lte/model/lte-ue-rrc.h
--- ns-3.40/src/lte/model/lte-ue-rrc.h	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/lte-ue-rrc.h	2024-02-09 14:44:08.325793000 -0800
@@ -136,7 +136,7 @@
      */
     static TypeId GetTypeId();
 
-    /// Initiaize SAP
+    /// Initialize SAP
     void InitializeSap();
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/lte-vendor-specific-parameters.cc ns-3.41/src/lte/model/lte-vendor-specific-parameters.cc
--- ns-3.40/src/lte/model/lte-vendor-specific-parameters.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/lte-vendor-specific-parameters.cc	2024-02-09 14:44:08.325793000 -0800
@@ -38,7 +38,7 @@
 uint16_t
 SrsCqiRntiVsp::GetRnti() const
 {
-    return (m_rnti);
+    return m_rnti;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/pf-ff-mac-scheduler.cc ns-3.41/src/lte/model/pf-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/pf-ff-mac-scheduler.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/pf-ff-mac-scheduler.cc	2024-02-09 14:44:08.325793000 -0800
@@ -164,36 +164,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -216,15 +210,15 @@
             pfsFlowPerf_t flowStatsDl;
             flowStatsDl.flowStart = Simulator::Now();
             flowStatsDl.totalBytesTransmitted = 0;
-            flowStatsDl.lastTtiBytesTrasmitted = 0;
+            flowStatsDl.lastTtiBytesTransmitted = 0;
             flowStatsDl.lastAveragedThroughput = 1;
-            m_flowStatsDl.insert(std::pair<uint16_t, pfsFlowPerf_t>(params.m_rnti, flowStatsDl));
+            m_flowStatsDl[params.m_rnti] = flowStatsDl;
             pfsFlowPerf_t flowStatsUl;
             flowStatsUl.flowStart = Simulator::Now();
             flowStatsUl.totalBytesTransmitted = 0;
-            flowStatsUl.lastTtiBytesTrasmitted = 0;
+            flowStatsUl.lastTtiBytesTransmitted = 0;
             flowStatsUl.lastAveragedThroughput = 1;
-            m_flowStatsUl.insert(std::pair<uint16_t, pfsFlowPerf_t>(params.m_rnti, flowStatsUl));
+            m_flowStatsUl[params.m_rnti] = flowStatsUl;
         }
     }
 }
@@ -305,9 +299,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -338,11 +330,11 @@
     {
         if (dlbandwidth < PfType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -362,7 +354,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -396,7 +388,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -425,7 +417,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -1025,8 +1017,7 @@
                     // insert new element
                     std::vector<uint16_t> tempMap;
                     tempMap.push_back(i);
-                    allocationMap.insert(
-                        std::pair<uint16_t, std::vector<uint16_t>>((*itMax).first, tempMap));
+                    allocationMap[(*itMax).first] = tempMap;
                 }
                 else
                 {
@@ -1040,7 +1031,7 @@
     // reset TTI stats of users
     for (auto itStats = m_flowStatsDl.begin(); itStats != m_flowStatsDl.end(); itStats++)
     {
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
 
     // generate the transmission opportunities by grouping the RBGs of the same RNTI and
@@ -1224,8 +1215,8 @@
         auto it = m_flowStatsDl.find((*itMap).first);
         if (it != m_flowStatsDl.end())
         {
-            (*it).second.lastTtiBytesTrasmitted = bytesTxed;
-            NS_LOG_INFO(this << " UE total bytes txed " << (*it).second.lastTtiBytesTrasmitted);
+            (*it).second.lastTtiBytesTransmitted = bytesTxed;
+            NS_LOG_INFO(this << " UE total bytes txed " << (*it).second.lastTtiBytesTransmitted);
         }
         else
         {
@@ -1240,15 +1231,15 @@
     NS_LOG_INFO(this << " Update UEs statistics");
     for (auto itStats = m_flowStatsDl.begin(); itStats != m_flowStatsDl.end(); itStats++)
     {
-        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTrasmitted;
+        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted;
         // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term
         // Evolution, Ed Wiley)
         (*itStats).second.lastAveragedThroughput =
             ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) +
-            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTrasmitted / 0.001));
+            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001));
         NS_LOG_INFO(this << " UE total bytes " << (*itStats).second.totalBytesTransmitted);
         NS_LOG_INFO(this << " UE average throughput " << (*itStats).second.lastAveragedThroughput);
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
 
     m_schedSapUser->SchedDlConfigInd(ret);
@@ -1281,11 +1272,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1304,10 +1294,8 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
-                m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
+                m_a30CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1331,7 +1319,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1350,7 +1338,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1496,8 +1484,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1626,8 +1613,7 @@
             //            {
             //              m_schedSapUser->SchedUlConfigInd (ret);
             //            }
-            //          m_allocationMaps.insert (std::pair <uint16_t, std::vector <uint16_t> >
-            //          (params.m_sfnSf, rbgAllocationMap)); return;
+            //          m_allocationMaps[params.m_sfnSf] = rbgAllocationMap; return;
             break;
         }
 
@@ -1735,7 +1721,7 @@
         auto itStats = m_flowStatsUl.find((*it).first);
         if (itStats != m_flowStatsUl.end())
         {
-            (*itStats).second.lastTtiBytesTrasmitted = uldci.m_tbSize;
+            (*itStats).second.lastTtiBytesTransmitted = uldci.m_tbSize;
         }
         else
         {
@@ -1760,18 +1746,17 @@
     // update UEs stats
     for (auto itStats = m_flowStatsUl.begin(); itStats != m_flowStatsUl.end(); itStats++)
     {
-        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTrasmitted;
+        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted;
         // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term
         // Evolution, Ed Wiley)
         (*itStats).second.lastAveragedThroughput =
             ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) +
-            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTrasmitted / 0.001));
+            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001));
         NS_LOG_INFO(this << " UE total bytes " << (*itStats).second.totalBytesTransmitted);
         NS_LOG_INFO(this << " UE average throughput " << (*itStats).second.lastAveragedThroughput);
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -1819,7 +1804,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
             }
             else
             {
@@ -1892,11 +1877,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1940,9 +1923,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/pf-ff-mac-scheduler.h ns-3.41/src/lte/model/pf-ff-mac-scheduler.h
--- ns-3.40/src/lte/model/pf-ff-mac-scheduler.h	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/pf-ff-mac-scheduler.h	2024-02-09 14:44:08.329793200 -0800
@@ -38,10 +38,10 @@
 /// pfsFlowPerf_t structure
 struct pfsFlowPerf_t
 {
-    Time flowStart;                      ///< flow start time
-    unsigned long totalBytesTransmitted; ///< total bytes transmitted
-    unsigned int lastTtiBytesTrasmitted; ///< last total bytes transmitted
-    double lastAveragedThroughput;       ///< last averaged throughput
+    Time flowStart;                       ///< flow start time
+    unsigned long totalBytesTransmitted;  ///< total bytes transmitted
+    unsigned int lastTtiBytesTransmitted; ///< last total bytes transmitted
+    double lastAveragedThroughput;        ///< last averaged throughput
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/pss-ff-mac-scheduler.cc ns-3.41/src/lte/model/pss-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/pss-ff-mac-scheduler.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/pss-ff-mac-scheduler.cc	2024-02-09 14:44:08.329793200 -0800
@@ -177,36 +177,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -238,7 +232,7 @@
             flowStatsDl.lastAveragedThroughput = 1;
             flowStatsDl.secondLastAveragedThroughput = 1;
             flowStatsDl.targetThroughput = tbrDlInBytes;
-            m_flowStatsDl.insert(std::pair<uint16_t, pssFlowPerf_t>(params.m_rnti, flowStatsDl));
+            m_flowStatsDl[params.m_rnti] = flowStatsDl;
             pssFlowPerf_t flowStatsUl;
             flowStatsUl.flowStart = Simulator::Now();
             flowStatsUl.totalBytesTransmitted = 0;
@@ -246,7 +240,7 @@
             flowStatsUl.lastAveragedThroughput = 1;
             flowStatsUl.secondLastAveragedThroughput = 1;
             flowStatsUl.targetThroughput = tbrUlInBytes;
-            m_flowStatsUl.insert(std::pair<uint16_t, pssFlowPerf_t>(params.m_rnti, flowStatsUl));
+            m_flowStatsUl[params.m_rnti] = flowStatsUl;
         }
         else
         {
@@ -337,9 +331,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -370,11 +362,11 @@
     {
         if (dlbandwidth < PssType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -394,7 +386,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -428,7 +420,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -457,7 +449,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -741,7 +733,7 @@
                     NS_LOG_ERROR("No info find in HARQ buffer for UE (might change eNB) "
                                  << m_dlInfoListBuffered.at(i).m_rnti);
                 }
-                (*it).second.at(harqId) = 0;
+                it->second.at(harqId) = 0;
                 auto itRlcPdu = m_dlHarqProcessesRlcPduListBuffer.find(rnti);
                 if (itRlcPdu == m_dlHarqProcessesRlcPduListBuffer.end())
                 {
@@ -960,7 +952,7 @@
     {
         if (LcActivePerFlow((*it).first) > 0)
         {
-            ueSet.insert(std::pair<uint16_t, pssFlowPerf_t>((*it).first, (*it).second));
+            ueSet[(*it).first] = (*it).second;
         }
     }
 
@@ -1089,14 +1081,14 @@
             for (auto itSet = ueSet1.begin(); itSet != ueSet1.end() && nMux != 0; itSet++)
             {
                 auto itUe = m_flowStatsDl.find((*itSet).second);
-                tdUeSet.insert(std::pair<uint16_t, pssFlowPerf_t>((*itUe).first, (*itUe).second));
+                tdUeSet[(*itUe).first] = (*itUe).second;
                 nMux--;
             }
 
             for (auto itSet = ueSet2.begin(); itSet != ueSet2.end() && nMux != 0; itSet++)
             {
                 auto itUe = m_flowStatsDl.find((*itSet).second);
-                tdUeSet.insert(std::pair<uint16_t, pssFlowPerf_t>((*itUe).first, (*itUe).second));
+                tdUeSet[(*itUe).first] = (*itUe).second;
                 nMux--;
             }
 
@@ -1154,7 +1146,7 @@
                         } // end if cqi
                     }     // end of rbgNum
 
-                    sbCqiSum.insert(std::pair<uint16_t, uint8_t>((*it).first, sum));
+                    sbCqiSum[(*it).first] = sum;
                 } // end tdUeSet
 
                 for (int i = 0; i < rbgNum; i++)
@@ -1611,11 +1603,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1634,10 +1625,8 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
-                m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
+                m_a30CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1661,7 +1650,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1680,7 +1669,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1826,8 +1815,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1956,8 +1944,7 @@
             //            {
             //              m_schedSapUser->SchedUlConfigInd (ret);
             //            }
-            //          m_allocationMaps.insert (std::pair <uint16_t, std::vector <uint16_t> >
-            //          (params.m_sfnSf, rbgAllocationMap)); return;
+            //          m_allocationMaps[params.m_sfnSf] = rbgAllocationMap; return;
             break;
         }
 
@@ -2075,8 +2062,7 @@
         }
     } while (((*it).first != m_nextRntiUl) && (rbPerFlow != 0));
 
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -2124,7 +2110,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
             }
             else
             {
@@ -2194,11 +2180,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -2240,9 +2224,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/rr-ff-mac-scheduler.cc ns-3.41/src/lte/model/rr-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/rr-ff-mac-scheduler.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/rr-ff-mac-scheduler.cc	2024-02-09 14:44:08.329793200 -0800
@@ -163,36 +163,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -304,10 +298,9 @@
     // initialize statistics of the flow in case of new flows
     if (newLc)
     {
-        m_p10CqiRxed.insert(
-            std::pair<uint16_t, uint8_t>(params.m_rnti, 1)); // only codeword 0 at this stage (SISO)
+        m_p10CqiRxed[params.m_rnti] = 1; // only codeword 0 at this stage (SISO)
         // initialized to 1 (i.e., the lowest value for transmitting a signal)
-        m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(params.m_rnti, m_cqiTimersThreshold));
+        m_p10CqiTimers[params.m_rnti] = m_cqiTimersThreshold;
     }
 }
 
@@ -334,18 +327,18 @@
     {
         if (dlbandwidth < Type0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 bool
 RrFfMacScheduler::SortRlcBufferReq(FfMacSchedSapProvider::SchedDlRlcBufferReqParameters i,
                                    FfMacSchedSapProvider::SchedDlRlcBufferReqParameters j)
 {
-    return (i.m_rnti < j.m_rnti);
+    return i.m_rnti < j.m_rnti;
 }
 
 bool
@@ -379,7 +372,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -404,10 +397,10 @@
     }
     else
     {
-        return (9); // return a not valid harq proc id
+        return 9; // return a not valid harq proc id
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -893,7 +886,7 @@
                 }
                 else
                 {
-                    lcActivesPerRnti.insert(std::pair<uint16_t, uint8_t>((*it).m_rnti, 1));
+                    lcActivesPerRnti[(*it).m_rnti] = 1;
                     nTbs++;
                 }
             }
@@ -1134,11 +1127,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1282,8 +1274,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
         return; // no flows to be scheduled
@@ -1400,8 +1391,7 @@
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             return;
         }
         auto itCqi = m_ueCqi.find((*it).first);
@@ -1510,8 +1500,7 @@
         }
     } while (((*it).first != m_nextRntiUl) && (rbPerFlow != 0));
 
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
 
     m_schedSapUser->SchedUlConfigInd(ret);
 }
@@ -1559,7 +1548,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
                 NS_LOG_INFO(this << " Insert RNTI " << rnti << " queue " << buffer);
             }
             else
@@ -1630,11 +1619,9 @@
                         newCqi.push_back(30.0);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1674,9 +1661,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/simple-ue-component-carrier-manager.cc ns-3.41/src/lte/model/simple-ue-component-carrier-manager.cc
--- ns-3.40/src/lte/model/simple-ue-component-carrier-manager.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/simple-ue-component-carrier-manager.cc	2024-02-09 14:44:08.329793200 -0800
@@ -229,7 +229,7 @@
     NS_ABORT_MSG_IF(lcidIt == m_lcAttached.end(),
                     "could not find LCID" << (uint16_t)txOpParams.lcid);
     NS_LOG_DEBUG(this << " lcid = " << (uint32_t)txOpParams.lcid
-                      << " layer= " << (uint16_t)txOpParams.layer << " componentCarierId "
+                      << " layer= " << (uint16_t)txOpParams.layer << " componentCarrierId "
                       << (uint16_t)txOpParams.componentCarrierId << " rnti " << txOpParams.rnti);
 
     NS_LOG_DEBUG(this << " MAC is asking component carrier id = "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/tdbet-ff-mac-scheduler.cc ns-3.41/src/lte/model/tdbet-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/tdbet-ff-mac-scheduler.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/tdbet-ff-mac-scheduler.cc	2024-02-09 14:44:08.329793200 -0800
@@ -162,36 +162,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -214,15 +208,15 @@
             tdbetsFlowPerf_t flowStatsDl;
             flowStatsDl.flowStart = Simulator::Now();
             flowStatsDl.totalBytesTransmitted = 0;
-            flowStatsDl.lastTtiBytesTrasmitted = 0;
+            flowStatsDl.lastTtiBytesTransmitted = 0;
             flowStatsDl.lastAveragedThroughput = 1;
-            m_flowStatsDl.insert(std::pair<uint16_t, tdbetsFlowPerf_t>(params.m_rnti, flowStatsDl));
+            m_flowStatsDl[params.m_rnti] = flowStatsDl;
             tdbetsFlowPerf_t flowStatsUl;
             flowStatsUl.flowStart = Simulator::Now();
             flowStatsUl.totalBytesTransmitted = 0;
-            flowStatsUl.lastTtiBytesTrasmitted = 0;
+            flowStatsUl.lastTtiBytesTransmitted = 0;
             flowStatsUl.lastAveragedThroughput = 1;
-            m_flowStatsUl.insert(std::pair<uint16_t, tdbetsFlowPerf_t>(params.m_rnti, flowStatsUl));
+            m_flowStatsUl[params.m_rnti] = flowStatsUl;
         }
     }
 }
@@ -303,9 +297,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -336,11 +328,11 @@
     {
         if (dlbandwidth < TdBetType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -360,7 +352,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -394,7 +386,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -423,7 +415,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -939,13 +931,13 @@
         {
             tempMap.push_back(i);
         }
-        allocationMap.insert(std::pair<uint16_t, std::vector<uint16_t>>((*itMax).first, tempMap));
+        allocationMap[(*itMax).first] = tempMap;
     }
 
     // reset TTI stats of users
     for (auto itStats = m_flowStatsDl.begin(); itStats != m_flowStatsDl.end(); itStats++)
     {
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
 
     // generate the transmission opportunities by grouping the RBGs of the same RNTI and
@@ -1080,8 +1072,8 @@
         auto it = m_flowStatsDl.find((*itMap).first);
         if (it != m_flowStatsDl.end())
         {
-            (*it).second.lastTtiBytesTrasmitted = bytesTxed;
-            NS_LOG_INFO(this << " UE total bytes txed " << (*it).second.lastTtiBytesTrasmitted);
+            (*it).second.lastTtiBytesTransmitted = bytesTxed;
+            NS_LOG_INFO(this << " UE total bytes txed " << (*it).second.lastTtiBytesTransmitted);
         }
         else
         {
@@ -1096,15 +1088,15 @@
     NS_LOG_INFO(this << " Update UEs statistics");
     for (auto itStats = m_flowStatsDl.begin(); itStats != m_flowStatsDl.end(); itStats++)
     {
-        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTrasmitted;
+        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted;
         // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term
         // Evolution, Ed Wiley)
         (*itStats).second.lastAveragedThroughput =
             ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) +
-            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTrasmitted / 0.001));
+            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001));
         NS_LOG_INFO(this << " UE total bytes " << (*itStats).second.totalBytesTransmitted);
         NS_LOG_INFO(this << " UE average throughput " << (*itStats).second.lastAveragedThroughput);
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
 
     m_schedSapUser->SchedDlConfigInd(ret);
@@ -1136,11 +1128,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1159,10 +1150,8 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
-                m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
+                m_a30CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1186,7 +1175,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1205,7 +1194,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1334,8 +1323,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1453,8 +1441,7 @@
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             return;
         }
 
@@ -1562,7 +1549,7 @@
         auto itStats = m_flowStatsUl.find((*it).first);
         if (itStats != m_flowStatsUl.end())
         {
-            (*itStats).second.lastTtiBytesTrasmitted = uldci.m_tbSize;
+            (*itStats).second.lastTtiBytesTransmitted = uldci.m_tbSize;
         }
         else
         {
@@ -1587,18 +1574,17 @@
     // update UEs stats
     for (auto itStats = m_flowStatsUl.begin(); itStats != m_flowStatsUl.end(); itStats++)
     {
-        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTrasmitted;
+        (*itStats).second.totalBytesTransmitted += (*itStats).second.lastTtiBytesTransmitted;
         // update average throughput (see eq. 12.3 of Sec 12.3.1.2 of LTE – The UMTS Long Term
         // Evolution, Ed Wiley)
         (*itStats).second.lastAveragedThroughput =
             ((1.0 - (1.0 / m_timeWindow)) * (*itStats).second.lastAveragedThroughput) +
-            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTrasmitted / 0.001));
+            ((1.0 / m_timeWindow) * (double)((*itStats).second.lastTtiBytesTransmitted / 0.001));
         NS_LOG_INFO(this << " UE total bytes " << (*itStats).second.totalBytesTransmitted);
         NS_LOG_INFO(this << " UE average throughput " << (*itStats).second.lastAveragedThroughput);
-        (*itStats).second.lastTtiBytesTrasmitted = 0;
+        (*itStats).second.lastTtiBytesTransmitted = 0;
     }
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -1646,7 +1632,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
             }
             else
             {
@@ -1716,11 +1702,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1762,9 +1746,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/tdbet-ff-mac-scheduler.h ns-3.41/src/lte/model/tdbet-ff-mac-scheduler.h
--- ns-3.40/src/lte/model/tdbet-ff-mac-scheduler.h	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/tdbet-ff-mac-scheduler.h	2024-02-09 14:44:08.329793200 -0800
@@ -39,10 +39,10 @@
 /// tdbetsFlowPerf_t structure
 struct tdbetsFlowPerf_t
 {
-    Time flowStart;                      ///< flow start time
-    unsigned long totalBytesTransmitted; ///< total bytes transmitted
-    unsigned int lastTtiBytesTrasmitted; ///< last total bytes transmitted
-    double lastAveragedThroughput;       ///< last average throughput
+    Time flowStart;                       ///< flow start time
+    unsigned long totalBytesTransmitted;  ///< total bytes transmitted
+    unsigned int lastTtiBytesTransmitted; ///< last total bytes transmitted
+    double lastAveragedThroughput;        ///< last average throughput
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/tdmt-ff-mac-scheduler.cc ns-3.41/src/lte/model/tdmt-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/tdmt-ff-mac-scheduler.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/tdmt-ff-mac-scheduler.cc	2024-02-09 14:44:08.329793200 -0800
@@ -161,36 +161,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -292,9 +286,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -325,11 +317,11 @@
     {
         if (dlbandwidth < TdMtType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -349,7 +341,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -383,7 +375,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -412,7 +404,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -863,15 +855,15 @@
     double metricMax = 0.0;
     for (auto it = m_flowStatsDl.begin(); it != m_flowStatsDl.end(); it++)
     {
-        auto itRnti = rntiAllocated.find((*it));
-        if ((itRnti != rntiAllocated.end()) || (!HarqProcessAvailability((*it))))
+        auto itRnti = rntiAllocated.find(*it);
+        if (itRnti != rntiAllocated.end() || !HarqProcessAvailability(*it))
         {
             // UE already allocated for HARQ or without HARQ process available -> drop it
             if (itRnti != rntiAllocated.end())
             {
                 NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it));
             }
-            if (!HarqProcessAvailability((*it)))
+            if (!HarqProcessAvailability(*it))
             {
                 NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it));
             }
@@ -879,13 +871,13 @@
             continue;
         }
 
-        auto itTxMode = m_uesTxMode.find((*it));
+        auto itTxMode = m_uesTxMode.find(*it);
         if (itTxMode == m_uesTxMode.end())
         {
             NS_FATAL_ERROR("No Transmission Mode info on user " << (*it));
         }
         auto nLayer = TransmissionModesLayers::TxMode2LayerNum((*itTxMode).second);
-        auto itCqi = m_p10CqiRxed.find((*it));
+        auto itCqi = m_p10CqiRxed.find(*it);
         uint8_t wbCqi = 0;
         if (itCqi != m_p10CqiRxed.end())
         {
@@ -949,7 +941,7 @@
         } // end for RBGs
         if (!tempMap.empty())
         {
-            allocationMap.insert(std::pair<uint16_t, std::vector<uint16_t>>((*itMax), tempMap));
+            allocationMap[*itMax] = tempMap;
         }
     }
 
@@ -1112,11 +1104,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1135,10 +1126,8 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
-                m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
+                m_a30CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1162,7 +1151,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1181,7 +1170,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1310,8 +1299,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1429,8 +1417,7 @@
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             return;
         }
 
@@ -1548,8 +1535,7 @@
         }
     } while (((*it).first != m_nextRntiUl) && (rbPerFlow != 0));
 
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -1597,7 +1583,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
             }
             else
             {
@@ -1667,11 +1653,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1713,9 +1697,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/tdtbfq-ff-mac-scheduler.cc ns-3.41/src/lte/model/tdtbfq-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/tdtbfq-ff-mac-scheduler.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/tdtbfq-ff-mac-scheduler.cc	2024-02-09 14:44:08.329793200 -0800
@@ -187,36 +187,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -251,8 +245,7 @@
             flowStatsDl.burstCredit = m_creditLimit; // bytes
             flowStatsDl.debtLimit = m_debtLimit;     // bytes
             flowStatsDl.creditableThreshold = m_creditableThreshold;
-            m_flowStatsDl.insert(
-                std::pair<uint16_t, tdtbfqsFlowPerf_t>(params.m_rnti, flowStatsDl));
+            m_flowStatsDl[params.m_rnti] = flowStatsDl;
             tdtbfqsFlowPerf_t flowStatsUl;
             flowStatsUl.flowStart = Simulator::Now();
             flowStatsUl.packetArrivalRate = 0;
@@ -263,8 +256,7 @@
             flowStatsUl.burstCredit = m_creditLimit; // bytes
             flowStatsUl.debtLimit = m_debtLimit;     // bytes
             flowStatsUl.creditableThreshold = m_creditableThreshold;
-            m_flowStatsUl.insert(
-                std::pair<uint16_t, tdtbfqsFlowPerf_t>(params.m_rnti, flowStatsUl));
+            m_flowStatsUl[params.m_rnti] = flowStatsUl;
         }
         else
         {
@@ -355,9 +347,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -388,11 +378,11 @@
     {
         if (dlbandwidth < TdTbfqType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -412,7 +402,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -446,7 +436,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -475,7 +465,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -1092,8 +1082,7 @@
         }
         if (!tempMap.empty())
         {
-            allocationMap.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>((*itMax).first, tempMap));
+            allocationMap[(*itMax).first] = tempMap;
         }
     }
 
@@ -1317,11 +1306,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1340,10 +1328,8 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
-                m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
+                m_a30CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1367,7 +1353,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1386,7 +1372,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1532,8 +1518,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1662,8 +1647,7 @@
             //            {
             //              m_schedSapUser->SchedUlConfigInd (ret);
             //            }
-            //          m_allocationMaps.insert (std::pair <uint16_t, std::vector <uint16_t> >
-            //          (params.m_sfnSf, rbgAllocationMap)); return;
+            //          m_allocationMaps[params.m_sfnSf] = rbgAllocationMap; return;
             break;
         }
 
@@ -1781,8 +1765,7 @@
         }
     } while (((*it).first != m_nextRntiUl) && (rbPerFlow != 0));
 
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -1830,7 +1813,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
             }
             else
             {
@@ -1900,11 +1883,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1946,9 +1927,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/model/tta-ff-mac-scheduler.cc ns-3.41/src/lte/model/tta-ff-mac-scheduler.cc
--- ns-3.40/src/lte/model/tta-ff-mac-scheduler.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/model/tta-ff-mac-scheduler.cc	2024-02-09 14:44:08.329793200 -0800
@@ -161,36 +161,30 @@
     auto it = m_uesTxMode.find(params.m_rnti);
     if (it == m_uesTxMode.end())
     {
-        m_uesTxMode.insert(std::pair<uint16_t, double>(params.m_rnti, params.m_transmissionMode));
+        m_uesTxMode[params.m_rnti] = params.m_transmissionMode;
         // generate HARQ buffers
-        m_dlHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqCurrentProcessId[params.m_rnti] = 0;
         DlHarqProcessesStatus_t dlHarqPrcStatus;
         dlHarqPrcStatus.resize(8, 0);
-        m_dlHarqProcessesStatus.insert(
-            std::pair<uint16_t, DlHarqProcessesStatus_t>(params.m_rnti, dlHarqPrcStatus));
+        m_dlHarqProcessesStatus[params.m_rnti] = dlHarqPrcStatus;
         DlHarqProcessesTimer_t dlHarqProcessesTimer;
         dlHarqProcessesTimer.resize(8, 0);
-        m_dlHarqProcessesTimer.insert(
-            std::pair<uint16_t, DlHarqProcessesTimer_t>(params.m_rnti, dlHarqProcessesTimer));
+        m_dlHarqProcessesTimer[params.m_rnti] = dlHarqProcessesTimer;
         DlHarqProcessesDciBuffer_t dlHarqdci;
         dlHarqdci.resize(8);
-        m_dlHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, DlHarqProcessesDciBuffer_t>(params.m_rnti, dlHarqdci));
+        m_dlHarqProcessesDciBuffer[params.m_rnti] = dlHarqdci;
         DlHarqRlcPduListBuffer_t dlHarqRlcPdu;
         dlHarqRlcPdu.resize(2);
         dlHarqRlcPdu.at(0).resize(8);
         dlHarqRlcPdu.at(1).resize(8);
-        m_dlHarqProcessesRlcPduListBuffer.insert(
-            std::pair<uint16_t, DlHarqRlcPduListBuffer_t>(params.m_rnti, dlHarqRlcPdu));
-        m_ulHarqCurrentProcessId.insert(std::pair<uint16_t, uint8_t>(params.m_rnti, 0));
+        m_dlHarqProcessesRlcPduListBuffer[params.m_rnti] = dlHarqRlcPdu;
+        m_ulHarqCurrentProcessId[params.m_rnti] = 0;
         UlHarqProcessesStatus_t ulHarqPrcStatus;
         ulHarqPrcStatus.resize(8, 0);
-        m_ulHarqProcessesStatus.insert(
-            std::pair<uint16_t, UlHarqProcessesStatus_t>(params.m_rnti, ulHarqPrcStatus));
+        m_ulHarqProcessesStatus[params.m_rnti] = ulHarqPrcStatus;
         UlHarqProcessesDciBuffer_t ulHarqdci;
         ulHarqdci.resize(8);
-        m_ulHarqProcessesDciBuffer.insert(
-            std::pair<uint16_t, UlHarqProcessesDciBuffer_t>(params.m_rnti, ulHarqdci));
+        m_ulHarqProcessesDciBuffer[params.m_rnti] = ulHarqdci;
     }
     else
     {
@@ -292,9 +286,7 @@
 
     if (it == m_rlcBufferReq.end())
     {
-        m_rlcBufferReq.insert(
-            std::pair<LteFlowId_t, FfMacSchedSapProvider::SchedDlRlcBufferReqParameters>(flow,
-                                                                                         params));
+        m_rlcBufferReq[flow] = params;
     }
     else
     {
@@ -325,11 +317,11 @@
     {
         if (dlbandwidth < TtaType0AllocationRbg[i])
         {
-            return (i + 1);
+            return i + 1;
         }
     }
 
-    return (-1);
+    return -1;
 }
 
 unsigned int
@@ -349,7 +341,7 @@
             break;
         }
     }
-    return (lcActive);
+    return lcActive;
 }
 
 bool
@@ -383,7 +375,7 @@
 
     if (!m_harqOn)
     {
-        return (0);
+        return 0;
     }
 
     auto it = m_dlHarqCurrentProcessId.find(rnti);
@@ -412,7 +404,7 @@
                        << rnti << " check before update with HarqProcessAvailability");
     }
 
-    return ((*it).second);
+    return (*it).second;
 }
 
 void
@@ -868,25 +860,25 @@
             double rcqiMax = 0.0;
             for (auto it = m_flowStatsDl.begin(); it != m_flowStatsDl.end(); it++)
             {
-                auto itRnti = rntiAllocated.find((*it));
-                if ((itRnti != rntiAllocated.end()) || (!HarqProcessAvailability((*it))))
+                auto itRnti = rntiAllocated.find(*it);
+                if (itRnti != rntiAllocated.end() || !HarqProcessAvailability(*it))
                 {
                     // UE already allocated for HARQ or without HARQ process available -> drop it
                     if (itRnti != rntiAllocated.end())
                     {
                         NS_LOG_DEBUG(this << " RNTI discarded for HARQ tx" << (uint16_t)(*it));
                     }
-                    if (!HarqProcessAvailability((*it)))
+                    if (!HarqProcessAvailability(*it))
                     {
                         NS_LOG_DEBUG(this << " RNTI discarded for HARQ id" << (uint16_t)(*it));
                     }
                     continue;
                 }
 
-                auto itSbCqi = m_a30CqiRxed.find((*it));
-                auto itWbCqi = m_p10CqiRxed.find((*it));
+                auto itSbCqi = m_a30CqiRxed.find(*it);
+                auto itWbCqi = m_p10CqiRxed.find(*it);
 
-                auto itTxMode = m_uesTxMode.find((*it));
+                auto itTxMode = m_uesTxMode.find(*it);
                 if (itTxMode == m_uesTxMode.end())
                 {
                     NS_FATAL_ERROR("No Transmission Mode info on user " << (*it));
@@ -922,7 +914,7 @@
                 if ((cqi1 > 0) ||
                     (cqi2 > 0)) // CQI == 0 means "out of range" (see table 7.2.3-1 of 36.213)
                 {
-                    if (LcActivePerFlow((*it)) > 0)
+                    if (LcActivePerFlow(*it) > 0)
                     {
                         // this UE has data to transmit
                         double achievableSbRate = 0.0;
@@ -967,14 +959,13 @@
             else
             {
                 rbgMap.at(i) = true;
-                auto itMap = allocationMap.find((*itMax));
+                auto itMap = allocationMap.find(*itMax);
                 if (itMap == allocationMap.end())
                 {
                     // insert new element
                     std::vector<uint16_t> tempMap;
                     tempMap.push_back(i);
-                    allocationMap.insert(
-                        std::pair<uint16_t, std::vector<uint16_t>>((*itMax), tempMap));
+                    allocationMap[(*itMax)] = tempMap;
                 }
                 else
                 {
@@ -1194,11 +1185,10 @@
             if (it == m_p10CqiRxed.end())
             {
                 // create the new entry
-                m_p10CqiRxed.insert(std::pair<uint16_t, uint8_t>(
-                    rnti,
-                    params.m_cqiList.at(i).m_wbCqi.at(0))); // only codeword 0 at this stage (SISO)
+                m_p10CqiRxed[rnti] =
+                    params.m_cqiList.at(i).m_wbCqi.at(0); // only codeword 0 at this stage (SISO)
                 // generate correspondent timer
-                m_p10CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_p10CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1217,10 +1207,8 @@
             if (it == m_a30CqiRxed.end())
             {
                 // create the new entry
-                m_a30CqiRxed.insert(
-                    std::pair<uint16_t, SbMeasResult_s>(rnti,
-                                                        params.m_cqiList.at(i).m_sbMeasResult));
-                m_a30CqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+                m_a30CqiRxed[rnti] = params.m_cqiList.at(i).m_sbMeasResult;
+                m_a30CqiTimers[rnti] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1244,7 +1232,7 @@
     if (itCqi == m_ueCqi.end())
     {
         // no cqi info about this UE
-        return (NO_SINR);
+        return NO_SINR;
     }
     else
     {
@@ -1263,7 +1251,7 @@
         double estimatedSinr = (sinrNum > 0) ? (sinrSum / sinrNum) : DBL_MAX;
         // store the value
         (*itCqi).second.at(rb) = estimatedSinr;
-        return (estimatedSinr);
+        return estimatedSinr;
     }
 }
 
@@ -1392,8 +1380,7 @@
     {
         if (!ret.m_dciList.empty())
         {
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             m_schedSapUser->SchedUlConfigInd(ret);
         }
 
@@ -1511,8 +1498,7 @@
             {
                 m_schedSapUser->SchedUlConfigInd(ret);
             }
-            m_allocationMaps.insert(
-                std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+            m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
             return;
         }
 
@@ -1630,8 +1616,7 @@
         }
     } while (((*it).first != m_nextRntiUl) && (rbPerFlow != 0));
 
-    m_allocationMaps.insert(
-        std::pair<uint16_t, std::vector<uint16_t>>(params.m_sfnSf, rbgAllocationMap));
+    m_allocationMaps[params.m_sfnSf] = rbgAllocationMap;
     m_schedSapUser->SchedUlConfigInd(ret);
 }
 
@@ -1679,7 +1664,7 @@
             if (it == m_ceBsrRxed.end())
             {
                 // create the new entry
-                m_ceBsrRxed.insert(std::pair<uint16_t, uint32_t>(rnti, buffer));
+                m_ceBsrRxed[rnti] = buffer;
             }
             else
             {
@@ -1749,11 +1734,9 @@
                         newCqi.push_back(NO_SINR);
                     }
                 }
-                m_ueCqi.insert(
-                    std::pair<uint16_t, std::vector<double>>((*itMap).second.at(i), newCqi));
+                m_ueCqi[(*itMap).second.at(i)] = newCqi;
                 // generate correspondent timer
-                m_ueCqiTimers.insert(
-                    std::pair<uint16_t, uint32_t>((*itMap).second.at(i), m_cqiTimersThreshold));
+                m_ueCqiTimers[(*itMap).second.at(i)] = m_cqiTimersThreshold;
             }
             else
             {
@@ -1795,9 +1778,9 @@
                 NS_LOG_INFO(this << " RNTI " << rnti << " new SRS-CQI for RB  " << j << " value "
                                  << sinr);
             }
-            m_ueCqi.insert(std::pair<uint16_t, std::vector<double>>(rnti, newCqi));
+            m_ueCqi[rnti] = newCqi;
             // generate correspondent timer
-            m_ueCqiTimers.insert(std::pair<uint16_t, uint32_t>(rnti, m_cqiTimersThreshold));
+            m_ueCqiTimers[rnti] = m_cqiTimersThreshold;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/epc-test-run-time.pl ns-3.41/src/lte/test/epc-test-run-time.pl
--- ns-3.40/src/lte/test/epc-test-run-time.pl	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/epc-test-run-time.pl	2024-02-09 14:44:08.329793200 -0800
@@ -13,7 +13,7 @@
 my @simTime = (5, 10);
 
 
-# Configure and complite first the program to avoid counting compilation time as running time
+# Configure and compile first the program to avoid counting compilation time as running time
 my $launch = "CXXFLAGS=\"-O3 -w\" ./ns3 configure -d optimized --enable-static --enable-examples --enable-modules=lte";
 my $out, my $err;
 capture { system($launch ) } \$out, \$err;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/examples-to-run.py ns-3.41/src/lte/test/examples-to-run.py
--- ns-3.40/src/lte/test/examples-to-run.py	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/examples-to-run.py	2024-02-09 14:44:08.329793200 -0800
@@ -9,14 +9,34 @@
 cpp_examples = [
     ("lena-cqi-threshold", "True", "True"),
     ("lena-dual-stripe", "True", "True"),
-    ("lena-dual-stripe --simTime=0.0 --nApartmentsX=1 --homeEnbDeploymentRatio=0.5 --nMacroEnbSites=0 --macroUeDensity=0 --nBlocks=1", "True", "True"),
-    ("lena-dual-stripe --epc=1 --simTime=0.0 --nApartmentsX=1 --homeEnbDeploymentRatio=0.5 --nMacroEnbSites=0 --macroUeDensity=0 --nBlocks=1", "True", "True"),
+    (
+        "lena-dual-stripe --simTime=0.0 --nApartmentsX=1 --homeEnbDeploymentRatio=0.5 --nMacroEnbSites=0 --macroUeDensity=0 --nBlocks=1",
+        "True",
+        "True",
+    ),
+    (
+        "lena-dual-stripe --epc=1 --simTime=0.0 --nApartmentsX=1 --homeEnbDeploymentRatio=0.5 --nMacroEnbSites=0 --macroUeDensity=0 --nBlocks=1",
+        "True",
+        "True",
+    ),
     ("lena-dual-stripe --simTime=0.01", "True", "True"),
     ("lena-dual-stripe --epc=1 --simTime=0.01", "True", "True"),
     ("lena-dual-stripe --epc=1 --useUdp=0 --simTime=0.01", "True", "True"),
-    ("lena-dual-stripe --epc=1 --fadingTrace=../../src/lte/model/fading-traces/fading_trace_EPA_3kmph.fad --simTime=0.01", "True", "True"),
-    ("lena-dual-stripe --nBlocks=1  --nMacroEnbSites=0 --macroUeDensity=0 --homeEnbDeploymentRatio=1 --homeEnbActivationRatio=1 --homeUesHomeEnbRatio=2 --macroEnbTxPowerDbm=0 --simTime=0.01", "True", "True"),
-    ("lena-dual-stripe --nMacroEnbSites=0 --macroUeDensity=0 --nBlocks=1 --nApartmentsX=4 --nMacroEnbSitesX=0 --homeEnbDeploymentRatio=1 --homeEnbActivationRatio=1 --macroEnbTxPowerDbm=0 --epcDl=1 --epcUl=0 --epc=1 --numBearersPerUe=4 --homeUesHomeEnbRatio=15 --simTime=0.01", "True", "True"),
+    (
+        "lena-dual-stripe --epc=1 --fadingTrace=../../src/lte/model/fading-traces/fading_trace_EPA_3kmph.fad --simTime=0.01",
+        "True",
+        "True",
+    ),
+    (
+        "lena-dual-stripe --nBlocks=1  --nMacroEnbSites=0 --macroUeDensity=0 --homeEnbDeploymentRatio=1 --homeEnbActivationRatio=1 --homeUesHomeEnbRatio=2 --macroEnbTxPowerDbm=0 --simTime=0.01",
+        "True",
+        "True",
+    ),
+    (
+        "lena-dual-stripe --nMacroEnbSites=0 --macroUeDensity=0 --nBlocks=1 --nApartmentsX=4 --nMacroEnbSitesX=0 --homeEnbDeploymentRatio=1 --homeEnbActivationRatio=1 --macroEnbTxPowerDbm=0 --epcDl=1 --epcUl=0 --epc=1 --numBearersPerUe=4 --homeUesHomeEnbRatio=15 --simTime=0.01",
+        "True",
+        "True",
+    ),
     ("lena-fading", "True", "True"),
     ("lena-gtpu-tunnel", "True", "True"),
     ("lena-intercell-interference --simTime=0.1", "True", "True"),
@@ -30,23 +50,71 @@
     ("lena-simple", "True", "True"),
     ("lena-simple-epc", "True", "True"),
     ("lena-x2-handover", "True", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TtaFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdTbfqFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdMtFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdBetFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::RrFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::PssFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::PfFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdTbfqFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdMtFfMacScheduler", "args.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdBetFfMacScheduler", "args.valgrind", "True"),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TtaFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdTbfqFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdMtFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdBetFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::RrFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::PssFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::PfFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdTbfqFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdMtFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
+    (
+        "lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdBetFfMacScheduler",
+        "args.valgrind",
+        "True",
+    ),
     ("lena-ipv6-addr-conf", "True", "True"),
     ("lena-ipv6-ue-rh", "True", "True"),
     ("lena-ipv6-ue-ue", "True", "True"),
     ("lena-radio-link-failure --numberOfEnbs=1 --simTime=17", "True", "True"),
-    ("lena-radio-link-failure --numberOfEnbs=2 --interSiteDistance=700 --simTime=17", "True", "True"),
+    (
+        "lena-radio-link-failure --numberOfEnbs=2 --interSiteDistance=700 --simTime=17",
+        "True",
+        "True",
+    ),
     ("lena-radio-link-failure --numberOfEnbs=1 --useIdealRrc=0 --simTime=17", "True", "True"),
-    ("lena-radio-link-failure --numberOfEnbs=2 --useIdealRrc=0 --interSiteDistance=700 --simTime=17", "True", "True"),
+    (
+        "lena-radio-link-failure --numberOfEnbs=2 --useIdealRrc=0 --interSiteDistance=700 --simTime=17",
+        "True",
+        "True",
+    ),
 ]
 
 # A list of Python examples to run in order to ensure that they remain
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-ffr-simple.cc ns-3.41/src/lte/test/lte-ffr-simple.cc
--- ns-3.40/src/lte/test/lte-ffr-simple.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-ffr-simple.cc	2024-02-09 14:44:08.329793200 -0800
@@ -165,11 +165,11 @@
 }
 
 void
-LteFfrSimple::SetTpc(uint32_t tpc, uint32_t num, bool acculumatedMode)
+LteFfrSimple::SetTpc(uint32_t tpc, uint32_t num, bool accumulatedMode)
 {
     m_tpc = tpc;
     m_tpcNum = num;
-    m_accumulatedMode = acculumatedMode;
+    m_accumulatedMode = accumulatedMode;
 }
 
 std::vector<bool>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-ffr-simple.h ns-3.41/src/lte/test/lte-ffr-simple.h
--- ns-3.40/src/lte/test/lte-ffr-simple.h	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-ffr-simple.h	2024-02-09 14:44:08.329793200 -0800
@@ -71,9 +71,9 @@
      * \brief Set transmission power control
      * \param tpc TPC
      * \param num number of TPC configurations in the test case
-     * \param acculumatedMode whether TPC accumulated mode is used
+     * \param accumulatedMode whether TPC accumulated mode is used
      */
-    void SetTpc(uint32_t tpc, uint32_t num, bool acculumatedMode);
+    void SetTpc(uint32_t tpc, uint32_t num, bool accumulatedMode);
 
     // inherited from LteFfrAlgorithm
     void SetLteFfrSapUser(LteFfrSapUser* s) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-simple-helper.cc ns-3.41/src/lte/test/lte-simple-helper.cc
--- ns-3.40/src/lte/test/lte-simple-helper.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-simple-helper.cc	2024-02-09 14:44:08.329793200 -0800
@@ -14,7 +14,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Manuel Requena <manuel.requena@cttc.es> (Based on lte-helper.cc)
+ * Author: Manuel Requena <manuel.requena@cttc.es>
+ * (Based on lte-helper.cc)
  */
 
 #include "lte-simple-helper.h"
@@ -66,7 +67,8 @@
                             .AddAttribute("RlcEntity",
                                           "Specify which type of RLC will be used. ",
                                           EnumValue(RLC_UM),
-                                          MakeEnumAccessor(&LteSimpleHelper::m_lteRlcEntityType),
+                                          MakeEnumAccessor<LteRlcEntityType_t>(
+                                              &LteSimpleHelper::m_lteRlcEntityType),
                                           MakeEnumChecker(RLC_UM, "RlcUm", RLC_AM, "RlcAm"));
     return tid;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-simple-helper.h ns-3.41/src/lte/test/lte-simple-helper.h
--- ns-3.40/src/lte/test/lte-simple-helper.h	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-simple-helper.h	2024-02-09 14:44:08.329793200 -0800
@@ -14,7 +14,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Manuel Requena <manuel.requena@cttc.es> (Based on lte-helper.h)
+ * Author: Manuel Requena <manuel.requena@cttc.es>
+ * (Based on lte-helper.h)
  */
 
 #ifndef LTE_SIMPLE_HELPER_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-carrier-aggregation.cc ns-3.41/src/lte/test/lte-test-carrier-aggregation.cc
--- ns-3.40/src/lte/test/lte-test-carrier-aggregation.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-test-carrier-aggregation.cc	2024-02-09 14:44:08.329793200 -0800
@@ -25,7 +25,6 @@
 #include "ns3/radio-bearer-stats-calculator.h"
 #include "ns3/string.h"
 #include <ns3/boolean.h>
-#include <ns3/config-store-module.h>
 #include <ns3/constant-position-mobility-model.h>
 #include <ns3/enum.h>
 #include <ns3/eps-bearer.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-entities.cc ns-3.41/src/lte/test/lte-test-entities.cc
--- ns-3.40/src/lte/test/lte-test-entities.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-test-entities.cc	2024-02-09 14:44:08.333793200 -0800
@@ -473,13 +473,13 @@
             haveContext = true;
         }
     }
-    LteMacSapUser::TxOpportunityParameters txOpParmas;
-    txOpParmas.bytes = bytes;
-    txOpParmas.layer = 0;
-    txOpParmas.componentCarrierId = 0;
-    txOpParmas.harqId = 0;
-    txOpParmas.rnti = 0;
-    txOpParmas.lcid = 0;
+    LteMacSapUser::TxOpportunityParameters txOpParams;
+    txOpParams.bytes = bytes;
+    txOpParams.layer = 0;
+    txOpParams.componentCarrierId = 0;
+    txOpParams.harqId = 0;
+    txOpParams.rnti = 0;
+    txOpParams.lcid = 0;
 
     if (haveContext)
     {
@@ -487,11 +487,11 @@
                                        time,
                                        &LteMacSapUser::NotifyTxOpportunity,
                                        m_macSapUser,
-                                       txOpParmas);
+                                       txOpParams);
     }
     else
     {
-        Simulator::Schedule(time, &LteMacSapUser::NotifyTxOpportunity, m_macSapUser, txOpParmas);
+        Simulator::Schedule(time, &LteMacSapUser::NotifyTxOpportunity, m_macSapUser, txOpParams);
     }
 
     if (m_txOpportunityMode == RANDOM_MODE)
@@ -631,19 +631,19 @@
 
         int32_t size = params.statusPduSize + params.txQueueSize + params.retxQueueSize;
         Time time = m_txOppTime;
-        LteMacSapUser::TxOpportunityParameters txOpParmas;
-        txOpParmas.bytes = m_txOppSize;
-        txOpParmas.layer = 0;
-        txOpParmas.componentCarrierId = 0;
-        txOpParmas.harqId = 0;
-        txOpParmas.rnti = params.rnti;
-        txOpParmas.lcid = params.lcid;
+        LteMacSapUser::TxOpportunityParameters txOpParams;
+        txOpParams.bytes = m_txOppSize;
+        txOpParams.layer = 0;
+        txOpParams.componentCarrierId = 0;
+        txOpParams.harqId = 0;
+        txOpParams.rnti = params.rnti;
+        txOpParams.lcid = params.lcid;
         while (size > 0)
         {
             EventId e = Simulator::Schedule(time,
                                             &LteMacSapUser::NotifyTxOpportunity,
                                             m_macSapUser,
-                                            txOpParmas);
+                                            txOpParams);
             m_nextTxOppList.push_back(e);
             size -= m_txOppSize;
             time += m_txOppTime;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-frequency-reuse.cc ns-3.41/src/lte/test/lte-test-frequency-reuse.cc
--- ns-3.40/src/lte/test/lte-test-frequency-reuse.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-test-frequency-reuse.cc	2024-02-09 14:44:08.333793200 -0800
@@ -444,13 +444,13 @@
  * TestCase Data
  */
 void
-DlDataRxStartNofitication(LteFrTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
+DlDataRxStartNotification(LteFrTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
 {
     testcase->DlDataRxStart(spectrumValue);
 }
 
 void
-UlDataRxStartNofitication(LteFrTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
+UlDataRxStartNotification(LteFrTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
 {
     testcase->UlDataRxStart(spectrumValue);
 }
@@ -617,7 +617,7 @@
 
     testDlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&DlDataRxStartNofitication, this));
+        MakeBoundCallback(&DlDataRxStartNotification, this));
 
     // Test SpectrumPhy to get signals form UL channel
     Ptr<LteSpectrumPhy> ueUlSpectrumPhy = ueDevs.Get(0)
@@ -634,7 +634,7 @@
 
     testUlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&UlDataRxStartNofitication, this));
+        MakeBoundCallback(&UlDataRxStartNotification, this));
 
     Simulator::Stop(Seconds(0.500));
     Simulator::Run();
@@ -755,7 +755,7 @@
 
     testDlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&DlDataRxStartNofitication, this));
+        MakeBoundCallback(&DlDataRxStartNotification, this));
 
     // Test SpectrumPhy to get signals form UL channel
     Ptr<LteSpectrumPhy> ueUlSpectrumPhy = ueDevs.Get(0)
@@ -772,7 +772,7 @@
 
     testUlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&UlDataRxStartNofitication, this));
+        MakeBoundCallback(&UlDataRxStartNotification, this));
 
     Simulator::Stop(Seconds(0.500));
     Simulator::Run();
@@ -785,13 +785,13 @@
 }
 
 void
-DlDataRxStartNofiticationArea(LteFrAreaTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
+DlDataRxStartNotificationArea(LteFrAreaTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
 {
     testcase->DlDataRxStart(spectrumValue);
 }
 
 void
-UlDataRxStartNofiticationArea(LteFrAreaTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
+UlDataRxStartNotificationArea(LteFrAreaTestCase* testcase, Ptr<const SpectrumValue> spectrumValue)
 {
     testcase->UlDataRxStart(spectrumValue);
 }
@@ -1087,7 +1087,7 @@
 
     testDlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&DlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&DlDataRxStartNotificationArea, this));
 
     // Test SpectrumPhy to get signals form UL channel
     Ptr<LteSpectrumPhy> ueUlSpectrumPhy = ueDevs1.Get(0)
@@ -1106,7 +1106,7 @@
 
     testUlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&UlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&UlDataRxStartNotificationArea, this));
 
     std::vector<bool> expectedDlRbCenterArea;
     expectedDlRbCenterArea.resize(m_dlBandwidth, false);
@@ -1303,7 +1303,7 @@
 
     testDlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&DlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&DlDataRxStartNotificationArea, this));
 
     // Test SpectrumPhy to get signals form UL channel
     Ptr<LteSpectrumPhy> ueUlSpectrumPhy = ueDevs1.Get(0)
@@ -1322,7 +1322,7 @@
 
     testUlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&UlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&UlDataRxStartNotificationArea, this));
 
     std::vector<bool> expectedDlRbCenterArea;
     expectedDlRbCenterArea.resize(m_dlBandwidth, false);
@@ -1532,7 +1532,7 @@
 
     testDlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&DlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&DlDataRxStartNotificationArea, this));
 
     // Test SpectrumPhy to get signals form UL channel
     Ptr<LteSpectrumPhy> ueUlSpectrumPhy = ueDevs1.Get(0)
@@ -1551,7 +1551,7 @@
 
     testUlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&UlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&UlDataRxStartNotificationArea, this));
 
     double expectedDlPowerCenterArea = 0.5;
     std::vector<bool> expectedDlRbCenterArea;
@@ -1803,7 +1803,7 @@
 
     testDlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&DlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&DlDataRxStartNotificationArea, this));
 
     // Test SpectrumPhy to get signals form UL channel
     Ptr<LteSpectrumPhy> ueUlSpectrumPhy = ueDevs1.Get(0)
@@ -1822,7 +1822,7 @@
 
     testUlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&UlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&UlDataRxStartNotificationArea, this));
 
     double expectedDlPowerCenterArea = 0.251189;
     std::vector<bool> expectedDlRbCenterArea;
@@ -2184,7 +2184,7 @@
 
     testDlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&DlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&DlDataRxStartNotificationArea, this));
 
     // Test SpectrumPhy to get signals form UL channel
     Ptr<LteSpectrumPhy> ueUlSpectrumPhy = ueDevs1.Get(0)
@@ -2203,7 +2203,7 @@
 
     testUlSpectrumPhy->TraceConnectWithoutContext(
         "RxStart",
-        MakeBoundCallback(&UlDataRxStartNofiticationArea, this));
+        MakeBoundCallback(&UlDataRxStartNotificationArea, this));
 
     double expectedDlPowerCenterArea = 1.0;
     std::vector<bool> expectedDlRbCenterArea;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-interference-fr.cc ns-3.41/src/lte/test/lte-test-interference-fr.cc
--- ns-3.40/src/lte/test/lte-test-interference-fr.cc	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-test-interference-fr.cc	2024-02-09 14:44:08.333793200 -0800
@@ -15,9 +15,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Author: Piotr Gawlowicz <gawlowicz.p@gmail.com>
- * Based on lte-test-interference.{h,cc} by Manuel Requena <manuel.requena@cttc.es>
- *                                                                              Nicola Baldo
- * <nbaldo@cttc.es>
+ * Based on lte-test-interference.{h,cc} by:
+ *   Manuel Requena <manuel.requena@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
  */
 
 #include "lte-test-interference-fr.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-interference-fr.h ns-3.41/src/lte/test/lte-test-interference-fr.h
--- ns-3.40/src/lte/test/lte-test-interference-fr.h	2023-09-27 13:30:37.826839700 -0700
+++ ns-3.41/src/lte/test/lte-test-interference-fr.h	2024-02-09 14:44:08.333793200 -0800
@@ -15,9 +15,9 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Author: Piotr Gawlowicz <gawlowicz.p@gmail.com>
- * Based on lte-test-interference.{h,cc} by Manuel Requena <manuel.requena@cttc.es>
- *                                          Nicola Baldo <nbaldo@cttc.es>
- *
+ * Based on lte-test-interference.{h,cc} by:
+ *   Manuel Requena <manuel.requena@cttc.es>
+ *   Nicola Baldo <nbaldo@cttc.es>
  */
 
 #ifndef LTE_TEST_INTERFERENCE_FR_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-radio-link-failure.cc ns-3.41/src/lte/test/lte-test-radio-link-failure.cc
--- ns-3.40/src/lte/test/lte-test-radio-link-failure.cc	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/lte-test-radio-link-failure.cc	2024-02-09 14:44:08.333793200 -0800
@@ -24,8 +24,6 @@
 
 #include "lte-test-radio-link-failure.h"
 
-#include "ns3/config-store-module.h"
-#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/lte-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-rlc-am-e2e.cc ns-3.41/src/lte/test/lte-test-rlc-am-e2e.cc
--- ns-3.40/src/lte/test/lte-test-rlc-am-e2e.cc	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/lte-test-rlc-am-e2e.cc	2024-02-09 14:44:08.333793200 -0800
@@ -23,7 +23,6 @@
 #include "lte-simple-helper.h"
 #include "lte-test-entities.h"
 
-#include "ns3/config-store.h"
 #include "ns3/config.h"
 #include "ns3/error-model.h"
 #include "ns3/log.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-rr-ff-mac-scheduler.cc ns-3.41/src/lte/test/lte-test-rr-ff-mac-scheduler.cc
--- ns-3.40/src/lte/test/lte-test-rr-ff-mac-scheduler.cc	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/lte-test-rr-ff-mac-scheduler.cc	2024-02-09 14:44:08.333793200 -0800
@@ -24,7 +24,6 @@
 #include "ns3/radio-bearer-stats-calculator.h"
 #include "ns3/string.h"
 #include <ns3/boolean.h>
-#include <ns3/config-store-module.h>
 #include <ns3/constant-position-mobility-model.h>
 #include <ns3/enum.h>
 #include <ns3/eps-bearer.h>
@@ -306,7 +305,7 @@
     std::vector<uint64_t> dlDataRxed;
     if (m_errorModelEnabled)
     {
-        m_thrRefDl *= 0.95; // for couting the Vienna AMC behavior: BLER between 0% and 10%
+        m_thrRefDl *= 0.95; // for counting the Vienna AMC behavior: BLER between 0% and 10%
     }
     for (int i = 0; i < m_nUser; i++)
     {
@@ -327,7 +326,7 @@
     std::vector<uint64_t> ulDataRxed;
     if (m_errorModelEnabled)
     {
-        m_thrRefUl *= 0.95; // for couting the Vienna AMC behavior: BLER between 0% and 10%
+        m_thrRefUl *= 0.95; // for counting the Vienna AMC behavior: BLER between 0% and 10%
     }
     for (int i = 0; i < m_nUser; i++)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-run-time.pl ns-3.41/src/lte/test/lte-test-run-time.pl
--- ns-3.40/src/lte/test/lte-test-run-time.pl	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/lte-test-run-time.pl	2024-02-09 14:44:08.333793200 -0800
@@ -13,7 +13,7 @@
 my @nFloors = (0, 1);
 my @simTime = (5, 10);
 
-# Configure and complite first the program to avoid counting compilation time as running time
+# Configure and compile first the program to avoid counting compilation time as running time
 my $launch = "CXXFLAGS=\"-O3 -w\" ./ns3 configure -d optimized --enable-static --enable-examples --enable-modules=lte";
 my $out;
 my $err;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-spectrum-value-helper.cc ns-3.41/src/lte/test/lte-test-spectrum-value-helper.cc
--- ns-3.40/src/lte/test/lte-test-spectrum-value-helper.cc	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/lte-test-spectrum-value-helper.cc	2024-02-09 14:44:08.337793000 -0800
@@ -155,7 +155,7 @@
      * \param str the reference name
      * \param earfcn
      * \param bw bandwidth
-     * \param txPowerDbm tranmit power in dBm
+     * \param txPowerDbm transmit power in dBm
      * \param activeRbs active RBs
      * \param expected executed spectrum value
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-ue-phy.h ns-3.41/src/lte/test/lte-test-ue-phy.h
--- ns-3.40/src/lte/test/lte-test-ue-phy.h	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/lte-test-ue-phy.h	2024-02-09 14:44:08.337793000 -0800
@@ -14,7 +14,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Author: Manuel Requena <manuel.requena@cttc.es> : Based on lte-ue-phy code
+ * Author: Manuel Requena <manuel.requena@cttc.es>
+ * (Based on lte-ue-phy code)
  */
 
 #ifndef LTE_TEST_UE_PHY_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/lte-test-uplink-power-control.cc ns-3.41/src/lte/test/lte-test-uplink-power-control.cc
--- ns-3.40/src/lte/test/lte-test-uplink-power-control.cc	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/lte-test-uplink-power-control.cc	2024-02-09 14:44:08.337793000 -0800
@@ -78,7 +78,7 @@
  * TestCase Data
  */
 void
-PuschTxPowerNofitication(LteUplinkPowerControlTestCase* testcase,
+PuschTxPowerNotification(LteUplinkPowerControlTestCase* testcase,
                          uint16_t cellId,
                          uint16_t rnti,
                          double txPower)
@@ -87,7 +87,7 @@
 }
 
 void
-PucchTxPowerNofitication(LteUplinkPowerControlTestCase* testcase,
+PucchTxPowerNotification(LteUplinkPowerControlTestCase* testcase,
                          uint16_t cellId,
                          uint16_t rnti,
                          double txPower)
@@ -96,7 +96,7 @@
 }
 
 void
-SrsTxPowerNofitication(LteUplinkPowerControlTestCase* testcase,
+SrsTxPowerNotification(LteUplinkPowerControlTestCase* testcase,
                        uint16_t cellId,
                        uint16_t rnti,
                        double txPower)
@@ -269,11 +269,11 @@
     m_ueUpc = uePhy->GetUplinkPowerControl();
 
     m_ueUpc->TraceConnectWithoutContext("ReportPuschTxPower",
-                                        MakeBoundCallback(&PuschTxPowerNofitication, this));
+                                        MakeBoundCallback(&PuschTxPowerNotification, this));
     m_ueUpc->TraceConnectWithoutContext("ReportPucchTxPower",
-                                        MakeBoundCallback(&PucchTxPowerNofitication, this));
+                                        MakeBoundCallback(&PucchTxPowerNotification, this));
     m_ueUpc->TraceConnectWithoutContext("ReportSrsTxPower",
-                                        MakeBoundCallback(&SrsTxPowerNofitication, this));
+                                        MakeBoundCallback(&SrsTxPowerNotification, this));
 
     // Attach a UE to a eNB
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
@@ -452,11 +452,11 @@
     m_ueUpc = uePhy->GetUplinkPowerControl();
 
     m_ueUpc->TraceConnectWithoutContext("ReportPuschTxPower",
-                                        MakeBoundCallback(&PuschTxPowerNofitication, this));
+                                        MakeBoundCallback(&PuschTxPowerNotification, this));
     m_ueUpc->TraceConnectWithoutContext("ReportPucchTxPower",
-                                        MakeBoundCallback(&PucchTxPowerNofitication, this));
+                                        MakeBoundCallback(&PucchTxPowerNotification, this));
     m_ueUpc->TraceConnectWithoutContext("ReportSrsTxPower",
-                                        MakeBoundCallback(&SrsTxPowerNofitication, this));
+                                        MakeBoundCallback(&SrsTxPowerNotification, this));
 
     // Attach a UE to a eNB
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
@@ -616,11 +616,11 @@
     m_ueUpc = uePhy->GetUplinkPowerControl();
 
     m_ueUpc->TraceConnectWithoutContext("ReportPuschTxPower",
-                                        MakeBoundCallback(&PuschTxPowerNofitication, this));
+                                        MakeBoundCallback(&PuschTxPowerNotification, this));
     m_ueUpc->TraceConnectWithoutContext("ReportPucchTxPower",
-                                        MakeBoundCallback(&PucchTxPowerNofitication, this));
+                                        MakeBoundCallback(&PucchTxPowerNotification, this));
     m_ueUpc->TraceConnectWithoutContext("ReportSrsTxPower",
-                                        MakeBoundCallback(&SrsTxPowerNofitication, this));
+                                        MakeBoundCallback(&SrsTxPowerNotification, this));
 
     // Attach a UE to a eNB
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/test-epc-tft-classifier.cc ns-3.41/src/lte/test/test-epc-tft-classifier.cc
--- ns-3.40/src/lte/test/test-epc-tft-classifier.cc	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/test-epc-tft-classifier.cc	2024-02-09 14:44:08.337793000 -0800
@@ -14,8 +14,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Nicola Baldo <nbaldo@cttc.es>
- *          Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #include "ns3/epc-tft-classifier.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/lte/test/test-lte-x2-handover-measures.cc ns-3.41/src/lte/test/test-lte-x2-handover-measures.cc
--- ns-3.40/src/lte/test/test-lte-x2-handover-measures.cc	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/lte/test/test-lte-x2-handover-measures.cc	2024-02-09 14:44:08.337793000 -0800
@@ -14,8 +14,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
- * Authors: Nicola Baldo <nbaldo@cttc.es>
- *          Manuel Requena <manuel.requena@cttc.es>
+ * Authors:
+ *   Nicola Baldo <nbaldo@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
  */
 
 #include <ns3/bulk-send-helper.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/CMakeLists.txt ns-3.41/src/mesh/CMakeLists.txt
--- ns-3.40/src/mesh/CMakeLists.txt	2023-09-27 13:30:37.830839600 -0700
+++ ns-3.41/src/mesh/CMakeLists.txt	2024-02-09 14:44:08.341793300 -0800
@@ -65,9 +65,8 @@
     model/mesh-wifi-beacon.h
     model/mesh-wifi-interface-mac-plugin.h
     model/mesh-wifi-interface-mac.h
-  LIBRARIES_TO_LINK
-    ${libinternet}
-    ${libwifi}
+  LIBRARIES_TO_LINK ${libwifi}
+                    ${libapplications}
   TEST_SOURCES
     test/dot11s/dot11s-test-suite.cc
     test/dot11s/hwmp-proactive-regression.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/doc/source/conf.py ns-3.41/src/mesh/doc/source/conf.py
--- ns-3.40/src/mesh/doc/source/conf.py	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/doc/source/conf.py	2024-02-09 14:44:08.341793300 -0800
@@ -11,202 +11,208 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'mesh'
+master_doc = "mesh"
 
 # General information about the project.
-project = u'ns-3'
-copyright = u'ns-3 project'
+project = "ns-3"
+copyright = "ns-3 project"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3-dev'
+version = "ns-3-dev"
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3-dev'
+release = "ns-3-dev"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = "default"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-#htmlhelp_basename = 'ns-3doc'
+# htmlhelp_basename = 'ns-3doc'
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-#  ('mesh-testing', 'mesh-doc-testing.tex', u'Mesh Wi-Fi Testing Documentation', u'ns-3 project', 'manual'),
-#  ('mesh-design', 'mesh-doc-design.tex', u'Mesh Wi-Fi Design Documentation', u'ns-3 project', 'manual'),
-#  ('mesh-user', 'mesh-doc-user.tex', u'Mesh Wi-Fi User Documentation', u'ns-3 project', 'manual'),
-  ('mesh', 'mesh-module-doc.tex', u'The ns-3 Mesh Wi-Fi Module Documentation', u'ns-3 project', 'manual'),
+    #  ('mesh-testing', 'mesh-doc-testing.tex', u'Mesh Wi-Fi Testing Documentation', u'ns-3 project', 'manual'),
+    #  ('mesh-design', 'mesh-doc-design.tex', u'Mesh Wi-Fi Design Documentation', u'ns-3 project', 'manual'),
+    #  ('mesh-user', 'mesh-doc-user.tex', u'Mesh Wi-Fi User Documentation', u'ns-3 project', 'manual'),
+    (
+        "mesh",
+        "mesh-module-doc.tex",
+        "The ns-3 Mesh Wi-Fi Module Documentation",
+        "ns-3 project",
+        "manual",
+    ),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
-
+# latex_domain_indices = True
 
 
 # add page breaks in the pdf. Level 1 is for top-level sections, level 2 for subsections, and so on.
@@ -217,7 +223,4 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-model-library', u'ns-3 Model Library',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-model-library", "ns-3 Model Library", ["ns-3 project"], 1)]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/dot11s/hwmp-protocol-mac.cc ns-3.41/src/mesh/model/dot11s/hwmp-protocol-mac.cc
--- ns-3.40/src/mesh/model/dot11s/hwmp-protocol-mac.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/dot11s/hwmp-protocol-mac.cc	2024-02-09 14:44:08.341793300 -0800
@@ -28,6 +28,7 @@
 
 #include "ns3/log.h"
 #include "ns3/mesh-wifi-interface-mac.h"
+#include "ns3/mgt-action-headers.h"
 #include "ns3/nstime.h"
 #include "ns3/packet.h"
 #include "ns3/simulator.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/dot11s/ie-dot11s-beacon-timing.cc ns-3.41/src/mesh/model/dot11s/ie-dot11s-beacon-timing.cc
--- ns-3.40/src/mesh/model/dot11s/ie-dot11s-beacon-timing.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/dot11s/ie-dot11s-beacon-timing.cc	2024-02-09 14:44:08.341793300 -0800
@@ -185,13 +185,13 @@
         m_neighbours.push_back(new_element);
     }
     return i.GetDistanceFrom(start);
-};
+}
 
 uint16_t
 IeBeaconTiming::TimestampToU16(Time t)
 {
     return ((uint16_t)((t.GetMicroSeconds() >> 8) & 0xffff));
-};
+}
 
 uint16_t
 IeBeaconTiming::BeaconIntervalToU16(Time t)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/dot11s/peer-link-frame.cc ns-3.41/src/mesh/model/dot11s/peer-link-frame.cc
--- ns-3.40/src/mesh/model/dot11s/peer-link-frame.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/dot11s/peer-link-frame.cc	2024-02-09 14:44:08.357793300 -0800
@@ -243,7 +243,7 @@
 bool
 operator==(const PeerLinkCloseStart& a, const PeerLinkCloseStart& b)
 {
-    return ((a.m_meshId.IsEqual(b.m_meshId)));
+    return (a.m_meshId.IsEqual(b.m_meshId));
 }
 
 NS_OBJECT_ENSURE_REGISTERED(PeerLinkConfirmStart);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/dot11s/peer-management-protocol.cc ns-3.41/src/mesh/model/dot11s/peer-management-protocol.cc
--- ns-3.40/src/mesh/model/dot11s/peer-management-protocol.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/dot11s/peer-management-protocol.cc	2024-02-09 14:44:08.357793300 -0800
@@ -336,7 +336,7 @@
             }
             else
             {
-                return (*i);
+                return *i;
             }
         }
     }
@@ -390,7 +390,7 @@
     Ptr<PeerLink> peerLink = FindPeerLink(interface, peerAddress);
     if (peerLink)
     {
-        return (peerLink->LinkIsEstab());
+        return peerLink->LinkIsEstab();
     }
     return false;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/dot11s/peer-management-protocol-mac.cc ns-3.41/src/mesh/model/dot11s/peer-management-protocol-mac.cc
--- ns-3.40/src/mesh/model/dot11s/peer-management-protocol-mac.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/dot11s/peer-management-protocol-mac.cc	2024-02-09 14:44:08.357793300 -0800
@@ -28,6 +28,7 @@
 #include "ns3/log.h"
 #include "ns3/mesh-information-element-vector.h"
 #include "ns3/mesh-wifi-interface-mac.h"
+#include "ns3/mgt-action-headers.h"
 #include "ns3/simulator.h"
 #include "ns3/wifi-mpdu.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/flame/flame-rtable.cc ns-3.41/src/mesh/model/flame/flame-rtable.cc
--- ns-3.40/src/mesh/model/flame/flame-rtable.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/flame/flame-rtable.cc	2024-02-09 14:44:08.357793300 -0800
@@ -98,7 +98,7 @@
     {
         return LookupResult();
     }
-    if ((i->second.whenExpire < Simulator::Now()))
+    if (i->second.whenExpire < Simulator::Now())
     {
         NS_LOG_DEBUG("Route has expired, sorry.");
         m_routes.erase(i);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/mesh-information-element-vector.cc ns-3.41/src/mesh/model/mesh-information-element-vector.cc
--- ns-3.40/src/mesh/model/mesh-information-element-vector.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/mesh-information-element-vector.cc	2024-02-09 14:44:08.357793300 -0800
@@ -202,7 +202,7 @@
     {
         if ((*i)->ElementId() == id)
         {
-            return (*i);
+            return *i;
         }
     }
     return nullptr;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mesh/model/mesh-point-device.cc ns-3.41/src/mesh/model/mesh-point-device.cc
--- ns-3.40/src/mesh/model/mesh-point-device.cc	2023-09-27 13:30:37.834839600 -0700
+++ ns-3.41/src/mesh/model/mesh-point-device.cc	2024-02-09 14:44:08.357793300 -0800
@@ -174,16 +174,16 @@
 }
 
 void
-MeshPointDevice::Forward(Ptr<NetDevice> inport,
+MeshPointDevice::Forward(Ptr<NetDevice> incomingPort,
                          Ptr<const Packet> packet,
                          uint16_t protocol,
                          const Mac48Address src,
                          const Mac48Address dst)
 {
-    NS_LOG_FUNCTION(this << inport << packet << protocol << src << dst);
+    NS_LOG_FUNCTION(this << incomingPort << packet << protocol << src << dst);
     // pass through routing protocol
     NS_LOG_DEBUG("Forwarding from " << src << " to " << dst << " at " << m_address);
-    bool result = m_routingProtocol->RequestRoute(inport->GetIfIndex(),
+    bool result = m_routingProtocol->RequestRoute(incomingPort->GetIfIndex(),
                                                   src,
                                                   dst,
                                                   packet,
@@ -402,7 +402,7 @@
     {
         if ((*i)->GetIfIndex() == n)
         {
-            return (*i);
+            return *i;
         }
     }
     NS_FATAL_ERROR("Mesh point interface is not found by index");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mobility/examples/reference-point-group-mobility-example.cc ns-3.41/src/mobility/examples/reference-point-group-mobility-example.cc
--- ns-3.40/src/mobility/examples/reference-point-group-mobility-example.cc	2023-09-27 13:30:37.838839500 -0700
+++ ns-3.41/src/mobility/examples/reference-point-group-mobility-example.cc	2024-02-09 14:44:08.365793500 -0800
@@ -33,7 +33,7 @@
  * and different child mobility models.
  *
  * There is no node associated with the parent (reference) model.
- * Instead, all nodes are associated with a hiearchical mobility model
+ * Instead, all nodes are associated with a hierarchical mobility model
  * containing both the parent and child models, and the position of
  * the node is the vector sum of these parent and child positions.
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mobility/helper/ns2-mobility-helper.cc ns-3.41/src/mobility/helper/ns2-mobility-helper.cc
--- ns-3.40/src/mobility/helper/ns2-mobility-helper.cc	2023-09-27 13:30:37.838839500 -0700
+++ ns-3.41/src/mobility/helper/ns2-mobility-helper.cc	2024-02-09 14:44:08.365793500 -0800
@@ -854,7 +854,7 @@
     position.y = model->GetPosition().y;
     position.z = model->GetPosition().z;
 
-    // Chedule next positions
+    // Schedule next positions
     Simulator::Schedule(Seconds(at), &ConstantVelocityMobilityModel::SetPosition, model, position);
 
     return position;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mobility/model/box.cc ns-3.41/src/mobility/model/box.cc
--- ns-3.40/src/mobility/model/box.cc	2023-09-27 13:30:37.838839500 -0700
+++ ns-3.41/src/mobility/model/box.cc	2024-02-09 14:44:08.365793500 -0800
@@ -145,7 +145,7 @@
 {
     // If any of the position falls inside the box,
     // return true.
-    if ((IsInside(l1) || IsInside(l2)))
+    if (IsInside(l1) || IsInside(l2))
     {
         return true;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mobility/model/position-allocator.cc ns-3.41/src/mobility/model/position-allocator.cc
--- ns-3.40/src/mobility/model/position-allocator.cc	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mobility/model/position-allocator.cc	2024-02-09 14:44:08.365793500 -0800
@@ -185,7 +185,7 @@
             .AddAttribute("LayoutType",
                           "The type of layout.",
                           EnumValue(ROW_FIRST),
-                          MakeEnumAccessor(&GridPositionAllocator::m_layoutType),
+                          MakeEnumAccessor<LayoutType>(&GridPositionAllocator::m_layoutType),
                           MakeEnumChecker(ROW_FIRST, "RowFirst", COLUMN_FIRST, "ColumnFirst"));
     return tid;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mobility/model/position-allocator.h ns-3.41/src/mobility/model/position-allocator.h
--- ns-3.40/src/mobility/model/position-allocator.h	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mobility/model/position-allocator.h	2024-02-09 14:44:08.365793500 -0800
@@ -370,7 +370,7 @@
  *
  * \note using UniformDiscPositionAllocator is not equivalent to using
  * a RandomDiscPositionAllocator with a uniformly-distributed radius,
- * since doing that would results in a point distribution which is
+ * since doing that would result in a point distribution which is
  * more dense towards the center of the disc.
  */
 class UniformDiscPositionAllocator : public PositionAllocator
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mobility/model/random-walk-2d-mobility-model.cc ns-3.41/src/mobility/model/random-walk-2d-mobility-model.cc
--- ns-3.40/src/mobility/model/random-walk-2d-mobility-model.cc	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mobility/model/random-walk-2d-mobility-model.cc	2024-02-09 14:44:08.365793500 -0800
@@ -61,7 +61,8 @@
                           "The mode indicates the condition used to "
                           "change the current speed and direction",
                           EnumValue(RandomWalk2dMobilityModel::MODE_DISTANCE),
-                          MakeEnumAccessor(&RandomWalk2dMobilityModel::m_mode),
+                          MakeEnumAccessor<RandomWalk2dMobilityModel::Mode>(
+                              &RandomWalk2dMobilityModel::m_mode),
                           MakeEnumChecker(RandomWalk2dMobilityModel::MODE_DISTANCE,
                                           "Distance",
                                           RandomWalk2dMobilityModel::MODE_TIME,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mobility/test/ns2-mobility-helper-test-suite.cc ns-3.41/src/mobility/test/ns2-mobility-helper-test-suite.cc
--- ns-3.40/src/mobility/test/ns2-mobility-helper-test-suite.cc	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mobility/test/ns2-mobility-helper-test-suite.cc	2024-02-09 14:44:08.365793500 -0800
@@ -118,7 +118,7 @@
          */
         bool operator<(const ReferencePoint& o) const
         {
-            return (time < o.time);
+            return time < o.time;
         }
     };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mpi/CMakeLists.txt ns-3.41/src/mpi/CMakeLists.txt
--- ns-3.40/src/mpi/CMakeLists.txt	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mpi/CMakeLists.txt	2024-02-09 14:44:08.369793400 -0800
@@ -21,9 +21,7 @@
     model/mpi-interface.h
     model/mpi-receiver.h
     model/parallel-communication-interface.h
-  LIBRARIES_TO_LINK
-    ${libcore}
-    ${libnetwork}
-    ${MPI_CXX_LIBRARIES}
+  LIBRARIES_TO_LINK ${libnetwork}
+                    ${MPI_CXX_LIBRARIES}
   TEST_SOURCES ${example_as_test_suite}
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mpi/model/distributed-simulator-impl.cc ns-3.41/src/mpi/model/distributed-simulator-impl.cc
--- ns-3.40/src/mpi/model/distributed-simulator-impl.cc	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mpi/model/distributed-simulator-impl.cc	2024-02-09 14:44:08.369793400 -0800
@@ -464,12 +464,12 @@
     m_stop = true;
 }
 
-void
+EventId
 DistributedSimulatorImpl::Stop(const Time& delay)
 {
     NS_LOG_FUNCTION(this << delay.GetTimeStep());
 
-    Simulator::Schedule(delay, &Simulator::Stop);
+    return Simulator::Schedule(delay, &Simulator::Stop);
 }
 
 //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mpi/model/distributed-simulator-impl.h ns-3.41/src/mpi/model/distributed-simulator-impl.h
--- ns-3.40/src/mpi/model/distributed-simulator-impl.h	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mpi/model/distributed-simulator-impl.h	2024-02-09 14:44:08.369793400 -0800
@@ -122,7 +122,7 @@
     void Destroy() override;
     bool IsFinished() const override;
     void Stop() override;
-    void Stop(const Time& delay) override;
+    EventId Stop(const Time& delay) override;
     EventId Schedule(const Time& delay, EventImpl* event) override;
     void ScheduleWithContext(uint32_t context, const Time& delay, EventImpl* event) override;
     EventId ScheduleNow(EventImpl* event) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mpi/model/granted-time-window-mpi-interface.cc ns-3.41/src/mpi/model/granted-time-window-mpi-interface.cc
--- ns-3.40/src/mpi/model/granted-time-window-mpi-interface.cc	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mpi/model/granted-time-window-mpi-interface.cc	2024-02-09 14:44:08.369793400 -0800
@@ -52,7 +52,7 @@
 SentBuffer::SentBuffer()
 {
     m_buffer = nullptr;
-    m_request = nullptr;
+    m_request = MPI_REQUEST_NULL;
 }
 
 SentBuffer::~SentBuffer()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mpi/model/null-message-mpi-interface.cc ns-3.41/src/mpi/model/null-message-mpi-interface.cc
--- ns-3.40/src/mpi/model/null-message-mpi-interface.cc	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mpi/model/null-message-mpi-interface.cc	2024-02-09 14:44:08.369793400 -0800
@@ -97,7 +97,7 @@
 NullMessageSentBuffer::NullMessageSentBuffer()
 {
     m_buffer = nullptr;
-    m_request = nullptr;
+    m_request = MPI_REQUEST_NULL;
 }
 
 NullMessageSentBuffer::~NullMessageSentBuffer()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mpi/model/null-message-simulator-impl.cc ns-3.41/src/mpi/model/null-message-simulator-impl.cc
--- ns-3.40/src/mpi/model/null-message-simulator-impl.cc	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mpi/model/null-message-simulator-impl.cc	2024-02-09 14:44:08.369793400 -0800
@@ -382,12 +382,12 @@
     m_stop = true;
 }
 
-void
+EventId
 NullMessageSimulatorImpl::Stop(const Time& delay)
 {
     NS_LOG_FUNCTION(this << delay.GetTimeStep());
 
-    Simulator::Schedule(delay, &Simulator::Stop);
+    return Simulator::Schedule(delay, &Simulator::Stop);
 }
 
 //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/mpi/model/null-message-simulator-impl.h ns-3.41/src/mpi/model/null-message-simulator-impl.h
--- ns-3.40/src/mpi/model/null-message-simulator-impl.h	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/mpi/model/null-message-simulator-impl.h	2024-02-09 14:44:08.369793400 -0800
@@ -66,7 +66,7 @@
     void Destroy() override;
     bool IsFinished() const override;
     void Stop() override;
-    void Stop(const Time& delay) override;
+    EventId Stop(const Time& delay) override;
     EventId Schedule(const Time& delay, EventImpl* event) override;
     void ScheduleWithContext(uint32_t context, const Time& delay, EventImpl* event) override;
     EventId ScheduleNow(EventImpl* event) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/netanim/CMakeLists.txt ns-3.41/src/netanim/CMakeLists.txt
--- ns-3.40/src/netanim/CMakeLists.txt	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/netanim/CMakeLists.txt	2024-02-09 14:44:08.369793400 -0800
@@ -3,15 +3,10 @@
   SOURCE_FILES model/animation-interface.cc
   HEADER_FILES model/animation-interface.h
   LIBRARIES_TO_LINK
-    ${libinternet}
-    ${libmobility}
     ${libwimax}
     ${libwifi}
-    ${libcsma}
     ${liblte}
     ${libuan}
-    ${libenergy}
     ${liblr-wpan}
-    ${libpoint-to-point-layout}
   TEST_SOURCES test/netanim-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/netanim/model/animation-interface.cc ns-3.41/src/netanim/model/animation-interface.cc
--- ns-3.40/src/netanim/model/animation-interface.cc	2023-09-27 13:30:37.850839600 -0700
+++ ns-3.41/src/netanim/model/animation-interface.cc	2024-02-09 14:44:08.397793500 -0800
@@ -15,7 +15,7 @@
  * Author: George F. Riley<riley@ece.gatech.edu>
  * Modified by: John Abraham <john.abraham@gatech.edu>
  * Contributions: Eugene Kalishenko <ydginster@gmail.com> (Open Source and Linux Laboratory
- * http://dev.osll.ru/) Tommaso Pecorella <tommaso.pecorella@unifi.it> Pavel Vasilyev
+ * http://wiki.osll.ru/doku.php/start) Tommaso Pecorella <tommaso.pecorella@unifi.it> Pavel Vasilyev
  * <pavel.vasilyev@sredasolutions.com>
  */
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/netanim/model/animation-interface.h ns-3.41/src/netanim/model/animation-interface.h
--- ns-3.40/src/netanim/model/animation-interface.h	2023-09-27 13:30:37.850839600 -0700
+++ ns-3.41/src/netanim/model/animation-interface.h	2024-02-09 14:44:08.397793500 -0800
@@ -15,7 +15,7 @@
  * Author: George F. Riley<riley@ece.gatech.edu>
  * Author: John Abraham <john.abraham@gatech.edu>
  * Contributions: Eugene Kalishenko <ydginster@gmail.com> (Open Source and Linux Laboratory
- * http://dev.osll.ru/)
+ * http://wiki.osll.ru/doku.php/start)
  */
 
 // Interface between ns3 and the network animator
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/netanim/_required_netanim_version.py ns-3.41/src/netanim/_required_netanim_version.py
--- ns-3.40/src/netanim/_required_netanim_version.py	2023-09-27 13:30:37.842839700 -0700
+++ ns-3.41/src/netanim/_required_netanim_version.py	2024-02-09 14:44:08.369793400 -0800
@@ -6,4 +6,4 @@
 # on 'git describe --tags' command.  Example, if the latest release was 3.108,
 # and 'git describe --tags' reports "netanim-3.108-6-g8e7c0a9", then write the
 # version string below as 'netanim-3.108.post6+ng8e7c0a9'
-__required_netanim_version__ = 'netanim-3.109'
+__required_netanim_version__ = "netanim-3.109"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/netanim/test/netanim-test.cc ns-3.41/src/netanim/test/netanim-test.cc
--- ns-3.40/src/netanim/test/netanim-test.cc	2023-09-27 13:30:37.850839600 -0700
+++ ns-3.41/src/netanim/test/netanim-test.cc	2024-02-09 14:44:08.397793500 -0800
@@ -14,7 +14,7 @@
  *
  * Author: John Abraham <john.abraham@gatech.edu>
  * Contributions: Eugene Kalishenko <ydginster@gmail.com> (Open Source and Linux Laboratory
- * http://dev.osll.ru/)
+ * http://wiki.osll.ru/doku.php/start)
  */
 
 #include "unistd.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/CMakeLists.txt ns-3.41/src/network/CMakeLists.txt
--- ns-3.40/src/network/CMakeLists.txt	2023-09-27 13:30:37.850839600 -0700
+++ ns-3.41/src/network/CMakeLists.txt	2024-02-09 14:44:08.397793500 -0800
@@ -154,8 +154,7 @@
   LIBNAME network
   SOURCE_FILES ${source_files}
   HEADER_FILES ${header_files}
-  LIBRARIES_TO_LINK ${libcore}
-                    ${libstats}
+  LIBRARIES_TO_LINK ${libstats}
   TEST_SOURCES
     test/bit-serializer-test.cc
     test/buffer-test.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/buffer.cc ns-3.41/src/network/model/buffer.cc
--- ns-3.40/src/network/model/buffer.cc	2023-09-27 13:30:37.850839600 -0700
+++ ns-3.41/src/network/model/buffer.cc	2024-02-09 14:44:08.401793700 -0800
@@ -586,66 +586,60 @@
     uint32_t size = 0;
 
     // Add the zero data length
-    if (size + 4 <= maxSize)
-    {
-        size += 4;
-        *p++ = m_zeroAreaEnd - m_zeroAreaStart;
-    }
-    else
+    size += 4;
+
+    if (size > maxSize)
     {
         return 0;
     }
 
+    *p++ = m_zeroAreaEnd - m_zeroAreaStart;
+
     // Add the length of actual start data
-    uint32_t dataStartLength = m_zeroAreaStart - m_start;
-    if (size + 4 <= maxSize)
-    {
-        size += 4;
-        *p++ = dataStartLength;
-    }
-    else
+    size += 4;
+
+    if (size > maxSize)
     {
         return 0;
     }
 
+    uint32_t dataStartLength = m_zeroAreaStart - m_start;
+    *p++ = dataStartLength;
+
     // Add the actual data
-    if (size + ((dataStartLength + 3) & (~3)) <= maxSize)
-    {
-        size += (dataStartLength + 3) & (~3);
-        memcpy(p, m_data->m_data + m_start, dataStartLength);
-        p += (((dataStartLength + 3) & (~3)) / 4); // Advance p, insuring 4 byte boundary
-    }
-    else
+    size += (dataStartLength + 3) & (~3);
+
+    if (size > maxSize)
     {
         return 0;
     }
 
+    memcpy(p, m_data->m_data + m_start, dataStartLength);
+    p += (((dataStartLength + 3) & (~3)) / 4); // Advance p, insuring 4 byte boundary
+
     // Add the length of the actual end data
-    uint32_t dataEndLength = m_end - m_zeroAreaEnd;
-    if (size + 4 <= maxSize)
-    {
-        size += 4;
-        *p++ = dataEndLength;
-    }
-    else
+    size += 4;
+
+    if (size > maxSize)
     {
         return 0;
     }
 
+    uint32_t dataEndLength = m_end - m_zeroAreaEnd;
+    *p++ = dataEndLength;
+
     // Add the actual data
-    if (size + ((dataEndLength + 3) & (~3)) <= maxSize)
-    {
-        // The following line is unnecessary.
-        // size += (dataEndLength + 3) & (~3);
-        memcpy(p, m_data->m_data + m_zeroAreaStart, dataEndLength);
-        // The following line is unnecessary.
-        // p += (((dataEndLength + 3) & (~3))/4); // Advance p, insuring 4 byte boundary
-    }
-    else
+    size += (dataEndLength + 3) & (~3);
+
+    if (size > maxSize)
     {
         return 0;
     }
 
+    memcpy(p, m_data->m_data + m_zeroAreaStart, dataEndLength);
+    // The following line is unnecessary.
+    // p += (((dataEndLength + 3) & (~3))/4); // Advance p, insuring 4 byte boundary
+
     // Serialized everything successfully
     return 1;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/byte-tag-list.cc ns-3.41/src/network/model/byte-tag-list.cc
--- ns-3.40/src/network/model/byte-tag-list.cc	2023-09-27 13:30:37.850839600 -0700
+++ ns-3.41/src/network/model/byte-tag-list.cc	2024-02-09 14:44:08.401793700 -0800
@@ -470,19 +470,16 @@
     uint32_t* p = buffer;
     uint32_t size = 0;
 
-    uint32_t* numberOfTags = nullptr;
+    size += 4;
 
-    if (size + 4 <= maxSize)
-    {
-        numberOfTags = p;
-        *p++ = 0;
-        size += 4;
-    }
-    else
+    if (size > maxSize)
     {
         return 0;
     }
 
+    uint32_t* numberOfTags = p;
+    *p++ = 0;
+
     ByteTagList::Iterator i = BeginAll();
     while (i.HasNext())
     {
@@ -492,62 +489,56 @@
 
         // ensure size is multiple of 4 bytes for 4 byte boundaries
         uint32_t hashSize = (sizeof(TypeId::hash_t) + 3) & (~3);
-        if (size + hashSize <= maxSize)
-        {
-            TypeId::hash_t tid = item.tid.GetHash();
-            memcpy(p, &tid, sizeof(TypeId::hash_t));
-            p += hashSize / 4;
-            size += hashSize;
-        }
-        else
+        size += hashSize;
+
+        if (size > maxSize)
         {
             return 0;
         }
 
-        if (size + 4 <= maxSize)
-        {
-            *p++ = item.size;
-            size += 4;
-        }
-        else
+        TypeId::hash_t tid = item.tid.GetHash();
+        memcpy(p, &tid, sizeof(TypeId::hash_t));
+        p += hashSize / 4;
+
+        size += 4;
+
+        if (size > maxSize)
         {
             return 0;
         }
 
-        if (size + 4 <= maxSize)
-        {
-            *p++ = item.start;
-            size += 4;
-        }
-        else
+        *p++ = item.size;
+
+        size += 4;
+
+        if (size > maxSize)
         {
             return 0;
         }
 
-        if (size + 4 <= maxSize)
-        {
-            *p++ = item.end;
-            size += 4;
-        }
-        else
+        *p++ = item.start;
+
+        size += 4;
+
+        if (size > maxSize)
         {
             return 0;
         }
 
+        *p++ = item.end;
+
         // ensure size is multiple of 4 bytes for 4 byte boundaries
         uint32_t tagWordSize = (item.size + 3) & (~3);
+        size += tagWordSize;
 
-        if (size + tagWordSize <= maxSize)
-        {
-            item.buf.Read(reinterpret_cast<uint8_t*>(p), item.size);
-            size += tagWordSize;
-            p += tagWordSize / 4;
-        }
-        else
+        if (size > maxSize)
         {
             return 0;
         }
 
+        item.buf.Read(reinterpret_cast<uint8_t*>(p), item.size);
+        p += tagWordSize / 4;
+
         (*numberOfTags)++;
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/channel-list.cc ns-3.41/src/network/model/channel-list.cc
--- ns-3.40/src/network/model/channel-list.cc	2023-09-27 13:30:37.850839600 -0700
+++ ns-3.41/src/network/model/channel-list.cc	2024-02-09 14:44:08.401793700 -0800
@@ -178,6 +178,7 @@
     NS_LOG_FUNCTION(this << channel);
     uint32_t index = m_channels.size();
     m_channels.push_back(channel);
+    Simulator::Schedule(TimeStep(0), &Channel::Initialize, channel);
     return index;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/nix-vector.cc ns-3.41/src/network/model/nix-vector.cc
--- ns-3.40/src/network/model/nix-vector.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/model/nix-vector.cc	2024-02-09 14:44:08.401793700 -0800
@@ -282,7 +282,7 @@
         m_epoch = *p++;
     }
 
-    return (GetSerializedSize());
+    return GetSerializedSize();
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/packet.cc ns-3.41/src/network/model/packet.cc
--- ns-3.40/src/network/model/packet.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/model/packet.cc	2024-02-09 14:44:08.421793700 -0800
@@ -669,149 +669,130 @@
     if (m_nixVector)
     {
         uint32_t nixSize = m_nixVector->GetSerializedSize();
-        if (size + nixSize <= maxSize)
+        size += nixSize;
+        if (size > maxSize)
         {
-            // put the total length of nix-vector in the
-            // buffer. this includes 4-bytes for total
-            // length itself
-            *p++ = nixSize + 4;
-            size += nixSize;
-
-            // serialize the nix-vector
-            uint32_t serialized = m_nixVector->Serialize(p, nixSize);
-            if (serialized)
-            {
-                // increment p by nixSize bytes
-                // ensuring 4-byte boundary
-                p += ((nixSize + 3) & (~3)) / 4;
-            }
-            else
-            {
-                return 0;
-            }
+            return 0;
         }
-        else
+
+        // put the total length of nix-vector in the
+        // buffer. this includes 4-bytes for total
+        // length itself
+        *p++ = nixSize + 4;
+
+        // serialize the nix-vector
+        uint32_t serialized = m_nixVector->Serialize(p, nixSize);
+        if (!serialized)
         {
             return 0;
         }
+
+        // increment p by nixSize bytes
+        // ensuring 4-byte boundary
+        p += ((nixSize + 3) & (~3)) / 4;
     }
     else
     {
         // no nix vector, set zero length,
         // ie 4-bytes, since it must include
         // length for itself
-        if (size + 4 <= maxSize)
-        {
-            size += 4;
-            *p++ = 4;
-        }
-        else
+        size += 4;
+        if (size > maxSize)
         {
             return 0;
         }
+
+        *p++ = 4;
     }
 
     // Serialize byte tag list
     uint32_t byteTagSize = m_byteTagList.GetSerializedSize();
-    if (size + byteTagSize <= maxSize)
+    size += byteTagSize;
+    if (size > maxSize)
     {
-        // put the total length of byte tag list in the
-        // buffer. this includes 4-bytes for total
-        // length itself
-        *p++ = byteTagSize + 4;
-        size += byteTagSize;
-
-        // serialize the byte tag list
-        uint32_t serialized = m_byteTagList.Serialize(p, byteTagSize);
-        if (serialized)
-        {
-            // increment p by byteTagSize bytes
-            // ensuring 4-byte boundary
-            p += ((byteTagSize + 3) & (~3)) / 4;
-        }
-        else
-        {
-            return 0;
-        }
+        return 0;
     }
-    else
+
+    // put the total length of byte tag list in the
+    // buffer. this includes 4-bytes for total
+    // length itself
+    *p++ = byteTagSize + 4;
+
+    // serialize the byte tag list
+    uint32_t serialized = m_byteTagList.Serialize(p, byteTagSize);
+    if (!serialized)
     {
         return 0;
     }
 
+    // increment p by byteTagSize bytes
+    // ensuring 4-byte boundary
+    p += ((byteTagSize + 3) & (~3)) / 4;
+
     // Serialize packet tag list
     uint32_t packetTagSize = m_packetTagList.GetSerializedSize();
-    if (size + packetTagSize <= maxSize)
+    size += packetTagSize;
+    if (size > maxSize)
     {
-        // put the total length of packet tag list in the
-        // buffer. this includes 4-bytes for total
-        // length itself
-        *p++ = packetTagSize + 4;
-        size += packetTagSize;
-
-        // serialize the packet tag list
-        uint32_t serialized = m_packetTagList.Serialize(p, packetTagSize);
-        if (serialized)
-        {
-            // increment p by packetTagSize bytes
-            // ensuring 4-byte boundary
-            p += ((packetTagSize + 3) & (~3)) / 4;
-        }
-        else
-        {
-            return 0;
-        }
+        return 0;
     }
-    else
+
+    // put the total length of packet tag list in the
+    // buffer. this includes 4-bytes for total
+    // length itself
+    *p++ = packetTagSize + 4;
+
+    // serialize the packet tag list
+    serialized = m_packetTagList.Serialize(p, packetTagSize);
+    if (!serialized)
     {
         return 0;
     }
 
+    // increment p by packetTagSize bytes
+    // ensuring 4-byte boundary
+    p += ((packetTagSize + 3) & (~3)) / 4;
+
     // Serialize Metadata
     uint32_t metaSize = m_metadata.GetSerializedSize();
-    if (size + metaSize <= maxSize)
+    size += metaSize;
+    if (size > maxSize)
     {
-        // put the total length of metadata in the
-        // buffer. this includes 4-bytes for total
-        // length itself
-        *p++ = metaSize + 4;
-        size += metaSize;
-
-        // serialize the metadata
-        uint32_t serialized = m_metadata.Serialize(reinterpret_cast<uint8_t*>(p), metaSize);
-        if (serialized)
-        {
-            // increment p by metaSize bytes
-            // ensuring 4-byte boundary
-            p += ((metaSize + 3) & (~3)) / 4;
-        }
-        else
-        {
-            return 0;
-        }
+        return 0;
     }
-    else
+
+    // put the total length of metadata in the
+    // buffer. this includes 4-bytes for total
+    // length itself
+    *p++ = metaSize + 4;
+
+    // serialize the metadata
+    serialized = m_metadata.Serialize(reinterpret_cast<uint8_t*>(p), metaSize);
+    if (!serialized)
     {
         return 0;
     }
 
+    // increment p by metaSize bytes
+    // ensuring 4-byte boundary
+    p += ((metaSize + 3) & (~3)) / 4;
+
     // Serialize the packet contents
     uint32_t bufSize = m_buffer.GetSerializedSize();
-    if (size + bufSize <= maxSize)
+    size += bufSize;
+    if (size > maxSize)
     {
-        // put the total length of the buffer in the
-        // buffer. this includes 4-bytes for total
-        // length itself
-        *p++ = bufSize + 4;
-
-        // serialize the buffer
-        uint32_t serialized = m_buffer.Serialize(reinterpret_cast<uint8_t*>(p), bufSize);
-        if (!serialized)
-        {
-            return 0;
-        }
+        return 0;
     }
-    else
+
+    // put the total length of the buffer in the
+    // buffer. this includes 4-bytes for total
+    // length itself
+    *p++ = bufSize + 4;
+
+    // serialize the buffer
+    serialized = m_buffer.Serialize(reinterpret_cast<uint8_t*>(p), bufSize);
+    if (!serialized)
     {
         return 0;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/packet-metadata.cc ns-3.41/src/network/model/packet-metadata.cc
--- ns-3.40/src/network/model/packet-metadata.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/model/packet-metadata.cc	2024-02-09 14:44:08.401793700 -0800
@@ -493,7 +493,7 @@
         AppendValue(extraItem->fragmentEnd, buffer);
         buffer += fragEndSize;
         Append32(extraItem->packetUid, buffer);
-        m_used = std::max(m_used, (uint16_t)(buffer - &m_data->m_data[0]));
+        m_used = std::max(m_used, static_cast<uint32_t>(buffer - &m_data->m_data[0]));
         m_data->m_dirtyEnd = m_used;
         return;
     }
@@ -1329,7 +1329,7 @@
 {
     NS_LOG_FUNCTION(static_cast<uint32_t>(data) << &start << &current << maxSize);
     // First check buffer overflow
-    if (static_cast<uint32_t>((current + sizeof(uint8_t) - start)) > maxSize)
+    if (static_cast<uint32_t>(current + sizeof(uint8_t) - start) > maxSize)
     {
         return nullptr;
     }
@@ -1345,7 +1345,7 @@
 {
     NS_LOG_FUNCTION(data << &start << &current << maxSize);
     // First check buffer overflow
-    if (static_cast<uint32_t>((current + sizeof(uint16_t) - start)) > maxSize)
+    if (static_cast<uint32_t>(current + sizeof(uint16_t) - start) > maxSize)
     {
         return nullptr;
     }
@@ -1361,7 +1361,7 @@
 {
     NS_LOG_FUNCTION(data << &start << &current << maxSize);
     // First check buffer overflow
-    if (static_cast<uint32_t>((current + sizeof(uint32_t) - start)) > maxSize)
+    if (static_cast<uint32_t>(current + sizeof(uint32_t) - start) > maxSize)
     {
         return nullptr;
     }
@@ -1377,7 +1377,7 @@
 {
     NS_LOG_FUNCTION(data << &start << &current << maxSize);
     // First check buffer overflow
-    if (static_cast<uint32_t>((current + sizeof(uint64_t) - start)) > maxSize)
+    if (static_cast<uint32_t>(current + sizeof(uint64_t) - start) > maxSize)
     {
         return nullptr;
     }
@@ -1394,7 +1394,7 @@
 {
     NS_LOG_FUNCTION(&data << dataSize << &start << &current << maxSize);
     // First check buffer overflow
-    if (static_cast<uint32_t>((current + dataSize - start)) > maxSize)
+    if (static_cast<uint32_t>(current + dataSize - start) > maxSize)
     {
         return nullptr;
     }
@@ -1410,7 +1410,7 @@
 {
     NS_LOG_FUNCTION(static_cast<uint32_t>(data) << &start << &current << maxSize);
     // First check buffer underflow
-    if (static_cast<uint32_t>((current + sizeof(uint8_t) - start)) > maxSize)
+    if (static_cast<uint32_t>(current + sizeof(uint8_t) - start) > maxSize)
     {
         return nullptr;
     }
@@ -1426,7 +1426,7 @@
 {
     NS_LOG_FUNCTION(data << &start << &current << maxSize);
     // First check buffer underflow
-    if (static_cast<uint32_t>((current + sizeof(uint16_t) - start)) > maxSize)
+    if (static_cast<uint32_t>(current + sizeof(uint16_t) - start) > maxSize)
     {
         return nullptr;
     }
@@ -1442,7 +1442,7 @@
 {
     NS_LOG_FUNCTION(data << &start << &current << maxSize);
     // First check buffer underflow
-    if (static_cast<uint32_t>((current + sizeof(uint32_t) - start)) > maxSize)
+    if (static_cast<uint32_t>(current + sizeof(uint32_t) - start) > maxSize)
     {
         return nullptr;
     }
@@ -1458,7 +1458,7 @@
 {
     NS_LOG_FUNCTION(data << &start << &current << maxSize);
     // First check buffer underflow
-    if ((uint32_t)((current + sizeof(uint64_t) - start)) > maxSize)
+    if ((uint32_t)(current + sizeof(uint64_t) - start) > maxSize)
     {
         return nullptr;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/packet-metadata.h ns-3.41/src/network/model/packet-metadata.h
--- ns-3.40/src/network/model/packet-metadata.h	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/model/packet-metadata.h	2024-02-09 14:44:08.421793700 -0800
@@ -428,7 +428,7 @@
         /** number of references to this struct Data instance. */
         uint32_t m_count;
         /** size (in bytes) of m_data buffer below */
-        uint16_t m_size;
+        uint32_t m_size;
         /** max of the m_used field over all objects which reference this struct Data instance */
         uint16_t m_dirtyEnd;
         /** variable-sized buffer of bytes */
@@ -697,7 +697,7 @@
      */
     uint16_t m_head;      //!< list head
     uint16_t m_tail;      //!< list tail
-    uint16_t m_used;      //!< used portion
+    uint32_t m_used;      //!< used portion
     uint64_t m_packetUid; //!< packet Uid
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/packet-tag-list.cc ns-3.41/src/network/model/packet-tag-list.cc
--- ns-3.40/src/network/model/packet-tag-list.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/model/packet-tag-list.cc	2024-02-09 14:44:08.421793700 -0800
@@ -266,7 +266,8 @@
     for (TagData* cur = m_next; cur != nullptr; cur = cur->next)
     {
         NS_ASSERT_MSG(cur->tid != tag.GetInstanceTypeId(),
-                      "Error: cannot add the same kind of tag twice.");
+                      "Error: cannot add the same kind of tag twice. The tag type is "
+                          << tag.GetInstanceTypeId().GetName());
     }
     TagData* head = CreateTagData(tag.GetSerializedSize());
     head->count = 1;
@@ -335,60 +336,54 @@
     uint32_t* p = buffer;
     uint32_t size = 0;
 
-    uint32_t* numberOfTags = nullptr;
+    size += 4;
 
-    if (size + 4 <= maxSize)
-    {
-        numberOfTags = p;
-        *p++ = 0;
-        size += 4;
-    }
-    else
+    if (size > maxSize)
     {
         return 0;
     }
 
+    uint32_t* numberOfTags = p;
+    *p++ = 0;
+
     for (TagData* cur = m_next; cur != nullptr; cur = cur->next)
     {
-        if (size + 4 <= maxSize)
-        {
-            *p++ = cur->size;
-            size += 4;
-        }
-        else
+        size += 4;
+
+        if (size > maxSize)
         {
             return 0;
         }
 
+        *p++ = cur->size;
+
         NS_LOG_INFO("Serializing tag id " << cur->tid);
 
         // ensure size is multiple of 4 bytes for 4 byte boundaries
         uint32_t hashSize = (sizeof(TypeId::hash_t) + 3) & (~3);
-        if (size + hashSize <= maxSize)
-        {
-            TypeId::hash_t tid = cur->tid.GetHash();
-            memcpy(p, &tid, sizeof(TypeId::hash_t));
-            p += hashSize / 4;
-            size += hashSize;
-        }
-        else
+        size += hashSize;
+
+        if (size > maxSize)
         {
             return 0;
         }
 
+        TypeId::hash_t tid = cur->tid.GetHash();
+        memcpy(p, &tid, sizeof(TypeId::hash_t));
+        p += hashSize / 4;
+
         // ensure size is multiple of 4 bytes for 4 byte boundaries
         uint32_t tagWordSize = (cur->size + 3) & (~3);
-        if (size + tagWordSize <= maxSize)
-        {
-            memcpy(p, cur->data, cur->size);
-            size += tagWordSize;
-            p += tagWordSize / 4;
-        }
-        else
+        size += tagWordSize;
+
+        if (size > maxSize)
         {
             return 0;
         }
 
+        memcpy(p, cur->data, cur->size);
+        p += tagWordSize / 4;
+
         (*numberOfTags)++;
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/model/socket.cc ns-3.41/src/network/model/socket.cc
--- ns-3.40/src/network/model/socket.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/model/socket.cc	2024-02-09 14:44:08.421793700 -0800
@@ -73,9 +73,11 @@
 {
     NS_LOG_FUNCTION(node << tid);
     Ptr<Socket> s;
-    NS_ASSERT(node);
+    NS_ASSERT_MSG(node, "CreateSocket: node is null.");
     Ptr<SocketFactory> socketFactory = node->GetObject<SocketFactory>(tid);
-    NS_ASSERT(socketFactory);
+    NS_ASSERT_MSG(socketFactory,
+                  "CreateSocket: can not create a "
+                      << tid.GetName() << " - perhaps the node is missing the required protocol.");
     s = socketFactory->CreateSocket();
     NS_ASSERT(s);
     return s;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/test/packet-test-suite.cc ns-3.41/src/network/test/packet-test-suite.cc
--- ns-3.40/src/network/test/packet-test-suite.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/test/packet-test-suite.cc	2024-02-09 14:44:08.421793700 -0800
@@ -1110,13 +1110,15 @@
     ref.Add(t6);       // pre-merge
     ref.Add(t7);       // first
 
-    { // Peek
+    // Peek
+    {
         std::cout << GetName() << "check Peek (missing tag) returns false" << std::endl;
         ATestTag<10> t10;
         NS_TEST_EXPECT_MSG_EQ(ref.Peek(t10), false, "missing tag");
     }
 
-    { // Copy ctor, assignment
+    // Copy ctor, assignment
+    {
         std::cout << GetName() << "check copy and assignment" << std::endl;
         {
             // Test copy constructor
@@ -1134,55 +1136,56 @@
         }
     }
 
-    {// Removal
+    // Removal
+    {
 #define RemoveCheck(n)                                                                             \
-    {                                                                                              \
-        PacketTagList p##n = ref;                                                                  \
-        p##n.Remove(t##n);                                                                         \
-        CheckRefList(ref, "remove " #n " orig");                                                   \
-        CheckRefList(p##n, "remove " #n " copy", n);                                               \
-    }
+    PacketTagList p##n = ref;                                                                      \
+    p##n.Remove(t##n);                                                                             \
+    CheckRefList(ref, "remove " #n " orig");                                                       \
+    CheckRefList(p##n, "remove " #n " copy", n);
 
-     {// Remove single tags from list
-      std::cout << GetName() << "check removal of each tag" << std::endl;
-    RemoveCheck(1);
-    RemoveCheck(2);
-    RemoveCheck(3);
-    RemoveCheck(4);
-    RemoveCheck(5);
-    RemoveCheck(6);
-    RemoveCheck(7);
-}
+        // Remove single tags from list
+        {
+            std::cout << GetName() << "check removal of each tag" << std::endl;
+            RemoveCheck(1);
+            RemoveCheck(2);
+            RemoveCheck(3);
+            RemoveCheck(4);
+            RemoveCheck(5);
+            RemoveCheck(6);
+            RemoveCheck(7);
+        }
 
-{ // Remove in the presence of a merge
-    std::cout << GetName() << "check removal doesn't disturb merge " << std::endl;
-    PacketTagList ptl = ref;
-    ptl.Remove(t7);
-    ptl.Remove(t6);
-    ptl.Remove(t5);
-
-    PacketTagList mrg = ptl; // merged list
-    ATestTag<8> m5(1);
-    mrg.Add(m5); // ptl and mrg differ
-    ptl.Add(t5);
-    ptl.Add(t6);
-    ptl.Add(t7);
-
-    CheckRefList(ref, "post merge, orig");
-    CheckRefList(ptl, "post merge, long chain");
-    const char* msg = "post merge, short chain";
-    CheckRef(mrg, t1, msg, false);
-    CheckRef(mrg, t2, msg, false);
-    CheckRef(mrg, t3, msg, false);
-    CheckRef(mrg, t4, msg, false);
-    CheckRef(mrg, m5, msg, false);
-}
+        // Remove in the presence of a merge
+        {
+            std::cout << GetName() << "check removal doesn't disturb merge " << std::endl;
+            PacketTagList ptl = ref;
+            ptl.Remove(t7);
+            ptl.Remove(t6);
+            ptl.Remove(t5);
+
+            PacketTagList mrg = ptl; // merged list
+            ATestTag<8> m5(1);
+            mrg.Add(m5); // ptl and mrg differ
+            ptl.Add(t5);
+            ptl.Add(t6);
+            ptl.Add(t7);
+
+            CheckRefList(ref, "post merge, orig");
+            CheckRefList(ptl, "post merge, long chain");
+            const char* msg = "post merge, short chain";
+            CheckRef(mrg, t1, msg, false);
+            CheckRef(mrg, t2, msg, false);
+            CheckRef(mrg, t3, msg, false);
+            CheckRef(mrg, t4, msg, false);
+            CheckRef(mrg, m5, msg, false);
+        }
 #undef RemoveCheck
-} // Removal
-
-{ // Replace
+    } // Removal
 
-    std::cout << GetName() << "check replacing each tag" << std::endl;
+    // Replace
+    {
+        std::cout << GetName() << "check replacing each tag" << std::endl;
 
 #define ReplaceCheck(n)                                                                            \
     t##n.m_data = 2;                                                                               \
@@ -1193,75 +1196,76 @@
         CheckRef(p##n, t##n, "replace " #n " copy");                                               \
     }
 
-    ReplaceCheck(1);
-    ReplaceCheck(2);
-    ReplaceCheck(3);
-    ReplaceCheck(4);
-    ReplaceCheck(5);
-    ReplaceCheck(6);
-    ReplaceCheck(7);
-}
-
-{ // Timing
-    std::cout << GetName() << "add+remove timing" << std::endl;
-    int flm = std::numeric_limits<int>::max();
-    const int nIterations = 100;
-    for (int i = 0; i < nIterations; ++i)
-    {
-        int now = AddRemoveTime();
-        if (now < flm)
-        {
-            flm = now;
-        }
+        ReplaceCheck(1);
+        ReplaceCheck(2);
+        ReplaceCheck(3);
+        ReplaceCheck(4);
+        ReplaceCheck(5);
+        ReplaceCheck(6);
+        ReplaceCheck(7);
     }
-    std::cout << GetName() << "min add+remove time: " << std::setw(8) << flm << " ticks"
-              << std::endl;
 
-    std::cout << GetName() << "remove timing" << std::endl;
-    // tags numbered from 1, so add one for (unused) entry at 0
-    std::vector<int> rmn(TAG_LAST + 1, std::numeric_limits<int>::max());
-    for (int i = 0; i < nIterations; ++i)
+    // Timing
     {
-        for (int j = 1; j <= TAG_LAST; ++j)
+        std::cout << GetName() << "add+remove timing" << std::endl;
+        int flm = std::numeric_limits<int>::max();
+        const int nIterations = 100;
+        for (int i = 0; i < nIterations; ++i)
         {
-            int now = 0;
-            switch (j)
+            int now = AddRemoveTime();
+            if (now < flm)
             {
-            case 7:
-                now = RemoveTime(ref, t7);
-                break;
-            case 6:
-                now = RemoveTime(ref, t6);
-                break;
-            case 5:
-                now = RemoveTime(ref, t5);
-                break;
-            case 4:
-                now = RemoveTime(ref, t4);
-                break;
-            case 3:
-                now = RemoveTime(ref, t3);
-                break;
-            case 2:
-                now = RemoveTime(ref, t2);
-                break;
-            case 1:
-                now = RemoveTime(ref, t1);
-                break;
-            } // switch
+                flm = now;
+            }
+        }
+        std::cout << GetName() << "min add+remove time: " << std::setw(8) << flm << " ticks"
+                  << std::endl;
 
-            if (now < rmn[j])
+        std::cout << GetName() << "remove timing" << std::endl;
+        // tags numbered from 1, so add one for (unused) entry at 0
+        std::vector<int> rmn(TAG_LAST + 1, std::numeric_limits<int>::max());
+        for (int i = 0; i < nIterations; ++i)
+        {
+            for (int j = 1; j <= TAG_LAST; ++j)
             {
-                rmn[j] = now;
-            }
-        } // for tag j
-    }     // for iteration i
-    for (int j = TAG_LAST; j > 0; --j)
-    {
-        std::cout << GetName() << "min remove time: t" << j << ": " << std::setw(8) << rmn[j]
-                  << " ticks" << std::endl;
-    }
-} // Timing
+                int now = 0;
+                switch (j)
+                {
+                case 7:
+                    now = RemoveTime(ref, t7);
+                    break;
+                case 6:
+                    now = RemoveTime(ref, t6);
+                    break;
+                case 5:
+                    now = RemoveTime(ref, t5);
+                    break;
+                case 4:
+                    now = RemoveTime(ref, t4);
+                    break;
+                case 3:
+                    now = RemoveTime(ref, t3);
+                    break;
+                case 2:
+                    now = RemoveTime(ref, t2);
+                    break;
+                case 1:
+                    now = RemoveTime(ref, t1);
+                    break;
+                } // switch
+
+                if (now < rmn[j])
+                {
+                    rmn[j] = now;
+                }
+            } // for tag j
+        }     // for iteration i
+        for (int j = TAG_LAST; j > 0; --j)
+        {
+            std::cout << GetName() << "min remove time: t" << j << ": " << std::setw(8) << rmn[j]
+                      << " ticks" << std::endl;
+        }
+    } // Timing
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/data-rate.cc ns-3.41/src/network/utils/data-rate.cc
--- ns-3.40/src/network/utils/data-rate.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/utils/data-rate.cc	2024-02-09 14:44:08.425794000 -0800
@@ -228,7 +228,7 @@
 DataRate
 DataRate::operator*(double rhs) const
 {
-    return DataRate(((uint64_t)(m_bps * rhs)));
+    return DataRate((uint64_t)(m_bps * rhs));
 }
 
 DataRate&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/error-model.cc ns-3.41/src/network/utils/error-model.cc
--- ns-3.40/src/network/utils/error-model.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/utils/error-model.cc	2024-02-09 14:44:08.425794000 -0800
@@ -160,7 +160,7 @@
             .AddAttribute("ErrorUnit",
                           "The error unit",
                           EnumValue(ERROR_UNIT_BYTE),
-                          MakeEnumAccessor(&RateErrorModel::m_unit),
+                          MakeEnumAccessor<ErrorUnit>(&RateErrorModel::m_unit),
                           MakeEnumChecker(ERROR_UNIT_BIT,
                                           "ERROR_UNIT_BIT",
                                           ERROR_UNIT_BYTE,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/ipv4-address.cc ns-3.41/src/network/utils/ipv4-address.cc
--- ns-3.40/src/network/utils/ipv4-address.cc	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/utils/ipv4-address.cc	2024-02-09 14:44:08.425794000 -0800
@@ -248,7 +248,7 @@
 Ipv4Address::IsInitialized() const
 {
     NS_LOG_FUNCTION(this);
-    return (m_initialized);
+    return m_initialized;
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/ipv4-address.h ns-3.41/src/network/utils/ipv4-address.h
--- ns-3.40/src/network/utils/ipv4-address.h	2023-09-27 13:30:37.854839600 -0700
+++ ns-3.41/src/network/utils/ipv4-address.h	2024-02-09 14:44:08.425794000 -0800
@@ -377,19 +377,19 @@
 inline bool
 operator==(const Ipv4Address& a, const Ipv4Address& b)
 {
-    return (a.m_address == b.m_address);
+    return a.m_address == b.m_address;
 }
 
 inline bool
 operator!=(const Ipv4Address& a, const Ipv4Address& b)
 {
-    return (a.m_address != b.m_address);
+    return a.m_address != b.m_address;
 }
 
 inline bool
 operator<(const Ipv4Address& a, const Ipv4Address& b)
 {
-    return (a.m_address < b.m_address);
+    return a.m_address < b.m_address;
 }
 
 /**
@@ -414,13 +414,13 @@
 inline bool
 operator==(const Ipv4Mask& a, const Ipv4Mask& b)
 {
-    return (a.m_mask == b.m_mask);
+    return a.m_mask == b.m_mask;
 }
 
 inline bool
 operator!=(const Ipv4Mask& a, const Ipv4Mask& b)
 {
-    return (a.m_mask != b.m_mask);
+    return a.m_mask != b.m_mask;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/ipv6-address.cc ns-3.41/src/network/utils/ipv6-address.cc
--- ns-3.40/src/network/utils/ipv6-address.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/ipv6-address.cc	2024-02-09 14:44:08.425794000 -0800
@@ -260,7 +260,7 @@
         0x00,
     };
     addr.Serialize(&buf[12]);
-    return (Ipv6Address(buf));
+    return Ipv6Address(buf);
 }
 
 Ipv4Address
@@ -272,7 +272,7 @@
 
     Serialize(buf);
     v4Addr = Ipv4Address::Deserialize(&buf[12]);
-    return (v4Addr);
+    return v4Addr;
 }
 
 Ipv6Address
@@ -568,11 +568,7 @@
     NS_LOG_FUNCTION(this);
     static uint8_t v4MappedPrefix[12] =
         {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
-    if (memcmp(m_address, v4MappedPrefix, sizeof(v4MappedPrefix)) == 0)
-    {
-        return (true);
-    }
-    return (false);
+    return memcmp(m_address, v4MappedPrefix, sizeof(v4MappedPrefix)) == 0;
 }
 
 Ipv6Address
@@ -651,7 +647,7 @@
     Ipv6Address masked = CombinePrefix(prefix);
     Ipv6Address reference = Ipv6Address::GetOnes().CombinePrefix(prefix);
 
-    return (masked == reference);
+    return masked == reference;
 }
 
 bool
@@ -768,7 +764,7 @@
 Ipv6Address::IsInitialized() const
 {
     NS_LOG_FUNCTION(this);
-    return (m_initialized);
+    return m_initialized;
 }
 
 std::ostream&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/ipv6-address.h ns-3.41/src/network/utils/ipv6-address.h
--- ns-3.40/src/network/utils/ipv6-address.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/ipv6-address.h	2024-02-09 14:44:08.425794000 -0800
@@ -654,7 +654,7 @@
 inline bool
 operator==(const Ipv6Address& a, const Ipv6Address& b)
 {
-    return (!std::memcmp(a.m_address, b.m_address, 16));
+    return !std::memcmp(a.m_address, b.m_address, 16);
 }
 
 inline bool
@@ -666,13 +666,13 @@
 inline bool
 operator<(const Ipv6Address& a, const Ipv6Address& b)
 {
-    return (std::memcmp(a.m_address, b.m_address, 16) < 0);
+    return std::memcmp(a.m_address, b.m_address, 16) < 0;
 }
 
 inline bool
 operator==(const Ipv6Prefix& a, const Ipv6Prefix& b)
 {
-    return (!std::memcmp(a.m_prefix, b.m_prefix, 16));
+    return !std::memcmp(a.m_prefix, b.m_prefix, 16);
 }
 
 inline bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac16-address.cc ns-3.41/src/network/utils/mac16-address.cc
--- ns-3.40/src/network/utils/mac16-address.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac16-address.cc	2024-02-09 14:44:08.425794000 -0800
@@ -35,73 +35,20 @@
 
 ATTRIBUTE_HELPER_CPP(Mac16Address);
 
-#define ASCII_a (0x41)
-#define ASCII_z (0x5a)
-#define ASCII_A (0x61)
-#define ASCII_Z (0x7a)
-#define ASCII_COLON (0x3a)
-#define ASCII_ZERO (0x30)
-
-/**
- * Converts a char to lower case.
- * \param c the char
- * \returns the lower case
- */
-static char
-AsciiToLowCase(char c)
-{
-    if (c >= ASCII_a && c <= ASCII_z)
-    {
-        return c;
-    }
-    else if (c >= ASCII_A && c <= ASCII_Z)
-    {
-        return c + (ASCII_a - ASCII_A);
-    }
-    else
-    {
-        return c;
-    }
-}
-
 uint64_t Mac16Address::m_allocationIndex = 0;
 
-Mac16Address::Mac16Address()
-{
-    NS_LOG_FUNCTION(this);
-    memset(m_address, 0, 2);
-}
-
 Mac16Address::Mac16Address(const char* str)
 {
     NS_LOG_FUNCTION(this << str);
-    int i = 0;
-    while (*str != 0 && i < 2)
-    {
-        uint8_t byte = 0;
-        while (*str != ASCII_COLON && *str != 0)
-        {
-            byte <<= 4;
-            char low = AsciiToLowCase(*str);
-            if (low >= ASCII_a)
-            {
-                byte |= low - ASCII_a + 10;
-            }
-            else
-            {
-                byte |= low - ASCII_ZERO;
-            }
-            str++;
-        }
-        m_address[i] = byte;
-        i++;
-        if (*str == 0)
-        {
-            break;
-        }
-        str++;
-    }
-    NS_ASSERT(i == 2);
+    NS_ASSERT_MSG(strlen(str) <= 5, "Mac16Address: illegal string (too long) " << str);
+
+    unsigned int bytes[2];
+    int charsRead = 0;
+
+    int i = sscanf(str, "%02x:%02x%n", bytes, bytes + 1, &charsRead);
+    NS_ASSERT_MSG(i == 2 && !str[charsRead], "Mac16Address: illegal string " << str);
+
+    std::copy(std::begin(bytes), std::end(bytes), std::begin(m_address));
 }
 
 Mac16Address::Mac16Address(uint16_t addr)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac16-address.h ns-3.41/src/network/utils/mac16-address.h
--- ns-3.40/src/network/utils/mac16-address.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac16-address.h	2024-02-09 14:44:08.425794000 -0800
@@ -43,10 +43,11 @@
 class Mac16Address
 {
   public:
-    Mac16Address();
+    Mac16Address() = default;
     /**
      * \param str a string representing the new Mac16Address
      *
+     * The format of the string is "xx:xx"
      */
     Mac16Address(const char* str);
 
@@ -228,7 +229,7 @@
     friend std::istream& operator>>(std::istream& is, Mac16Address& address);
 
     static uint64_t m_allocationIndex; //!< Address allocation index
-    uint8_t m_address[2];              //!< address value
+    uint8_t m_address[2]{0};           //!< Address value
 };
 
 ATTRIBUTE_HELPER_HEADER(Mac16Address);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac48-address.cc ns-3.41/src/network/utils/mac48-address.cc
--- ns-3.40/src/network/utils/mac48-address.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac48-address.cc	2024-02-09 14:44:08.425794000 -0800
@@ -34,74 +34,28 @@
 
 ATTRIBUTE_HELPER_CPP(Mac48Address);
 
-#define ASCII_a (0x41)
-#define ASCII_z (0x5a)
-#define ASCII_A (0x61)
-#define ASCII_Z (0x7a)
-#define ASCII_COLON (0x3a)
-#define ASCII_ZERO (0x30)
-
-/**
- * Converts a char to lower case.
- * \param c the char
- * \returns the lower case
- */
-static char
-AsciiToLowCase(char c)
-{
-    NS_LOG_FUNCTION(c);
-    if (c >= ASCII_a && c <= ASCII_z)
-    {
-        return c;
-    }
-    else if (c >= ASCII_A && c <= ASCII_Z)
-    {
-        return c + (ASCII_a - ASCII_A);
-    }
-    else
-    {
-        return c;
-    }
-}
-
 uint64_t Mac48Address::m_allocationIndex = 0;
 
-Mac48Address::Mac48Address()
-{
-    NS_LOG_FUNCTION(this);
-    std::memset(m_address, 0, 6);
-}
-
 Mac48Address::Mac48Address(const char* str)
 {
     NS_LOG_FUNCTION(this << str);
-    int i = 0;
-    while (*str != 0 && i < 6)
-    {
-        uint8_t byte = 0;
-        while (*str != ASCII_COLON && *str != 0)
-        {
-            byte <<= 4;
-            char low = AsciiToLowCase(*str);
-            if (low >= ASCII_a)
-            {
-                byte |= low - ASCII_a + 10;
-            }
-            else
-            {
-                byte |= low - ASCII_ZERO;
-            }
-            str++;
-        }
-        m_address[i] = byte;
-        i++;
-        if (*str == 0)
-        {
-            break;
-        }
-        str++;
-    }
-    NS_ASSERT(i == 6);
+    NS_ASSERT_MSG(strlen(str) <= 17, "Mac48Address: illegal string (too long) " << str);
+
+    unsigned int bytes[6];
+    int charsRead = 0;
+
+    int i = sscanf(str,
+                   "%02x:%02x:%02x:%02x:%02x:%02x%n",
+                   bytes,
+                   bytes + 1,
+                   bytes + 2,
+                   bytes + 3,
+                   bytes + 4,
+                   bytes + 5,
+                   &charsRead);
+    NS_ASSERT_MSG(i == 6 && !str[charsRead], "Mac48Address: illegal string " << str);
+
+    std::copy(std::begin(bytes), std::end(bytes), std::begin(m_address));
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac48-address.h ns-3.41/src/network/utils/mac48-address.h
--- ns-3.40/src/network/utils/mac48-address.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac48-address.h	2024-02-09 14:44:08.425794000 -0800
@@ -45,7 +45,7 @@
 class Mac48Address
 {
   public:
-    Mac48Address();
+    Mac48Address() = default;
     /**
      * \param str a string representing the new Mac48Address
      *
@@ -213,7 +213,7 @@
     friend std::istream& operator>>(std::istream& is, Mac48Address& address);
 
     static uint64_t m_allocationIndex; //!< Address allocation index
-    uint8_t m_address[6];              //!< address value
+    uint8_t m_address[6]{0};           //!< Address value
 };
 
 ATTRIBUTE_HELPER_HEADER(Mac48Address);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac64-address.cc ns-3.41/src/network/utils/mac64-address.cc
--- ns-3.40/src/network/utils/mac64-address.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac64-address.cc	2024-02-09 14:44:08.425794000 -0800
@@ -34,74 +34,30 @@
 
 ATTRIBUTE_HELPER_CPP(Mac64Address);
 
-#define ASCII_a (0x41)
-#define ASCII_z (0x5a)
-#define ASCII_A (0x61)
-#define ASCII_Z (0x7a)
-#define ASCII_COLON (0x3a)
-#define ASCII_ZERO (0x30)
-
-/**
- * Converts a char to lower case.
- * \param c the char
- * \returns the lower case
- */
-static char
-AsciiToLowCase(char c)
-{
-    NS_LOG_FUNCTION(c);
-    if (c >= ASCII_a && c <= ASCII_z)
-    {
-        return c;
-    }
-    else if (c >= ASCII_A && c <= ASCII_Z)
-    {
-        return c + (ASCII_a - ASCII_A);
-    }
-    else
-    {
-        return c;
-    }
-}
-
 uint64_t Mac64Address::m_allocationIndex = 0;
 
-Mac64Address::Mac64Address()
-{
-    NS_LOG_FUNCTION(this);
-    std::memset(m_address, 0, 8);
-}
-
 Mac64Address::Mac64Address(const char* str)
 {
     NS_LOG_FUNCTION(this << str);
-    int i = 0;
-    while (*str != 0 && i < 8)
-    {
-        uint8_t byte = 0;
-        while (*str != ASCII_COLON && *str != 0)
-        {
-            byte <<= 4;
-            char low = AsciiToLowCase(*str);
-            if (low >= ASCII_a)
-            {
-                byte |= low - ASCII_a + 10;
-            }
-            else
-            {
-                byte |= low - ASCII_ZERO;
-            }
-            str++;
-        }
-        m_address[i] = byte;
-        i++;
-        if (*str == 0)
-        {
-            break;
-        }
-        str++;
-    }
-    NS_ASSERT(i == 8);
+    NS_ASSERT_MSG(strlen(str) <= 23, "Mac64Address: illegal string (too long) " << str);
+
+    unsigned int bytes[8];
+    int charsRead = 0;
+
+    int i = sscanf(str,
+                   "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x%n",
+                   bytes,
+                   bytes + 1,
+                   bytes + 2,
+                   bytes + 3,
+                   bytes + 4,
+                   bytes + 5,
+                   bytes + 6,
+                   bytes + 7,
+                   &charsRead);
+    NS_ASSERT_MSG(i == 8 && !str[charsRead], "Mac64Address: illegal string " << str);
+
+    std::copy(std::begin(bytes), std::end(bytes), std::begin(m_address));
 }
 
 Mac64Address::Mac64Address(uint64_t addr)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac64-address.h ns-3.41/src/network/utils/mac64-address.h
--- ns-3.40/src/network/utils/mac64-address.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac64-address.h	2024-02-09 14:44:08.425794000 -0800
@@ -45,7 +45,7 @@
 class Mac64Address
 {
   public:
-    Mac64Address();
+    Mac64Address() = default;
     /**
      * \param str a string representing the new Mac64Address
      *
@@ -180,7 +180,7 @@
     friend std::istream& operator>>(std::istream& is, Mac64Address& address);
 
     static uint64_t m_allocationIndex; //!< Address allocation index
-    uint8_t m_address[8];              //!< address value
+    uint8_t m_address[8]{0};           //!< Address value
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac8-address.cc ns-3.41/src/network/utils/mac8-address.cc
--- ns-3.40/src/network/utils/mac8-address.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac8-address.cc	2024-02-09 14:44:08.425794000 -0800
@@ -30,11 +30,6 @@
 
 uint8_t Mac8Address::m_allocationIndex = 0;
 
-Mac8Address::Mac8Address()
-{
-    m_address = 255;
-}
-
 Mac8Address::Mac8Address(uint8_t addr)
     : m_address(addr)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/mac8-address.h ns-3.41/src/network/utils/mac8-address.h
--- ns-3.40/src/network/utils/mac8-address.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/mac8-address.h	2024-02-09 14:44:08.425794000 -0800
@@ -43,8 +43,7 @@
 class Mac8Address
 {
   public:
-    /** Constructor */
-    Mac8Address();
+    Mac8Address() = default;
     /**
      * Create Mac8Address object with address addr.
      *
@@ -132,7 +131,7 @@
 
   private:
     static uint8_t m_allocationIndex; //!< Address allocation index
-    uint8_t m_address;                //!< The address.
+    uint8_t m_address{255};           //!< The address.
 
     /**
      * Get the Mac8Address type.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/net-device-queue-interface.h ns-3.41/src/network/utils/net-device-queue-interface.h
--- ns-3.40/src/network/utils/net-device-queue-interface.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/net-device-queue-interface.h	2024-02-09 14:44:08.425794000 -0800
@@ -364,7 +364,7 @@
     NS_LOG_FUNCTION(this << queue << item);
     NS_ASSERT_MSG(m_device, "Aggregated NetDevice not set");
 
-    Simulator::ScheduleNow([=]() {
+    Simulator::ScheduleNow([=, this]() {
         // Inform BQL
         NotifyTransmittedBytes(item->GetSize());
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/packet-socket.cc ns-3.41/src/network/utils/packet-socket.cc
--- ns-3.40/src/network/utils/packet-socket.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/packet-socket.cc	2024-02-09 14:44:08.425794000 -0800
@@ -122,7 +122,7 @@
 PacketSocket::Bind6()
 {
     NS_LOG_FUNCTION(this);
-    return (Bind());
+    return Bind();
 }
 
 int
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/packet-socket-client.cc ns-3.41/src/network/utils/packet-socket-client.cc
--- ns-3.40/src/network/utils/packet-socket-client.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/packet-socket-client.cc	2024-02-09 14:44:08.425794000 -0800
@@ -183,7 +183,17 @@
 
     if ((m_sent < m_maxPackets) || (m_maxPackets == 0))
     {
-        m_sendEvent = Simulator::Schedule(m_interval, &PacketSocketClient::Send, this);
+        if (m_interval.IsZero())
+        {
+            NS_ABORT_MSG_IF(
+                m_maxPackets == 0,
+                "Generating infinite packets at the same time does not seem to be a good idea");
+            Send();
+        }
+        else
+        {
+            m_sendEvent = Simulator::Schedule(m_interval, &PacketSocketClient::Send, this);
+        }
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/packet-socket-client.h ns-3.41/src/network/utils/packet-socket-client.h
--- ns-3.40/src/network/utils/packet-socket-client.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/packet-socket-client.h	2024-02-09 14:44:08.425794000 -0800
@@ -91,6 +91,11 @@
 
     /**
      * \brief Send a packet
+     *
+     * Either <i>Interval</i> and <i>MaxPackets</i> may be zero, but not both.  If <i>Interval</i>
+     * is zero, the PacketSocketClient will send <i>MaxPackets</i> packets without any delay into
+     * the socket.  If <i>MaxPackets</i> is zero, then the PacketSocketClient will send every
+     * <i>Interval</i> until the application is stopped.
      */
     void Send();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/queue-size.cc ns-3.41/src/network/utils/queue-size.cc
--- ns-3.40/src/network/utils/queue-size.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/queue-size.cc	2024-02-09 14:44:08.429793800 -0800
@@ -1,21 +1,21 @@
-//
-// Copyright (c) 2018 Universita' degli Studi di Napoli Federico II
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation;
-//
-// 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; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-// Author: Stefano Avallone <stavallo@unina.it>
-//
+/*
+ * Copyright (c) 2018 Universita' degli Studi di Napoli Federico II
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Stefano Avallone <stavallo@unina.it>
+ */
 
 #include "queue-size.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/queue-size.h ns-3.41/src/network/utils/queue-size.h
--- ns-3.40/src/network/utils/queue-size.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/queue-size.h	2024-02-09 14:44:08.429793800 -0800
@@ -1,21 +1,21 @@
-//
-// Copyright (c) 2018 Universita' degli Studi di Napoli Federico II
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License version 2 as
-// published by the Free Software Foundation;
-//
-// 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; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-// Author: Stefano Avallone <stavallo@unina.it>
-//
+/*
+ * Copyright (c) 2018 Universita' degli Studi di Napoli Federico II
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Stefano Avallone <stavallo@unina.it>
+ */
 
 #ifndef QUEUE_SIZE_H
 #define QUEUE_SIZE_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/network/utils/sequence-number.h ns-3.41/src/network/utils/sequence-number.h
--- ns-3.40/src/network/utils/sequence-number.h	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/network/utils/sequence-number.h	2024-02-09 14:44:08.429793800 -0800
@@ -293,7 +293,7 @@
      */
     bool operator==(const SequenceNumber<NUMERIC_TYPE, SIGNED_TYPE>& other) const
     {
-        return (m_value == other.m_value);
+        return m_value == other.m_value;
     }
 
     /**
@@ -303,7 +303,7 @@
      */
     bool operator!=(const SequenceNumber<NUMERIC_TYPE, SIGNED_TYPE>& other) const
     {
-        return (m_value != other.m_value);
+        return m_value != other.m_value;
     }
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/nix-vector-routing/model/nix-vector-routing.cc ns-3.41/src/nix-vector-routing/model/nix-vector-routing.cc
--- ns-3.40/src/nix-vector-routing/model/nix-vector-routing.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/nix-vector-routing/model/nix-vector-routing.cc	2024-02-09 14:44:08.429793800 -0800
@@ -68,7 +68,7 @@
     {
         name = "Ipv6";
     }
-    static TypeId tid = TypeId(("ns3::" + name + "NixVectorRouting"))
+    static TypeId tid = TypeId("ns3::" + name + "NixVectorRouting")
                             .SetParent<T>()
                             .SetGroupName("NixVectorRouting")
                             .template AddConstructor<NixVectorRouting<T>>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/nix-vector-routing/test/examples-to-run.py ns-3.41/src/nix-vector-routing/test/examples-to-run.py
--- ns-3.40/src/nix-vector-routing/test/examples-to-run.py	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/nix-vector-routing/test/examples-to-run.py	2024-02-09 14:44:08.433793800 -0800
@@ -8,7 +8,7 @@
 # See test.py for more information.
 cpp_examples = [
     ("nix-simple", "True", "True"),
-    ("nms-p2p-nix", "False", "True"), # Takes too long to run
+    ("nms-p2p-nix", "False", "True"),  # Takes too long to run
 ]
 
 # A list of Python examples to run in order to ensure that they remain
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/olsr/examples/olsr-hna.cc ns-3.41/src/olsr/examples/olsr-hna.cc
--- ns-3.40/src/olsr/examples/olsr-hna.cc	2023-09-27 13:30:37.858839800 -0700
+++ ns-3.41/src/olsr/examples/olsr-hna.cc	2024-02-09 14:44:08.433793800 -0800
@@ -49,7 +49,6 @@
 // ./ns3 run "olsr-hna --assocMethod=AddHostNetwork"
 //
 
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/olsr/model/olsr-routing-protocol.cc ns-3.41/src/olsr/model/olsr-routing-protocol.cc
--- ns-3.40/src/olsr/model/olsr-routing-protocol.cc	2023-09-27 13:30:37.862839700 -0700
+++ ns-3.41/src/olsr/model/olsr-routing-protocol.cc	2024-02-09 14:44:08.433793800 -0800
@@ -226,7 +226,7 @@
             .AddAttribute("Willingness",
                           "Willingness of a node to carry and forward traffic for other nodes.",
                           EnumValue(Willingness::DEFAULT),
-                          MakeEnumAccessor(&RoutingProtocol::m_willingness),
+                          MakeEnumAccessor<Willingness>(&RoutingProtocol::m_willingness),
                           MakeEnumChecker(Willingness::NEVER,
                                           "never",
                                           Willingness::LOW,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/openflow/CMakeLists.txt ns-3.41/src/openflow/CMakeLists.txt
--- ns-3.40/src/openflow/CMakeLists.txt	2023-09-27 13:30:37.862839700 -0700
+++ ns-3.41/src/openflow/CMakeLists.txt	2024-02-09 14:44:08.433793800 -0800
@@ -14,6 +14,7 @@
   HEADER_NAME openflow.h
   LIBRARY_NAME openflow
   SEARCH_PATHS ${NS3_WITH_OPENFLOW}
+  OUTPUT_VARIABLE "NS3_OPENFLOW_REASON"
 )
 
 if((NOT
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/openflow/examples/openflow-switch.py ns-3.41/src/openflow/examples/openflow-switch.py
--- ns-3.40/src/openflow/examples/openflow-switch.py	2023-09-27 13:30:37.862839700 -0700
+++ ns-3.41/src/openflow/examples/openflow-switch.py	2024-02-09 14:44:08.433793800 -0800
@@ -18,7 +18,14 @@
 #              Gabriel Ferreira <gabrielcarvfer@gmail.com>
 #
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 ns.LogComponentEnable("OpenFlowInterface", ns.LOG_LEVEL_ALL)
 ns.LogComponentEnable("OpenFlowSwitchNetDevice", ns.LOG_LEVEL_ALL)
@@ -60,7 +67,9 @@
 
 port = 9
 
-onoff = ns.OnOffHelper("ns3::UdpSocketFactory", ns.InetSocketAddress(ns.Ipv4Address("10.1.1.2"), port).ConvertTo())
+onoff = ns.OnOffHelper(
+    "ns3::UdpSocketFactory", ns.InetSocketAddress(ns.Ipv4Address("10.1.1.2"), port).ConvertTo()
+)
 onoff.SetConstantRate(ns.DataRate("500kb/s"))
 
 app = onoff.Install(terminals.Get(0))
@@ -68,12 +77,15 @@
 app.Start(ns.Seconds(1.0))
 app.Stop(ns.Seconds(10.0))
 
-sink = ns.PacketSinkHelper("ns3::UdpSocketFactory",
-                           ns.InetSocketAddress(ns.Ipv4Address.GetAny(), port).ConvertTo())
+sink = ns.PacketSinkHelper(
+    "ns3::UdpSocketFactory", ns.InetSocketAddress(ns.Ipv4Address.GetAny(), port).ConvertTo()
+)
 app = sink.Install(terminals.Get(1))
 app.Start(ns.Seconds(0.0))
 
-onoff.SetAttribute("Remote", ns.AddressValue(ns.InetSocketAddress(ns.Ipv4Address("10.1.1.1"), port).ConvertTo()))
+onoff.SetAttribute(
+    "Remote", ns.AddressValue(ns.InetSocketAddress(ns.Ipv4Address("10.1.1.1"), port).ConvertTo())
+)
 app = onoff.Install(terminals.Get(3))
 app.Start(ns.Seconds(1.1))
 app.Stop(ns.Seconds(10.0))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/point-to-point/helper/point-to-point-helper.cc ns-3.41/src/point-to-point/helper/point-to-point-helper.cc
--- ns-3.40/src/point-to-point/helper/point-to-point-helper.cc	2023-09-27 13:30:37.862839700 -0700
+++ ns-3.41/src/point-to-point/helper/point-to-point-helper.cc	2024-02-09 14:44:08.453794000 -0800
@@ -197,7 +197,7 @@
     uint32_t deviceid = nd->GetIfIndex();
     std::ostringstream oss;
 
-    oss << "/NodeList/" << nd->GetNode()->GetId() << "/DeviceList/" << deviceid
+    oss << "/NodeList/" << nodeid << "/DeviceList/" << deviceid
         << "/$ns3::PointToPointNetDevice/MacRx";
     Config::Connect(oss.str(),
                     MakeBoundCallback(&AsciiTraceHelper::DefaultReceiveSinkWithContext, stream));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/CMakeLists.txt ns-3.41/src/propagation/CMakeLists.txt
--- ns-3.40/src/propagation/CMakeLists.txt	2023-09-27 13:30:37.862839700 -0700
+++ ns-3.41/src/propagation/CMakeLists.txt	2024-02-09 14:44:08.453794000 -0800
@@ -30,8 +30,7 @@
     model/propagation-loss-model.h
     model/three-gpp-propagation-loss-model.h
     model/three-gpp-v2v-propagation-loss-model.h
-  LIBRARIES_TO_LINK ${libnetwork}
-                    ${libmobility}
+  LIBRARIES_TO_LINK ${libmobility}
   TEST_SOURCES
     test/channel-condition-model-test-suite.cc
     test/itu-r-1411-los-test-suite.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/doc/propagation.rst ns-3.41/src/propagation/doc/propagation.rst
--- ns-3.40/src/propagation/doc/propagation.rst	2023-09-27 13:30:37.862839700 -0700
+++ ns-3.41/src/propagation/doc/propagation.rst	2024-02-09 14:44:08.453794000 -0800
@@ -410,7 +410,7 @@
 
 
 The literature lacks of extensions of the COST231 to open area (for suburban it seems that
-we can just impose C = 0); therefore we consider it a special case fo the suburban one.
+we can just impose C = 0); therefore we consider it a special case of the suburban one.
 
 
 Cost231PropagationLossModel
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/model/channel-condition-model.cc ns-3.41/src/propagation/model/channel-condition-model.cc
--- ns-3.40/src/propagation/model/channel-condition-model.cc	2023-09-27 13:30:37.862839700 -0700
+++ ns-3.41/src/propagation/model/channel-condition-model.cc	2024-02-09 14:44:08.453794000 -0800
@@ -102,43 +102,43 @@
 bool
 ChannelCondition::IsLos() const
 {
-    return (m_losCondition == ChannelCondition::LOS);
+    return m_losCondition == ChannelCondition::LOS;
 }
 
 bool
 ChannelCondition::IsNlos() const
 {
-    return (m_losCondition == ChannelCondition::NLOS);
+    return m_losCondition == ChannelCondition::NLOS;
 }
 
 bool
 ChannelCondition::IsNlosv() const
 {
-    return (m_losCondition == ChannelCondition::NLOSv);
+    return m_losCondition == ChannelCondition::NLOSv;
 }
 
 bool
 ChannelCondition::IsO2i() const
 {
-    return (m_o2iCondition == ChannelCondition::O2I);
+    return m_o2iCondition == ChannelCondition::O2I;
 }
 
 bool
 ChannelCondition::IsO2o() const
 {
-    return (m_o2iCondition == ChannelCondition::O2O);
+    return m_o2iCondition == ChannelCondition::O2O;
 }
 
 bool
 ChannelCondition::IsI2i() const
 {
-    return (m_o2iCondition == ChannelCondition::I2I);
+    return m_o2iCondition == ChannelCondition::I2I;
 }
 
 bool
 ChannelCondition::IsEqual(LosConditionValue losCondition, O2iConditionValue o2iCondition) const
 {
-    return (m_losCondition == losCondition && m_o2iCondition == o2iCondition);
+    return m_losCondition == losCondition && m_o2iCondition == o2iCondition;
 }
 
 std::ostream&
@@ -686,7 +686,7 @@
     // compute the 2D distance between a and b
     double distance2D = Calculate2dDistance(a->GetPosition(), b->GetPosition());
 
-    // NOTE: no idication is given about the UT height used to derive the
+    // NOTE: no indication is given about the UT height used to derive the
     // LOS probability
 
     // h_BS should be equal to 10 m. We check if at least one of the two
@@ -741,7 +741,7 @@
     // compute the 2D distance between a and b
     double distance2D = Calculate2dDistance(a->GetPosition(), b->GetPosition());
 
-    // NOTE: no idication is given about the UT height used to derive the
+    // NOTE: no indication is given about the UT height used to derive the
     // LOS probability
 
     // retrieve h_BS, it should be equal to 3 m
@@ -800,7 +800,7 @@
     // compute the 2D distance between a and b
     double distance2D = Calculate2dDistance(a->GetPosition(), b->GetPosition());
 
-    // NOTE: no idication is given about the UT height used to derive the
+    // NOTE: no indication is given about the UT height used to derive the
     // LOS probability
 
     // retrieve h_BS, it should be equal to 3 m
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/model/itu-r-1411-nlos-over-rooftop-propagation-loss-model.cc ns-3.41/src/propagation/model/itu-r-1411-nlos-over-rooftop-propagation-loss-model.cc
--- ns-3.40/src/propagation/model/itu-r-1411-nlos-over-rooftop-propagation-loss-model.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/propagation/model/itu-r-1411-nlos-over-rooftop-propagation-loss-model.cc	2024-02-09 14:44:08.457794000 -0800
@@ -48,22 +48,23 @@
                 DoubleValue(2160e6),
                 MakeDoubleAccessor(&ItuR1411NlosOverRooftopPropagationLossModel::SetFrequency),
                 MakeDoubleChecker<double>())
-            .AddAttribute(
-                "Environment",
-                "Environment Scenario",
-                EnumValue(UrbanEnvironment),
-                MakeEnumAccessor(&ItuR1411NlosOverRooftopPropagationLossModel::m_environment),
-                MakeEnumChecker(UrbanEnvironment,
-                                "Urban",
-                                SubUrbanEnvironment,
-                                "SubUrban",
-                                OpenAreasEnvironment,
-                                "OpenAreas"))
+            .AddAttribute("Environment",
+                          "Environment Scenario",
+                          EnumValue(UrbanEnvironment),
+                          MakeEnumAccessor<EnvironmentType>(
+                              &ItuR1411NlosOverRooftopPropagationLossModel::m_environment),
+                          MakeEnumChecker(UrbanEnvironment,
+                                          "Urban",
+                                          SubUrbanEnvironment,
+                                          "SubUrban",
+                                          OpenAreasEnvironment,
+                                          "OpenAreas"))
             .AddAttribute(
                 "CitySize",
                 "Dimension of the city",
                 EnumValue(LargeCity),
-                MakeEnumAccessor(&ItuR1411NlosOverRooftopPropagationLossModel::m_citySize),
+                MakeEnumAccessor<CitySize>(
+                    &ItuR1411NlosOverRooftopPropagationLossModel::m_citySize),
                 MakeEnumChecker(SmallCity, "Small", MediumCity, "Medium", LargeCity, "Large"))
             .AddAttribute(
                 "RooftopLevel",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/model/okumura-hata-propagation-loss-model.cc ns-3.41/src/propagation/model/okumura-hata-propagation-loss-model.cc
--- ns-3.40/src/propagation/model/okumura-hata-propagation-loss-model.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/propagation/model/okumura-hata-propagation-loss-model.cc	2024-02-09 14:44:08.457794000 -0800
@@ -48,21 +48,22 @@
                           DoubleValue(2160e6),
                           MakeDoubleAccessor(&OkumuraHataPropagationLossModel::m_frequency),
                           MakeDoubleChecker<double>())
-            .AddAttribute("Environment",
-                          "Environment Scenario",
-                          EnumValue(UrbanEnvironment),
-                          MakeEnumAccessor(&OkumuraHataPropagationLossModel::m_environment),
-                          MakeEnumChecker(UrbanEnvironment,
-                                          "Urban",
-                                          SubUrbanEnvironment,
-                                          "SubUrban",
-                                          OpenAreasEnvironment,
-                                          "OpenAreas"))
+            .AddAttribute(
+                "Environment",
+                "Environment Scenario",
+                EnumValue(UrbanEnvironment),
+                MakeEnumAccessor<EnvironmentType>(&OkumuraHataPropagationLossModel::m_environment),
+                MakeEnumChecker(UrbanEnvironment,
+                                "Urban",
+                                SubUrbanEnvironment,
+                                "SubUrban",
+                                OpenAreasEnvironment,
+                                "OpenAreas"))
             .AddAttribute(
                 "CitySize",
                 "Dimension of the city",
                 EnumValue(LargeCity),
-                MakeEnumAccessor(&OkumuraHataPropagationLossModel::m_citySize),
+                MakeEnumAccessor<CitySize>(&OkumuraHataPropagationLossModel::m_citySize),
                 MakeEnumChecker(SmallCity, "Small", MediumCity, "Medium", LargeCity, "Large"));
     return tid;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/model/probabilistic-v2v-channel-condition-model.cc ns-3.41/src/propagation/model/probabilistic-v2v-channel-condition-model.cc
--- ns-3.40/src/propagation/model/probabilistic-v2v-channel-condition-model.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/propagation/model/probabilistic-v2v-channel-condition-model.cc	2024-02-09 14:44:08.457794000 -0800
@@ -38,18 +38,18 @@
             .SetParent<ThreeGppChannelConditionModel>()
             .SetGroupName("Propagation")
             .AddConstructor<ProbabilisticV2vUrbanChannelConditionModel>()
-            .AddAttribute(
-                "Density",
-                "Specifies the density of the vehicles in the scenario."
-                "It can be set to Low, Medium or High.",
-                EnumValue(VehicleDensity::LOW),
-                MakeEnumAccessor(&ProbabilisticV2vUrbanChannelConditionModel::m_densityUrban),
-                MakeEnumChecker(VehicleDensity::LOW,
-                                "Low",
-                                VehicleDensity::MEDIUM,
-                                "Medium",
-                                VehicleDensity::HIGH,
-                                "High"));
+            .AddAttribute("Density",
+                          "Specifies the density of the vehicles in the scenario."
+                          "It can be set to Low, Medium or High.",
+                          EnumValue(VehicleDensity::LOW),
+                          MakeEnumAccessor<VehicleDensity>(
+                              &ProbabilisticV2vUrbanChannelConditionModel::m_densityUrban),
+                          MakeEnumChecker(VehicleDensity::LOW,
+                                          "Low",
+                                          VehicleDensity::MEDIUM,
+                                          "Medium",
+                                          VehicleDensity::HIGH,
+                                          "High"));
     return tid;
 }
 
@@ -141,18 +141,18 @@
             .SetParent<ThreeGppChannelConditionModel>()
             .SetGroupName("Propagation")
             .AddConstructor<ProbabilisticV2vHighwayChannelConditionModel>()
-            .AddAttribute(
-                "Density",
-                "Specifies the density of the vehicles in the scenario."
-                "It can be set to Low, Medium or High.",
-                EnumValue(VehicleDensity::LOW),
-                MakeEnumAccessor(&ProbabilisticV2vHighwayChannelConditionModel::m_densityHighway),
-                MakeEnumChecker(VehicleDensity::LOW,
-                                "Low",
-                                VehicleDensity::MEDIUM,
-                                "Medium",
-                                VehicleDensity::HIGH,
-                                "High"));
+            .AddAttribute("Density",
+                          "Specifies the density of the vehicles in the scenario."
+                          "It can be set to Low, Medium or High.",
+                          EnumValue(VehicleDensity::LOW),
+                          MakeEnumAccessor<VehicleDensity>(
+                              &ProbabilisticV2vHighwayChannelConditionModel::m_densityHighway),
+                          MakeEnumChecker(VehicleDensity::LOW,
+                                          "Low",
+                                          VehicleDensity::MEDIUM,
+                                          "Medium",
+                                          VehicleDensity::HIGH,
+                                          "High"));
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/model/propagation-cache.h ns-3.41/src/propagation/model/propagation-cache.h
--- ns-3.40/src/propagation/model/propagation-cache.h	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/propagation/model/propagation-cache.h	2024-02-09 14:44:08.457794000 -0800
@@ -54,7 +54,7 @@
             return nullptr;
         }
         return it->second;
-    };
+    }
 
     /**
      * Add a model to the path
@@ -71,7 +71,7 @@
         PropagationPathIdentifier key = PropagationPathIdentifier(a, b, modelUid);
         NS_ASSERT(m_pathCache.find(key) == m_pathCache.end());
         m_pathCache.insert(std::make_pair(key, data));
-    };
+    }
 
     /**
      * Clean the cache
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/propagation/model/propagation-loss-model.h ns-3.41/src/propagation/model/propagation-loss-model.h
--- ns-3.40/src/propagation/model/propagation-loss-model.h	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/propagation/model/propagation-loss-model.h	2024-02-09 14:44:08.457794000 -0800
@@ -27,7 +27,7 @@
 #include "ns3/object.h"
 #include "ns3/random-variable-stream.h"
 
-#include <map>
+#include <unordered_map>
 
 namespace ns3
 {
@@ -742,9 +742,29 @@
     double m_default; //!< default loss
 
     /// Typedef: Mobility models pair
-    typedef std::pair<Ptr<MobilityModel>, Ptr<MobilityModel>> MobilityPair;
+    typedef std::pair<const Ptr<MobilityModel>, const Ptr<MobilityModel>> MobilityPair;
 
-    std::map<MobilityPair, double> m_loss; //!< Propagation loss between pair of nodes
+    /**
+     * \ingroup propagation
+     *
+     * \brief Hasher for a pair of mobility models.
+     */
+    class MobilityPairHasher
+    {
+      public:
+        /**
+         * \brief Get the hash for a MobilityPair.
+         * \param key MobilityPair reference to hash
+         * \return the MobilityPair hash
+         */
+        size_t operator()(const MobilityPair& key) const
+        {
+            return uint64_t(key.first.operator->()) ^ uint64_t(key.second.operator->());
+        }
+    };
+
+    std::unordered_map<MobilityPair, double, MobilityPairHasher>
+        m_loss; //!< Propagation loss between pair of nodes
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/CMakeLists.txt ns-3.41/src/sixlowpan/CMakeLists.txt
--- ns-3.40/src/sixlowpan/CMakeLists.txt	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/CMakeLists.txt	2024-02-09 14:44:08.457794000 -0800
@@ -15,8 +15,7 @@
     helper/sixlowpan-helper.h
     model/sixlowpan-header.h
     model/sixlowpan-net-device.h
-  LIBRARIES_TO_LINK
-    ${libinternet}
+  LIBRARIES_TO_LINK ${libinternet}
   TEST_SOURCES
     ${example_as_test_suite}
     test/mock-net-device.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/doc/sixlowpan.rst ns-3.41/src/sixlowpan/doc/sixlowpan.rst
--- ns-3.40/src/sixlowpan/doc/sixlowpan.rst	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/doc/sixlowpan.rst	2024-02-09 14:44:08.457794000 -0800
@@ -52,8 +52,6 @@
 * FragmentReassemblyListSize (integer, default 0), indicating the number of packets that can be reassembled at the same time. If the limit is reached, the oldest packet is discarded. Zero means infinite.
 * FragmentExpirationTimeout (Time, default 60 seconds), being the timeout to wait for further fragments before discarding a partial packet.
 * CompressionThreshold (unsigned 32 bits integer, default 0), minimum compressed payload size.
-* ForceEtherType (boolean, default false).
-* EtherType (unsigned 16 bits integer, default 0xFFFF), to force a particular L2 EtherType.
 * UseMeshUnder (boolean, default false), it enables mesh-under flood routing.
 * MeshUnderRadius (unsigned 8 bits integer, default 10), the maximum number of hops that a packet will be forwarded.
 * MeshCacheLength (unsigned 16 bits integer, default 10), the length of the cache for each source.
@@ -65,23 +63,8 @@
 This option is useful when a MAC requires a minimum frame size (e.g., ContikiMAC) and the
 compression would violate the requirement.
 
-The last two attributes are needed to use the module with a NetDevice other than 802.15.4, as
-neither IANA or IEEE did reserve an EtherType for 6LoWPAN. As a consequence there might be a
-conflict with the L2 multiplexer/demultiplexer which is based on EtherType. The default
-value is 0xFFFF, which is reserved by IEEE (see [IANA802]_ and [Ethertype]_).
-The default module behaviour is to not change the EtherType, however this would not work with
-any NetDevice actually understanding and using the EtherType.
-
-Note that the `ForceEtherType` parameter have also a direct effect on the MAC address kind the
-module is expecting to handle:
-* ForceEtherType true: Mac48Address (Ethernet, WiFi, etc.).
-* ForceEtherType false: Mac16Address or Mac64Address (IEEE 802.15.4).
-
-Note that using 6LoWPAN over any NetDevice other than 802.15.4 will produce valid .pcap files,
-but they will not be correctly dissected by Wireshark.
-The reason lies on the fact that 6LoWPAN was really meant to be used only over 802.15.4, so
-Wireshark dissectors will not even try to decode 6LoWPAN headers on top of protocols other than
-802.15.4.
+Note that 6LoWPAN will use an EtherType equal to 0xA0ED, as mandated by :rfc:`7973`.
+If the device does not support EtherTypes (e.g., 802.15.4), this value is discarded.
 
 The Trace sources are:
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc ns-3.41/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc
--- ns-3.40/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/examples/example-ping-lr-wpan-beacon.cc	2024-02-09 14:44:08.457794000 -0800
@@ -35,7 +35,7 @@
 {
     // In the case of transmissions with the Ack flag activated, the transaction is only
     // successful if the Ack was received.
-    if (params.m_status == LrWpanMcpsDataConfirmStatus::IEEE_802_15_4_SUCCESS)
+    if (params.m_status == LrWpanMacStatus::SUCCESS)
     {
         std::cout << Simulator::Now().As(Time::S) << " | Node " << device->GetNode()->GetId()
                   << " | Transmission successfully sent\n";
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/examples/example-sixlowpan.cc ns-3.41/src/sixlowpan/examples/example-sixlowpan.cc
--- ns-3.40/src/sixlowpan/examples/example-sixlowpan.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/examples/example-sixlowpan.cc	2024-02-09 14:44:08.457794000 -0800
@@ -88,7 +88,6 @@
     NetDeviceContainer d1 = csma.Install(net1);
 
     SixLowPanHelper sixlowpan;
-    sixlowpan.SetDeviceAttribute("ForceEtherType", BooleanValue(true));
     NetDeviceContainer six1 = sixlowpan.Install(d1);
 
     NS_LOG_INFO("Create networks and assign IPv6 Addresses.");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/model/sixlowpan-net-device.cc ns-3.41/src/sixlowpan/model/sixlowpan-net-device.cc
--- ns-3.40/src/sixlowpan/model/sixlowpan-net-device.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/model/sixlowpan-net-device.cc	2024-02-09 14:44:08.461794100 -0800
@@ -83,16 +83,6 @@
                           UintegerValue(0x0),
                           MakeUintegerAccessor(&SixLowPanNetDevice::m_compressionThreshold),
                           MakeUintegerChecker<uint32_t>())
-            .AddAttribute("ForceEtherType",
-                          "Force a specific EtherType in L2 frames.",
-                          BooleanValue(false),
-                          MakeBooleanAccessor(&SixLowPanNetDevice::m_forceEtherType),
-                          MakeBooleanChecker())
-            .AddAttribute("EtherType",
-                          "The specific EtherType to be used in L2 frames.",
-                          UintegerValue(0xFFFF),
-                          MakeUintegerAccessor(&SixLowPanNetDevice::m_etherType),
-                          MakeUintegerChecker<uint16_t>())
             .AddAttribute("UseMeshUnder",
                           "Use a mesh-under routing protocol.",
                           BooleanValue(false),
@@ -158,10 +148,13 @@
 
     NS_LOG_DEBUG("RegisterProtocolHandler for " << device->GetInstanceTypeId().GetName());
 
-    uint16_t protocolType = 0;
-    if (m_forceEtherType)
+    uint16_t protocolType = PROT_NUMBER;
+    if (device->GetInstanceTypeId().GetName() == "ns3::LrWpanNetDevice")
     {
-        protocolType = m_etherType;
+        // LrWpanNetDevice does not have a protocol number in the frame.
+        // Hence, we must register for any protocol, and assume that any
+        // packet is 6LoWPAN.
+        protocolType = 0;
     }
     m_node->RegisterProtocolHandler(MakeCallback(&SixLowPanNetDevice::ReceiveFromDevice, this),
                                     protocolType,
@@ -596,10 +589,7 @@
 
     bool useMesh = m_meshUnder;
 
-    if (m_forceEtherType)
-    {
-        protocolNumber = m_etherType;
-    }
+    protocolNumber = PROT_NUMBER;
 
     if (m_useIphc)
     {
@@ -2714,7 +2704,7 @@
 
     auto iter = --m_timeoutEventList.end();
 
-    return (iter);
+    return iter;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/model/sixlowpan-net-device.h ns-3.41/src/sixlowpan/model/sixlowpan-net-device.h
--- ns-3.40/src/sixlowpan/model/sixlowpan-net-device.h	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/model/sixlowpan-net-device.h	2024-02-09 14:44:08.461794100 -0800
@@ -86,6 +86,11 @@
     };
 
     /**
+     * \brief The protocol number for 6LoWPAN (0xA0ED) - see \RFC{7973}.
+     */
+    static constexpr uint16_t PROT_NUMBER{0xA0ED};
+
+    /**
      * \brief Get the type ID.
      * \return The object TypeId.
      */
@@ -626,14 +631,6 @@
     Ptr<NetDevice> m_netDevice; //!< Smart pointer to the underlying NetDevice.
     uint32_t m_ifIndex;         //!< Interface index.
 
-    /**
-     * \brief Force the EtherType number.
-     * Also implying that the underlying NetDevice is using 48-bit Addresses, e.g., Ethernet, Wi-Fi,
-     * etc.
-     */
-    bool m_forceEtherType;
-
-    uint16_t m_etherType;   //!< EtherType number (used only if m_forceEtherType is true).
     bool m_omitUdpChecksum; //!< Omit UDP checksum in NC1 encoding.
 
     uint32_t m_compressionThreshold; //!< Minimum L2 payload size.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/test/sixlowpan-fragmentation-test.cc ns-3.41/src/sixlowpan/test/sixlowpan-fragmentation-test.cc
--- ns-3.40/src/sixlowpan/test/sixlowpan-fragmentation-test.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/test/sixlowpan-fragmentation-test.cc	2024-02-09 14:44:08.461794100 -0800
@@ -289,7 +289,6 @@
         serverNode->AddDevice(serverDev);
 
         Ptr<SixLowPanNetDevice> serverSix = CreateObject<SixLowPanNetDevice>();
-        serverSix->SetAttribute("ForceEtherType", BooleanValue(true));
         serverNode->AddDevice(serverSix);
         serverSix->SetNetDevice(serverDev);
 
@@ -320,7 +319,6 @@
         clientNode->AddDevice(clientDev);
 
         Ptr<SixLowPanNetDevice> clientSix = CreateObject<SixLowPanNetDevice>();
-        clientSix->SetAttribute("ForceEtherType", BooleanValue(true));
         clientNode->AddDevice(clientSix);
         clientSix->SetNetDevice(clientDev);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/test/sixlowpan-hc1-test.cc ns-3.41/src/sixlowpan/test/sixlowpan-hc1-test.cc
--- ns-3.40/src/sixlowpan/test/sixlowpan-hc1-test.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/test/sixlowpan-hc1-test.cc	2024-02-09 14:44:08.461794100 -0800
@@ -142,7 +142,6 @@
         rxNode->AddDevice(rxDev);
 
         Ptr<SixLowPanNetDevice> rxSix = CreateObject<SixLowPanNetDevice>();
-        rxSix->SetAttribute("ForceEtherType", BooleanValue(true));
         rxSix->SetAttribute("Rfc6282", BooleanValue(false));
         rxNode->AddDevice(rxSix);
         rxSix->SetNetDevice(rxDev);
@@ -166,7 +165,6 @@
         txNode->AddDevice(txDev);
 
         Ptr<SixLowPanNetDevice> txSix = CreateObject<SixLowPanNetDevice>();
-        txSix->SetAttribute("ForceEtherType", BooleanValue(true));
         txSix->SetAttribute("Rfc6282", BooleanValue(false));
         txNode->AddDevice(txSix);
         txSix->SetNetDevice(txDev);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/sixlowpan/test/sixlowpan-iphc-test.cc ns-3.41/src/sixlowpan/test/sixlowpan-iphc-test.cc
--- ns-3.40/src/sixlowpan/test/sixlowpan-iphc-test.cc	2023-09-27 13:30:37.866839600 -0700
+++ ns-3.41/src/sixlowpan/test/sixlowpan-iphc-test.cc	2024-02-09 14:44:08.461794100 -0800
@@ -142,7 +142,6 @@
         rxNode->AddDevice(rxDev);
 
         Ptr<SixLowPanNetDevice> rxSix = CreateObject<SixLowPanNetDevice>();
-        rxSix->SetAttribute("ForceEtherType", BooleanValue(true));
         rxNode->AddDevice(rxSix);
         rxSix->SetNetDevice(rxDev);
 
@@ -165,7 +164,6 @@
         txNode->AddDevice(txDev);
 
         Ptr<SixLowPanNetDevice> txSix = CreateObject<SixLowPanNetDevice>();
-        txSix->SetAttribute("ForceEtherType", BooleanValue(true));
         txNode->AddDevice(txSix);
         txSix->SetNetDevice(txDev);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/doc/spectrum.rst ns-3.41/src/spectrum/doc/spectrum.rst
--- ns-3.40/src/spectrum/doc/spectrum.rst	2023-09-27 13:30:37.870839600 -0700
+++ ns-3.41/src/spectrum/doc/spectrum.rst	2024-02-09 14:44:08.465794000 -0800
@@ -69,8 +69,14 @@
 
 * the duration of the signal
 
-* its Power Spectral Density (PSD) of the signal, which is assumed to be constant for
-  the duration of the signal.
+* the Power Spectral Density (PSD) of the signal, which is assumed to be constant for
+  the duration of the signal
+
+* the frequency domain 3D spectrum channel matrix that is needed for MIMO computations
+  when multiple streams are transmitted
+
+* the 3D precoding matrix that is needed for MIMO computations when multiple
+  streams are transmitted.
 
 The PSD is represented as a set of discrete scalar values each
 corresponding to a certain subband in frequency. The set of frequency subbands
@@ -83,6 +89,17 @@
 provides means for the conversion of ``SpectrumValue`` instances from
 one ``SpectrumModel`` to another.
 
+The frequency domain 3D channel matrix is needed in MIMO systems in which
+multiple transmit and receive antenna ports can exist, hence the PSD is multidimensional.
+The dimensions are: the number of receive antenna ports, the number of
+transmit antenna ports, and the number of subbands in frequency (or
+resource blocks).
+
+The 3D precoding matrix is also needed in MIMO systems to be able to correctly
+perform the calculations of the received signal and interference. The dimensions are:
+the number of transmit antenna ports, the number of transmit
+streams, and the number of subbands (or resource blocks).
+
 For a more formal mathematical description of the signal model just
 described, the reader is referred to [Baldo2009Spectrum]_.
 
@@ -275,7 +292,11 @@
    type it can attempt to receive. If not, the signal is normally
    expected to be considered as interference.
 
-
+*  Many propagation loss and delay models can be added to these channels.
+   The base class ``SpectrumChannel`` provides an ``AssignStreams()`` method
+   to allow the deterministic configuration of random variable stream numbers
+   with a single API call.  See the ns-3 Manual chapter on random variables
+   for more information.
 
 
 Helpers
@@ -609,7 +630,7 @@
 components, called clusters, each of which composed of M different rays.
 The channel matrix generation procedure accounts for large and small scale
 propagation phenomena. The classes ThreeGppSpectrumPropagationLossModel and
-ThreeGppChannelModel included in the spectrum module takes care of the generation
+ThreeGppChannelModel included in the spectrum module take care of the generation
 of the channel coefficients and the computation of the frequency-dependent
 propagation loss.
 
@@ -656,19 +677,28 @@
 PhasedArraySpectrumPropagationLossModel interface and enables the modeling of frequency
 dependent propagation phenomena while taking into account the specific pair of the
 phased antenna array at the transmitter and the receiver. The main method is
-DoCalcRxPowerSpectralDensity, which takes as input the power spectral density (PSD)
-of the transmitted signal, the mobility models of the transmitting node and receiving node,
-and the phased antenna array of the transmitting node, and of the receiving node.
-Finally, it returns the PSD of the received signal.
+DoCalcRxPowerSpectralDensity, which takes as input the SpectrumSignalParameters
+structure, which contains, among others, the power spectral density (PSD) of the
+transmitted signal, and the precoding matrix of the transmitter which is needed
+for MIMO computations. Other input parameters are the mobility models of the
+transmitting and receiving node, and the phased antenna array of the
+transmitting and receiving node.
+DoCalcRxPowerSpectralDensity computes the PSD of the received signal
+(used in case of single stream transmission), and the frequency domain 3D
+channel matrix between receive and transmit antenna ports (needed for MIMO
+computations when multiple streams are transmitted).
+Finally, it returns the SpectrumSignalParameters which contains the
+received PSD and the frequency domain 3D spectrum channel matrix.
 
-Procedure used to compute the PSD of to compute the PSD of the received signal:
+Procedure used to compute the PSD of the received signal and the frequency domain 3D
+spectrum channel matrix:
 
 1. Retrieve the beamforming vectors
 To account for the beamforming, ThreeGppSpectrumPropagationLossModel has to
 retrieve the beamforming vectors of the transmitting and receiving antennas.
 The method DoCalcRxPowerSpectralDensity uses the antenna objects
 that are passed as parameters for both the transmitting and receiving devices,
-and calls the method GetCurrentBeamformingVector to retrieve the beamforming vectors
+and calls the method GetBeamformingVector to retrieve the beamforming vectors
 of these antenna objects.
 
 2. Retrieve the channel matrix and the channel params
@@ -678,12 +708,12 @@
 which returns a ChannelMatrix object containing the channel
 matrix, the generation time, the node pair, and the phased antenna array pair among
 which is created this channel matrix.
-Apart from the function GetChannel, there is a function called GetParams which
+Additionally, it makes use of the method GetParams which
 returns a ChannelParams object containing the channel parameters.
 Notice that the channel information is split into these two structures
-(ChannelMatrix and ChannelParams) to support multiple collocate phased antenna arrays at
+(ChannelMatrix and ChannelParams) to support multiple collocated phased antenna arrays at
 TX/RX node. ChannelParams (also its specialization ThreeGppChannelParams structure)
-contains parameters which are common for all channels among
+contains parameters which are common for all the channels among
 the same RX/TX node pair, while ChannelMatrix contains the channel matrix for the specific pair
 of the phased antenna arrays of TX/RX nodes.
 For example, if the TX and the RX node have multiple collocated antenna arrays,
@@ -694,27 +724,35 @@
 AoD, AoA, ZoD, ZoA, K_factor, delay spread, etc.
 On the other hand, each pair of TX and RX antenna arrays has a specific channel matrix
 and fading, which depends on the actual antenna element positions and field patterns of
-each pair of antennas array subpartitions.
+each pair of antenna array subpartitions.
 The ThreeGppChannelModel instance is automatically
-created in the the ThreeGppSpectrumPropagationLossModel constructor and it can
-be configured using the method SetChannelModelAttribute ().
+created in the ThreeGppSpectrumPropagationLossModel constructor and it can
+be configured by using the method SetChannelModelAttribute.
 
-Notice that in MultiModelSpectrumChannel in StartTx function we added a
-condition that checks whether the TX/RX SpectrumPhy instances belong to different
-TX/RX nodes. This is needed to avoid pathloss models calculations among
+Notice that in MultiModelSpectrumChannel it is checked whether the TX/RX
+SpectrumPhy instances belong to different TX/RX nodes.
+This is needed to avoid pathloss models calculations among
 the phased antenna arrays of the same node, because there are no models yet
 in ns-3 that support the calculation of this kind of interference.
 
 4. Compute the long term component
 The method GetLongTerm returns the long term component obtained by multiplying
-the channel matrix and the beamforming vectors. To reduce the computational
-load, the long term components associated to the different channels are
-stored in the m_longTermMap and recomputed only if the associated channel
-matrix is updated or if the transmitting and/or receiving beamforming vectors
-have changed. Given the channel reciprocity assumption, for each node pair a
-single long term component is saved in the map.
+the channel matrix and the beamforming vectors. The function CalculateLongTermComponent
+calculates the long term component per RX and TX port pair. Finally, GetLongTerm
+returns a 3D long term channel matrix whose dimensions are the number of the
+receive antenna ports, the number transmit antenna ports, and
+the number of clusters. When multiple ports are being configured note that
+the sub-array partition model is adopted for TXRU virtualization, as described
+in Section 5.2.2 of 3GPP TR 36.897[TR36897]_, and so equal beam weights are used for all the ports.
+Support of the full-connection model for TXRU virtualization would need extensions.
+To reduce the computational load, the long term
+components associated to the different channels are stored in the m_longTermMap
+and recomputed only if the associated channel matrix is updated or if the
+transmitting and/or receiving beamforming vectors have changed. Given the channel
+reciprocity assumption, for each node pair a single long term component is saved in the map.
 
-5. Apply the small scale fading and compute the channel gain
+5. Apply the small scale fading, calculate the channel gain, generate the
+frequency domain 3D spectrum channel matrix, and finally compute the received PSD
 The method CalcBeamformingGain computes the channel gain in each sub-band and
 applies it to the PSD of the transmitted signal to obtain the received PSD.
 To compute the sub-band gain, it accounts for the Doppler phenomenon and the
@@ -728,6 +766,14 @@
 distribution depends on the parameter :math:`v_{scatt}`.
 The value of :math:`v_{scatt}` can be configured using the attribute "vScatt"
 (by default it is set to 0, so that the scattering effect is not considered).
+Function GenSpectrumChannelMatrix generates the received PSD for each pair of
+the transmit and receive antenna ports. It creates a frequency domain 3D spectrum
+channel matrix whose dimensions are the number of receive antena ports,
+the number of transmit antenna ports, and the number of resource blocks.
+Finally, the frequency domain 3D spectrum channel matrix is used to obtain the
+received PSD. In case of multiple ports at the transmitter the PSD is calculated
+by summing per each RB the real parts of the diagonal elements of the (H*P)^h * (H*P),
+where H is the frequency domain spectrum channel matrix and P is the precoding matrix.
 
 
 ThreeGppChannelModel
@@ -751,8 +797,7 @@
 
 * other channel parameters
 
-The ChannelMatrix objects are saved
-in the map m_channelMap and updated when the coherence time
+The ChannelMatrix objects are saved in the map m_channelMap and updated when the coherence time
 expires, or in case the LOS/NLOS channel condition changes.
 The coherence time can be configured through
 the attribute "UpdatePeriod", and should be chosen by taking into account all the
@@ -780,7 +825,7 @@
 
 Testing
 #######
-The test suite ThreeGppChannelTestSuite includes three test cases:
+The test suite ThreeGppChannelTestSuite includes five test cases:
 
 * ThreeGppChannelMatrixComputationTest checks if the channel matrix has the
   correct dimensions and if it correctly normalized
@@ -799,6 +844,11 @@
        the beamforming vectors,
     3. Checks if the long term is updated when changing the channel matrix
 
+* ThreeGppCalcLongTermMultiPortTest, which tests that the channel matrices are
+  correctly generated when multiple transmit and receive antenna ports are used.
+
+* ThreeGppMimoPolarizationTest, which tests that the channel matrices are
+  correctly generated when dual-polarized antennas are being used.
 
 **Note:** TR 38.901 includes a calibration procedure that can be used to validate
 the model, but it requires some additional features which are not currently
@@ -835,13 +885,16 @@
    ns-3". Submitted to the Workshop on ns-3 (WNS3 '20). 2020.
    Available: https://arxiv.org/abs/2002.09341
 
+.. [TR36897] 3GPP. 2015. TR 36.897. Study on elevation beamforming / Full-Dimension (FD)
+   Multiple Input Multiple Output (MIMO) for LTE. V13.0.0. (2015-06)
+
 
 Two-Ray fading model
 ====================
 The model aims to provide a performance-oriented alternative to the 3GPP TR 38.901
 framework [TR38901]_ which is implemented in the ``ThreeGppSpectrumPropagationLossModel`` and
 ``ThreeGppChannelModel`` classes and whose implementation is described in [Zugno2020]_.
-The overall design follows the general approach of [Polese2018]_, with aim of providing
+The overall design described in [Pagin2023]_ follows the general approach of [Polese2018]_, with aim of providing
 the means for computing a 3GPP TR 38.901-like end-to-end channel gain by combining
 several statistical terms. The frequency range of applicability is the same as
 that of [TR38901]_, i.e., 0.5 - 100 GHz.
@@ -1018,6 +1071,10 @@
 References
 ##########
 
+.. [Pagin2023] Pagin, Matteo, Sandra Lagen, Biljana Bojovic, Michele Polese,
+   Michele Zorzi. 2023. "Improving the Efficiency of MIMO Simulations in ns-3"
+   In Proceedings of the 2023 Workshop on ns-3, pp. 1–9. 2023
+
 .. [Zugno2020] Zugno, Tommaso, Michele Polese, Natale Patriciello, Biljana Bojović,
    Sandra Lagen, Michele Zorzi. "Implementation of a spatial channel model for ns-3."
    In Proceedings of the 2020 Workshop on ns-3, pp. 49-56. 2020.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/examples/three-gpp-channel-example.cc ns-3.41/src/spectrum/examples/three-gpp-channel-example.cc
--- ns-3.40/src/spectrum/examples/three-gpp-channel-example.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/examples/three-gpp-channel-example.cc	2024-02-09 14:44:08.493794200 -0800
@@ -90,7 +90,7 @@
     double vAngleRadian = completeAngle.GetInclination(); // the elevation angle
 
     // retrieve the number of antenna elements and resize the vector
-    uint64_t totNoArrayElements = thisAntenna->GetNumberOfElements();
+    uint64_t totNoArrayElements = thisAntenna->GetNumElems();
     PhasedArrayModel::ComplexVector antennaWeights(totNoArrayElements);
 
     // the total power is divided equally among the antenna elements
@@ -131,14 +131,14 @@
     {
         activeRbs0[i] = i;
     }
-    Ptr<SpectrumValue> txPsd =
+    auto txPsd =
         LteSpectrumValueHelper::CreateTxPowerSpectralDensity(2100, 100, params.txPow, activeRbs0);
-    Ptr<SpectrumSignalParameters> txParams = Create<SpectrumSignalParameters>();
+    auto txParams = Create<SpectrumSignalParameters>();
     txParams->psd = txPsd->Copy();
     NS_LOG_DEBUG("Average tx power " << 10 * log10(Sum(*txPsd) * 180e3) << " dB");
 
     // create the noise PSD
-    Ptr<SpectrumValue> noisePsd =
+    auto noisePsd =
         LteSpectrumValueHelper::CreateNoisePowerSpectralDensity(2100, 100, params.noiseFigure);
     NS_LOG_DEBUG("Average noise power " << 10 * log10(Sum(*noisePsd) * 180e3) << " dB");
 
@@ -152,11 +152,12 @@
     NS_ASSERT_MSG(params.rxAntenna, "params.rxAntenna is nullptr!");
 
     // apply the fast fading and the beamforming gain
-    Ptr<SpectrumValue> rxPsd = m_spectrumLossModel->CalcRxPowerSpectralDensity(txParams,
-                                                                               params.txMob,
-                                                                               params.rxMob,
-                                                                               params.txAntenna,
-                                                                               params.rxAntenna);
+    auto rxParams = m_spectrumLossModel->CalcRxPowerSpectralDensity(txParams,
+                                                                    params.txMob,
+                                                                    params.rxMob,
+                                                                    params.txAntenna,
+                                                                    params.rxAntenna);
+    auto rxPsd = rxParams->psd;
     NS_LOG_DEBUG("Average rx power " << 10 * log10(Sum(*rxPsd) * 180e3) << " dB");
 
     // compute the SNR
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc ns-3.41/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc
--- ns-3.40/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/examples/three-gpp-two-ray-channel-calibration.cc	2024-02-09 14:44:08.493794200 -0800
@@ -186,12 +186,12 @@
     bArray->SetBeamformingVector(bArray->GetBeamformingVector(angleAtoB));
 
     // Compute the received power due to multipath fading
-    auto rxPsd = threeGppSpectrumLossModel->DoCalcRxPowerSpectralDensity(signalParams,
-                                                                         aMob,
-                                                                         bMob,
-                                                                         aArray,
-                                                                         bArray);
-    double rxPower = ComputePowerSpectralDensityOverallPower(rxPsd);
+    auto rxParams = threeGppSpectrumLossModel->DoCalcRxPowerSpectralDensity(signalParams,
+                                                                            aMob,
+                                                                            bMob,
+                                                                            aArray,
+                                                                            bArray);
+    double rxPower = ComputePowerSpectralDensityOverallPower(rxParams->psd);
 
     return rxPower / txPower;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/constant-spectrum-propagation-loss.cc ns-3.41/src/spectrum/model/constant-spectrum-propagation-loss.cc
--- ns-3.40/src/spectrum/model/constant-spectrum-propagation-loss.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/constant-spectrum-propagation-loss.cc	2024-02-09 14:44:08.493794200 -0800
@@ -99,4 +99,10 @@
     return rxPsd;
 }
 
+int64_t
+ConstantSpectrumPropagationLossModel::DoAssignStreams(int64_t stream)
+{
+    return 0;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/constant-spectrum-propagation-loss.h ns-3.41/src/spectrum/model/constant-spectrum-propagation-loss.h
--- ns-3.40/src/spectrum/model/constant-spectrum-propagation-loss.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/constant-spectrum-propagation-loss.h	2024-02-09 14:44:08.493794200 -0800
@@ -57,8 +57,10 @@
     double GetLossDb() const;
 
   protected:
+    int64_t DoAssignStreams(int64_t stream) override;
     double m_lossDb;     //!< Propagation loss [dB]
     double m_lossLinear; //!< Propagation loss (linear)
+
   private:
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/friis-spectrum-propagation-loss.cc ns-3.41/src/spectrum/model/friis-spectrum-propagation-loss.cc
--- ns-3.40/src/spectrum/model/friis-spectrum-propagation-loss.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/friis-spectrum-propagation-loss.cc	2024-02-09 14:44:08.493794200 -0800
@@ -94,4 +94,10 @@
     return loss;
 }
 
+int64_t
+FriisSpectrumPropagationLossModel::DoAssignStreams(int64_t stream)
+{
+    return 0;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/friis-spectrum-propagation-loss.h ns-3.41/src/spectrum/model/friis-spectrum-propagation-loss.h
--- ns-3.40/src/spectrum/model/friis-spectrum-propagation-loss.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/friis-spectrum-propagation-loss.h	2024-02-09 14:44:08.493794200 -0800
@@ -65,6 +65,9 @@
      * @return if Prx < Ptx then return Prx; else return Ptx
      */
     double CalculateLoss(double f, double d) const;
+
+  protected:
+    int64_t DoAssignStreams(int64_t stream) override;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/multi-model-spectrum-channel.cc ns-3.41/src/spectrum/model/multi-model-spectrum-channel.cc
--- ns-3.40/src/spectrum/model/multi-model-spectrum-channel.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/multi-model-spectrum-channel.cc	2024-02-09 14:44:08.497794400 -0800
@@ -417,7 +417,7 @@
                       "PhasedArrayModel instances should be installed at both TX and RX "
                       "SpectrumPhy in order to use PhasedArraySpectrumPropagationLoss.");
 
-        params->psd = m_phasedArraySpectrumPropagationLoss->CalcRxPowerSpectralDensity(
+        params = m_phasedArraySpectrumPropagationLoss->CalcRxPowerSpectralDensity(
             params,
             params->txPhy->GetMobility(),
             receiver->GetMobility(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/phased-array-spectrum-propagation-loss-model.cc ns-3.41/src/spectrum/model/phased-array-spectrum-propagation-loss-model.cc
--- ns-3.40/src/spectrum/model/phased-array-spectrum-propagation-loss-model.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/phased-array-spectrum-propagation-loss-model.cc	2024-02-09 14:44:08.497794400 -0800
@@ -60,7 +60,13 @@
     m_next = next;
 }
 
-Ptr<SpectrumValue>
+Ptr<PhasedArraySpectrumPropagationLossModel>
+PhasedArraySpectrumPropagationLossModel::GetNext() const
+{
+    return m_next;
+}
+
+Ptr<SpectrumSignalParameters>
 PhasedArraySpectrumPropagationLossModel::CalcRxPowerSpectralDensity(
     Ptr<const SpectrumSignalParameters> params,
     Ptr<const MobilityModel> a,
@@ -71,14 +77,28 @@
     // Here we assume that all the models in the chain of models are of type
     // PhasedArraySpectrumPropagationLossModel that provides the implementation of
     // this function, i.e. has phased array model of TX and RX as parameters
-    Ptr<SpectrumValue> rxPsd =
+    auto rxParams =
         DoCalcRxPowerSpectralDensity(params, a, b, aPhasedArrayModel, bPhasedArrayModel);
+
     if (m_next)
     {
-        rxPsd =
+        rxParams =
             m_next->CalcRxPowerSpectralDensity(params, a, b, aPhasedArrayModel, bPhasedArrayModel);
     }
-    return rxPsd;
+    return rxParams;
+}
+
+int64_t
+PhasedArraySpectrumPropagationLossModel::AssignStreams(int64_t stream)
+{
+    NS_LOG_FUNCTION(this << stream);
+    auto currentStream = stream;
+    currentStream += DoAssignStreams(stream);
+    if (m_next)
+    {
+        currentStream += m_next->AssignStreams(currentStream);
+    }
+    return (currentStream - stream);
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/phased-array-spectrum-propagation-loss-model.h ns-3.41/src/spectrum/model/phased-array-spectrum-propagation-loss-model.h
--- ns-3.40/src/spectrum/model/phased-array-spectrum-propagation-loss-model.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/phased-array-spectrum-propagation-loss-model.h	2024-02-09 14:44:08.497794400 -0800
@@ -62,6 +62,13 @@
     void SetNext(Ptr<PhasedArraySpectrumPropagationLossModel> next);
 
     /**
+     * Return the pointer to the next PhasedArraySpectrumPropagationLossModel, if any.
+     *
+     * @return Pointer to the next model, if any
+     */
+    Ptr<PhasedArraySpectrumPropagationLossModel> GetNext() const;
+
+    /**
      * This method is to be called to calculate
      *
      * @param txPsd the spectrum signal parameters.
@@ -70,18 +77,42 @@
      * @param aPhasedArrayModel the instance of the phased antenna array of the sender
      * @param bPhasedArrayModel the instance of the phased antenna array of the receiver
      *
-     * @return set of values Vs frequency representing the received
-     * power in the same units used for the txPower parameter.
+     * @return SpectrumSignalParameters in which is updated the PSD to contain
+     * a set of values Vs frequency representing the received
+     * power in the same units used for the txPower parameter,
+     * and additional chanSpectrumMatrix is computed to support MIMO systems.
      */
-    Ptr<SpectrumValue> CalcRxPowerSpectralDensity(
+    Ptr<SpectrumSignalParameters> CalcRxPowerSpectralDensity(
         Ptr<const SpectrumSignalParameters> txPsd,
         Ptr<const MobilityModel> a,
         Ptr<const MobilityModel> b,
         Ptr<const PhasedArrayModel> aPhasedArrayModel,
         Ptr<const PhasedArrayModel> bPhasedArrayModel) const;
 
+    /**
+     * If this loss model uses objects of type RandomVariableStream,
+     * set the stream numbers to the integers starting with the offset
+     * 'stream'. Return the number of streams (possibly zero) that
+     * have been assigned.  If there are PhasedArraySpectrumPropagationLossModels
+     * chained together, this method will also assign streams to the
+     * downstream models.
+     *
+     * \param stream the stream index offset start
+     * \return the number of stream indices assigned by this model
+     */
+    int64_t AssignStreams(int64_t stream);
+
   protected:
     void DoDispose() override;
+    /**
+     * Assign a fixed random variable stream number to the random variables used by this model.
+     *
+     * Subclasses must implement this; those not using random variables can return zero.
+     *
+     * \param stream first stream index to use
+     * \return the number of stream indices assigned by this model
+     */
+    virtual int64_t DoAssignStreams(int64_t stream) = 0;
 
   private:
     /**
@@ -92,10 +123,12 @@
      * @param aPhasedArrayModel the instance of the phased antenna array of the sender
      * @param bPhasedArrayModel the instance of the phased antenna array of the receiver
      *
-     * @return set of values Vs frequency representing the received
-     * power in the same units used for the txPower parameter.
+     * @return SpectrumSignalParameters in which is updated the PSD to contain
+     * a set of values Vs frequency representing the received
+     * power in the same units used for the txPower parameter,
+     * and additional chanSpectrumMatrix is set.
      */
-    virtual Ptr<SpectrumValue> DoCalcRxPowerSpectralDensity(
+    virtual Ptr<SpectrumSignalParameters> DoCalcRxPowerSpectralDensity(
         Ptr<const SpectrumSignalParameters> params,
         Ptr<const MobilityModel> a,
         Ptr<const MobilityModel> b,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-channel.cc ns-3.41/src/spectrum/model/spectrum-channel.cc
--- ns-3.40/src/spectrum/model/spectrum-channel.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-channel.cc	2024-02-09 14:44:08.497794400 -0800
@@ -19,6 +19,7 @@
 
 #include "spectrum-channel.h"
 
+#include <ns3/abort.h>
 #include <ns3/double.h>
 #include <ns3/log.h>
 #include <ns3/pointer.h>
@@ -158,7 +159,7 @@
     m_filter = filter;
 }
 
-Ptr<const SpectrumTransmitFilter>
+Ptr<SpectrumTransmitFilter>
 SpectrumChannel::GetSpectrumTransmitFilter() const
 {
     return m_filter;
@@ -167,28 +168,100 @@
 void
 SpectrumChannel::SetPropagationDelayModel(Ptr<PropagationDelayModel> delay)
 {
-    NS_ASSERT(!m_propagationDelay);
+    NS_ABORT_MSG_IF(m_propagationDelay, "Error, called SetPropagationDelayModel() twice");
     m_propagationDelay = delay;
 }
 
 Ptr<SpectrumPropagationLossModel>
-SpectrumChannel::GetSpectrumPropagationLossModel()
+SpectrumChannel::GetSpectrumPropagationLossModel() const
 {
-    NS_LOG_FUNCTION(this);
     return m_spectrumPropagationLoss;
 }
 
 Ptr<PhasedArraySpectrumPropagationLossModel>
-SpectrumChannel::GetPhasedArraySpectrumPropagationLossModel()
+SpectrumChannel::GetPhasedArraySpectrumPropagationLossModel() const
 {
-    NS_LOG_FUNCTION(this);
     return m_phasedArraySpectrumPropagationLoss;
 }
 
 Ptr<PropagationLossModel>
-SpectrumChannel::GetPropagationLossModel()
+SpectrumChannel::GetPropagationLossModel() const
 {
     return m_propagationLoss;
 }
 
+Ptr<PropagationDelayModel>
+SpectrumChannel::GetPropagationDelayModel() const
+{
+    return m_propagationDelay;
+}
+
+int64_t
+SpectrumChannel::AssignStreams(int64_t stream)
+{
+    NS_LOG_FUNCTION(this << stream);
+    auto currentStream = stream;
+    auto lastCurrentStream = stream;
+    if (m_propagationLoss)
+    {
+        currentStream += m_propagationLoss->AssignStreams(currentStream);
+    }
+    if (currentStream - lastCurrentStream)
+    {
+        NS_LOG_DEBUG("PropagationLossModel objects used " << currentStream - lastCurrentStream
+                                                          << " streams");
+    }
+    lastCurrentStream = currentStream;
+    if (m_propagationDelay)
+    {
+        m_propagationDelay->AssignStreams(currentStream);
+        currentStream += 1;
+    }
+    if (currentStream - lastCurrentStream)
+    {
+        NS_LOG_DEBUG("PropagationDelayModel object used " << currentStream - lastCurrentStream
+                                                          << " streams");
+    }
+    lastCurrentStream = currentStream;
+    if (m_spectrumPropagationLoss)
+    {
+        currentStream += m_spectrumPropagationLoss->AssignStreams(currentStream);
+    }
+    if (currentStream - lastCurrentStream)
+    {
+        NS_LOG_DEBUG("SpectrumPropagationLossModel objects used "
+                     << currentStream - lastCurrentStream << " streams");
+    }
+    lastCurrentStream = currentStream;
+    if (m_phasedArraySpectrumPropagationLoss)
+    {
+        currentStream += m_phasedArraySpectrumPropagationLoss->AssignStreams(currentStream);
+    }
+    if (currentStream - lastCurrentStream)
+    {
+        NS_LOG_DEBUG("PhasedArraySpectrumPropagationLossModel objects used "
+                     << currentStream - lastCurrentStream << " streams");
+    }
+    lastCurrentStream = currentStream;
+    if (m_filter)
+    {
+        currentStream += m_filter->AssignStreams(currentStream);
+    }
+    if (currentStream - lastCurrentStream)
+    {
+        NS_LOG_DEBUG("SpectrumTransmitFilter objects used " << currentStream - lastCurrentStream
+                                                            << " streams");
+    }
+    currentStream += DoAssignStreams(currentStream);
+    NS_LOG_DEBUG("Assigned a total of " << currentStream - stream << " streams");
+    return (currentStream - stream);
+}
+
+int64_t
+SpectrumChannel::DoAssignStreams(int64_t stream)
+{
+    NS_LOG_FUNCTION(this << stream);
+    return 0;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-channel.h ns-3.41/src/spectrum/model/spectrum-channel.h
--- ns-3.40/src/spectrum/model/spectrum-channel.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-channel.h	2024-02-09 14:44:08.497794400 -0800
@@ -92,7 +92,10 @@
         Ptr<PhasedArraySpectrumPropagationLossModel> loss);
 
     /**
-     * Set the propagation delay model to be used
+     * Set the propagation delay model to be used.  This method will abort
+     * the simulation if there exists a previously set propagation delay model
+     * (i.e., unlike propagation loss models, multiple of which can be chained
+     * together, there is only one propagation delay model).
      * \param delay Ptr to the propagation delay model to be used.
      */
     void SetPropagationDelayModel(Ptr<PropagationDelayModel> delay);
@@ -101,20 +104,26 @@
      * Get the frequency-dependent propagation loss model.
      * \returns a pointer to the propagation loss model.
      */
-    Ptr<SpectrumPropagationLossModel> GetSpectrumPropagationLossModel();
+    Ptr<SpectrumPropagationLossModel> GetSpectrumPropagationLossModel() const;
 
     /**
      * Get the frequency-dependent propagation loss model that is
      * compatible with the phased antenna arrays at TX and RX
      * \returns a pointer to the propagation loss model.
      */
-    Ptr<PhasedArraySpectrumPropagationLossModel> GetPhasedArraySpectrumPropagationLossModel();
+    Ptr<PhasedArraySpectrumPropagationLossModel> GetPhasedArraySpectrumPropagationLossModel() const;
 
     /**
      * Get the propagation loss model.
      * \returns a pointer to the propagation loss model.
      */
-    Ptr<PropagationLossModel> GetPropagationLossModel();
+    Ptr<PropagationLossModel> GetPropagationLossModel() const;
+
+    /**
+     * Get the propagation delay model that has been set on the channel.
+     * \returns a pointer to the propagation delay model.
+     */
+    Ptr<PropagationDelayModel> GetPropagationDelayModel() const;
 
     /**
      * Add the transmit filter to be used to filter possible signal receptions
@@ -131,7 +140,7 @@
      * if more than one is present.
      * \returns a pointer to the transmit filter.
      */
-    Ptr<const SpectrumTransmitFilter> GetSpectrumTransmitFilter() const;
+    Ptr<SpectrumTransmitFilter> GetSpectrumTransmitFilter() const;
 
     /**
      * Used by attached PHY instances to transmit signals on the channel
@@ -141,6 +150,20 @@
     virtual void StartTx(Ptr<SpectrumSignalParameters> params) = 0;
 
     /**
+     * This method calls AssignStreams() on any/all of the PropagationLossModel,
+     * PropagationDelayModel, SpectrumPropagationLossModel,
+     * PhasedArraySpectrumPropagationLossModel, and SpectrumTransmitFilter
+     * objects that have been added to this channel.  If any of those
+     * objects are chained together (e.g., multiple PropagationDelayModel
+     * objects), the base class of that object is responsible for ensuring
+     * that all models in the chain have AssignStreams recursively called.
+     *
+     * \param stream the stream index offset start
+     * \return the number of stream indices assigned by this model
+     */
+    int64_t AssignStreams(int64_t stream);
+
+    /**
      * \brief Remove a SpectrumPhy from a channel
      *
      * This method is used to detach a SpectrumPhy instance from a
@@ -207,6 +230,15 @@
 
   protected:
     /**
+     * This provides a base class implementation that may be subclassed
+     * if needed by subclasses that might need additional stream assignments.
+     *
+     * \param stream first stream index to use
+     * \return the number of stream indices assigned by this model
+     */
+    virtual int64_t DoAssignStreams(int64_t stream);
+
+    /**
      * The `PathLoss` trace source. Exporting the pointers to the Tx and Rx
      * SpectrumPhy and a pathloss value, in dB.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-model.h ns-3.41/src/spectrum/model/spectrum-model.h
--- ns-3.40/src/spectrum/model/spectrum-model.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-model.h	2024-02-09 14:44:08.497794400 -0800
@@ -22,6 +22,7 @@
 
 #include <ns3/simple-ref-count.h>
 
+#include <cstddef>
 #include <vector>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-propagation-loss-model.cc ns-3.41/src/spectrum/model/spectrum-propagation-loss-model.cc
--- ns-3.40/src/spectrum/model/spectrum-propagation-loss-model.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-propagation-loss-model.cc	2024-02-09 14:44:08.497794400 -0800
@@ -59,6 +59,12 @@
     m_next = next;
 }
 
+Ptr<SpectrumPropagationLossModel>
+SpectrumPropagationLossModel::GetNext() const
+{
+    return m_next;
+}
+
 Ptr<SpectrumValue>
 SpectrumPropagationLossModel::CalcRxPowerSpectralDensity(Ptr<const SpectrumSignalParameters> params,
                                                          Ptr<const MobilityModel> a,
@@ -72,4 +78,16 @@
     return rxPsd;
 }
 
+int64_t
+SpectrumPropagationLossModel::AssignStreams(int64_t stream)
+{
+    auto currentStream = stream;
+    currentStream += DoAssignStreams(stream);
+    if (m_next)
+    {
+        currentStream += m_next->AssignStreams(currentStream);
+    }
+    return (currentStream - stream);
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-propagation-loss-model.h ns-3.41/src/spectrum/model/spectrum-propagation-loss-model.h
--- ns-3.40/src/spectrum/model/spectrum-propagation-loss-model.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-propagation-loss-model.h	2024-02-09 14:44:08.497794400 -0800
@@ -60,6 +60,13 @@
     void SetNext(Ptr<SpectrumPropagationLossModel> next);
 
     /**
+     * Return the pointer to the next SpectrumPropagationLossModel, if any.
+     *
+     * @return Pointer to the next model, if any.
+     */
+    Ptr<SpectrumPropagationLossModel> GetNext() const;
+
+    /**
      * This method is to be called to calculate
      *
      * @param params the spectrum signal parameters.
@@ -73,8 +80,30 @@
                                                   Ptr<const MobilityModel> a,
                                                   Ptr<const MobilityModel> b) const;
 
+    /**
+     * If this loss model uses objects of type RandomVariableStream,
+     * set the stream numbers to the integers starting with the offset
+     * 'stream'. Return the number of streams (possibly zero) that
+     * have been assigned.  If there are SpectrumPropagationLossModels chained
+     * together, this method will also assign streams to the
+     * downstream models.
+     *
+     * \param stream the stream index offset start
+     * \return the number of stream indices assigned by this model
+     */
+    int64_t AssignStreams(int64_t stream);
+
   protected:
     void DoDispose() override;
+    /**
+     * Assign a fixed random variable stream number to the random variables used by this model.
+     *
+     * Subclasses must implement this; those not using random variables can return zero.
+     *
+     * \param stream first stream index to use
+     * \return the number of stream indices assigned by this model
+     */
+    virtual int64_t DoAssignStreams(int64_t stream) = 0;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-signal-parameters.cc ns-3.41/src/spectrum/model/spectrum-signal-parameters.cc
--- ns-3.40/src/spectrum/model/spectrum-signal-parameters.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-signal-parameters.cc	2024-02-09 14:44:08.497794400 -0800
@@ -47,6 +47,10 @@
     duration = p.duration;
     txPhy = p.txPhy;
     txAntenna = p.txAntenna;
+    spectrumChannelMatrix = p.spectrumChannelMatrix; // we do not need a deep copy, it will not be
+                                                     // changed once is created
+    precodingMatrix =
+        p.precodingMatrix; // we do not need a deep copy, it will not be changed once is created
 }
 
 Ptr<SpectrumSignalParameters>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-signal-parameters.h ns-3.41/src/spectrum/model/spectrum-signal-parameters.h
--- ns-3.40/src/spectrum/model/spectrum-signal-parameters.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-signal-parameters.h	2024-02-09 14:44:08.497794400 -0800
@@ -20,9 +20,8 @@
 #ifndef SPECTRUM_SIGNAL_PARAMETERS_H
 #define SPECTRUM_SIGNAL_PARAMETERS_H
 
+#include <ns3/matrix-array.h>
 #include <ns3/nstime.h>
-#include <ns3/ptr.h>
-#include <ns3/simple-ref-count.h>
 
 namespace ns3
 {
@@ -111,6 +110,20 @@
      * The AntennaModel instance that was used to transmit this signal.
      */
     Ptr<AntennaModel> txAntenna;
+
+    /**
+     * The 3D channel matrix where the dimensions are: the number of RX ports,
+     * the number of TX Ports, the number of resource blocks (RBs).
+     * Needed in the MIMO system in which multiple TX and RX ports can exist,
+     * hence the PSD is multidimensional. Elements are the complex numbers.
+     */
+    Ptr<const ComplexMatrixArray> spectrumChannelMatrix;
+
+    /**
+     * The 3D precoding matrix where the dimensions are: the number of TX ports,
+     * the number of TX streams, the number of RBs.
+     */
+    Ptr<const ComplexMatrixArray> precodingMatrix;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-transmit-filter.cc ns-3.41/src/spectrum/model/spectrum-transmit-filter.cc
--- ns-3.40/src/spectrum/model/spectrum-transmit-filter.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-transmit-filter.cc	2024-02-09 14:44:08.497794400 -0800
@@ -87,4 +87,16 @@
     }
 }
 
+int64_t
+SpectrumTransmitFilter::AssignStreams(int64_t stream)
+{
+    auto currentStream = stream;
+    currentStream += DoAssignStreams(stream);
+    if (m_next)
+    {
+        currentStream += m_next->AssignStreams(currentStream);
+    }
+    return (currentStream - stream);
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-transmit-filter.h ns-3.41/src/spectrum/model/spectrum-transmit-filter.h
--- ns-3.40/src/spectrum/model/spectrum-transmit-filter.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-transmit-filter.h	2024-02-09 14:44:08.497794400 -0800
@@ -74,8 +74,30 @@
      */
     bool Filter(Ptr<const SpectrumSignalParameters> params, Ptr<const SpectrumPhy> receiverPhy);
 
+    /**
+     * If this loss model uses objects of type RandomVariableStream,
+     * set the stream numbers to the integers starting with the offset
+     * 'stream'. Return the number of streams (possibly zero) that
+     * have been assigned.  If there are SpectrumTransmitFilters chained
+     * together, this method will also assign streams to the
+     * downstream models.
+     *
+     * \param stream the stream index offset start
+     * \return the number of stream indices assigned by this model
+     */
+    int64_t AssignStreams(int64_t stream);
+
   protected:
     void DoDispose() override;
+    /**
+     * Assign a fixed random variable stream number to the random variables used by this model.
+     *
+     * Subclasses must implement this; those not using random variables can return zero.
+     *
+     * \param stream first stream index to use
+     * \return the number of stream indices assigned by this model
+     */
+    virtual int64_t DoAssignStreams(int64_t stream) = 0;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-value.cc ns-3.41/src/spectrum/model/spectrum-value.cc
--- ns-3.40/src/spectrum/model/spectrum-value.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-value.cc	2024-02-09 14:44:08.497794400 -0800
@@ -411,6 +411,18 @@
     return res;
 }
 
+bool
+operator==(const SpectrumValue& lhs, const SpectrumValue& rhs)
+{
+    return (lhs.m_values == rhs.m_values);
+}
+
+bool
+operator!=(const SpectrumValue& lhs, const SpectrumValue& rhs)
+{
+    return (lhs.m_values != rhs.m_values);
+}
+
 SpectrumValue
 operator+(const SpectrumValue& lhs, double rhs)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/spectrum-value.h ns-3.41/src/spectrum/model/spectrum-value.h
--- ns-3.40/src/spectrum/model/spectrum-value.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/spectrum-value.h	2024-02-09 14:44:08.497794400 -0800
@@ -160,6 +160,50 @@
     uint32_t GetValuesN() const;
 
     /**
+     * Directly set the values using the std::vector<double>
+     * \param values a reference to the std::vector<double>
+     */
+    inline void SetValues(Values& values)
+    {
+        NS_ASSERT_MSG(
+            values.size() == m_spectrumModel->GetNumBands(),
+            "Values size does not correspond to the SpectrumModel in use by this SpectrumValue.");
+        m_values = values;
+    }
+
+    /**
+     * Directly set the values by moving the values from the input std::vector<double>
+     * \param values a reference to the std::vector<double> to be moved
+     */
+    inline void SetValues(Values&& values)
+    {
+        NS_ASSERT_MSG(
+            values.size() == m_spectrumModel->GetNumBands(),
+            "Values size does not correspond to the SpectrumModel in use by this SpectrumValue.");
+        m_values = std::move(values);
+    }
+
+    /**
+     * \brief Provides the direct access to the underlying std::vector<double>
+     * that stores the spectrum values.
+     * \return a reference to the stored values
+     */
+    inline Values& GetValues()
+    {
+        return m_values;
+    }
+
+    /**
+     * \brief Provides the direct read-only access to the underlying std::vector<double>
+     * that stores the spectrum values.
+     * \return a const reference to the stored values
+     */
+    inline const Values& GetValues() const
+    {
+        return m_values;
+    }
+
+    /**
      * \brief Get the value element at the position
      * \param pos position
      * \return the value element in that position (with bounds checking)
@@ -287,6 +331,26 @@
     friend SpectrumValue operator/(double lhs, const SpectrumValue& rhs);
 
     /**
+     * Compare two spectrum values
+     *
+     * @param lhs Left Hand Side of the operator
+     * @param rhs Right Hand Side of the operator
+     *
+     * @return true if lhs and rhs SpectruValues are equal
+     */
+    friend bool operator==(const SpectrumValue& lhs, const SpectrumValue& rhs);
+
+    /**
+     * Compare two spectrum values
+     *
+     * @param lhs Left Hand Side of the operator
+     * @param rhs Right Hand Side of the operator
+     *
+     * @return true if lhs and rhs SpectruValues are not equal
+     */
+    friend bool operator!=(const SpectrumValue& lhs, const SpectrumValue& rhs);
+
+    /**
      * unary plus operator
      *
      * @param rhs Right Hand Side of the operator
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/three-gpp-channel-model.cc ns-3.41/src/spectrum/model/three-gpp-channel-model.cc
--- ns-3.40/src/spectrum/model/three-gpp-channel-model.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/three-gpp-channel-model.cc	2024-02-09 14:44:08.497794400 -0800
@@ -32,6 +32,7 @@
 #include <ns3/simulator.h>
 
 #include <algorithm>
+#include <map>
 #include <random>
 
 namespace ns3
@@ -1869,8 +1870,8 @@
     // Step 11: Generate channel coefficients for each cluster n and each receiver
     //  and transmitter element pair u,s.
     // where n is cluster index, u and s are receive and transmit antenna element.
-    size_t uSize = uAntenna->GetNumberOfElements();
-    size_t sSize = sAntenna->GetNumberOfElements();
+    size_t uSize = uAntenna->GetNumElems();
+    size_t sSize = sAntenna->GetNumElems();
 
     // NOTE: Since each of the strongest 2 clusters are divided into 3 sub-clusters,
     // the total cluster will generally be numReducedCLuster + 4.
@@ -1909,9 +1910,6 @@
     Angles sAngle(uMob->GetPosition(), sMob->GetPosition());
     Angles uAngle(sMob->GetPosition(), uMob->GetPosition());
 
-    Complex2DVector raysPreComp(channelParams->m_reducedClusterNumber,
-                                table3gpp->m_raysPerCluster); // stores part of the ray expression,
-    // cached as independent from the u- and s-indexes
     Double2DVector sinCosA; // cached multiplications of sin and cos of the ZoA and AoA angles
     Double2DVector sinSinA; // cached multiplications of sines of the ZoA and AoA angles
     Double2DVector cosZoA;  // cached cos of the ZoA angle
@@ -1919,6 +1917,18 @@
     Double2DVector sinSinD; // cached multiplications of the cosines of the ZoA and AoA angles
     Double2DVector cosZoD;  // cached cos of the ZoD angle
 
+    // contains part of the ray expression, cached as independent from the u- and s-indexes,
+    // but calculate it for different polarization angles of s and u
+    std::map<std::pair<uint8_t, uint8_t>, Complex2DVector> raysPreComp;
+    for (size_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
+    {
+        for (size_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
+        {
+            raysPreComp[std::make_pair(polSa, polUa)] =
+                Complex2DVector(channelParams->m_reducedClusterNumber, table3gpp->m_raysPerCluster);
+        }
+    }
+
     // resize to appropriate dimensions
     sinCosA.resize(channelParams->m_reducedClusterNumber);
     sinSinA.resize(channelParams->m_reducedClusterNumber);
@@ -1946,21 +1956,30 @@
 
             // cache the component of the "rays" terms which depend on the random angle of arrivals
             // and departures and initial phases only
-            auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
-                Angles(channelParams->m_rayAoaRadian[nIndex][mIndex],
-                       channelParams->m_rayZoaRadian[nIndex][mIndex]));
-            auto [txFieldPatternPhi, txFieldPatternTheta] = sAntenna->GetElementFieldPattern(
-                Angles(channelParams->m_rayAodRadian[nIndex][mIndex],
-                       channelParams->m_rayZodRadian[nIndex][mIndex]));
-            raysPreComp(nIndex, mIndex) =
-                std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
-                    rxFieldPatternTheta * txFieldPatternTheta +
-                std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
-                    std::sqrt(1.0 / k) * rxFieldPatternTheta * txFieldPatternPhi +
-                std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
-                    std::sqrt(1.0 / k) * rxFieldPatternPhi * txFieldPatternTheta +
-                std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
-                    rxFieldPatternPhi * txFieldPatternPhi;
+            for (uint8_t polUa = 0; polUa < uAntenna->GetNumPols(); ++polUa)
+            {
+                auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
+                    Angles(channelParams->m_rayAoaRadian[nIndex][mIndex],
+                           channelParams->m_rayZoaRadian[nIndex][mIndex]),
+                    polUa);
+                for (uint8_t polSa = 0; polSa < sAntenna->GetNumPols(); ++polSa)
+                {
+                    auto [txFieldPatternPhi, txFieldPatternTheta] =
+                        sAntenna->GetElementFieldPattern(
+                            Angles(channelParams->m_rayAodRadian[nIndex][mIndex],
+                                   channelParams->m_rayZodRadian[nIndex][mIndex]),
+                            polSa);
+                    raysPreComp[std::make_pair(polSa, polUa)](nIndex, mIndex) =
+                        std::complex<double>(cos(initialPhase[0]), sin(initialPhase[0])) *
+                            rxFieldPatternTheta * txFieldPatternTheta +
+                        std::complex<double>(cos(initialPhase[1]), sin(initialPhase[1])) *
+                            std::sqrt(1.0 / k) * rxFieldPatternTheta * txFieldPatternPhi +
+                        std::complex<double>(cos(initialPhase[2]), sin(initialPhase[2])) *
+                            std::sqrt(1.0 / k) * rxFieldPatternPhi * txFieldPatternTheta +
+                        std::complex<double>(cos(initialPhase[3]), sin(initialPhase[3])) *
+                            rxFieldPatternPhi * txFieldPatternPhi;
+                }
+            }
 
             // cache the component of the "rxPhaseDiff" terms which depend on the random angle of
             // arrivals only
@@ -2011,10 +2030,11 @@
                             2 * M_PI *
                             (sinCosD[nIndex][mIndex] * sLoc.x + sinSinD[nIndex][mIndex] * sLoc.y +
                              cosZoD[nIndex][mIndex] * sLoc.z);
-
                         // NOTE Doppler is computed in the CalcBeamformingGain function and is
                         // simplified to only account for the center angle of each cluster.
-                        rays += raysPreComp(nIndex, mIndex) *
+                        rays += raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
+                                                           uAntenna->GetElemPol(uIndex))](nIndex,
+                                                                                          mIndex) *
                                 std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
                                 std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
                     }
@@ -2043,7 +2063,9 @@
                              cosZoD[nIndex][mIndex] * sLoc.z);
 
                         std::complex<double> raySub =
-                            raysPreComp(nIndex, mIndex) *
+                            raysPreComp[std::make_pair(sAntenna->GetElemPol(sIndex),
+                                                       uAntenna->GetElemPol(uIndex))](nIndex,
+                                                                                      mIndex) *
                             std::complex<double>(cos(rxPhaseDiff), sin(rxPhaseDiff)) *
                             std::complex<double>(cos(txPhaseDiff), sin(txPhaseDiff));
 
@@ -2123,9 +2145,11 @@
                      cosSAngleIncl * sLoc.z);
 
                 auto [rxFieldPatternPhi, rxFieldPatternTheta] = uAntenna->GetElementFieldPattern(
-                    Angles(uAngle.GetAzimuth(), uAngle.GetInclination()));
+                    Angles(uAngle.GetAzimuth(), uAngle.GetInclination()),
+                    uAntenna->GetElemPol(uIndex));
                 auto [txFieldPatternPhi, txFieldPatternTheta] = sAntenna->GetElementFieldPattern(
-                    Angles(sAngle.GetAzimuth(), sAngle.GetInclination()));
+                    Angles(sAngle.GetAzimuth(), sAngle.GetInclination()),
+                    sAntenna->GetElemPol(sIndex));
 
                 ray = (rxFieldPatternTheta * txFieldPatternTheta -
                        rxFieldPatternPhi * txFieldPatternPhi) *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc ns-3.41/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc
--- ns-3.40/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.cc	2024-02-09 14:44:08.497794400 -0800
@@ -111,45 +111,114 @@
     m_channelModel->GetAttribute(name, value);
 }
 
-PhasedArrayModel::ComplexVector
+Ptr<const MatrixBasedChannelModel::Complex3DVector>
 ThreeGppSpectrumPropagationLossModel::CalcLongTerm(
     Ptr<const MatrixBasedChannelModel::ChannelMatrix> params,
-    const PhasedArrayModel::ComplexVector& sW,
-    const PhasedArrayModel::ComplexVector& uW) const
+    Ptr<const PhasedArrayModel> sAnt,
+    Ptr<const PhasedArrayModel> uAnt) const
 {
     NS_LOG_FUNCTION(this);
 
-    size_t uAntennaNum = uW.GetSize();
-    size_t sAntennaNum = sW.GetSize();
+    const PhasedArrayModel::ComplexVector& sW = sAnt->GetBeamformingVectorRef();
+    const PhasedArrayModel::ComplexVector& uW = uAnt->GetBeamformingVectorRef();
+    size_t sAntNumElems = sW.GetSize();
+    size_t uAntNumElems = uW.GetSize();
+    NS_ASSERT(uAntNumElems == params->m_channel.GetNumRows());
+    NS_ASSERT(sAntNumElems == params->m_channel.GetNumCols());
+    NS_LOG_DEBUG("CalcLongTerm with " << uW.GetSize() << " u antenna elements and " << sW.GetSize()
+                                      << " s antenna elements, and with "
+                                      << " s ports: " << sAnt->GetNumPorts()
+                                      << " u ports: " << uAnt->GetNumPorts());
+    NS_ASSERT_MSG((sAnt != nullptr) && (uAnt != nullptr), "Improper call to the method");
+    size_t numClusters = params->m_channel.GetNumPages();
+    // create and initialize the size of the longTerm 3D matrix
+    Ptr<MatrixBasedChannelModel::Complex3DVector> longTerm =
+        Create<MatrixBasedChannelModel::Complex3DVector>(uAnt->GetNumPorts(),
+                                                         sAnt->GetNumPorts(),
+                                                         numClusters);
+    // Calculate long term uW * Husn * sW, the result is a matrix
+    // with the dimensions #uPorts, #sPorts, #cluster
+    for (auto sPortIdx = 0; sPortIdx < sAnt->GetNumPorts(); sPortIdx++)
+    {
+        for (auto uPortIdx = 0; uPortIdx < uAnt->GetNumPorts(); uPortIdx++)
+        {
+            for (size_t cIndex = 0; cIndex < numClusters; cIndex++)
+            {
+                longTerm->Elem(uPortIdx, sPortIdx, cIndex) =
+                    CalculateLongTermComponent(params, sAnt, uAnt, sPortIdx, uPortIdx, cIndex);
+            }
+        }
+    }
+    return longTerm;
+}
 
-    NS_ASSERT(uAntennaNum == params->m_channel.GetNumRows());
-    NS_ASSERT(sAntennaNum == params->m_channel.GetNumCols());
+std::complex<double>
+ThreeGppSpectrumPropagationLossModel::CalculateLongTermComponent(
+    Ptr<const MatrixBasedChannelModel::ChannelMatrix> params,
+    Ptr<const PhasedArrayModel> sAnt,
+    Ptr<const PhasedArrayModel> uAnt,
+    uint16_t sPortIdx,
+    uint16_t uPortIdx,
+    uint16_t cIndex) const
+{
+    NS_LOG_FUNCTION(this);
+    const PhasedArrayModel::ComplexVector& sW = sAnt->GetBeamformingVectorRef();
+    const PhasedArrayModel::ComplexVector& uW = uAnt->GetBeamformingVectorRef();
+    auto sPortElems = sAnt->GetNumElemsPerPort();
+    auto uPortElems = uAnt->GetNumElemsPerPort();
+    auto startS = sAnt->ArrayIndexFromPortIndex(sPortIdx, 0);
+    auto startU = uAnt->ArrayIndexFromPortIndex(uPortIdx, 0);
+    std::complex<double> txSum(0, 0);
+    // limiting multiplication operations to the port location
+    auto sIndex = startS;
+    // The sub-array partition model is adopted for TXRU virtualization,
+    // as described in Section 5.2.2 of 3GPP TR 36.897,
+    // and so equal beam weights are used for all the ports.
+    // Support of the full-connection model for TXRU virtualization would need extensions.
+    for (size_t tIndex = 0; tIndex < sPortElems; tIndex++, sIndex++)
+    {
+        std::complex<double> rxSum(0, 0);
+        auto uIndex = startU;
+        for (size_t rIndex = 0; rIndex < uPortElems; rIndex++, uIndex++)
+        {
+            rxSum += uW[uIndex - startU] * params->m_channel(uIndex, sIndex, cIndex);
+            auto testV = (rIndex % uAnt->GetHElemsPerPort());
+            auto ptInc = uAnt->GetHElemsPerPort() - 1;
+            if (testV == ptInc)
+            {
+                auto incVal = uAnt->GetNumColumns() - uAnt->GetHElemsPerPort();
+                uIndex += incVal; // Increment by a factor to reach next column in a port
+            }
+        }
 
-    NS_LOG_DEBUG("CalcLongTerm with " << uAntennaNum << " u antenna elements and " << sAntennaNum
-                                      << " s antenna elements.");
-    // store the long term part to reduce computation load
-    // only the small scale fading needs to be updated if the large scale parameters and antenna
-    // weights remain unchanged. here we calculate long term uW * Husn * sW, the result is an array
-    // of values per cluster
-    return params->m_channel.MultiplyByLeftAndRightMatrix(uW.Transpose(), sW);
+        txSum += sW[sIndex - startS] * rxSum;
+        auto testV = (tIndex % sAnt->GetHElemsPerPort());
+        auto ptInc = sAnt->GetHElemsPerPort() - 1;
+        if (testV == ptInc)
+        {
+            size_t incVal = sAnt->GetNumColumns() - sAnt->GetHElemsPerPort();
+            sIndex += incVal; // Increment by a factor to reach next column in a port
+        }
+    }
+    return txSum;
 }
 
-Ptr<SpectrumValue>
+Ptr<SpectrumSignalParameters>
 ThreeGppSpectrumPropagationLossModel::CalcBeamformingGain(
-    Ptr<SpectrumValue> txPsd,
-    PhasedArrayModel::ComplexVector longTerm,
+    Ptr<const SpectrumSignalParameters> params,
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> longTerm,
     Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
     Ptr<const MatrixBasedChannelModel::ChannelParams> channelParams,
-    const ns3::Vector& sSpeed,
-    const ns3::Vector& uSpeed) const
+    const Vector& sSpeed,
+    const ns3::Vector& uSpeed,
+    uint8_t numTxPorts,
+    uint8_t numRxPorts,
+    bool isReverse) const
+
 {
     NS_LOG_FUNCTION(this);
-
-    Ptr<SpectrumValue> tempPsd = Copy<SpectrumValue>(txPsd);
-
-    // channel[cluster][rx][tx]
-    uint16_t numCluster = channelMatrix->m_channel.GetNumPages();
-
+    Ptr<SpectrumSignalParameters> rxParams = params->Copy();
+    size_t numCluster = channelMatrix->m_channel.GetNumPages();
     // compute the doppler term
     // NOTE the update of Doppler is simplified by only taking the center angle of
     // each cluster in to consideration.
@@ -157,18 +226,15 @@
     double factor = 2 * M_PI * slotTime * GetFrequency() / 3e8;
     PhasedArrayModel::ComplexVector doppler(numCluster);
 
-    // The following asserts might seem paranoic, but it is important to
-    // make sure that all the structures that are passed to this function
+    // Make sure that all the structures that are passed to this function
     // are of the correct dimensions before using the operator [].
-    // If you dont understand the comment read about the difference of .at()
-    // and [] operators, ...
     NS_ASSERT(numCluster <= channelParams->m_alpha.size());
     NS_ASSERT(numCluster <= channelParams->m_D.size());
     NS_ASSERT(numCluster <= channelParams->m_angle[MatrixBasedChannelModel::ZOA_INDEX].size());
     NS_ASSERT(numCluster <= channelParams->m_angle[MatrixBasedChannelModel::ZOD_INDEX].size());
     NS_ASSERT(numCluster <= channelParams->m_angle[MatrixBasedChannelModel::AOA_INDEX].size());
     NS_ASSERT(numCluster <= channelParams->m_angle[MatrixBasedChannelModel::AOD_INDEX].size());
-    NS_ASSERT(numCluster <= longTerm.GetSize());
+    NS_ASSERT(numCluster <= longTerm->GetNumPages());
 
     // check if channelParams structure is generated in direction s-to-u or u-to-s
     bool isSameDirection = (channelParams->m_nodeIds == channelMatrix->m_nodeIds);
@@ -179,7 +245,7 @@
     MatrixBasedChannelModel::DoubleVector aod;
 
     // if channel params is generated in the same direction in which we
-    // generate the channel matrix, angles and zenith od departure and arrival are ok,
+    // generate the channel matrix, angles and zenith of departure and arrival are ok,
     // just set them to corresponding variable that will be used for the generation
     // of channel matrix, otherwise we need to flip angles and zeniths of departure and arrival
     if (isSameDirection)
@@ -197,7 +263,7 @@
         aoa = channelParams->m_angle[MatrixBasedChannelModel::AOD_INDEX];
     }
 
-    for (uint16_t cIndex = 0; cIndex < numCluster; cIndex++)
+    for (size_t cIndex = 0; cIndex < numCluster; cIndex++)
     {
         // Compute alpha and D as described in 3GPP TR 37.885 v15.3.0, Sec. 6.2.3
         // These terms account for an additional Doppler contribution due to the
@@ -227,44 +293,139 @@
 
     NS_ASSERT(numCluster <= doppler.GetSize());
 
-    // apply the doppler term and the propagation delay to the long term component
-    // to obtain the beamforming gain
-    auto vit = tempPsd->ValuesBegin();      // psd iterator
-    auto sbit = tempPsd->ConstBandsBegin(); // band iterator
-    while (vit != tempPsd->ValuesEnd())
+    // set the channel matrix
+    rxParams->spectrumChannelMatrix = GenSpectrumChannelMatrix(rxParams->psd,
+                                                               longTerm,
+                                                               channelMatrix,
+                                                               channelParams,
+                                                               doppler,
+                                                               numTxPorts,
+                                                               numRxPorts,
+                                                               isReverse);
+
+    // The precoding matrix is not set
+    if (!rxParams->precodingMatrix)
+    {
+        // Update rxParams->Psd.
+        // Compute RX PSD from the channel matrix
+        auto vit = rxParams->psd->ValuesBegin(); // psd iterator
+        size_t rbIdx = 0;
+        while (vit != rxParams->psd->ValuesEnd())
+        {
+            // Calculate PSD for the first antenna port (correct for SISO)
+            *vit = std::norm(rxParams->spectrumChannelMatrix->Elem(0, 0, rbIdx));
+            vit++;
+            rbIdx++;
+        }
+    }
+    else
+    {
+        NS_ASSERT_MSG(rxParams->psd->GetValuesN() == rxParams->spectrumChannelMatrix->GetNumPages(),
+                      "RX PSD and the spectrum channel matrix should have the same number of RBs ");
+        // Calculate RX PSD from the spectrum channel matrix, H and
+        // the precoding matrix, P as:
+        // PSD = (H*P)^h * (H*P),
+        // where the dimensions are:
+        // H (rxPorts,txPorts,numRbs) x P (txPorts,txStreams, numRbs) =
+        // HxP (rxPorts,txStreams, numRbs)
+        MatrixBasedChannelModel::Complex3DVector hP =
+            *rxParams->spectrumChannelMatrix * (*rxParams->precodingMatrix);
+        // (HxP)^h dimensions are (txStreams, rxPorts, numRbs)
+        MatrixBasedChannelModel::Complex3DVector hPHerm = hP.HermitianTranspose();
+
+        // Finally, (HxP)^h x (HxP) = PSD (txStreams, txStreams, numRbs)
+        MatrixBasedChannelModel::Complex3DVector psd = hPHerm * hP;
+        // Update rxParams->Psd
+        for (uint32_t rbIdx = 0; rbIdx < rxParams->psd->GetValuesN(); ++rbIdx)
+        {
+            (*rxParams->psd)[rbIdx] = 0.0;
+
+            for (size_t txStream = 0; txStream < psd.GetNumRows(); ++txStream)
+            {
+                (*rxParams->psd)[rbIdx] += std::real(psd(txStream, txStream, rbIdx));
+            }
+        }
+    }
+    return rxParams;
+}
+
+Ptr<MatrixBasedChannelModel::Complex3DVector>
+ThreeGppSpectrumPropagationLossModel::GenSpectrumChannelMatrix(
+    Ptr<SpectrumValue> inPsd,
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> longTerm,
+    Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
+    Ptr<const MatrixBasedChannelModel::ChannelParams> channelParams,
+    PhasedArrayModel::ComplexVector doppler,
+    uint8_t numTxPorts,
+    uint8_t numRxPorts,
+    bool isReverse) const
+{
+    size_t numCluster = channelMatrix->m_channel.GetNumPages();
+    auto numRb = inPsd->GetValuesN();
+
+    auto directionalLongTerm = isReverse ? longTerm->Transpose() : (*longTerm);
+
+    Ptr<MatrixBasedChannelModel::Complex3DVector> chanSpct =
+        Create<MatrixBasedChannelModel::Complex3DVector>(numRxPorts, numTxPorts, (uint16_t)numRb);
+
+    // If "params" (ChannelMatrix) and longTerm were computed for the reverse direction (e.g. this
+    // is a DL transmission but params and longTerm were last updated during UL), then the elements
+    // in longTerm start from different offsets.
+
+    auto vit = inPsd->ValuesBegin();      // psd iterator
+    auto sbit = inPsd->ConstBandsBegin(); // band iterator
+    size_t iRb = 0;
+    // Compute the frequency-domain channel matrix
+    while (vit != inPsd->ValuesEnd())
     {
         if ((*vit) != 0.00)
         {
-            std::complex<double> subsbandGain(0.0, 0.0);
             double fsb = (*sbit).fc; // center frequency of the sub-band
-            for (uint16_t cIndex = 0; cIndex < numCluster; cIndex++)
+            for (auto rxPortIdx = 0; rxPortIdx < numRxPorts; rxPortIdx++)
             {
-                double delay = -2 * M_PI * fsb * (channelParams->m_delay[cIndex]);
-                subsbandGain = subsbandGain + longTerm[cIndex] * doppler[cIndex] *
-                                                  std::complex<double>(cos(delay), sin(delay));
+                for (auto txPortIdx = 0; txPortIdx < numTxPorts; txPortIdx++)
+                {
+                    std::complex<double> subsbandGain(0.0, 0.0);
+
+                    for (size_t cIndex = 0; cIndex < numCluster; cIndex++)
+                    {
+                        double delay = -2 * M_PI * fsb * (channelParams->m_delay[cIndex]);
+                        subsbandGain += directionalLongTerm(rxPortIdx, txPortIdx, cIndex) *
+                                        doppler[cIndex] *
+                                        std::complex<double>(cos(delay), sin(delay));
+                    }
+                    // Multiply with the square root of the input PSD so that the norm (absolute
+                    // value squared) of chanSpct will be the output PSD
+                    chanSpct->Elem(rxPortIdx, txPortIdx, iRb) = sqrt(*vit) * subsbandGain;
+                }
             }
-            *vit = (*vit) * (norm(subsbandGain));
         }
         vit++;
         sbit++;
+        iRb++;
     }
-    return tempPsd;
+    return chanSpct;
 }
 
-PhasedArrayModel::ComplexVector
+Ptr<const MatrixBasedChannelModel::Complex3DVector>
 ThreeGppSpectrumPropagationLossModel::GetLongTerm(
     Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
     Ptr<const PhasedArrayModel> aPhasedArrayModel,
     Ptr<const PhasedArrayModel> bPhasedArrayModel) const
 {
-    PhasedArrayModel::ComplexVector
+    Ptr<const MatrixBasedChannelModel::Complex3DVector>
         longTerm; // vector containing the long term component for each cluster
 
     // check if the channel matrix was generated considering a as the s-node and
     // b as the u-node or vice-versa
+    auto isReverse =
+        channelMatrix->IsReverse(aPhasedArrayModel->GetId(), bPhasedArrayModel->GetId());
+    auto sAntenna = isReverse ? bPhasedArrayModel : aPhasedArrayModel;
+    auto uAntenna = isReverse ? aPhasedArrayModel : bPhasedArrayModel;
+
     PhasedArrayModel::ComplexVector sW;
     PhasedArrayModel::ComplexVector uW;
-    if (!channelMatrix->IsReverse(aPhasedArrayModel->GetId(), bPhasedArrayModel->GetId()))
+    if (!isReverse)
     {
         sW = aPhasedArrayModel->GetBeamformingVector();
         uW = bPhasedArrayModel->GetBeamformingVector();
@@ -305,45 +466,41 @@
     {
         NS_LOG_DEBUG("compute the long term");
         // compute the long term component
-        longTerm = CalcLongTerm(channelMatrix, sW, uW);
-
-        // store the long term
+        longTerm = CalcLongTerm(channelMatrix, sAntenna, uAntenna);
         Ptr<LongTerm> longTermItem = Create<LongTerm>();
         longTermItem->m_longTerm = longTerm;
         longTermItem->m_channel = channelMatrix;
-        longTermItem->m_sW = sW;
-        longTermItem->m_uW = uW;
-
+        longTermItem->m_sW = std::move(sW);
+        longTermItem->m_uW = std::move(uW);
+        // store the long term to reduce computation load
+        // only the small scale fading needs to be updated if the large scale parameters and antenna
+        // weights remain unchanged.
         m_longTermMap[longTermId] = longTermItem;
     }
 
     return longTerm;
 }
 
-Ptr<SpectrumValue>
+Ptr<SpectrumSignalParameters>
 ThreeGppSpectrumPropagationLossModel::DoCalcRxPowerSpectralDensity(
-    Ptr<const SpectrumSignalParameters> params,
+    Ptr<const SpectrumSignalParameters> spectrumSignalParams,
     Ptr<const MobilityModel> a,
     Ptr<const MobilityModel> b,
     Ptr<const PhasedArrayModel> aPhasedArrayModel,
     Ptr<const PhasedArrayModel> bPhasedArrayModel) const
 {
-    NS_LOG_FUNCTION(this);
+    NS_LOG_FUNCTION(this << spectrumSignalParams << a << b << aPhasedArrayModel
+                         << bPhasedArrayModel);
+
+    if (a->GetPosition() == b->GetPosition())
+    {
+        return spectrumSignalParams->Copy();
+    }
     uint32_t aId = a->GetObject<Node>()->GetId(); // id of the node a
     uint32_t bId = b->GetObject<Node>()->GetId(); // id of the node b
-
-    NS_ASSERT(aId != bId);
-    NS_ASSERT_MSG(a->GetDistanceFrom(b) > 0.0,
-                  "The position of a and b devices cannot be the same");
-
-    Ptr<SpectrumValue> rxPsd = Copy<SpectrumValue>(params->psd);
-
-    // retrieve the antenna of device a
     NS_ASSERT_MSG(aPhasedArrayModel, "Antenna not found for node " << aId);
-    NS_LOG_DEBUG("a node " << a->GetObject<Node>() << " antenna " << aPhasedArrayModel);
-
-    // retrieve the antenna of the device b
-    NS_ASSERT_MSG(bPhasedArrayModel, "Antenna not found for device " << bId);
+    NS_LOG_DEBUG("a node " << aId << " antenna " << aPhasedArrayModel);
+    NS_ASSERT_MSG(bPhasedArrayModel, "Antenna not found for node " << bId);
     NS_LOG_DEBUG("b node " << bId << " antenna " << bPhasedArrayModel);
 
     Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix =
@@ -352,18 +509,28 @@
         m_channelModel->GetParams(a, b);
 
     // retrieve the long term component
-    PhasedArrayModel::ComplexVector longTerm =
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> longTerm =
         GetLongTerm(channelMatrix, aPhasedArrayModel, bPhasedArrayModel);
 
+    auto isReverse =
+        channelMatrix->IsReverse(aPhasedArrayModel->GetId(), bPhasedArrayModel->GetId());
+
     // apply the beamforming gain
-    rxPsd = CalcBeamformingGain(rxPsd,
-                                longTerm,
-                                channelMatrix,
-                                channelParams,
-                                a->GetVelocity(),
-                                b->GetVelocity());
+    return CalcBeamformingGain(spectrumSignalParams,
+                               longTerm,
+                               channelMatrix,
+                               channelParams,
+                               a->GetVelocity(),
+                               b->GetVelocity(),
+                               aPhasedArrayModel->GetNumPorts(),
+                               bPhasedArrayModel->GetNumPorts(),
+                               isReverse);
+}
 
-    return rxPsd;
+int64_t
+ThreeGppSpectrumPropagationLossModel::DoAssignStreams(int64_t stream)
+{
+    return 0;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.h ns-3.41/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.h
--- ns-3.40/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/three-gpp-spectrum-propagation-loss-model.h	2024-02-09 14:44:08.497794400 -0800
@@ -30,6 +30,9 @@
 #include <map>
 #include <unordered_map>
 
+class ThreeGppCalcLongTermMultiPortTest;
+class ThreeGppMimoPolarizationTest;
+
 namespace ns3
 {
 
@@ -51,6 +54,9 @@
  */
 class ThreeGppSpectrumPropagationLossModel : public PhasedArraySpectrumPropagationLossModel
 {
+    friend class ::ThreeGppCalcLongTermMultiPortTest;
+    friend class ::ThreeGppMimoPolarizationTest;
+
   public:
     /**
      * Constructor
@@ -112,27 +118,27 @@
      * a certain channel is cached and recomputed only when the channel realization
      * is updated, or when the beamforming vectors change.
      *
-     * \param params tx parameters
+     * \param spectrumSignalParams spectrum signal tx parameters
      * \param a first node mobility model
      * \param b second node mobility model
      * \param aPhasedArrayModel the antenna array of the first node
      * \param bPhasedArrayModel the antenna array of the second node
      * \return the received PSD
      */
-    Ptr<SpectrumValue> DoCalcRxPowerSpectralDensity(
-        Ptr<const SpectrumSignalParameters> params,
+    Ptr<SpectrumSignalParameters> DoCalcRxPowerSpectralDensity(
+        Ptr<const SpectrumSignalParameters> spectrumSignalParams,
         Ptr<const MobilityModel> a,
         Ptr<const MobilityModel> b,
         Ptr<const PhasedArrayModel> aPhasedArrayModel,
         Ptr<const PhasedArrayModel> bPhasedArrayModel) const override;
 
-  private:
+  protected:
     /**
      * Data structure that stores the long term component for a tx-rx pair
      */
     struct LongTerm : public SimpleRefCount<LongTerm>
     {
-        PhasedArrayModel::ComplexVector
+        Ptr<const MatrixBasedChannelModel::Complex3DVector>
             m_longTerm; //!< vector containing the long term component for each cluster
         Ptr<const MatrixBasedChannelModel::ChannelMatrix>
             m_channel; //!< pointer to the channel matrix used to compute the long term
@@ -143,6 +149,28 @@
     };
 
     /**
+     * Computes the frequency-domain channel matrix with the dimensions numRxPorts*numTxPorts*numRBs
+     * \param inPsd the input PSD
+     * \param longTerm the long term component
+     * \param channelMatrix the channel matrix structure
+     * \param channelParams the channel parameters, including delays
+     * \param doppler the doppler for each cluster
+     * \param numTxPorts the number of antenna ports at the transmitter
+     * \param numRxPorts the number of antenna ports at the receiver
+     * \param isReverse true if params and longTerm were computed with RX->TX switched
+     * \return 3D spectrum channel matrix with dimensions numRxPorts * numTxPorts * numRBs
+     */
+    Ptr<MatrixBasedChannelModel::Complex3DVector> GenSpectrumChannelMatrix(
+        Ptr<SpectrumValue> inPsd,
+        Ptr<const MatrixBasedChannelModel::Complex3DVector> longTerm,
+        Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
+        Ptr<const MatrixBasedChannelModel::ChannelParams> channelParams,
+        PhasedArrayModel::ComplexVector doppler,
+        uint8_t numTxPorts,
+        uint8_t numRxPorts,
+        bool isReverse) const;
+
+    /**
      * Get the operating frequency
      * \return the operating frequency in Hz
      */
@@ -157,39 +185,66 @@
      * \param bPhasedArrayModel the antenna array of the rx device
      * \return vector containing the long term component for each cluster
      */
-    PhasedArrayModel::ComplexVector GetLongTerm(
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> GetLongTerm(
         Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
         Ptr<const PhasedArrayModel> aPhasedArrayModel,
         Ptr<const PhasedArrayModel> bPhasedArrayModel) const;
     /**
      * Computes the long term component
      * \param channelMatrix the channel matrix H
-     * \param sW the beamforming vector of the s device
-     * \param uW the beamforming vector of the u device
+     * \param sAnt the pointer to the antenna of the s device
+     * \param uAnt the pointer to the antenna of the u device
      * \return the long term component
      */
-    PhasedArrayModel::ComplexVector CalcLongTerm(
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> CalcLongTerm(
         Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
-        const PhasedArrayModel::ComplexVector& sW,
-        const PhasedArrayModel::ComplexVector& uW) const;
+        Ptr<const PhasedArrayModel> sAnt,
+        Ptr<const PhasedArrayModel> uAnt) const;
+
+    /**
+     * \brief Computes a longTerm component from a specific port of s device to the
+     * specific port of u device and for a specific cluster index
+     * \param params The params that include the channel matrix
+     * \param sAnt pointer to first antenna
+     * \param uAnt uAnt pointer to second antenna
+     * \param sPortIdx the port index of the s device
+     * \param uPortIdx the port index of the u device
+     * \param cIndex the cluster index
+     * \return longTerm component for port pair and for a specific cluster index
+     */
+    std::complex<double> CalculateLongTermComponent(
+        Ptr<const MatrixBasedChannelModel::ChannelMatrix> params,
+        Ptr<const PhasedArrayModel> sAnt,
+        Ptr<const PhasedArrayModel> uAnt,
+        uint16_t sPortIdx,
+        uint16_t uPortIdx,
+        uint16_t cIndex) const;
 
     /**
-     * Computes the beamforming gain and applies it to the tx PSD
-     * \param txPsd the tx PSD
+     * \brief Computes the beamforming gain and applies it to the TX PSD
+     * \param params SpectrumSignalParameters holding TX PSD
      * \param longTerm the long term component
-     * \param channelMatrix The channel matrix structure
-     * \param channelParams The channel params structure
-     * \param sSpeed speed of the first node
-     * \param uSpeed speed of the second node
-     * \return the rx PSD
-     */
-    Ptr<SpectrumValue> CalcBeamformingGain(
-        Ptr<SpectrumValue> txPsd,
-        PhasedArrayModel::ComplexVector longTerm,
+     * \param channelMatrix the channel matrix structure
+     * \param channelParams the channel params structure
+     * \param sSpeed the speed of the first node
+     * \param uSpeed the speed of the second node
+     * \param numTxPorts the number of the ports of the first node
+     * \param numRxPorts the number of the porst of the second node
+     * \param isReverse indicator that tells whether the channel matrix is reverse
+     * \return
+     */
+    Ptr<SpectrumSignalParameters> CalcBeamformingGain(
+        Ptr<const SpectrumSignalParameters> params,
+        Ptr<const MatrixBasedChannelModel::Complex3DVector> longTerm,
         Ptr<const MatrixBasedChannelModel::ChannelMatrix> channelMatrix,
         Ptr<const MatrixBasedChannelModel::ChannelParams> channelParams,
         const Vector& sSpeed,
-        const Vector& uSpeed) const;
+        const Vector& uSpeed,
+        uint8_t numTxPorts,
+        uint8_t numRxPorts,
+        bool isReverse) const;
+
+    int64_t DoAssignStreams(int64_t stream) override;
 
     mutable std::unordered_map<uint64_t, Ptr<const LongTerm>>
         m_longTermMap;                           //!< map containing the long term components
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/trace-fading-loss-model.cc ns-3.41/src/spectrum/model/trace-fading-loss-model.cc
--- ns-3.40/src/spectrum/model/trace-fading-loss-model.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/trace-fading-loss-model.cc	2024-02-09 14:44:08.497794400 -0800
@@ -238,7 +238,7 @@
 }
 
 int64_t
-TraceFadingLossModel::AssignStreams(int64_t stream)
+TraceFadingLossModel::DoAssignStreams(int64_t stream)
 {
     NS_LOG_FUNCTION(this << stream);
     NS_ASSERT(m_streamsAssigned == false);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/trace-fading-loss-model.h ns-3.41/src/spectrum/model/trace-fading-loss-model.h
--- ns-3.40/src/spectrum/model/trace-fading-loss-model.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/trace-fading-loss-model.h	2024-02-09 14:44:08.497794400 -0800
@@ -58,15 +58,8 @@
      */
     typedef std::pair<Ptr<const MobilityModel>, Ptr<const MobilityModel>> ChannelRealizationId_t;
 
-    /**
-     * Assign a fixed random variable stream number to the random variables
-     * used by this model.  Return the number of streams (possibly zero) that
-     * have been assigned.
-     *
-     * \param stream first stream index to use
-     * \return the number of stream indices assigned by this model
-     */
-    int64_t AssignStreams(int64_t stream);
+  protected:
+    int64_t DoAssignStreams(int64_t stream) override;
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/tv-spectrum-transmitter.cc ns-3.41/src/spectrum/model/tv-spectrum-transmitter.cc
--- ns-3.40/src/spectrum/model/tv-spectrum-transmitter.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/tv-spectrum-transmitter.cc	2024-02-09 14:44:08.497794400 -0800
@@ -74,16 +74,17 @@
             .SetParent<SpectrumPhy>()
             .SetGroupName("Spectrum")
             .AddConstructor<TvSpectrumTransmitter>()
-            .AddAttribute("TvType",
-                          "The type of TV transmitter/modulation to be used.",
-                          EnumValue(TvSpectrumTransmitter::TVTYPE_8VSB),
-                          MakeEnumAccessor(&TvSpectrumTransmitter::m_tvType),
-                          MakeEnumChecker(TvSpectrumTransmitter::TVTYPE_8VSB,
-                                          "8vsb",
-                                          TvSpectrumTransmitter::TVTYPE_COFDM,
-                                          "cofdm",
-                                          TvSpectrumTransmitter::TVTYPE_ANALOG,
-                                          "analog"))
+            .AddAttribute(
+                "TvType",
+                "The type of TV transmitter/modulation to be used.",
+                EnumValue(TvSpectrumTransmitter::TVTYPE_8VSB),
+                MakeEnumAccessor<TvSpectrumTransmitter::TvType>(&TvSpectrumTransmitter::m_tvType),
+                MakeEnumChecker(TvSpectrumTransmitter::TVTYPE_8VSB,
+                                "8vsb",
+                                TvSpectrumTransmitter::TVTYPE_COFDM,
+                                "cofdm",
+                                TvSpectrumTransmitter::TVTYPE_ANALOG,
+                                "analog"))
             .AddAttribute("StartFrequency",
                           "The lower end frequency (in Hz) of the TV transmitter's "
                           "signal. Must be greater than or equal to 0.",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc ns-3.41/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc
--- ns-3.40/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/two-ray-spectrum-propagation-loss-model.cc	2024-02-09 14:44:08.501794300 -0800
@@ -1052,11 +1052,11 @@
     std::complex<double> bArrayOverallResponse = 0;
 
     // Compute the dot products between the array responses and the beamforming vectors
-    for (size_t i = 0; i < aPhasedArrayModel->GetNumberOfElements(); i++)
+    for (size_t i = 0; i < aPhasedArrayModel->GetNumElems(); i++)
     {
         aArrayOverallResponse += aArrayResponse[i] * aBfVector[i];
     }
-    for (size_t i = 0; i < bPhasedArrayModel->GetNumberOfElements(); i++)
+    for (size_t i = 0; i < bPhasedArrayModel->GetNumElems(); i++)
     {
         bArrayOverallResponse += bArrayResponse[i] * bBfVector[i];
     }
@@ -1112,7 +1112,7 @@
     return norm(h);
 }
 
-Ptr<SpectrumValue>
+Ptr<SpectrumSignalParameters>
 TwoRaySpectrumPropagationLossModel::DoCalcRxPowerSpectralDensity(
     Ptr<const SpectrumSignalParameters> params,
     Ptr<const MobilityModel> a,
@@ -1128,8 +1128,6 @@
     NS_ASSERT_MSG(a->GetDistanceFrom(b) > 0.0,
                   "The position of a and b devices cannot be the same");
 
-    Ptr<SpectrumValue> rxPsd = Copy<SpectrumValue>(params->psd);
-
     // Retrieve the antenna of device a
     NS_ASSERT_MSG(aPhasedArrayModel, "Antenna not found for node " << aId);
     NS_LOG_DEBUG("a node " << a->GetObject<Node>() << " antenna " << aPhasedArrayModel);
@@ -1147,10 +1145,11 @@
     // Compute the beamforming gain
     double bfGain = CalcBeamformingGain(a, b, aPhasedArrayModel, bPhasedArrayModel);
 
-    // Apply the above terms to the TX PSD
-    *rxPsd *= (fading * bfGain);
+    Ptr<SpectrumSignalParameters> rxParams = params->Copy();
+    // Apply the above terms to the TX PSD to calculate RX PSD
+    (*(rxParams->psd)) *= (fading * bfGain);
 
-    return rxPsd;
+    return rxParams;
 }
 
 std::size_t
@@ -1166,7 +1165,7 @@
 }
 
 int64_t
-TwoRaySpectrumPropagationLossModel::AssignStreams(int64_t stream)
+TwoRaySpectrumPropagationLossModel::DoAssignStreams(int64_t stream)
 {
     NS_LOG_FUNCTION(this << stream);
     m_normalRv->SetStream(stream);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h ns-3.41/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h
--- ns-3.40/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h	2023-09-27 13:30:37.874839800 -0700
+++ ns-3.41/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h	2024-02-09 14:44:08.501794300 -0800
@@ -163,15 +163,6 @@
     void SetFrequency(double f);
 
     /**
-     * \brief Assign a fixed random variable stream number to the random variables
-     * used by this model.
-     *
-     * \param stream first stream index to use
-     * \return the number of stream indices assigned by this model
-     */
-    int64_t AssignStreams(int64_t stream);
-
-    /**
      * \brief Compute the received PSD.
      *
      * This function computes the received PSD by applying the Fluctuating Two-Ray (FTR)
@@ -186,15 +177,18 @@
      * \param b second node mobility model
      * \param aPhasedArrayModel the antenna array of the first node
      * \param bPhasedArrayModel the antenna array of the second node
-     * \return the PSD of the received signal
+     * \return SpectrumSignalParameters including the PSD of the received signal
      */
-    Ptr<SpectrumValue> DoCalcRxPowerSpectralDensity(
+    Ptr<SpectrumSignalParameters> DoCalcRxPowerSpectralDensity(
         Ptr<const SpectrumSignalParameters> txPsd,
         Ptr<const MobilityModel> a,
         Ptr<const MobilityModel> b,
         Ptr<const PhasedArrayModel> aPhasedArrayModel,
         Ptr<const PhasedArrayModel> bPhasedArrayModel) const override;
 
+  protected:
+    int64_t DoAssignStreams(int64_t stream) override;
+
   private:
     /**
      * Retrieves the LOS condition associated to the specified mobility models
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/test/examples-to-run.py ns-3.41/src/spectrum/test/examples-to-run.py
--- ns-3.40/src/spectrum/test/examples-to-run.py	2023-09-27 13:30:37.878839700 -0700
+++ ns-3.41/src/spectrum/test/examples-to-run.py	2024-02-09 14:44:08.501794300 -0800
@@ -10,7 +10,7 @@
     ("adhoc-aloha-ideal-phy", "True", "True"),
     ("adhoc-aloha-ideal-phy-with-microwave-oven", "True", "True"),
     ("adhoc-aloha-ideal-phy-matrix-propagation-loss-model", "True", "True"),
-    ("three-gpp-channel-example", "True", "True")
+    ("three-gpp-channel-example", "True", "True"),
 ]
 
 # A list of Python examples to run in order to ensure that they remain
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/test/three-gpp-channel-test-suite.cc ns-3.41/src/spectrum/test/three-gpp-channel-test-suite.cc
--- ns-3.40/src/spectrum/test/three-gpp-channel-test-suite.cc	2023-09-27 13:30:37.878839700 -0700
+++ ns-3.41/src/spectrum/test/three-gpp-channel-test-suite.cc	2024-02-09 14:44:08.501794300 -0800
@@ -26,16 +26,20 @@
 #include "ns3/log.h"
 #include "ns3/node-container.h"
 #include "ns3/pointer.h"
+#include "ns3/rng-seed-manager.h"
 #include "ns3/simple-net-device.h"
 #include "ns3/simulator.h"
 #include "ns3/spectrum-signal-parameters.h"
 #include "ns3/string.h"
 #include "ns3/test.h"
+#include "ns3/three-gpp-antenna-model.h"
 #include "ns3/three-gpp-channel-model.h"
 #include "ns3/three-gpp-spectrum-propagation-loss-model.h"
 #include "ns3/uinteger.h"
 #include "ns3/uniform-planar-array.h"
 
+#include <valarray>
+
 using namespace ns3;
 
 NS_LOG_COMPONENT_DEFINE("ThreeGppChannelTestSuite");
@@ -51,9 +55,19 @@
 {
   public:
     /**
-     * Constructor
+     *Constructor
+     * \param txAntennaElements the number of rows and columns of the antenna array of the
+     * transmitter
+     * \param rxAntennaElements the number of rows and columns of the antenna array of
+     * \param txPorts the number of vertical and horizontal ports of the antenna array
+     * of the transmitter
+     * \param rxPorts the number of vertical and horizontal ports of the antenna
+     * array of the receiver
      */
-    ThreeGppChannelMatrixComputationTest();
+    ThreeGppChannelMatrixComputationTest(uint32_t txAntennaElements = 2,
+                                         uint32_t rxAntennaElements = 2,
+                                         uint32_t txPorts = 1,
+                                         uint32_t rxPorts = 1);
 
     /**
      * Destructor
@@ -81,11 +95,23 @@
                        Ptr<PhasedArrayModel> rxAntenna);
 
     std::vector<double> m_normVector; //!< each element is the norm of a channel realization
+    uint32_t m_txAntennaElements{4};  //!< number of rows and columns of tx antenna array
+    uint32_t m_rxAntennaElements{4};  //!< number of rows and columns of rx antenna array
+    uint32_t m_txPorts{1}; //!< number of horizontal and vertical ports of tx antenna array
+    uint32_t m_rxPorts{1}; //!< number of horizontal and vertical ports of rx antenna array
 };
 
-ThreeGppChannelMatrixComputationTest::ThreeGppChannelMatrixComputationTest()
+ThreeGppChannelMatrixComputationTest::ThreeGppChannelMatrixComputationTest(
+    uint32_t txAntennaElements,
+    uint32_t rxAntennaElements,
+    uint32_t txPorts,
+    uint32_t rxPorts)
     : TestCase("Check the dimensions and the norm of the channel matrix")
 {
+    m_txAntennaElements = txAntennaElements;
+    m_rxAntennaElements = rxAntennaElements;
+    m_txPorts = txPorts;
+    m_rxPorts = rxPorts;
 }
 
 ThreeGppChannelMatrixComputationTest::~ThreeGppChannelMatrixComputationTest()
@@ -99,8 +125,8 @@
                                                     Ptr<PhasedArrayModel> txAntenna,
                                                     Ptr<PhasedArrayModel> rxAntenna)
 {
-    uint64_t txAntennaElements = txAntenna->GetNumberOfElements();
-    uint64_t rxAntennaElements = rxAntenna->GetNumberOfElements();
+    uint64_t txAntennaElements = txAntenna->GetNumElems();
+    uint64_t rxAntennaElements = rxAntenna->GetNumElems();
 
     Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix =
         channelModel->GetChannel(txMob, rxMob, txAntenna, rxAntenna);
@@ -126,10 +152,10 @@
 void
 ThreeGppChannelMatrixComputationTest::DoRun()
 {
+    RngSeedManager::SetSeed(1);
+    RngSeedManager::SetRun(1);
     // Build the scenario for the test
-    uint8_t txAntennaElements[]{2, 2}; // tx antenna dimensions
-    uint8_t rxAntennaElements[]{2, 2}; // rx antenna dimensions
-    uint32_t updatePeriodMs = 100;     // update period in ms
+    uint32_t updatePeriodMs = 100; // update period in ms
 
     // create the channel condition model
     Ptr<ChannelConditionModel> channelConditionModel =
@@ -141,6 +167,7 @@
     channelModel->SetAttribute("Scenario", StringValue("RMa"));
     channelModel->SetAttribute("ChannelConditionModel", PointerValue(channelConditionModel));
     channelModel->SetAttribute("UpdatePeriod", TimeValue(MilliSeconds(updatePeriodMs - 1)));
+    channelModel->AssignStreams(1);
 
     // create the tx and rx nodes
     NodeContainer nodes;
@@ -169,19 +196,27 @@
     // create the tx and rx antennas and set the their dimensions
     Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray>(
         "NumColumns",
-        UintegerValue(txAntennaElements[0]),
+        UintegerValue(m_txAntennaElements),
         "NumRows",
-        UintegerValue(txAntennaElements[1]),
+        UintegerValue(m_txAntennaElements),
         "AntennaElement",
-        PointerValue(CreateObject<IsotropicAntennaModel>()));
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(m_txPorts),
+        "NumHorizontalPorts",
+        UintegerValue(m_txPorts));
+
     Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray>(
         "NumColumns",
-        UintegerValue(rxAntennaElements[0]),
+        UintegerValue(m_rxAntennaElements),
         "NumRows",
-        UintegerValue(rxAntennaElements[1]),
+        UintegerValue(m_rxAntennaElements),
         "AntennaElement",
-        PointerValue(CreateObject<IsotropicAntennaModel>()));
-
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(m_rxPorts),
+        "NumHorizontalPorts",
+        UintegerValue(m_rxPorts));
     // generate the channel matrix
     Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix =
         channelModel->GetChannel(txMob, rxMob, txAntenna, rxAntenna);
@@ -189,11 +224,11 @@
     // check the channel matrix dimensions, expected H[cluster][rx][tx]
     NS_TEST_ASSERT_MSG_EQ(
         channelMatrix->m_channel.GetNumCols(),
-        txAntennaElements[0] * txAntennaElements[1],
+        m_txAntennaElements * m_txAntennaElements,
         "The third dimension of H should be equal to the number of tx antenna elements");
     NS_TEST_ASSERT_MSG_EQ(
         channelMatrix->m_channel.GetNumRows(),
-        rxAntennaElements[0] * rxAntennaElements[1],
+        m_rxAntennaElements * m_rxAntennaElements,
         "The second dimension of H should be equal to the number of rx antenna elements");
 
     // test if the channel matrix is correctly generated
@@ -232,9 +267,9 @@
     // the hypothesis "E [|H|^2] = M*N, where |H| indicates the Frobenius norm of
     // H, M is the number of transmit antenna elements, and N is the number of
     // the receive antenna elements"
-    double t = (sampleMean - txAntennaElements[0] * txAntennaElements[1] * rxAntennaElements[0] *
-                                 rxAntennaElements[1]) /
-               (sampleMean / std::sqrt(numIt));
+    double t = (sampleMean - m_txAntennaElements * m_txAntennaElements * m_rxAntennaElements *
+                                 m_rxAntennaElements) /
+               (sampleStd / std::sqrt(numIt));
 
     // Using a significance level of 0.05, we reject the null hypothesis if |t| is
     // greater than the critical value from a t-distribution with df = numIt-1
@@ -258,9 +293,19 @@
 {
   public:
     /**
-     * Constructor
+     *Constructor
+     * \param txAntennaElements the number of rows and columns of the antenna array of the
+     * transmitter
+     * \param rxAntennaElements the number of rows and columns of the antenna array of
+     * \param txPorts the number of vertical and horizontal ports of the antenna array
+     * of the transmitter
+     * \param rxPorts the number of vertical and horizontal ports of the antenna
+     * array of the receiver
      */
-    ThreeGppChannelMatrixUpdateTest();
+    ThreeGppChannelMatrixUpdateTest(uint32_t txAntennaElements = 2,
+                                    uint32_t rxAntennaElements = 4,
+                                    uint32_t txPorts = 1,
+                                    uint32_t rxPorts = 1);
 
     /**
      * Destructor
@@ -291,12 +336,23 @@
                       bool update);
 
     Ptr<const ThreeGppChannelModel::ChannelMatrix>
-        m_currentChannel; //!< used by DoGetChannel to store the current channel matrix
+        m_currentChannel;            //!< used by DoGetChannel to store the current channel matrix
+    uint32_t m_txAntennaElements{4}; //!< number of rows and columns of tx antenna array
+    uint32_t m_rxAntennaElements{4}; //!< number of rows and columns of rx antenna array
+    uint32_t m_txPorts{1}; //!< number of horizontal and vertical ports of tx antenna array
+    uint32_t m_rxPorts{1}; //!< number of horizontal and vertical ports of rx antenna array
 };
 
-ThreeGppChannelMatrixUpdateTest::ThreeGppChannelMatrixUpdateTest()
+ThreeGppChannelMatrixUpdateTest::ThreeGppChannelMatrixUpdateTest(uint32_t txAntennaElements,
+                                                                 uint32_t rxAntennaElements,
+                                                                 uint32_t txPorts,
+                                                                 uint32_t rxPorts)
     : TestCase("Check if the channel realizations are correctly updated during the simulation")
 {
+    m_txAntennaElements = txAntennaElements;
+    m_rxAntennaElements = rxAntennaElements;
+    m_txPorts = txPorts;
+    m_rxPorts = rxPorts;
 }
 
 ThreeGppChannelMatrixUpdateTest::~ThreeGppChannelMatrixUpdateTest()
@@ -324,7 +380,7 @@
     else
     {
         // compare the old and the new channel matrices
-        NS_TEST_ASSERT_MSG_EQ((m_currentChannel != channelMatrix),
+        NS_TEST_ASSERT_MSG_EQ((m_currentChannel->m_channel != channelMatrix->m_channel),
                               update,
                               Simulator::Now().GetMilliSeconds()
                                   << " The channel matrix is not correctly updated");
@@ -335,10 +391,7 @@
 ThreeGppChannelMatrixUpdateTest::DoRun()
 {
     // Build the scenario for the test
-
-    uint8_t txAntennaElements[]{2, 2}; // tx antenna dimensions
-    uint8_t rxAntennaElements[]{4, 4}; // rx antenna dimensions
-    uint32_t updatePeriodMs = 100;     // update period in ms
+    uint32_t updatePeriodMs = 100; // update period in ms
 
     // create the channel condition model
     Ptr<ChannelConditionModel> channelConditionModel =
@@ -378,18 +431,27 @@
     // create the tx and rx antennas and set the their dimensions
     Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray>(
         "NumColumns",
-        UintegerValue(txAntennaElements[0]),
+        UintegerValue(m_txAntennaElements),
         "NumRows",
-        UintegerValue(txAntennaElements[1]),
+        UintegerValue(m_txAntennaElements),
         "AntennaElement",
-        PointerValue(CreateObject<IsotropicAntennaModel>()));
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(m_txPorts),
+        "NumHorizontalPorts",
+        UintegerValue(m_txPorts));
+
     Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray>(
         "NumColumns",
-        UintegerValue(rxAntennaElements[0]),
+        UintegerValue(m_rxAntennaElements),
         "NumRows",
-        UintegerValue(rxAntennaElements[1]),
+        UintegerValue(m_rxAntennaElements),
         "AntennaElement",
-        PointerValue(CreateObject<IsotropicAntennaModel>()));
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(m_rxPorts),
+        "NumHorizontalPorts",
+        UintegerValue(m_rxPorts));
 
     // check if the channel matrix is correctly updated
 
@@ -467,9 +529,19 @@
   public:
     /**
      * Constructor
+     * \param txAntennaElements the number of rows and columns of the antenna array of the
+     * transmitter
+     * \param rxAntennaElements the number of rows and columns of the antenna array of
+     * the receiver
+     * \param txPorts the number of vertical and horizontal ports of the antenna array
+     * of the transmitter
+     * \param rxPorts the number of vertical and horizontal ports of the antenna
+     * array of the receiver
      */
-    ThreeGppSpectrumPropagationLossModelTest();
-
+    ThreeGppSpectrumPropagationLossModelTest(uint32_t txAntennaElements = 4,
+                                             uint32_t rxAntennaElements = 4,
+                                             uint32_t txPorts = 1,
+                                             uint32_t rxPorts = 1);
     /**
      * Destructor
      */
@@ -501,18 +573,23 @@
      */
     void CheckLongTermUpdate(const CheckLongTermUpdateParams& params);
 
-    /**
-     * Checks if two PSDs are equal
-     * \param first the first PSD
-     * \param second the second PSD
-     * \return true if first and second are equal, false otherwise
-     */
-    static bool ArePsdEqual(Ptr<SpectrumValue> first, Ptr<SpectrumValue> second);
+    uint32_t m_txAntennaElements{4}; //!< number of rows and columns of tx antenna array
+    uint32_t m_rxAntennaElements{4}; //!< number of rows and columns of rx antenna array
+    uint32_t m_txPorts{1}; //!< number of horizontal and vertical ports of tx antenna array
+    uint32_t m_rxPorts{1}; //!< number of horizontal and vertical ports of rx antenna array
 };
 
-ThreeGppSpectrumPropagationLossModelTest::ThreeGppSpectrumPropagationLossModelTest()
+ThreeGppSpectrumPropagationLossModelTest::ThreeGppSpectrumPropagationLossModelTest(
+    uint32_t txAntennaElements,
+    uint32_t rxAntennaElements,
+    uint32_t txPorts,
+    uint32_t rxPorts)
     : TestCase("Test case for the ThreeGppSpectrumPropagationLossModel class")
 {
+    m_txAntennaElements = txAntennaElements;
+    m_rxAntennaElements = rxAntennaElements;
+    m_txPorts = txPorts;
+    m_rxPorts = rxPorts;
 }
 
 ThreeGppSpectrumPropagationLossModelTest::~ThreeGppSpectrumPropagationLossModelTest()
@@ -536,32 +613,16 @@
     thisAntenna->SetBeamformingVector(antennaWeights);
 }
 
-bool
-ThreeGppSpectrumPropagationLossModelTest::ArePsdEqual(Ptr<SpectrumValue> first,
-                                                      Ptr<SpectrumValue> second)
-{
-    bool ret = true;
-    for (uint8_t i = 0; i < first->GetSpectrumModel()->GetNumBands(); i++)
-    {
-        if ((*first)[i] != (*second)[i])
-        {
-            ret = false;
-            continue;
-        }
-    }
-    return ret;
-}
-
 void
 ThreeGppSpectrumPropagationLossModelTest::CheckLongTermUpdate(
     const CheckLongTermUpdateParams& params)
 {
-    Ptr<SpectrumValue> rxPsdNew = params.lossModel->DoCalcRxPowerSpectralDensity(params.txParams,
-                                                                                 params.txMob,
-                                                                                 params.rxMob,
-                                                                                 params.txAntenna,
-                                                                                 params.rxAntenna);
-    NS_TEST_ASSERT_MSG_EQ(ArePsdEqual(params.rxPsdOld, rxPsdNew),
+    auto rxPsdNewParams = params.lossModel->DoCalcRxPowerSpectralDensity(params.txParams,
+                                                                         params.txMob,
+                                                                         params.rxMob,
+                                                                         params.txAntenna,
+                                                                         params.rxAntenna);
+    NS_TEST_ASSERT_MSG_EQ((*params.rxPsdOld == *rxPsdNewParams->psd),
                           false,
                           "The long term is not updated when the channel matrix is recomputed");
 }
@@ -572,9 +633,6 @@
     // Build the scenario for the test
     Config::SetDefault("ns3::ThreeGppChannelModel::UpdatePeriod", TimeValue(MilliSeconds(100)));
 
-    uint8_t txAntennaElements[]{4, 4}; // tx antenna dimensions
-    uint8_t rxAntennaElements[]{4, 4}; // rx antenna dimensions
-
     // create the ChannelConditionModel object to be used to retrieve the
     // channel condition
     Ptr<ChannelConditionModel> condModel = CreateObject<AlwaysLosChannelConditionModel>();
@@ -618,18 +676,27 @@
     // create the tx and rx antennas and set the their dimensions
     Ptr<PhasedArrayModel> txAntenna = CreateObjectWithAttributes<UniformPlanarArray>(
         "NumColumns",
-        UintegerValue(txAntennaElements[0]),
+        UintegerValue(m_txAntennaElements),
         "NumRows",
-        UintegerValue(txAntennaElements[1]),
+        UintegerValue(m_rxAntennaElements),
         "AntennaElement",
-        PointerValue(CreateObject<IsotropicAntennaModel>()));
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(m_txPorts),
+        "NumHorizontalPorts",
+        UintegerValue(m_txPorts));
+
     Ptr<PhasedArrayModel> rxAntenna = CreateObjectWithAttributes<UniformPlanarArray>(
         "NumColumns",
-        UintegerValue(rxAntennaElements[0]),
+        UintegerValue(m_rxAntennaElements),
         "NumRows",
-        UintegerValue(rxAntennaElements[1]),
+        UintegerValue(m_rxAntennaElements),
         "AntennaElement",
-        PointerValue(CreateObject<IsotropicAntennaModel>()));
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(m_rxPorts),
+        "NumHorizontalPorts",
+        UintegerValue(m_rxPorts));
 
     // set the beamforming vectors
     DoBeamforming(txDev, txAntenna, rxDev, rxAntenna);
@@ -644,13 +711,13 @@
     txParams->psd = txPsd->Copy();
 
     // compute the rx psd
-    Ptr<SpectrumValue> rxPsdOld =
+    auto rxParamsOld =
         lossModel->DoCalcRxPowerSpectralDensity(txParams, txMob, rxMob, txAntenna, rxAntenna);
 
     // 1) check that the rx PSD is equal for both the direct and the reverse channel
-    Ptr<SpectrumValue> rxPsdNew =
+    auto rxParamsNew =
         lossModel->DoCalcRxPowerSpectralDensity(txParams, rxMob, txMob, rxAntenna, txAntenna);
-    NS_TEST_ASSERT_MSG_EQ(ArePsdEqual(rxPsdOld, rxPsdNew),
+    NS_TEST_ASSERT_MSG_EQ((*rxParamsOld->psd == *rxParamsNew->psd),
                           true,
                           "The long term for the direct and the reverse channel are different");
 
@@ -661,18 +728,23 @@
     txBfVector[0] = std::complex<double>(0.0, 0.0);
     txAntenna->SetBeamformingVector(txBfVector);
 
-    rxPsdNew =
+    rxParamsNew =
         lossModel->DoCalcRxPowerSpectralDensity(txParams, rxMob, txMob, rxAntenna, txAntenna);
-    NS_TEST_ASSERT_MSG_EQ(ArePsdEqual(rxPsdOld, rxPsdNew),
+    NS_TEST_ASSERT_MSG_EQ((*rxParamsOld->psd == *rxParamsNew->psd),
                           false,
                           "Changing the BF vectors the rx PSD does not change");
 
+    NS_TEST_ASSERT_MSG_EQ(
+        (*rxParamsOld->spectrumChannelMatrix == *rxParamsNew->spectrumChannelMatrix),
+        false,
+        "Changing the BF should change de frequency domain channel matrix");
+
     // update rxPsdOld
-    rxPsdOld = rxPsdNew;
+    rxParamsOld = rxParamsNew;
 
     // 3) check if the long term is updated when the channel matrix is recomputed
     CheckLongTermUpdateParams
-        params{lossModel, txParams, txMob, rxMob, rxPsdOld, txAntenna, rxAntenna};
+        params{lossModel, txParams, txMob, rxMob, rxParamsOld->psd, txAntenna, rxAntenna};
     Simulator::Schedule(MilliSeconds(101),
                         &ThreeGppSpectrumPropagationLossModelTest::CheckLongTermUpdate,
                         this,
@@ -685,6 +757,465 @@
 /**
  * \ingroup spectrum-tests
  *
+ * Test case that test the correct use of the multi-port antennas in spectrum.
+ * The test does the following:
+ * 1) Generates a time domain channel matrix of a fixed size
+ * (num gNB elements = 32 (4x8), num UE elements = 16 (4x4), num Clusters).
+ * This matrix is called channelMatrixM0.
+ * 2) Divides gNB antenna and UE antenna into ports using a fixed element to port
+ * mapping (gNB: 1 vertical port, 4 horizontal ports, UE: 1 vertical port,
+ * 2 horizontal ports). Using the channelMatrixM0, it generates port to port
+ * long term channel matrix using the both gNB and UE having beams directed towards the other.
+ * The resulting long term matrix dimensions are gNBports = 4, UE ports = 2, num Clusters.
+ * This channel matrix is called matrixA.
+ * 3) Constructs a single port to single port long term channel matrix
+ * using the initial time domain channel matrix (channelMatrixM0) and beams
+ * from gNB and UE towards each other. Single port mapping means gNB: 1 vertical port,
+ * 1 horizontal port, UE: 1 vertical port, 1 horizontal port.
+ * Matrix dimensions are: gNBports = 1, UE ports = 1, num Clusters. This long
+ * term channel matrix is called matrixB.
+ * 4) Creates a single port to single port long term channel matrix between
+ * two virtual gNB and UE antenna by using matrixA and beam facing each other.
+ * Matrix dimension of the resulting matrix are gNBports = 1, UE ports = 1, num Clusters.
+ * This long term channel matrix is called matrixC.
+ * 5) Checks that matrixB and matrixC are identical.
+ */
+class ThreeGppCalcLongTermMultiPortTest : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     */
+    ThreeGppCalcLongTermMultiPortTest();
+
+    /**
+     * Destructor
+     */
+    ~ThreeGppCalcLongTermMultiPortTest() override;
+
+  private:
+    /**
+     * Build the test scenario
+     */
+    void DoRun() override;
+};
+
+ThreeGppCalcLongTermMultiPortTest::ThreeGppCalcLongTermMultiPortTest()
+    : TestCase("Check long term channel matrix generation when multiple ports at TX and RX are "
+               "being used.")
+{
+}
+
+ThreeGppCalcLongTermMultiPortTest::~ThreeGppCalcLongTermMultiPortTest()
+{
+}
+
+void
+ThreeGppCalcLongTermMultiPortTest::DoRun()
+{
+    // create the channel condition model
+    Ptr<ChannelConditionModel> channelConditionModel =
+        CreateObject<AlwaysLosChannelConditionModel>();
+
+    // create the ThreeGppChannelModel object used to generate the channel matrix
+    Ptr<ThreeGppChannelModel> channelModel = CreateObject<ThreeGppChannelModel>();
+    channelModel->SetAttribute("Frequency", DoubleValue(2.0e9));
+    channelModel->SetAttribute("Scenario", StringValue("RMa"));
+    channelModel->SetAttribute("ChannelConditionModel", PointerValue(channelConditionModel));
+
+    // create the tx and rx nodes
+    NodeContainer nodes;
+    nodes.Create(2);
+
+    // create the tx and rx devices
+    Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice>();
+    Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice>();
+
+    // associate the nodes and the devices
+    nodes.Get(0)->AddDevice(txDev);
+    txDev->SetNode(nodes.Get(0));
+    nodes.Get(1)->AddDevice(rxDev);
+    rxDev->SetNode(nodes.Get(1));
+
+    // create the tx and rx mobility models and set their positions
+    Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel>();
+    txMob->SetPosition(Vector(0.0, 0.0, 10.0));
+    Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel>();
+    rxMob->SetPosition(Vector(10.0, 0.0, 10.0));
+
+    // associate the nodes and the mobility models
+    nodes.Get(0)->AggregateObject(txMob);
+    nodes.Get(1)->AggregateObject(rxMob);
+
+    // create the tx and rx antennas and set the their dimensions
+    Ptr<PhasedArrayModel> txAntenna1 = CreateObjectWithAttributes<UniformPlanarArray>(
+        "NumColumns",
+        UintegerValue(8),
+        "NumRows",
+        UintegerValue(4),
+        "AntennaElement",
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(1),
+        "NumHorizontalPorts",
+        UintegerValue(4));
+
+    Ptr<PhasedArrayModel> rxAntenna1 = CreateObjectWithAttributes<UniformPlanarArray>(
+        "NumColumns",
+        UintegerValue(4),
+        "NumRows",
+        UintegerValue(4),
+        "AntennaElement",
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(1),
+        "NumHorizontalPorts",
+        UintegerValue(2));
+
+    // compute the azimuth and the elevation angles
+    Angles completeAngleTxRx(rxMob->GetPosition(), txMob->GetPosition());
+    Angles completeAngleRxTx(txMob->GetPosition(), rxMob->GetPosition());
+
+    txAntenna1->SetBeamformingVector(txAntenna1->GetBeamformingVector(completeAngleTxRx));
+    rxAntenna1->SetBeamformingVector(rxAntenna1->GetBeamformingVector(completeAngleRxTx));
+
+    // generate the channel matrix
+    Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrixM0 =
+        channelModel->GetChannel(txMob, rxMob, txAntenna1, rxAntenna1);
+
+    // create ThreeGppSpectrumPropagationLossModel instance so that we
+    // can call CalcLongTerm
+    Ptr<const ThreeGppSpectrumPropagationLossModel> threeGppSplm =
+        CreateObject<ThreeGppSpectrumPropagationLossModel>();
+
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> matrixA =
+        threeGppSplm->CalcLongTerm(channelMatrixM0, txAntenna1, rxAntenna1);
+
+    // create the tx and rx antennas and set the their dimensions
+    Ptr<PhasedArrayModel> txAntenna2 = CreateObjectWithAttributes<UniformPlanarArray>(
+        "NumColumns",
+        UintegerValue(8),
+        "NumRows",
+        UintegerValue(4),
+        "AntennaElement",
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(1),
+        "NumHorizontalPorts",
+        UintegerValue(1));
+
+    Ptr<PhasedArrayModel> rxAntenna2 = CreateObjectWithAttributes<UniformPlanarArray>(
+        "NumColumns",
+        UintegerValue(4),
+        "NumRows",
+        UintegerValue(4),
+        "AntennaElement",
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(1),
+        "NumHorizontalPorts",
+        UintegerValue(1));
+
+    txAntenna2->SetBeamformingVector(txAntenna2->GetBeamformingVector(completeAngleTxRx));
+    rxAntenna2->SetBeamformingVector(rxAntenna2->GetBeamformingVector(completeAngleRxTx));
+
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> matrixB =
+        threeGppSplm->CalcLongTerm(channelMatrixM0, txAntenna2, rxAntenna2);
+
+    // create the tx and rx antennas and set the their dimensions
+    Ptr<PhasedArrayModel> txAntenna3 = CreateObjectWithAttributes<UniformPlanarArray>(
+        "NumColumns",
+        UintegerValue(4),
+        "NumRows",
+        UintegerValue(1),
+        "AntennaElement",
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(1),
+        "NumHorizontalPorts",
+        UintegerValue(1),
+        "AntennaHorizontalSpacing",
+        DoubleValue(1));
+
+    Ptr<PhasedArrayModel> rxAntenna3 = CreateObjectWithAttributes<UniformPlanarArray>(
+        "NumColumns",
+        UintegerValue(2),
+        "NumRows",
+        UintegerValue(1),
+        "AntennaElement",
+        PointerValue(CreateObject<IsotropicAntennaModel>()),
+        "NumVerticalPorts",
+        UintegerValue(1),
+        "NumHorizontalPorts",
+        UintegerValue(1),
+        "AntennaHorizontalSpacing",
+        DoubleValue(1));
+
+    Ptr<ThreeGppChannelModel::ChannelMatrix> channelMatrixMA =
+        Create<ThreeGppChannelModel::ChannelMatrix>();
+    channelMatrixMA->m_channel = *matrixA;
+
+    txAntenna3->SetBeamformingVector(txAntenna3->GetBeamformingVector(completeAngleTxRx));
+    rxAntenna3->SetBeamformingVector(rxAntenna3->GetBeamformingVector(completeAngleRxTx));
+
+    Ptr<const MatrixBasedChannelModel::Complex3DVector> matrixC =
+        threeGppSplm->CalcLongTerm(channelMatrixMA, txAntenna3, rxAntenna3);
+
+    NS_TEST_ASSERT_MSG_EQ(matrixB->IsAlmostEqual(*matrixC, 1e-6),
+                          true,
+                          "Matrix B and Matrix C should be equal.");
+
+    Simulator::Run();
+    Simulator::Destroy();
+}
+
+/**
+ * Structure that contains some of the main configuration parameters of the antenna
+ * array that are used in the ThreeGppMimoPolarizationTest
+ */
+struct MimoPolarizationAntennaParams
+{
+    uint32_t m_rows = 1;        //!< the number of rows of antenna array
+    uint32_t m_cols = 2;        //!< the number of columns of antenna array
+    uint32_t m_vPorts = 1;      //!< the number of vertical ports of antenna array
+    uint32_t m_hPorts = 2;      //!< the number of horizontal ports of antenna array
+    bool m_isotropic = false;   //!< defines whether the antenna elements are isotropic
+    double m_polSlantAngle = 0; //!< polarization angle of the antenna array
+    double m_bearingAngle = 0;  //!< bearing angle of the antenna array
+
+    /**
+     * Constructor
+     * Currently only configurable through constructor are polSlantAngle and bearingAngle.
+     * \param isotropic whether the antenna elements are isotropic, or 3GPP
+     * \param polSlantAngle the polarization slant angle
+     * \param bearingAngle the bearing angle
+     */
+    MimoPolarizationAntennaParams(bool isotropic, double polSlantAngle = 0, double bearingAngle = 0)
+        : m_isotropic(isotropic),
+          m_polSlantAngle(polSlantAngle),
+          m_bearingAngle(bearingAngle)
+    {
+    }
+};
+
+/**
+ * \ingroup spectrum-tests
+ * This test tests that the channel matrix is correctly generated when dual-polarized
+ * antennas are being used at TX and RX. In the conditions in which the channel between
+ * the TX and Rx device is LOS channel, and the beams of the transmitter and the
+ * receiver are pointing one towards the other, then in the presence of multiple ports
+ * at the TX and RX, and the antenna array at the TX and RX are dual polarized,
+ * the channel matrix should exhibit the strong symmetry between the two polarizations.
+ * E.g. if we have 1x2 antenna elements and two polarizations at both TX and RX,
+ * and the 1x2 ports at the TX and RX, then the channel matrix will have the
+ * structure as:
+ *
+ *                ch00 ch01 |ch02 ch03
+ *   Hvv  Hvh     ch10 ch11 |ch12 ch13
+ *             =  --------------------
+ *   Hhv  Hhh     ch20 ch21 |ch22 ch23
+ *                ch30 ch31 |ch32 ch33
+ *
+ * We test different cases of the polarization slant angles of the TX and RX,
+ * e.g., 0, 30, 45, 90.
+ * In each of these setups we check if the channel matrix in its strongest
+ * cluster experiences strong symmetry, and if the values appear in pairs.
+ * We also test additional cases in which we change the bearing angle and
+ * the height of the TX. In these cases we also observe strong symmetry, with
+ * the difference that in these cases we can observe different values in the
+ * pairs. We can still observe strong impact of the dual polarization on the
+ * channel matrix.
+ */
+class ThreeGppMimoPolarizationTest : public TestCase
+{
+  public:
+    /**
+     * Constructor that receives MIMO polarization parameters of TX and RX
+     * devices
+     * \param testCaseName the test case name
+     * \param txLoc the position of the transmitter
+     * \param txAntennaParams the antenna parameters of the transmitter
+     * \param rxLoc the position of the receiver
+     * \param rxAntennaParams the antenna parameters of the receiver
+     * \param testChannel the test matrix that represent the strongest cluster
+     * \param tolerance the tolerance to be used when testing
+     */
+    ThreeGppMimoPolarizationTest(std::string testCaseName,
+                                 Vector txLoc,
+                                 const MimoPolarizationAntennaParams& txAntennaParams,
+                                 Vector rxLoc,
+                                 const MimoPolarizationAntennaParams& rxAntennaParams,
+                                 std::valarray<std::complex<double>> testChannel,
+                                 double tolerance);
+
+    /**
+     * Destructor
+     */
+    ~ThreeGppMimoPolarizationTest() override;
+
+  private:
+    /**
+     * Build the test scenario
+     */
+    void DoRun() override;
+
+    /**
+     * @brief Function that can be used to configure the antenna using the set of
+     * parameters.
+     *
+     * @param params The parameters to be set to the antenna
+     * @return A pointer to the antenna that is created and configured by using input params
+     */
+    Ptr<PhasedArrayModel> CreateAndConfigureAntenna(const MimoPolarizationAntennaParams& params);
+
+    Vector m_txLoc;                           //!< Position of the TX device
+    MimoPolarizationAntennaParams m_txParams; //!< Parameters used to configure the TX antenna array
+    Vector m_rxLoc;                           //!< Position of the RX device
+    MimoPolarizationAntennaParams m_rxParams; //!< Parameters used to configure the RX antenna array
+    std::valarray<std::complex<double>>
+        m_testChannel;  //!< The test value for the matrix representing the strongest cluster
+    double m_tolerance; //!< The tolerance to be used when comparing the channel matrix with the
+                        //!< test matrix
+};
+
+ThreeGppMimoPolarizationTest::ThreeGppMimoPolarizationTest(
+    std::string testCaseName,
+    Vector txLoc,
+    const MimoPolarizationAntennaParams& txParams,
+    Vector rxLoc,
+    const MimoPolarizationAntennaParams& rxParams,
+    std::valarray<std::complex<double>> testChannel,
+    double tolerance)
+    : TestCase("Test MIMO using dual polarization." + testCaseName),
+      m_txLoc(txLoc),
+      m_txParams(txParams),
+      m_rxLoc(rxLoc),
+      m_rxParams(rxParams),
+      m_testChannel(testChannel),
+      m_tolerance(tolerance)
+{
+}
+
+ThreeGppMimoPolarizationTest::~ThreeGppMimoPolarizationTest()
+{
+}
+
+Ptr<PhasedArrayModel>
+ThreeGppMimoPolarizationTest::CreateAndConfigureAntenna(const MimoPolarizationAntennaParams& params)
+{
+    NS_LOG_FUNCTION(this);
+    Ptr<AntennaModel> antenna;
+    if (params.m_isotropic)
+    {
+        antenna = CreateObject<IsotropicAntennaModel>();
+    }
+    else
+    {
+        antenna = CreateObject<ThreeGppAntennaModel>();
+    }
+    // create the tx and rx antennas and set the their dimensions
+    return CreateObjectWithAttributes<UniformPlanarArray>("NumColumns",
+                                                          UintegerValue(params.m_cols),
+                                                          "NumRows",
+                                                          UintegerValue(params.m_rows),
+                                                          "AntennaElement",
+                                                          PointerValue(antenna),
+                                                          "NumVerticalPorts",
+                                                          UintegerValue(params.m_vPorts),
+                                                          "NumHorizontalPorts",
+                                                          UintegerValue(params.m_hPorts),
+                                                          "BearingAngle",
+                                                          DoubleValue(params.m_bearingAngle),
+                                                          "PolSlantAngle",
+                                                          DoubleValue(params.m_polSlantAngle),
+                                                          "IsDualPolarized",
+                                                          BooleanValue(true));
+}
+
+void
+ThreeGppMimoPolarizationTest::DoRun()
+{
+    RngSeedManager::SetSeed(1);
+    RngSeedManager::SetRun(1);
+    // create the ThreeGppChannelModel object used to generate the channel matrix
+    Ptr<ThreeGppChannelModel> channelModel = CreateObject<ThreeGppChannelModel>();
+    channelModel->SetAttribute("Frequency", DoubleValue(60e9));
+    channelModel->SetAttribute("Scenario", StringValue("RMa"));
+    channelModel->SetAttribute("ChannelConditionModel",
+                               PointerValue(CreateObject<AlwaysLosChannelConditionModel>()));
+
+    int64_t randomStream = 1;
+    randomStream += channelModel->AssignStreams(randomStream);
+
+    // create the tx and rx nodes
+    NodeContainer nodes;
+    nodes.Create(2);
+
+    // create the tx and rx devices
+    Ptr<SimpleNetDevice> txDev = CreateObject<SimpleNetDevice>();
+    Ptr<SimpleNetDevice> rxDev = CreateObject<SimpleNetDevice>();
+
+    // associate the nodes and the devices
+    nodes.Get(0)->AddDevice(txDev);
+    txDev->SetNode(nodes.Get(0));
+    nodes.Get(1)->AddDevice(rxDev);
+    rxDev->SetNode(nodes.Get(1));
+
+    // create the tx and rx mobility models and set their positions
+    Ptr<MobilityModel> txMob = CreateObject<ConstantPositionMobilityModel>();
+    txMob->SetPosition(m_txLoc);
+    Ptr<MobilityModel> rxMob = CreateObject<ConstantPositionMobilityModel>();
+    rxMob->SetPosition(m_rxLoc);
+
+    // associate the nodes and the mobility models
+    nodes.Get(0)->AggregateObject(txMob);
+    nodes.Get(1)->AggregateObject(rxMob);
+
+    // create the tx and rx antennas and set the their dimensions
+    Ptr<PhasedArrayModel> txAntenna = CreateAndConfigureAntenna(m_txParams);
+    Ptr<PhasedArrayModel> rxAntenna = CreateAndConfigureAntenna(m_rxParams);
+
+    // configure direct beamforming vectors to point to each other
+    txAntenna->SetBeamformingVector(
+        txAntenna->GetBeamformingVector(Angles(rxMob->GetPosition(), txMob->GetPosition())));
+    rxAntenna->SetBeamformingVector(
+        rxAntenna->GetBeamformingVector(Angles(txMob->GetPosition(), rxMob->GetPosition())));
+
+    // generate the time domain channel matrix
+    Ptr<const ThreeGppChannelModel::ChannelMatrix> channelMatrix =
+        channelModel->GetChannel(txMob, rxMob, txAntenna, rxAntenna);
+
+    // test whether the channel matrix for the first cluster experiences strong
+    // symmetry that is caused by existence of dual polarized ports at the
+    // transmitter and the receiver
+    const std::complex<double>* strongestClusterPtr = channelMatrix->m_channel.GetPagePtr(0);
+    size_t matrixSize =
+        channelMatrix->m_channel.GetNumRows() * channelMatrix->m_channel.GetNumCols();
+
+    MatrixBasedChannelModel::Complex2DVector strongestCluster(
+        channelMatrix->m_channel.GetNumRows(),
+        channelMatrix->m_channel.GetNumCols(),
+        std::valarray<std::complex<double>>(strongestClusterPtr, matrixSize));
+
+    MatrixBasedChannelModel::Complex2DVector testChannel(channelMatrix->m_channel.GetNumRows(),
+                                                         channelMatrix->m_channel.GetNumCols(),
+                                                         m_testChannel);
+
+    NS_LOG_INFO("Channel matrix:" << strongestCluster);
+    NS_LOG_INFO("Test channel matrix: " << testChannel);
+
+    NS_TEST_ASSERT_MSG_EQ(
+        strongestCluster.IsAlmostEqual(testChannel, m_tolerance),
+        true,
+        "The strongest cluster and the test channel matrix should be almost equal");
+
+    Simulator::Run();
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup spectrum-tests
+ *
  * Test suite for the ThreeGppChannelModel class
  */
 class ThreeGppChannelTestSuite : public TestSuite
@@ -699,9 +1230,156 @@
 ThreeGppChannelTestSuite::ThreeGppChannelTestSuite()
     : TestSuite("three-gpp-channel", UNIT)
 {
-    AddTestCase(new ThreeGppChannelMatrixComputationTest, TestCase::QUICK);
-    AddTestCase(new ThreeGppChannelMatrixUpdateTest, TestCase::QUICK);
-    AddTestCase(new ThreeGppSpectrumPropagationLossModelTest, TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixComputationTest(2, 2, 1, 1), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixComputationTest(4, 2, 1, 1), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixComputationTest(2, 2, 2, 2), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixComputationTest(4, 4, 2, 2), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixComputationTest(4, 2, 2, 1), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixUpdateTest(2, 4, 1, 1), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixUpdateTest(2, 2, 1, 1), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixUpdateTest(2, 4, 2, 2), TestCase::QUICK);
+    AddTestCase(new ThreeGppChannelMatrixUpdateTest(2, 2, 2, 2), TestCase::QUICK);
+    AddTestCase(new ThreeGppSpectrumPropagationLossModelTest(4, 4, 1, 1), TestCase::QUICK);
+    AddTestCase(new ThreeGppSpectrumPropagationLossModelTest(4, 4, 2, 2), TestCase::QUICK);
+    AddTestCase(new ThreeGppSpectrumPropagationLossModelTest(4, 2, 2, 2), TestCase::QUICK);
+    AddTestCase(new ThreeGppSpectrumPropagationLossModelTest(4, 2, 2, 1), TestCase::QUICK);
+    AddTestCase(new ThreeGppCalcLongTermMultiPortTest(), TestCase::QUICK);
+
+    /**
+     *  The TX and RX antennas are configured face-to-face.
+     *  When polarization slant angles are 0 and 0 at TX and RX,
+     *  we expect the strongest cluster to be similar to the following matrix:
+     *   (5.9,0) (5.9,0) (0,0)     (0,0)
+     *   (5.9,0) (5.9,0) (0,0)     (0,0)
+     *   (0,0)   (0,0)   (-5.8,)   (-5.8,0)
+     *   (0,0)   (0,0)   (-5.8,0)  (-5.8,0)
+     */
+    std::valarray<std::complex<double>> testChannel1 =
+        {5.9, 5.9, 0, 0, 5.9, 5.9, 0, 0, 0, 0, -5.8, -5.8, 0, 0, -5.8, -5.8};
+    AddTestCase(new ThreeGppMimoPolarizationTest("Face-to-face. 0 and 0 pol. slant angles.",
+                                                 Vector{0, 0, 3},
+                                                 MimoPolarizationAntennaParams(false, 0, 0),
+                                                 Vector{9, 0, 3},
+                                                 MimoPolarizationAntennaParams(false, 0, M_PI),
+                                                 testChannel1,
+                                                 0.9),
+                TestCase::QUICK);
+
+    /**
+     *  The TX and RX antennas are configured face-to-face.
+     *  When polarization slant angles are 30 and 0 at TX and RX,
+     *  we expect the strongest cluster to be similar to the following matrix:
+     *   (5,0)   (5,0)   (3,0)   (3,0)
+     *   (5,0)   (5,0)   (3,0)   (3,0)
+     *   (3,0)   (3,0)   (-5,0)  (-5,0)
+     *   (3,0)   (3,0)   (-5,0)  (-5,0)
+     */
+    AddTestCase(
+        new ThreeGppMimoPolarizationTest("Face-to-face. 30 and 0 pol. slant angles.",
+                                         Vector{0, 0, 3},
+                                         MimoPolarizationAntennaParams(false, M_PI / 6, 0),
+                                         Vector{6, 0, 3},
+                                         MimoPolarizationAntennaParams(false, 0, M_PI),
+                                         {5, 5, 3, 3, 5, 5, 3, 3, 3, 3, -5, -5, 3, 3, -5, -5},
+                                         0.8),
+        TestCase::QUICK);
+
+    /**
+     *  The TX and RX antennas are configured face-to-face.
+     *  When polarization slant angles are 45 and 0 at TX and RX,
+     *  we expect the strongest cluster to be similar to the following matrix:
+     *  (4,0)  (4,0)  (4,0)  (4,0)
+     *  (4,0)  (4,0)  (4,0)  (4,0)
+     *  (4,0)  (4,0)  (4,0)  (4,0)
+     *  (4,0)  (4,0)  (4,0)  (4,0)
+     */
+    AddTestCase(
+        new ThreeGppMimoPolarizationTest("Face-to-face. 45 and 0 pol. slant angles.",
+                                         Vector{0, 0, 3},
+                                         MimoPolarizationAntennaParams(false, M_PI / 4, 0),
+                                         Vector{6, 0, 3},
+                                         MimoPolarizationAntennaParams(false, 0, M_PI),
+                                         {4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -4, -4, 4, 4, -4, -4},
+                                         0.7),
+        TestCase::QUICK);
+
+    /**
+     *  The TX and RX antennas are configured face-to-face.
+     *  When polarization slant angles are 45 and 0 at TX and RX,
+     *  we expect the strongest cluster to be similar to the following matrix:
+     *  (0,0)  (0,0)  (5.9,0)  (5.9,0)
+     *  (0,0)  (0,0)  (5.9,0)  (5.9,0)
+     *  (5.8,0)  (5.8,0)  (0,0)  (0,0)
+     *  (5.8,0)  (5.8,0)  (0,0)  (0,0)
+     */
+    AddTestCase(new ThreeGppMimoPolarizationTest(
+                    "Face-to-face. 90 and 0 pol. slant angles.",
+                    Vector{0, 0, 3},
+                    MimoPolarizationAntennaParams(false, M_PI / 2, 0),
+                    Vector{6, 0, 3},
+                    MimoPolarizationAntennaParams(false, 0, M_PI),
+                    {0, 0, 5.8, 5.8, 0, 0, 5.8, 5.8, 5.9, 5.9, 0, 0, 5.9, 5.9, 0, 0},
+                    0.9),
+                TestCase::QUICK);
+
+    /**
+     *  The TX and RX antennas are face to face. We test the configuration of
+     *  the bearing angle along with the configuration of the different position
+     *  of the RX antenna, and the bearing angles.
+     *  When polarization slant angles are 0 and 0 at TX and RX,
+     *  we expect the strongest cluster to be similar to the following matrix:
+     *   (5.9,0) (5.9,0) (0,0)     (0,0)
+     *   (5.9,0) (5.9,0) (0,0)     (0,0)
+     *   (0,0)   (0,0)   (-5.8,)   (-5.8,0)
+     *   (0,0)   (0,0)   (-5.8,0)  (-5.8,0)
+     *  Notice that we expect almost the same matrix as in the first case in
+     *  which
+     */
+    AddTestCase(
+        new ThreeGppMimoPolarizationTest("Face-to-face. Different positions. Different bearing "
+                                         "angles. 0 and 0 pol. slant angles.",
+                                         Vector{0, 0, 3},
+                                         MimoPolarizationAntennaParams(false, 0, M_PI / 4),
+                                         Vector{6.363961031, 6.363961031, 3},
+                                         MimoPolarizationAntennaParams(false, 0, -(M_PI / 4) * 3),
+                                         testChannel1,
+                                         0.9),
+        TestCase::QUICK);
+
+    /**
+     *  The TX and RX antenna have different height.
+     *  Bearing angle is configured to point one toward the other.
+     *  When polarization slant angles are 0 and 0 at TX and RX,
+     *  we expect the strongest cluster to be similar to the following matrix:
+     *   (2.5,-4.7)  (2.5,-4.7)   (0,0)     (0,0)
+     *   (2.5,-4.7)  (2.5,-4.7)   (0,0)     (0,0)
+     *   (0,0)   (0,0)    (-2.4,4)    (-2.4,4)
+     *   (0,0)   (0,0)    (-2.4,4)    (-2.4,4)
+     */
+    AddTestCase(new ThreeGppMimoPolarizationTest(
+                    "Not face-to-face. Different heights. 0 and 0 pol. slant angles.",
+                    Vector{0, 0, 10},
+                    MimoPolarizationAntennaParams(false, 0, 0),
+                    Vector{30, 0, 3},
+                    MimoPolarizationAntennaParams(false, 0, M_PI),
+                    {{2.5, -4.7},
+                     {2.5, -4.7},
+                     0,
+                     0,
+                     {2.5, -4.7},
+                     {2.5, -4.7},
+                     0,
+                     0,
+                     0,
+                     0,
+                     {-2.4, 4},
+                     {-2.4, 4},
+                     0,
+                     0,
+                     {-2.4, 4},
+                     {-2.4, 4}},
+                    0.5),
+                TestCase::QUICK);
 }
 
 /// Static variable for test initialization
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/test/two-ray-splm-test-suite.cc ns-3.41/src/spectrum/test/two-ray-splm-test-suite.cc
--- ns-3.40/src/spectrum/test/two-ray-splm-test-suite.cc	2023-09-27 13:30:37.878839700 -0700
+++ ns-3.41/src/spectrum/test/two-ray-splm-test-suite.cc	2024-02-09 14:44:08.501794300 -0800
@@ -259,7 +259,7 @@
                                      double expectedGain)
     // TODO: Create a string with the test parameters as the test case name like in
     // test-uniform-planar-array ?
-    : TestCase("Check that the overall array response gain has the proper trend with respect to"
+    : TestCase("Check that the overall array response gain has the proper trend with respect to "
                "the number of antennas and the type of single element antenna"),
       m_txAntElem(txAntElem),
       m_rxAntElem(rxAntElem),
@@ -561,15 +561,15 @@
         auto rxBfVec = rxArray->GetBeamformingVector(Angles(txPosVec, rxPosVec));
         rxArray->SetBeamformingVector(rxBfVec);
 
-        auto twoRayRxPsd =
+        auto twoRayRxParams =
             twoRaySplm->DoCalcRxPowerSpectralDensity(signalParams, txMob, rxMob, txArray, rxArray);
-        auto threeGppRayRxPsd = threeGppSplm->DoCalcRxPowerSpectralDensity(signalParams,
-                                                                           txMob,
-                                                                           rxMob,
-                                                                           txArray,
-                                                                           rxArray);
-        double twoRayRxPower = ComputePowerSpectralDensityOverallPower(twoRayRxPsd);
-        double threeGppRxPower = ComputePowerSpectralDensityOverallPower(threeGppRayRxPsd);
+        auto threeGppRayRxParams = threeGppSplm->DoCalcRxPowerSpectralDensity(signalParams,
+                                                                              txMob,
+                                                                              rxMob,
+                                                                              txArray,
+                                                                              rxArray);
+        double twoRayRxPower = ComputePowerSpectralDensityOverallPower(twoRayRxParams->psd);
+        double threeGppRxPower = ComputePowerSpectralDensityOverallPower(threeGppRayRxParams->psd);
 
         twoRayGainMean += (twoRayRxPower / txPower);
         threeGppGainMean += (threeGppRxPower / txPower);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py ns-3.41/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py
--- ns-3.40/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py	2023-09-27 13:30:37.878839700 -0700
+++ ns-3.41/src/spectrum/utils/two-ray-to-three-gpp-ch-calibration.py	2024-02-09 14:44:08.501794300 -0800
@@ -19,42 +19,67 @@
 this program. If not, see <http://www.gnu.org/licenses/>.
 """
 
+import argparse as argp
+import contextlib
+from itertools import product
+from pathlib import Path
+
+import joblib
+import numpy as np
 import pandas as pd
-from matplotlib import pyplot as plt
 import seaborn as sns
-import numpy as np
-from pathlib import Path
-from itertools import product
+from matplotlib import pyplot as plt
 from tqdm import tqdm
-import joblib
-import contextlib
-import argparse as argp
-
 
 # Command line arguments
 parser = argp.ArgumentParser(formatter_class=argp.ArgumentDefaultsHelpFormatter)
-parser.add_argument("--num_search_grid_params", default=30,
-                    help="Number of values for each parameter of the search grids")
-parser.add_argument("--num_refinements", default=1,
-                    help="Number of refinement local search runs to be carried out")
-parser.add_argument("--ref_data_fname", default="two-ray-to-three-gpp-splm-calibration.csv",
-                    help="Filename of the fit reference data, obtained from ns-3")
-parser.add_argument("--fit_out_fname", default="two-ray-splm-fitted-params.txt",
-                    help="Filename of the fit results")
-parser.add_argument("--c_plus_plus_out_fname", default="two-ray-cplusplus-fitted-params.txt",
-                    help="Filename of the fit results, encoded as a C++ data structure to be imported in ns-3")
-parser.add_argument("--figs_folder", default="FiguresTwoRayThreeGppChCalibration/",
-                    help="Output folder for the fit results figures")
-parser.add_argument("--epsilon", default=1e-7,
-                    help="Tolerance value for the preliminary tests")
-parser.add_argument("--preliminary_fit_test", default=True,
-                    help="Whether to run preliminary tests which check the correctness of the script functions")
-parser.add_argument("--fit_ftr_to_threegpp", default=True,
-                    help="Whether to run the calibration with respect to the 3GPP reference channel gains")
-parser.add_argument("--output_ns3_table", default=True,
-                    help="Whether to output the code for importing the calibration results in ns-3")
-parser.add_argument("--plot_fit_results", default=False,
-                    help="Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions")
+parser.add_argument(
+    "--num_search_grid_params",
+    default=30,
+    help="Number of values for each parameter of the search grids",
+)
+parser.add_argument(
+    "--num_refinements", default=1, help="Number of refinement local search runs to be carried out"
+)
+parser.add_argument(
+    "--ref_data_fname",
+    default="two-ray-to-three-gpp-splm-calibration.csv",
+    help="Filename of the fit reference data, obtained from ns-3",
+)
+parser.add_argument(
+    "--fit_out_fname", default="two-ray-splm-fitted-params.txt", help="Filename of the fit results"
+)
+parser.add_argument(
+    "--c_plus_plus_out_fname",
+    default="two-ray-cplusplus-fitted-params.txt",
+    help="Filename of the fit results, encoded as a C++ data structure to be imported in ns-3",
+)
+parser.add_argument(
+    "--figs_folder",
+    default="FiguresTwoRayThreeGppChCalibration/",
+    help="Output folder for the fit results figures",
+)
+parser.add_argument("--epsilon", default=1e-7, help="Tolerance value for the preliminary tests")
+parser.add_argument(
+    "--preliminary_fit_test",
+    default=True,
+    help="Whether to run preliminary tests which check the correctness of the script functions",
+)
+parser.add_argument(
+    "--fit_ftr_to_threegpp",
+    default=True,
+    help="Whether to run the calibration with respect to the 3GPP reference channel gains",
+)
+parser.add_argument(
+    "--output_ns3_table",
+    default=True,
+    help="Whether to output the code for importing the calibration results in ns-3",
+)
+parser.add_argument(
+    "--plot_fit_results",
+    default=False,
+    help="Whether to plot a comparison of the reference data ECDFs vs the fitted FTR distributions",
+)
 
 args = parser.parse_args()
 # Number of values for each parameter of the search grids
@@ -84,10 +109,11 @@
 @contextlib.contextmanager
 def tqdm_joblib(tqdm_object):
     """
-      Context manager to patch joblib to report into tqdm progress bar given as argument.
-      Taken from: https://stackoverflow.com/questions/24983493/tracking-progress-of-joblib-parallel-execution
+    Context manager to patch joblib to report into tqdm progress bar given as argument.
+    Taken from: https://stackoverflow.com/questions/24983493/tracking-progress-of-joblib-parallel-execution
 
     """
+
     class TqdmBatchCompletionCallback(joblib.parallel.BatchCompletionCallBack):
         def __call__(self, *args, **kwargs):
             tqdm_object.update(n=self.batch_size)
@@ -114,13 +140,13 @@
     #  Parameter delta [0, 1]. Expresses how similar the amplitudes of the two dominant specular components are.
 
     def __init__(self, m: float, sigma: float, k: float, delta: float):
-        '''! The initializer.
+        """! The initializer.
         @param self: the object pointer
         @param m: Parameter m for the Gamma variable. Used both as the shape and rate parameters.
         @param sigma: Parameter sigma. Used as the variance of the amplitudes of the normal diffuse components.
         @param k: Parameter K. Expresses ratio between dominant specular components and diffuse components.
         @param delta: Parameter delta [0, 1]. Expresses how similar the amplitudes of the two dominant specular components are.
-        '''
+        """
 
         self.m = m
         self.sigma = sigma
@@ -128,9 +154,9 @@
         self.delta = delta
 
     def __init__(self):
-        '''! The initializer with default values.
+        """! The initializer with default values.
         @param self: the object pointer
-        '''
+        """
 
         self.m = 1
         self.sigma = 1.0
@@ -138,37 +164,36 @@
         self.delta = 0.0
 
     def __str__(self):
-        '''! The initializer with default values.
+        """! The initializer with default values.
         @param self: the object pointer
         @returns A string reporting the value of each of the FTR fading model parameters
-        '''
+        """
 
-        return f'm: {self.m}, sigma: {self.sigma}, k: {self.k}, delta: {self.delta}'
+        return f"m: {self.m}, sigma: {self.sigma}, k: {self.k}, delta: {self.delta}"
 
 
 def get_ftr_ecdf(params: FtrParams, n_samples: int, db=False):
-    '''!  Returns the ECDF for the FTR fading model, for a given parameter grid.
-        @param params: The FTR parameters grid.
-        @param n_samples: The number of samples of the output ECDF
-        @param db: Whether to return the ECDF with the gain expressed in dB
-        @returns The ECDF for the FTR fading model
-    '''
+    """!  Returns the ECDF for the FTR fading model, for a given parameter grid.
+    @param params: The FTR parameters grid.
+    @param n_samples: The number of samples of the output ECDF
+    @param db: Whether to return the ECDF with the gain expressed in dB
+    @returns The ECDF for the FTR fading model
+    """
 
-    assert (params.delta >= 0 and params.delta <= 1.0)
+    assert params.delta >= 0 and params.delta <= 1.0
 
     # Compute the specular components amplitudes from the FTR parameters
     cmn_sqrt_term = np.sqrt(1 - params.delta**2)
     v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
     v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
 
-    assert (abs((v1**2 + v2**2)/(2*params.sigma) - params.k) < 1e-5)
+    assert abs((v1**2 + v2**2) / (2 * params.sigma) - params.k) < 1e-5
     if params.k > 0:
-        assert (abs((2*v1*v2)/(v1**2 + v2**2) - params.delta) < 1e-4)
+        assert abs((2 * v1 * v2) / (v1**2 + v2**2) - params.delta) < 1e-4
     else:
-        assert (v1 == v2 == params.k)
+        assert v1 == v2 == params.k
 
-    sqrt_gamma = np.sqrt(np.random.gamma(
-        shape=params.m, scale=1/params.m, size=n_samples))
+    sqrt_gamma = np.sqrt(np.random.gamma(shape=params.m, scale=1 / params.m, size=n_samples))
 
     # Sample the random phases of the specular components, which are uniformly distributed in [0, 2*PI]
     phi1 = np.random.uniform(low=0, high=1.0, size=n_samples)
@@ -182,75 +207,78 @@
     compl_phi1 = np.vectorize(complex)(np.cos(phi1), np.sin(phi1))
     compl_phi2 = np.vectorize(complex)(np.cos(phi2), np.sin(phi2))
     compl_xy = np.vectorize(complex)(x, y)
-    h = np.multiply(sqrt_gamma, compl_phi1) * v1 + \
-        np.multiply(sqrt_gamma, compl_phi2) * v2 + compl_xy
+    h = (
+        np.multiply(sqrt_gamma, compl_phi1) * v1
+        + np.multiply(sqrt_gamma, compl_phi2) * v2
+        + compl_xy
+    )
 
     # Compute the squared norms
     power = np.square(np.absolute(h))
 
     if db:
-        power = 10*np.log10(power)
+        power = 10 * np.log10(power)
 
     return np.sort(power)
 
 
 def compute_ftr_mean(params: FtrParams):
-    '''!  Computes the mean of the FTR fading model, given a specific set of parameters.
-        @param params: The FTR fading model parameters.
-    '''
+    """!  Computes the mean of the FTR fading model, given a specific set of parameters.
+    @param params: The FTR fading model parameters.
+    """
 
     cmn_sqrt_term = np.sqrt(1 - params.delta**2)
     v1 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 - cmn_sqrt_term))
     v2 = np.sqrt(params.sigma) * np.sqrt(params.k * (1 + cmn_sqrt_term))
 
-    mean = v1**2 + v2**2 + 2*params.sigma
+    mean = v1**2 + v2**2 + 2 * params.sigma
 
     return mean
 
 
 def compute_ftr_th_mean(params: FtrParams):
-    '''!  Computes the mean of the FTR fading model using the formula reported in the corresponding paper,
-          given a specific set of parameters.
-        @param params: The FTR fading model parameters.
-    '''
+    """!  Computes the mean of the FTR fading model using the formula reported in the corresponding paper,
+      given a specific set of parameters.
+    @param params: The FTR fading model parameters.
+    """
 
     return 2 * params.sigma * (1 + params.k)
 
 
 def compute_anderson_darling_measure(ref_ecdf: list, target_ecdf: list) -> float:
-    '''!  Computes the Anderson-Darling measure for the specified reference and targets distributions.
-          In particular, the Anderson-Darling measure is defined as:
-          \f$A^2 = -N -S\f$, where \f$S = \sum_{i=1}^N \frac{2i - 1}{N} \left[ ln F(Y_i) + ln F(Y_{N + 1 - i}) \right]\f$.
-
-          See https://www.itl.nist.gov/div898/handbook/eda/section3/eda35e.htm for further details.
-
-        @param ref_ecdf: The reference ECDF.
-        @param target_ecdf: The target ECDF we wish to match the reference distribution to.
-        @returns The Anderson-Darling measure for the specified reference and targets distributions.
-    '''
+    """!  Computes the Anderson-Darling measure for the specified reference and targets distributions.
+      In particular, the Anderson-Darling measure is defined as:
+      \f$A^2 = -N -S\f$, where \f$S = \sum_{i=1}^N \frac{2i - 1}{N} \left[ ln F(Y_i) + ln F(Y_{N + 1 - i}) \right]\f$.
+
+      See https://www.itl.nist.gov/div898/handbook/eda/section3/eda35e.htm for further details.
+
+    @param ref_ecdf: The reference ECDF.
+    @param target_ecdf: The target ECDF we wish to match the reference distribution to.
+    @returns The Anderson-Darling measure for the specified reference and targets distributions.
+    """
 
-    assert (len(ref_ecdf) == len(target_ecdf))
+    assert len(ref_ecdf) == len(target_ecdf)
 
     n = len(ref_ecdf)
-    mult_factors = np.linspace(start=1, stop=n, num=n)*2 + 1
+    mult_factors = np.linspace(start=1, stop=n, num=n) * 2 + 1
     ecdf_values = compute_ecdf_value(ref_ecdf, target_ecdf)
 
     # First and last elements of the ECDF may lead to NaNs
-    with np.errstate(divide='ignore'):
+    with np.errstate(divide="ignore"):
         log_a_plus_b = np.log(ecdf_values) + np.log(1 - np.flip(ecdf_values))
 
     valid_idxs = np.isfinite(log_a_plus_b)
-    A_sq = - np.dot(mult_factors[valid_idxs], log_a_plus_b[valid_idxs])
+    A_sq = -np.dot(mult_factors[valid_idxs], log_a_plus_b[valid_idxs])
 
     return A_sq
 
 
 def compute_ecdf_value(ecdf: list, data_points: float) -> np.ndarray:
-    '''!  Given an ECDF and data points belonging to its domain, returns their associated EDCF value.
-        @param ecdf: The ECDF, represented as a sorted list of samples.
-        @param data_points: A list of data points belonging to the same domain as the samples.
-        @returns The ECDF value of the domain points of the specified ECDF
-    '''
+    """!  Given an ECDF and data points belonging to its domain, returns their associated EDCF value.
+    @param ecdf: The ECDF, represented as a sorted list of samples.
+    @param data_points: A list of data points belonging to the same domain as the samples.
+    @returns The ECDF value of the domain points of the specified ECDF
+    """
 
     ecdf_values = []
     for point in data_points:
@@ -261,36 +289,39 @@
 
 
 def get_sigma_from_k(k: float) -> float:
-    '''!  Computes the value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
-        @param k: The K parameter of the FTR fading model, which represents the ratio of the average power
-                  of the dominant components to the power of the remaining diffuse multipath.
-        @returns The value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
-    '''
+    """!  Computes the value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
+    @param k: The K parameter of the FTR fading model, which represents the ratio of the average power
+              of the dominant components to the power of the remaining diffuse multipath.
+    @returns The value for the FTR parameter sigma, given k, yielding a unit-mean fading process.
+    """
 
     return 1 / (2 + 2 * k)
 
 
-def fit_ftr_to_reference(ref_data: pd.DataFrame, ref_params_combo: tuple,  num_params: int, num_refinements: int) -> str:
-    '''!  Estimate the FTR parameters yielding the closest ECDF to the reference one.
+def fit_ftr_to_reference(
+    ref_data: pd.DataFrame, ref_params_combo: tuple, num_params: int, num_refinements: int
+) -> str:
+    """!  Estimate the FTR parameters yielding the closest ECDF to the reference one.
+
+      Uses a global search to estimate the FTR parameters yielding the best fit to the reference ECDF.
+      Then, the search is refined by repeating the procedure in the neighborhood of the parameters
+      identified with the global search. Such a neighborhood is determined as the interval whose center
+      is the previous iteration best value, and the lower and upper bounds are the first lower and upper
+      values which were previously considered, respectively.
+
+    @param ref_data: The reference data, represented as a DataFrame of samples.
+    @param ref_params_combo: The specific combination of simulation parameters corresponding
+                             to the reference ECDF
+    @param num_params: The number of values of each parameter in the global and local search grids.
+    @param num_refinements: The number of local refinement search to be carried out after the global search.
 
-          Uses a global search to estimate the FTR parameters yielding the best fit to the reference ECDF.
-          Then, the search is refined by repeating the procedure in the neighborhood of the parameters
-          identified with the global search. Such a neighborhood is determined as the interval whose center
-          is the previous iteration best value, and the lower and upper bounds are the first lower and upper
-          values which were previously considered, respectively.
-
-        @param ref_data: The reference data, represented as a DataFrame of samples.
-        @param ref_params_combo: The specific combination of simulation parameters corresponding
-                                 to the reference ECDF
-        @param num_params: The number of values of each parameter in the global and local search grids.
-        @param num_refinements: The number of local refinement search to be carried out after the global search.
-
-        @returns An estimate of the FTR parameters yielding the closest ECDF to the reference one.
-    '''
+    @returns An estimate of the FTR parameters yielding the closest ECDF to the reference one.
+    """
 
     # Retrieve the reference ECDF
     ref_ecdf = ref_data.query(
-        'scen == @ref_params_combo[0] and cond == @ref_params_combo[1] and fc == @ref_params_combo[2]')
+        "scen == @ref_params_combo[0] and cond == @ref_params_combo[1] and fc == @ref_params_combo[2]"
+    )
 
     # Perform the fit
     n_samples = len(ref_ecdf)
@@ -303,21 +334,26 @@
     m_and_k_step = (m_and_k_ub - m_and_k_lb) / n_samples
 
     # The delta parameter can range in [0, 1]
-    delta_step = 1/n_samples
+    delta_step = 1 / n_samples
 
     # Define the coarse grid
     coarse_search_grid = {
         # m must be in [0, +inf]
-        'm': np.power(np.ones(num_params)*10, np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params)),
+        "m": np.power(
+            np.ones(num_params) * 10,
+            np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params),
+        ),
         # k must be in [0, +inf]
-        'k': np.power(np.ones(num_params)*10, np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params)),
+        "k": np.power(
+            np.ones(num_params) * 10,
+            np.linspace(start=m_and_k_lb, stop=m_and_k_ub, endpoint=True, num=num_params),
+        ),
         # delta must be in [0, 1]
-        'delta': np.linspace(start=0.0, stop=1.0, endpoint=True, num=num_params)
+        "delta": np.linspace(start=0.0, stop=1.0, endpoint=True, num=num_params),
         # sigma determined from k, due to the unit-mean constraint
     }
 
     for element in product(*coarse_search_grid.values()):
-
         # Create FTR params object
         params = FtrParams()
         params.m = element[0]
@@ -329,36 +365,48 @@
         ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
         ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
 
-        if (ad_meas < best_ad):
+        if ad_meas < best_ad:
             best_params = params
             best_ad = ad_meas
 
     for _ in range(num_refinements):
         # Refine search in the neighborhood of the previously identified params
         finer_search_grid = {
-            'm': np.power(np.ones(num_params)*10,
-                          np.linspace(start=max(0, np.log10(best_params.m) - m_and_k_step),
-                                      stop=np.log10(best_params.m) +
-                                      m_and_k_step,
-                                      endpoint=True, num=num_params)),
-            'k': np.power(np.ones(num_params)*10,
-                          np.linspace(start=max(0, np.log10(best_params.k) - m_and_k_step),
-                                      stop=np.log10(best_params.k) +
-                                      m_and_k_step,
-                                      endpoint=True, num=num_params)),
-            'delta': np.linspace(start=max(0, best_params.delta - delta_step),
-                                 stop=min(1, best_params.delta + delta_step),
-                                 endpoint=True, num=num_params)
+            "m": np.power(
+                np.ones(num_params) * 10,
+                np.linspace(
+                    start=max(0, np.log10(best_params.m) - m_and_k_step),
+                    stop=np.log10(best_params.m) + m_and_k_step,
+                    endpoint=True,
+                    num=num_params,
+                ),
+            ),
+            "k": np.power(
+                np.ones(num_params) * 10,
+                np.linspace(
+                    start=max(0, np.log10(best_params.k) - m_and_k_step),
+                    stop=np.log10(best_params.k) + m_and_k_step,
+                    endpoint=True,
+                    num=num_params,
+                ),
+            ),
+            "delta": np.linspace(
+                start=max(0, best_params.delta - delta_step),
+                stop=min(1, best_params.delta + delta_step),
+                endpoint=True,
+                num=num_params,
+            ),
             # sigma determined from k, due to the unit-mean constraint
         }
 
-        m_and_k_step = (np.log10(best_params.m) + m_and_k_step -
-                        max(0, np.log10(best_params.m) - m_and_k_step)) / n_samples
-        delta_step = (min(1, best_params.delta + 1/num_params) -
-                      max(0, best_params.delta - 1/num_params)) / n_samples
+        m_and_k_step = (
+            np.log10(best_params.m) + m_and_k_step - max(0, np.log10(best_params.m) - m_and_k_step)
+        ) / n_samples
+        delta_step = (
+            min(1, best_params.delta + 1 / num_params) - max(0, best_params.delta - 1 / num_params)
+        ) / n_samples
 
         for element in product(*finer_search_grid.values()):
-
             # Create FTR params object
             params = FtrParams()
             params.m = element[0]
@@ -370,19 +418,21 @@
             ftr_ecdf = get_ftr_ecdf(params, n_samples, db=True)
             ad_meas = compute_anderson_darling_measure(ref_ecdf, ftr_ecdf)
 
-            if (ad_meas < best_ad):
+            if ad_meas < best_ad:
                 best_params = params
                 best_ad = ad_meas
 
-    out_str = f"{ref_params_combo[0]}\t{ref_params_combo[1]}\t{ref_params_combo[2]}" + \
-        f" \t{best_params.sigma}\t{best_params.k}\t{best_params.delta}\t{best_params.m}\n"
+    out_str = (
+        f"{ref_params_combo[0]}\t{ref_params_combo[1]}\t{ref_params_combo[2]}"
+        + f" \t{best_params.sigma}\t{best_params.k}\t{best_params.delta}\t{best_params.m}\n"
+    )
 
     return out_str
 
 
 def append_ftr_params_to_cpp_string(text: str, params: FtrParams) -> str:
-    text += f'TwoRaySpectrumPropagationLossModel::FtrParams({np.format_float_scientific(params.m)}, {np.format_float_scientific(params.sigma)}, \
-                                                            {np.format_float_scientific(params.k)}, {np.format_float_scientific(params.delta)})'
+    text += f"TwoRaySpectrumPropagationLossModel::FtrParams({np.format_float_scientific(params.m)}, {np.format_float_scientific(params.sigma)}, \
+                                                            {np.format_float_scientific(params.k)}, {np.format_float_scientific(params.delta)})"
 
     return text
 
@@ -396,74 +446,71 @@
       out_fname (str): The name of the file to print the C++ code to.
     """
 
-    out_str = '{'
+    out_str = "{"
 
-    for scen in set(fit['scen']):
-        out_str += f'{{\"{scen}\",\n{{'
+    for scen in set(fit["scen"]):
+        out_str += f'{{"{scen}",\n{{'
 
-        for cond in set(fit['cond']):
-            out_str += f'{{ChannelCondition::LosConditionValue::{cond}, \n'
+        for cond in set(fit["cond"]):
+            out_str += f"{{ChannelCondition::LosConditionValue::{cond}, \n"
 
             # Print vector of carrier frequencies
-            freqs = np.sort(list(set(fit['fc'])))
+            freqs = np.sort(list(set(fit["fc"])))
             out_str += "{{"
             for fc in freqs:
-                out_str += f'{float(fc)}, '
+                out_str += f"{float(fc)}, "
             out_str = out_str[0:-2]
-            out_str += '},\n{'
+            out_str += "},\n{"
 
             # Load corresponding fit results
             for fc in freqs:
-                fit_line = fit.query(
-                    'scen == @scen and cond == @cond and fc == @fc')
-                assert(fit_line.reset_index().shape[0] == 1)
+                fit_line = fit.query("scen == @scen and cond == @cond and fc == @fc")
+                assert fit_line.reset_index().shape[0] == 1
 
                 params = FtrParams()
-                params.m = fit_line.iloc[0]['m']
-                params.k = fit_line.iloc[0]['k']
-                params.delta = fit_line.iloc[0]['delta']
-                params.sigma = fit_line.iloc[0]['sigma']
+                params.m = fit_line.iloc[0]["m"]
+                params.k = fit_line.iloc[0]["k"]
+                params.delta = fit_line.iloc[0]["delta"]
+                params.sigma = fit_line.iloc[0]["sigma"]
 
                 # Print vector of corresponding FTR parameters
                 out_str = append_ftr_params_to_cpp_string(out_str, params)
-                out_str += ', '
+                out_str += ", "
 
             out_str = out_str[0:-2]
-            out_str += '}'
-            out_str += '}},\n'
+            out_str += "}"
+            out_str += "}},\n"
 
         out_str = out_str[0:-2]
-        out_str += '}},\n'
+        out_str += "}},\n"
 
     out_str = out_str[0:-2]
-    out_str += '}\n'
+    out_str += "}\n"
 
     with open(out_fname, "w", encoding="utf-8") as f:
         f.write(out_str)
 
 
-if __name__ == '__main__':
-
+if __name__ == "__main__":
     #########################
     ## Data pre-processing ##
     #########################
 
     # Load reference data obtained from the ns-3 TR 38.901 implementation
-    df = pd.read_csv(ref_data_fname, sep='\t')
+    df = pd.read_csv(ref_data_fname, sep="\t")
     # Linear gain --> gain in dB
-    df['gain'] = 10*np.log10(df['gain'])
+    df["gain"] = 10 * np.log10(df["gain"])
 
     # Retrieve the possible parameters configurations
-    scenarios = set(df['scen'])
-    is_los = set(df['cond'])
-    frequencies = np.sort(list(set(df['fc'])))
+    scenarios = set(df["scen"])
+    is_los = set(df["cond"])
+    frequencies = np.sort(list(set(df["fc"])))
 
     ####################################################################################################
     ## Fit Fluctuating Two Ray model to the 3GPP TR 38.901 using the Anderson-Darling goodness-of-fit ##
     ####################################################################################################
 
     if preliminary_fit_test:
-
         params = FtrParams()
         get_ftr_ecdf(params, 100)
 
@@ -492,11 +539,19 @@
         assert np.all(np.abs(mean_th_list - np.float64(1.0)) < epsilon)
 
     if fit_ftr_to_threegpp:
-
         # Parallel search for the different simulation parameters combination
-        with tqdm_joblib(tqdm(desc="Fitting FTR to the 3GPP fading model", total=(len(scenarios) * len(is_los) * len(frequencies)))) as progress_bar:
+        with tqdm_joblib(
+            tqdm(
+                desc="Fitting FTR to the 3GPP fading model",
+                total=(len(scenarios) * len(is_los) * len(frequencies)),
+            )
+        ) as progress_bar:
             res = joblib.Parallel(n_jobs=10)(
-                joblib.delayed(fit_ftr_to_reference)(df, params_comb, num_search_grid_params, num_refinements) for params_comb in product(scenarios, is_los, frequencies))
+                joblib.delayed(fit_ftr_to_reference)(
+                    df, params_comb, num_search_grid_params, num_refinements
+                )
+                for params_comb in product(scenarios, is_los, frequencies)
+            )
 
         with open(fit_out_fname, "w", encoding="utf-8") as f:
             f.write("scen\tcond\tfc\tsigma\tk\tdelta\tm\n")
@@ -504,64 +559,61 @@
                 f.write(line)
 
     if output_ns3_table:
-
         # Load the fit results
-        fit = pd.read_csv(fit_out_fname, delimiter='\t')
+        fit = pd.read_csv(fit_out_fname, delimiter="\t")
 
         # Output the C++ data structure
         print_cplusplus_map_from_fit_results(fit, c_plus_plus_out_fname)
 
     if plot_fit_results:
-
         # Set Seaborn defaults and setup output folder
-        sns.set(rc={'figure.figsize': (7, 5)})
+        sns.set(rc={"figure.figsize": (7, 5)})
         sns.set_theme()
-        sns.set_style('darkgrid')
+        sns.set_style("darkgrid")
 
-        fit = pd.read_csv(fit_out_fname, delimiter='\t')
+        fit = pd.read_csv(fit_out_fname, delimiter="\t")
         # Create folder if it does not exist
         Path(figs_folder).mkdir(parents=True, exist_ok=True)
         ad_measures = []
 
         for params_comb in product(scenarios, is_los, frequencies):
-
-            data_query = 'scen == @params_comb[0] and cond == @params_comb[1] and fc == @params_comb[2]'
+            data_query = (
+                "scen == @params_comb[0] and cond == @params_comb[1] and fc == @params_comb[2]"
+            )
 
             # Load corresponding reference data
             ref_data = df.query(data_query)
 
             # Create FTR params object
             fit_line = fit.query(data_query)
-            assert(fit_line.reset_index().shape[0] == 1)
+            assert fit_line.reset_index().shape[0] == 1
             params = FtrParams()
-            params.m = fit_line.iloc[0]['m']
-            params.k = fit_line.iloc[0]['k']
-            params.delta = fit_line.iloc[0]['delta']
-            params.sigma = fit_line.iloc[0]['sigma']
+            params.m = fit_line.iloc[0]["m"]
+            params.k = fit_line.iloc[0]["k"]
+            params.delta = fit_line.iloc[0]["delta"]
+            params.sigma = fit_line.iloc[0]["sigma"]
 
             # Retrieve the corresponding FTR ECDF
             ftr_ecdf = get_ftr_ecdf(params, len(ref_data), db=True)
 
             # Compute the AD measure
-            ad_meas = compute_anderson_darling_measure(
-                np.sort(ref_data['gain']), ftr_ecdf)
+            ad_meas = compute_anderson_darling_measure(np.sort(ref_data["gain"]), ftr_ecdf)
             ad_measures.append(np.sqrt(ad_meas))
 
-            sns.ecdfplot(data=ref_data, x='gain',
-                         label='38.901 reference model')
-            sns.ecdfplot(
-                ftr_ecdf, label=f'Fitted FTR, sqrt(AD)={round(np.sqrt(ad_meas), 2)}')
-            plt.xlabel(
-                'End-to-end channel gain due to small scale fading [dB]')
+            sns.ecdfplot(data=ref_data, x="gain", label="38.901 reference model")
+            sns.ecdfplot(ftr_ecdf, label=f"Fitted FTR, sqrt(AD)={round(np.sqrt(ad_meas), 2)}")
+            plt.xlabel("End-to-end channel gain due to small scale fading [dB]")
             plt.legend()
             plt.savefig(
-                f'{figs_folder}{params_comb[0]}_{params_comb[1]}_{params_comb[2]/1e9}GHz_fit.png', dpi=500, bbox_inches='tight')
+                f"{figs_folder}{params_comb[0]}_{params_comb[1]}_{params_comb[2]/1e9}GHz_fit.png",
+                dpi=500,
+                bbox_inches="tight",
+            )
             plt.clf()
 
         # Plot ECDF of the scaled and normalized AD measures
-        sns.ecdfplot(ad_measures, label='AD measures')
-        plt.xlabel('Anderson-Darling goodness-of-fit')
+        sns.ecdfplot(ad_measures, label="AD measures")
+        plt.xlabel("Anderson-Darling goodness-of-fit")
         plt.legend()
-        plt.savefig(f'{figs_folder}AD_measures.png',
-                    dpi=500, bbox_inches='tight')
+        plt.savefig(f"{figs_folder}AD_measures.png", dpi=500, bbox_inches="tight")
         plt.clf()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/stats/CMakeLists.txt ns-3.41/src/stats/CMakeLists.txt
--- ns-3.40/src/stats/CMakeLists.txt	2023-09-27 13:30:37.878839700 -0700
+++ ns-3.41/src/stats/CMakeLists.txt	2024-02-09 14:44:08.501794300 -0800
@@ -1,6 +1,6 @@
 set(sqlite_sources)
 set(sqlite_headers)
-set(private_sqlite_header)
+set(private_sqlite_headers)
 set(sqlite_libraries)
 if(${ENABLE_SQLITE})
   set(sqlite_sources
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/CMakeLists.txt ns-3.41/src/tap-bridge/CMakeLists.txt
--- ns-3.40/src/tap-bridge/CMakeLists.txt	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/CMakeLists.txt	2024-02-09 14:44:08.505794300 -0800
@@ -28,10 +28,7 @@
     helper/tap-bridge-helper.h
     model/tap-bridge.h
     model/tap-encode-decode.h
-  LIBRARIES_TO_LINK
-    ${libcore}
-    ${libinternet}
-    ${libnetwork}
+  LIBRARIES_TO_LINK ${libinternet}
 )
 
 add_definitions(
@@ -41,6 +38,7 @@
   EXECNAME tap-creator
   SOURCE_FILES model/tap-creator.cc
                model/tap-encode-decode.cc
+  LIBRARIES_TO_LINK ${libnetwork}
   EXECUTABLE_DIRECTORY_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/src/tap-bridge/
   INSTALL_DIRECTORY_PATH ${CMAKE_INSTALL_LIBEXECDIR}/ns3
   STANDALONE
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/examples/tap-csma-virtual-machine.py ns-3.41/src/tap-bridge/examples/tap-csma-virtual-machine.py
--- ns-3.40/src/tap-bridge/examples/tap-csma-virtual-machine.py	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/examples/tap-csma-virtual-machine.py	2024-02-09 14:44:08.505794300 -0800
@@ -18,10 +18,17 @@
 
 import sys
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
-def main(argv):
 
+def main(argv):
     ns.core.CommandLine().Parse(argv)
 
     #
@@ -29,7 +36,9 @@
     # interact in real-time and therefore we have to use the real-time simulator
     # and take the time to calculate checksums.
     #
-    ns.core.GlobalValue.Bind("SimulatorImplementationType", ns.core.StringValue("ns3::RealtimeSimulatorImpl"))
+    ns.core.GlobalValue.Bind(
+        "SimulatorImplementationType", ns.core.StringValue("ns3::RealtimeSimulatorImpl")
+    )
     ns.core.GlobalValue.Bind("ChecksumEnabled", ns.core.BooleanValue(True))
 
     #
@@ -38,7 +47,7 @@
     # the right side.
     #
     nodes = ns.network.NodeContainer()
-    nodes.Create (2)
+    nodes.Create(2)
 
     #
     # Use a CsmaHelper to get a CSMA channel created, and the needed net
@@ -57,24 +66,25 @@
     # for this configuration.
     #
     tapBridge = ns.tap_bridge.TapBridgeHelper()
-    tapBridge.SetAttribute ("Mode", ns.core.StringValue ("UseLocal"))
-    tapBridge.SetAttribute ("DeviceName", ns.core.StringValue ("tap-left"))
-    tapBridge.Install (nodes.Get (0), devices.Get (0))
+    tapBridge.SetAttribute("Mode", ns.core.StringValue("UseLocal"))
+    tapBridge.SetAttribute("DeviceName", ns.core.StringValue("tap-left"))
+    tapBridge.Install(nodes.Get(0), devices.Get(0))
 
     #
     # Connect the right side tap to the right side wifi device on the right-side
     # ghost node.
     #
-    tapBridge.SetAttribute ("DeviceName", ns.core.StringValue ("tap-right"))
-    tapBridge.Install (nodes.Get (1), devices.Get (1))
+    tapBridge.SetAttribute("DeviceName", ns.core.StringValue("tap-right"))
+    tapBridge.Install(nodes.Get(1), devices.Get(1))
 
     #
     # Run the simulation for ten minutes to give the user time to play around
     #
-    ns.core.Simulator.Stop (ns.core.Seconds (600))
-    ns.core.Simulator.Run()#signal_check_frequency = -1
+    ns.core.Simulator.Stop(ns.core.Seconds(600))
+    ns.core.Simulator.Run()  # signal_check_frequency = -1
     ns.core.Simulator.Destroy()
     return 0
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     sys.exit(main(sys.argv))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/examples/tap-wifi-dumbbell.cc ns-3.41/src/tap-bridge/examples/tap-wifi-dumbbell.cc
--- ns-3.40/src/tap-bridge/examples/tap-wifi-dumbbell.cc	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/examples/tap-wifi-dumbbell.cc	2024-02-09 14:44:08.505794300 -0800
@@ -144,7 +144,7 @@
     Ssid ssid = Ssid("left");
     WifiHelper wifi;
     WifiMacHelper wifiMac;
-    wifi.SetRemoteStationManager("ns3::ArfWifiManager");
+    wifi.SetStandard(WIFI_STANDARD_80211a);
 
     wifiMac.SetType("ns3::ApWifiMac", "Ssid", SsidValue(ssid));
     NetDeviceContainer devicesLeft = wifi.Install(wifiPhy, wifiMac, nodesLeft.Get(0));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/examples/tap-wifi-virtual-machine.py ns-3.41/src/tap-bridge/examples/tap-wifi-virtual-machine.py
--- ns-3.40/src/tap-bridge/examples/tap-wifi-virtual-machine.py	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/examples/tap-wifi-virtual-machine.py	2024-02-09 14:44:08.525794500 -0800
@@ -17,10 +17,18 @@
 #
 
 import sys
-from ns import ns
 
-def main(argv):
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
+
 
+def main(argv):
     ns.core.CommandLine().Parse(argv)
 
     #
@@ -28,7 +36,9 @@
     # interact in real-time and therefore we have to use the real-time simulator
     # and take the time to calculate checksums.
     #
-    ns.core.GlobalValue.Bind("SimulatorImplementationType", ns.core.StringValue("ns3::RealtimeSimulatorImpl"))
+    ns.core.GlobalValue.Bind(
+        "SimulatorImplementationType", ns.core.StringValue("ns3::RealtimeSimulatorImpl")
+    )
     ns.core.GlobalValue.Bind("ChecksumEnabled", ns.core.BooleanValue(True))
 
     #
@@ -37,20 +47,22 @@
     # the right side.
     #
     nodes = ns.network.NodeContainer()
-    nodes.Create (2);
+    nodes.Create(2)
 
     #
     # We're going to use 802.11 A so set up a wifi helper to reflect that.
     #
     wifi = ns.wifi.WifiHelper()
-    wifi.SetStandard (ns.wifi.WIFI_STANDARD_80211a);
-    wifi.SetRemoteStationManager ("ns3::ConstantRateWifiManager", "DataMode", ns.core.StringValue ("OfdmRate54Mbps"));
+    wifi.SetStandard(ns.wifi.WIFI_STANDARD_80211a)
+    wifi.SetRemoteStationManager(
+        "ns3::ConstantRateWifiManager", "DataMode", ns.core.StringValue("OfdmRate54Mbps")
+    )
 
     #
     # No reason for pesky access points, so we'll use an ad-hoc network.
     #
     wifiMac = ns.wifi.WifiMacHelper()
-    wifiMac.SetType ("ns3::AdhocWifiMac");
+    wifiMac.SetType("ns3::AdhocWifiMac")
 
     #
     # Configure the physical layer.
@@ -73,7 +85,7 @@
     positionAlloc.Add(ns.core.Vector(0.0, 0.0, 0.0))
     positionAlloc.Add(ns.core.Vector(5.0, 0.0, 0.0))
     mobility.SetPositionAllocator(positionAlloc)
-    mobility.SetMobilityModel ("ns3::ConstantPositionMobilityModel")
+    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel")
     mobility.Install(nodes)
 
     #
@@ -85,25 +97,25 @@
     # for this configuration.
     #
     tapBridge = ns.tap_bridge.TapBridgeHelper()
-    tapBridge.SetAttribute ("Mode", ns.core.StringValue ("UseLocal"));
-    tapBridge.SetAttribute ("DeviceName", ns.core.StringValue ("tap-left"));
-    tapBridge.Install (nodes.Get (0), devices.Get (0));
+    tapBridge.SetAttribute("Mode", ns.core.StringValue("UseLocal"))
+    tapBridge.SetAttribute("DeviceName", ns.core.StringValue("tap-left"))
+    tapBridge.Install(nodes.Get(0), devices.Get(0))
 
     #
     # Connect the right side tap to the right side wifi device on the right-side
     # ghost node.
     #
-    tapBridge.SetAttribute ("DeviceName", ns.core.StringValue ("tap-right"));
-    tapBridge.Install (nodes.Get (1), devices.Get (1));
+    tapBridge.SetAttribute("DeviceName", ns.core.StringValue("tap-right"))
+    tapBridge.Install(nodes.Get(1), devices.Get(1))
 
     #
     # Run the simulation for ten minutes to give the user time to play around
     #
-    ns.core.Simulator.Stop (ns.core.Seconds (600));
-    ns.core.Simulator.Run()#signal_check_frequency = -1
+    ns.core.Simulator.Stop(ns.core.Seconds(600))
+    ns.core.Simulator.Run()  # signal_check_frequency = -1
     ns.core.Simulator.Destroy()
     return 0
 
-if __name__ == '__main__':
-    sys.exit(main(sys.argv))
 
+if __name__ == "__main__":
+    sys.exit(main(sys.argv))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/model/tap-bridge.cc ns-3.41/src/tap-bridge/model/tap-bridge.cc
--- ns-3.40/src/tap-bridge/model/tap-bridge.cc	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/model/tap-bridge.cc	2024-02-09 14:44:08.525794500 -0800
@@ -135,7 +135,7 @@
             .AddAttribute("Mode",
                           "The operating and configuration mode to use.",
                           EnumValue(USE_LOCAL),
-                          MakeEnumAccessor(&TapBridge::SetMode),
+                          MakeEnumAccessor<Mode>(&TapBridge::SetMode),
                           MakeEnumChecker(CONFIGURE_LOCAL,
                                           "ConfigureLocal",
                                           USE_LOCAL,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/model/tap-bridge.h ns-3.41/src/tap-bridge/model/tap-bridge.h
--- ns-3.40/src/tap-bridge/model/tap-bridge.h	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/model/tap-bridge.h	2024-02-09 14:44:08.525794500 -0800
@@ -459,7 +459,7 @@
      * whether or not ns-3 is connected to the underlying TAP device
      * with a file descriptor.
      */
-    bool m_linkUp;
+    bool m_linkUp{false};
 
     /**
      * Flag indicating whether or not the link is up.  In this case,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/model/tap-creator.cc ns-3.41/src/tap-bridge/model/tap-creator.cc
--- ns-3.40/src/tap-bridge/model/tap-creator.cc	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/model/tap-creator.cc	2024-02-09 14:44:08.525794500 -0800
@@ -17,6 +17,8 @@
 
 #include "tap-encode-decode.h"
 
+#include "ns3/mac48-address.h"
+
 #include <cerrno>
 #include <cstdlib>
 #include <cstring> // for strerror
@@ -61,89 +63,6 @@
         ABORT(msg, printErrno);                                                                    \
     }
 
-//
-// Lots of the following helper code taken from corresponding functions in src/node.
-//
-#define ASCII_DOT (0x2e)
-#define ASCII_ZERO (0x30)
-#define ASCII_a (0x41)
-#define ASCII_z (0x5a)
-#define ASCII_A (0x61)
-#define ASCII_Z (0x7a)
-#define ASCII_COLON (0x3a)
-
-static char
-AsciiToLowCase(char c)
-{
-    if (c >= ASCII_a && c <= ASCII_z)
-    {
-        return c;
-    }
-    else if (c >= ASCII_A && c <= ASCII_Z)
-    {
-        return c + (ASCII_a - ASCII_A);
-    }
-    else
-    {
-        return c;
-    }
-}
-
-static uint32_t
-AsciiToIpv4(const char* address)
-{
-    uint32_t host = 0;
-    while (true)
-    {
-        uint8_t byte = 0;
-        while (*address != ASCII_DOT && *address != 0)
-        {
-            byte *= 10;
-            byte += *address - ASCII_ZERO;
-            address++;
-        }
-        host <<= 8;
-        host |= byte;
-        if (*address == 0)
-        {
-            break;
-        }
-        address++;
-    }
-    return host;
-}
-
-static void
-AsciiToMac48(const char* str, uint8_t addr[6])
-{
-    int i = 0;
-    while (*str != 0 && i < 6)
-    {
-        uint8_t byte = 0;
-        while (*str != ASCII_COLON && *str != 0)
-        {
-            byte <<= 4;
-            char low = AsciiToLowCase(*str);
-            if (low >= ASCII_a)
-            {
-                byte |= low - ASCII_a + 10;
-            }
-            else
-            {
-                byte |= low - ASCII_ZERO;
-            }
-            str++;
-        }
-        addr[i] = byte;
-        i++;
-        if (*str == 0)
-        {
-            break;
-        }
-        str++;
-    }
-}
-
 static sockaddr
 CreateInetAddress(uint32_t networkOrder)
 {
@@ -326,7 +245,7 @@
     // Set the hardware (MAC) address of the new device
     //
     ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h
-    AsciiToMac48(mac, (uint8_t*)ifr.ifr_hwaddr.sa_data);
+    ns3::Mac48Address(mac).CopyTo((uint8_t*)ifr.ifr_hwaddr.sa_data);
     status = ioctl(tap, SIOCSIFHWADDR, &ifr);
     ABORT_IF(status == -1, "Could not set MAC address", true);
     LOG("Set device MAC address to " << mac);
@@ -346,7 +265,7 @@
     //
     // Set the IP address of the new interface/device.
     //
-    ifr.ifr_addr = CreateInetAddress(AsciiToIpv4(ip));
+    ifr.ifr_addr = CreateInetAddress(ns3::Ipv4Address(ip).Get());
     status = ioctl(fd, SIOCSIFADDR, &ifr);
     ABORT_IF(status == -1, "Could not set IP address", true);
     LOG("Set device IP address to " << ip);
@@ -354,7 +273,7 @@
     //
     // Set the net mask of the new interface/device
     //
-    ifr.ifr_netmask = CreateInetAddress(AsciiToIpv4(netmask));
+    ifr.ifr_netmask = CreateInetAddress(ns3::Ipv4Mask(netmask).Get());
     status = ioctl(fd, SIOCSIFNETMASK, &ifr);
     ABORT_IF(status == -1, "Could not set net mask", true);
     LOG("Set device Net Mask to " << netmask);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/tap-bridge/test/examples-to-run.py ns-3.41/src/tap-bridge/test/examples-to-run.py
--- ns-3.40/src/tap-bridge/test/examples-to-run.py	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/tap-bridge/test/examples-to-run.py	2024-02-09 14:44:08.525794500 -0800
@@ -7,7 +7,7 @@
 #
 # See test.py for more information.
 cpp_examples = [
-    ("tap-wifi-dumbbell", "False", "True"), # Requires manual configuration
+    ("tap-wifi-dumbbell", "False", "True"),  # Requires manual configuration
 ]
 
 # A list of Python examples to run in order to ensure that they remain
@@ -17,6 +17,6 @@
 #
 # See test.py for more information.
 python_examples = [
-    ("tap-csma-virtual-machine.py", "False"), # requires enable-sudo
-    ("tap-wifi-virtual-machine.py", "False"), # requires enable-sudo
+    ("tap-csma-virtual-machine.py", "False"),  # requires enable-sudo
+    ("tap-wifi-virtual-machine.py", "False"),  # requires enable-sudo
 ]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/test/CMakeLists.txt ns-3.41/src/test/CMakeLists.txt
--- ns-3.40/src/test/CMakeLists.txt	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/test/CMakeLists.txt	2024-02-09 14:44:08.525794500 -0800
@@ -22,6 +22,7 @@
   )
     # cmake-format: off
     set(applications_sources
+        ns3tcp/ns3tcp-cubic-test-suite.cc
         ns3tcp/ns3tcp-loss-test-suite.cc
         ns3tcp/ns3tcp-no-delay-test-suite.cc
         ns3tcp/ns3tcp-socket-test-suite.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/test/ns3tcp/ns3tcp-cubic-test-suite.cc ns-3.41/src/test/ns3tcp/ns3tcp-cubic-test-suite.cc
--- ns-3.40/src/test/ns3tcp/ns3tcp-cubic-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/test/ns3tcp/ns3tcp-cubic-test-suite.cc	2024-02-09 14:44:08.525794500 -0800
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2010 University of Washington (trace writing)
+ * Copyright (c) 2018-20 NITK Surathkal (topology setup)
+ * Copyright (c) 2024 Tom Henderson (test definition)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// Test suite based on tcp-bbr-example.cc modified topology setup:
+//
+//           1 Gbps            50/100Mbps         1 Gbps
+//  Sender -------------- R1 -------------- R2 -------------- Receiver
+//            5us               baseRtt/2          5us
+//
+// This is a test suite written initially to test the addition of
+// TCP friendliness but can also be extended for other Cubic tests.  By
+// changing some compile-time constants below, it can be used to also produce
+// gnuplots and pcaps that demonstrate the behavior.  See the WRITE_PCAP and
+// WRITE_GNUPLOT booleans below to enable this output.
+//
+// There are four cases configured presently.
+// 1. A Cubic TCP on a 5 ms RTT bottleneck, with no fast convergence nor
+//    TCP friendliness configured
+// 2. A Cubic TCP on a 5 ms RTT bottleneck, with fast convergence but no
+//    TCP friendliness configured.  This exhibits a different cwnd plot from 1.
+// 3. A Cubic TCP on a 20 ms RTT bottleneck, with fast convergence but no
+//    TCP friendliness configured.  There is a step change increase in
+//    bottleneck link capacity but the TCP is slow to exploit it.
+// 4. A Cubic TCP on a 20 ms RTT bottleneck, with fast convergence and
+//    TCP friendliness configured.  There is a step change increase in
+//    bottleneck link capacity and the TCP responds more quickly than in 3.
+
+#include "ns3/abort.h"
+#include "ns3/bulk-send-helper.h"
+#include "ns3/config.h"
+#include "ns3/data-rate.h"
+#include "ns3/error-model.h"
+#include "ns3/gnuplot.h"
+#include "ns3/inet-socket-address.h"
+#include "ns3/internet-stack-helper.h"
+#include "ns3/ipv4-address-helper.h"
+#include "ns3/ipv4-global-routing-helper.h"
+#include "ns3/log.h"
+#include "ns3/node-container.h"
+#include "ns3/output-stream-wrapper.h"
+#include "ns3/packet-sink-helper.h"
+#include "ns3/pcap-file.h"
+#include "ns3/point-to-point-helper.h"
+#include "ns3/point-to-point-net-device.h"
+#include "ns3/pointer.h"
+#include "ns3/simulator.h"
+#include "ns3/string.h"
+#include "ns3/tcp-header.h"
+#include "ns3/tcp-socket-factory.h"
+#include "ns3/test.h"
+#include "ns3/trace-helper.h"
+#include "ns3/traffic-control-helper.h"
+#include "ns3/uinteger.h"
+
+#include <iomanip>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("Ns3TcpCubicTest");
+
+static constexpr bool WRITE_PCAP = false;    //!< Set to true to write out pcap.
+static constexpr bool WRITE_GNUPLOT = false; //!< Set to true to write out gnuplot.
+
+/**
+ * \ingroup system-tests-tcp
+ */
+
+/**
+ * Add sample trace values to data structures
+ * \param gnuplotTimeSeries Gnuplot data structure
+ * \param timeSeries time series of cwnd changes
+ * \param oldval old value of cwnd
+ * \param newval new value of cwnd
+ */
+void
+CubicCwndTracer(Gnuplot2dDataset* gnuplotTimeSeries,
+                std::map<Time, double>* timeSeries,
+                uint32_t oldval,
+                uint32_t newval)
+{
+    // We store data in two structures because Gnuplot2DDataset data is not exportable
+    NS_LOG_DEBUG("cwnd " << newval / 1448.0);
+    gnuplotTimeSeries->Add(Simulator::Now().GetSeconds(), newval / 1448.0);
+    timeSeries->insert_or_assign(Simulator::Now(), newval / 1448.0);
+}
+
+/**
+ * Test Cubic response
+ */
+class Ns3TcpCubicTestCase : public TestCase
+{
+  public:
+    /**
+     * Constructor.
+     *
+     * \param testCase testcase name
+     * \param prefix filename prefix if writing output files
+     * \param fastConvergence whether to enable fast convergence
+     * \param tcpFriendliness whether to enable TCP friendliness
+     * \param baseRtt base RTT to use for test case
+     * \param capacityIncrease whether to trigger a sudden capacity increase
+     */
+    Ns3TcpCubicTestCase(std::string testCase,
+                        std::string prefix,
+                        bool fastConvergence,
+                        bool tcpFriendliness,
+                        Time baseRtt,
+                        bool capacityIncrease);
+    ~Ns3TcpCubicTestCase() override;
+
+  private:
+    void DoRun() override;
+
+    /**
+     * Connect TCP cwnd trace after socket is instantiated
+     *
+     * \param nodeId node ID to connect to
+     * \param socketId socket ID to connect to
+     */
+    void ConnectCwndTrace(uint32_t nodeId, uint32_t socketId);
+
+    /**
+     * Increases the device bandwidth to 100 Mbps
+     * \param device device to modify
+     */
+    void IncreaseBandwidth(Ptr<PointToPointNetDevice> device);
+
+    /**
+     * Check that time series values within a time range are within a value range.
+     * \param start start of time range
+     * \param end end of time range
+     * \param lowerBound lower bound of acceptable values
+     * \param upperBound upper bound of acceptable values
+     * \return true if values are within range
+     */
+    bool CheckValues(Time start, Time end, double lowerBound, double upperBound);
+
+    bool m_writeResults{WRITE_PCAP};     //!< Whether to write pcaps
+    bool m_writeGnuplot{WRITE_GNUPLOT};  //!< Whether to write gnuplot files
+    Gnuplot2dDataset m_cwndTimeSeries;   //!< cwnd time series
+    std::map<Time, double> m_timeSeries; //!< time series to check
+    std::string m_prefix;                //!< filename prefix if writing files
+    bool m_fastConvergence;              //!< whether to enable fast convergence
+    bool m_tcpFriendliness;              //!< whether to enable TCP friendliness
+    Time m_baseRtt;                      //!< the base RTT to use
+    bool m_capacityIncrease;             //!< whether to trigger a capacity increase
+};
+
+Ns3TcpCubicTestCase::Ns3TcpCubicTestCase(std::string testCase,
+                                         std::string prefix,
+                                         bool fastConvergence,
+                                         bool tcpFriendliness,
+                                         Time baseRtt,
+                                         bool capacityIncrease)
+    : TestCase(testCase),
+      m_prefix(prefix),
+      m_fastConvergence(fastConvergence),
+      m_tcpFriendliness(tcpFriendliness),
+      m_baseRtt(baseRtt),
+      m_capacityIncrease(capacityIncrease)
+{
+}
+
+Ns3TcpCubicTestCase::~Ns3TcpCubicTestCase()
+{
+}
+
+void
+Ns3TcpCubicTestCase::IncreaseBandwidth(Ptr<PointToPointNetDevice> device)
+{
+    device->SetDataRate(DataRate("100Mbps"));
+}
+
+void
+Ns3TcpCubicTestCase::ConnectCwndTrace(uint32_t nodeId, uint32_t socketId)
+{
+    Config::ConnectWithoutContext(
+        "/NodeList/" + std::to_string(nodeId) + "/$ns3::TcpL4Protocol/SocketList/" +
+            std::to_string(socketId) + "/CongestionWindow",
+        MakeBoundCallback(&CubicCwndTracer, &m_cwndTimeSeries, &m_timeSeries));
+}
+
+bool
+Ns3TcpCubicTestCase::CheckValues(Time start, Time end, double lowerBound, double upperBound)
+{
+    auto itStart = m_timeSeries.lower_bound(start);
+    auto itEnd = m_timeSeries.upper_bound(end);
+    for (auto it = itStart; it != itEnd; it++)
+    {
+        if (it->second < lowerBound || it->second > upperBound)
+        {
+            NS_LOG_DEBUG("Value " << it->second << " at time " << it->first.GetSeconds()
+                                  << " outside range (" << lowerBound << "," << upperBound << ")");
+            return false;
+        }
+    }
+    return true;
+}
+
+void
+Ns3TcpCubicTestCase::DoRun()
+{
+    NS_LOG_DEBUG("Running " << m_prefix);
+    // The maximum bandwidth delay product is 20 ms (base RTT) times 100 Mbps
+    // which works out to 250KB.  Setting the socket buffer sizes to four times
+    // that value (1MB) should be more than enough.
+    Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(1000000));
+    Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(1000000));
+    Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(10));
+    Config::SetDefault("ns3::TcpSocket::DelAckCount", UintegerValue(2));
+    Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(1448));
+    Config::SetDefault("ns3::TcpSocketState::EnablePacing", BooleanValue(true));
+    Config::SetDefault("ns3::TcpSocketState::PaceInitialWindow", BooleanValue(true));
+    Config::SetDefault("ns3::TcpCubic::FastConvergence", BooleanValue(m_fastConvergence));
+    Config::SetDefault("ns3::TcpCubic::TcpFriendliness", BooleanValue(m_tcpFriendliness));
+
+    Time stopTime = Seconds(20);
+
+    NodeContainer sender;
+    NodeContainer receiver;
+    NodeContainer routers;
+    sender.Create(1);
+    receiver.Create(1);
+    routers.Create(2);
+
+    // Create the point-to-point link helpers
+    PointToPointHelper bottleneckLink;
+    // With the DynamicQueueLimits setting below, the following statement
+    // will minimize the buffering in the device and force most buffering
+    // into the AQM
+    bottleneckLink.SetQueue("ns3::DropTailQueue", "MaxSize", StringValue("2p"));
+    if (m_capacityIncrease)
+    {
+        // Start at a lower capacity, and increase later
+        bottleneckLink.SetDeviceAttribute("DataRate", StringValue("50Mbps"));
+    }
+    else
+    {
+        bottleneckLink.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
+    }
+    bottleneckLink.SetChannelAttribute("Delay", TimeValue(m_baseRtt / 2));
+
+    PointToPointHelper edgeLink;
+    edgeLink.SetDeviceAttribute("DataRate", StringValue("1000Mbps"));
+    edgeLink.SetChannelAttribute("Delay", StringValue("5us"));
+
+    // Create NetDevice containers
+    NetDeviceContainer senderEdge = edgeLink.Install(sender.Get(0), routers.Get(0));
+    NetDeviceContainer r1r2 = bottleneckLink.Install(routers.Get(0), routers.Get(1));
+    NetDeviceContainer receiverEdge = edgeLink.Install(routers.Get(1), receiver.Get(0));
+
+    // Install Stack
+    InternetStackHelper internet;
+    internet.Install(sender);
+    internet.Install(receiver);
+    internet.Install(routers);
+
+    // Configure the root queue discipline
+    TrafficControlHelper tch;
+    tch.SetRootQueueDisc("ns3::FqCoDelQueueDisc");
+    tch.SetQueueLimits("ns3::DynamicQueueLimits");
+
+    tch.Install(senderEdge);
+    tch.Install(receiverEdge);
+
+    // Assign IP addresses
+    Ipv4AddressHelper ipv4;
+    ipv4.SetBase("10.0.0.0", "255.255.255.0");
+
+    Ipv4InterfaceContainer i1i2 = ipv4.Assign(r1r2);
+    ipv4.NewNetwork();
+    Ipv4InterfaceContainer is1 = ipv4.Assign(senderEdge);
+    ipv4.NewNetwork();
+    Ipv4InterfaceContainer ir1 = ipv4.Assign(receiverEdge);
+
+    // Populate routing tables
+    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
+
+    // Select sender side port
+    uint16_t port = 50001;
+
+    // Install application on the sender
+    BulkSendHelper source("ns3::TcpSocketFactory", InetSocketAddress(ir1.GetAddress(1), port));
+    source.SetAttribute("MaxBytes", UintegerValue(0));
+    ApplicationContainer sourceApps = source.Install(sender.Get(0));
+    sourceApps.Start(Seconds(0.1));
+    // Hook trace source after application starts
+    Simulator::Schedule(Seconds(0.1) + MilliSeconds(1),
+                        &Ns3TcpCubicTestCase::ConnectCwndTrace,
+                        this,
+                        0,
+                        0);
+    sourceApps.Stop(stopTime);
+
+    // Install application on the receiver
+    PacketSinkHelper sink("ns3::TcpSocketFactory", InetSocketAddress(Ipv4Address::GetAny(), port));
+    ApplicationContainer sinkApps = sink.Install(receiver.Get(0));
+    sinkApps.Start(Seconds(0.0));
+    sinkApps.Stop(stopTime);
+
+    if (m_capacityIncrease)
+    {
+        // Double the capacity of the bottleneck link at 10 seconds
+        Ptr<PointToPointNetDevice> device = r1r2.Get(0)->GetObject<PointToPointNetDevice>();
+        Simulator::Schedule(Seconds(10), &Ns3TcpCubicTestCase::IncreaseBandwidth, this, device);
+    }
+
+    std::ostringstream oss;
+    oss << m_prefix;
+    if (m_writeResults)
+    {
+        edgeLink.EnablePcap(oss.str(), senderEdge.Get(0));
+        edgeLink.EnablePcap(oss.str(), receiverEdge.Get(0));
+        edgeLink.EnableAscii(oss.str(), senderEdge.Get(0));
+        edgeLink.EnableAscii(oss.str(), receiverEdge.Get(0));
+    }
+    Simulator::Stop(Seconds(30));
+    Simulator::Run();
+
+    if (m_writeGnuplot)
+    {
+        std::ofstream outfile(oss.str() + ".plt");
+
+        std::string capacityIncreaseString;
+        if (m_capacityIncrease)
+        {
+            capacityIncreaseString = " capacity increase = 1";
+        }
+        Gnuplot gnuplot = Gnuplot(
+            oss.str() + ".eps",
+            "Cubic concave/convex response (" + std::to_string(m_baseRtt.GetMilliSeconds()) +
+                " ms RTT, 1448 byte segs, 100 Mbps bottleneck)\\nFast convergence = " +
+                std::to_string(m_fastConvergence) +
+                " friendliness = " + std::to_string(m_tcpFriendliness) + capacityIncreaseString);
+        gnuplot.SetTerminal("post eps color enhanced");
+        gnuplot.SetLegend("Time (seconds)", "Cwnd (segments)");
+        m_cwndTimeSeries.SetTitle("");
+        gnuplot.AddDataset(m_cwndTimeSeries);
+        gnuplot.GenerateOutput(outfile);
+    }
+
+    // Check that cwnd values are within tolerance of expected values
+    // The expected values were determined by inspecting the plots generated
+    if (m_prefix == "ns3-tcp-cubic-no-heuristic")
+    {
+        // Check overall min and max
+        NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(1), Seconds(19), 50, 90),
+                              true,
+                              "cwnd outside range");
+        // Time just before a reduction does not have much variation
+        NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(9), Seconds(9.7), 84, 89),
+                              true,
+                              "cwnd outside range");
+    }
+    else if (m_prefix == "ns3-tcp-cubic-fast-conv")
+    {
+        // Check overall min and max
+        NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(1), Seconds(19), 50, 90),
+                              true,
+                              "cwnd outside range");
+        // Initial convex region does not have much variation
+        NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(4), Seconds(6), 83, 88),
+                              true,
+                              "cwnd outside range");
+    }
+    else if (m_prefix == "ns3-tcp-cubic-no-friendly")
+    {
+        // Between time 12 and 16, cwnd should be fairly constant
+        // because without TCP friendliness, Cubic does not respond quickly
+        NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(12), Seconds(16), 107, 123),
+                              true,
+                              "cwnd outside range");
+        // After time 19.5, cwnd should have grown much higher
+        NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(19.5), Seconds(20), 180, 210),
+                              true,
+                              "cwnd outside range");
+    }
+    else if (m_prefix == "ns3-tcp-cubic-friendly")
+    {
+        // In contrast to previous case, cwnd should grow above 150 much sooner
+        NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(12), Seconds(14), 150, 210),
+                              true,
+                              "cwnd outside range");
+    }
+
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup tcp-cubic-tests
+ * TestSuite for module tcp-cubic
+ */
+class Ns3TcpCubicTestSuite : public TestSuite
+{
+  public:
+    Ns3TcpCubicTestSuite();
+};
+
+Ns3TcpCubicTestSuite::Ns3TcpCubicTestSuite()
+    : TestSuite("ns3-tcp-cubic", UNIT)
+{
+    // Test Cubic with no fast convergence or TCP friendliness enabled
+    // This results in a cwnd plot that has only the concave portion of
+    // returning cwnd to Wmax
+    AddTestCase(new Ns3TcpCubicTestCase("no heuristic",
+                                        "ns3-tcp-cubic-no-heuristic",
+                                        false,
+                                        false,
+                                        MilliSeconds(5),
+                                        false),
+                TestCase::QUICK);
+
+    // Test Cubic with fast convergence but no TCP friendliness enabled
+    // This results in a cwnd plot that has concave and convex regions, as
+    // well as convex-only regions; also observed on Linux testbeds
+    AddTestCase(new Ns3TcpCubicTestCase("fast convergence on",
+                                        "ns3-tcp-cubic-fast-conv",
+                                        true,
+                                        false,
+                                        MilliSeconds(5),
+                                        false),
+                TestCase::QUICK);
+
+    // Test Cubic with fast convergence but no TCP friendliness enabled
+    // with a higher RTT (20ms) and a step change in capacity at time 10s.
+    // This results in a sluggish response by TCP Cubic to use the new capacity.
+    AddTestCase(new Ns3TcpCubicTestCase("fast convergence on, Reno friendliness off",
+                                        "ns3-tcp-cubic-no-friendly",
+                                        true,
+                                        false,
+                                        MilliSeconds(20),
+                                        true),
+                TestCase::QUICK);
+
+    // Test Cubic with fast convergence but with TCP friendliness enabled
+    // with a higher RTT (20ms) and a step change in capacity at time 10s.
+    // This results in a faster response by TCP Cubic to use the new capacity.
+    AddTestCase(new Ns3TcpCubicTestCase("fast convergence on, Reno friendliness on",
+                                        "ns3-tcp-cubic-friendly",
+                                        true,
+                                        true,
+                                        MilliSeconds(20),
+                                        true),
+                TestCase::QUICK);
+}
+
+/**
+ * \ingroup tcp-cubic-tests
+ * Static variable for test initialization
+ */
+static Ns3TcpCubicTestSuite ns3TcpCubicTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/test/ns3wifi/wifi-issue-211-test-suite.cc ns-3.41/src/test/ns3wifi/wifi-issue-211-test-suite.cc
--- ns-3.40/src/test/ns3wifi/wifi-issue-211-test-suite.cc	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/test/ns3wifi/wifi-issue-211-test-suite.cc	2024-02-09 14:44:08.529794500 -0800
@@ -33,6 +33,7 @@
 #include "ns3/string.h"
 #include "ns3/test.h"
 #include "ns3/udp-client-server-helper.h"
+#include "ns3/udp-server.h"
 #include "ns3/wifi-net-device.h"
 
 using namespace ns3;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/topology-read/doc/topology.h ns-3.41/src/topology-read/doc/topology.h
--- ns-3.40/src/topology-read/doc/topology.h	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/topology-read/doc/topology.h	2024-02-09 14:44:08.529794500 -0800
@@ -14,7 +14,8 @@
  * Currently there are three models:
  * - ns3::OrbisTopologyReader for Orbis 0.7 traces
  * (https://web.archive.org/web/20181102004219/http://sysnet.ucsd.edu/~pmahadevan/topo_research/topo.html)
- * - ns3::InetTopologyReader for Inet 3.0 traces (http://topology.eecs.umich.edu/inet/)
+ * - ns3::InetTopologyReader for Inet 3.0 traces
+ * (https://web.archive.org/web/20210308100536/http://topology.eecs.umich.edu/inet/)
  * - ns3::RocketfuelTopologyReader for Rocketfuel traces
  * (http://www.cs.washington.edu/research/networking/rocketfuel/)
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/topology-read/doc/topology.rst ns-3.41/src/topology-read/doc/topology.rst
--- ns-3.40/src/topology-read/doc/topology.rst	2023-09-27 13:30:37.882839700 -0700
+++ ns-3.41/src/topology-read/doc/topology.rst	2024-02-09 14:44:08.529794500 -0800
@@ -53,7 +53,7 @@
 Examples can be found in the directory ``src/topology-read/examples/``
 
 .. _Orbis: https://web.archive.org/web/20181102004219/http://sysnet.ucsd.edu/~pmahadevan/topo_research/topo.html
-.. _Inet: http://topology.eecs.umich.edu/inet/
+.. _Inet: https://web.archive.org/web/20210308100536/http://topology.eecs.umich.edu/inet/
 .. _RocketFuel: http://www.cs.washington.edu/research/networking/rocketfuel/
 .. _Archipelago: http://www.caida.org/projects/ark/
 .. _Measurements: http://data.caida.org/datasets/topology/ipv4.allpref24-aslinks/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/topology-read/model/inet-topology-reader.h ns-3.41/src/topology-read/model/inet-topology-reader.h
--- ns-3.40/src/topology-read/model/inet-topology-reader.h	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/topology-read/model/inet-topology-reader.h	2024-02-09 14:44:08.529794500 -0800
@@ -43,7 +43,7 @@
  * the information needed to build the topology
  * (i.e.number of nodes, links and links structure).
  * It have been tested with Inet 3.0
- * http://topology.eecs.umich.edu/inet/
+ * https://web.archive.org/web/20210308100536/http://topology.eecs.umich.edu/inet/
  *
  * It might set a link attribute named "Weight", corresponding to
  * the euclidean distance between two nodes, the nodes being randomly positioned.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/CMakeLists.txt ns-3.41/src/traffic-control/CMakeLists.txt
--- ns-3.40/src/traffic-control/CMakeLists.txt	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/CMakeLists.txt	2024-02-09 14:44:08.529794500 -0800
@@ -36,9 +36,7 @@
     model/red-queue-disc.h
     model/tbf-queue-disc.h
     model/traffic-control-layer.h
-  LIBRARIES_TO_LINK
-    ${libnetwork}
-    ${libcore}
+  LIBRARIES_TO_LINK ${libnetwork}
   TEST_SOURCES
     test/adaptive-red-queue-disc-test-suite.cc
     test/cobalt-queue-disc-test-suite.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/doc/fq-cobalt.rst ns-3.41/src/traffic-control/doc/fq-cobalt.rst
--- ns-3.40/src/traffic-control/doc/fq-cobalt.rst	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/doc/fq-cobalt.rst	2024-02-09 14:44:08.533794600 -0800
@@ -32,7 +32,7 @@
 References
 ==========
 
-.. [Pal19]  J. Palmei, S. Gupta, P. Imputato, J. Morton, M. Tahiliani, S. Avallone, and D. Taht, Design and Evaluation of COBALT Queue Discipline, 2019 IEEE International Symposium on Local and Metropolitan Area Networks (LANMAN), Paris, France, 2019.
+.. [Pal19] J. Palmei, S. Gupta, P. Imputato, J. Morton, M. Tahiliani, S. Avallone, and D. Taht, Design and Evaluation of COBALT Queue Discipline, 2019 IEEE International Symposium on Local and Metropolitan Area Networks (LANMAN), Paris, France, 2019.
 
 .. [Hoe18] T. Hoiland-Jørgensen, D. Taht and J. Morton, "Piece of CAKE: A Comprehensive Queue Management Solution for Home Gateways," 2018 IEEE International Symposium on Local and Metropolitan Area Networks (LANMAN), Washington, DC, USA, 2018.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/doc/red.rst ns-3.41/src/traffic-control/doc/red.rst
--- ns-3.40/src/traffic-control/doc/red.rst	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/doc/red.rst	2024-02-09 14:44:08.533794600 -0800
@@ -201,4 +201,4 @@
 **********
 
 The RED model has been validated and the report is currently stored
-at: https://github.com/downloads/talau/ns-3-tcp-red/report-red-ns3.pdf
+at: https://gitlab.com/nsnam/ns-3-dev/uploads/6937dac2431a0b61209728ecbfd442ac/report-red-ns3.pdf
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/doc/tbf.rst ns-3.41/src/traffic-control/doc/tbf.rst
--- ns-3.40/src/traffic-control/doc/tbf.rst	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/doc/tbf.rst	2024-02-09 14:44:08.533794600 -0800
@@ -81,7 +81,7 @@
 
 .. [Ref1] A. Kuznetsov and D. Torokhov; Linux Cross Reference Source Code; Available online at `<https://raw.githubusercontent.com/torvalds/linux/8efd0d9c316af470377894a6a0f9ff63ce18c177/net/sched/sch_tbf.c>`_.
 
-.. [Ref2] J. Vehent; Journey to the Center of the Linux Kernel: Traffic Control, Shaping and QoS; Available online at `<http://wiki.linuxwall.info/doku.php/en:resources:dossiers:networking:traffic_control#tbf_-_token_bucket_filter>`_.
+.. [Ref2] J. Vehent; Journey to the Center of the Linux Kernel: Traffic Control, Shaping and QoS; Available online at `<https://web.archive.org/web/20190930031243/http://wiki.linuxwall.info/doku.php/en:ressources:dossiers:networking:traffic_control#tbf_-_token_bucket_filter>`_.
 
 .. [Ref3] Practical IP Network QoS: TBF queuing discipline; Available online at `<https://web.archive.org/web/20200516025221/http://web.opalsoft.net/qos/default.php>`_.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc ns-3.41/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc
--- ns-3.40/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc	2024-02-09 14:44:08.533794600 -0800
@@ -47,7 +47,7 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
+#include "ns3/config-store.h"
 #include "ns3/core-module.h"
 #include "ns3/enum.h"
 #include "ns3/error-model.h"
@@ -260,7 +260,7 @@
  *
  * \param remoteAddress Remote address.
  * \param sender Sender node.
- * \param pktSize Pakcet size.
+ * \param pktSize Packet size.
  * \param stopTime Stop time.
  */
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/model/codel-queue-disc.cc ns-3.41/src/traffic-control/model/codel-queue-disc.cc
--- ns-3.40/src/traffic-control/model/codel-queue-disc.cc	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/model/codel-queue-disc.cc	2024-02-09 14:44:08.533794600 -0800
@@ -18,8 +18,9 @@
  * Based on ns2 simulation code presented by Kathie Nichols
  *
  * This port based on linux kernel code by
- * Authors: Dave Täht <d@taht.net>
- *          Eric Dumazet <edumazet@google.com>
+ * Authors:
+ *   Dave Täht <d@taht.net>
+ *   Eric Dumazet <edumazet@google.com>
  *
  * Ported to ns-3 by: Andrew McGregor <andrewmcgr@gmail.com>
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/model/codel-queue-disc.h ns-3.41/src/traffic-control/model/codel-queue-disc.h
--- ns-3.40/src/traffic-control/model/codel-queue-disc.h	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/model/codel-queue-disc.h	2024-02-09 14:44:08.533794600 -0800
@@ -18,8 +18,9 @@
  * Based on ns2 simulation code presented by Kathie Nichols
  *
  * This port based on linux kernel code by
- * Authors: Dave Täht <d@taht.net>
- *          Eric Dumazet <edumazet@google.com>
+ * Authors:
+ *   Dave Täht <d@taht.net>
+ *   Eric Dumazet <edumazet@google.com>
  *
  * Ported to ns-3 by: Andrew McGregor <andrewmcgr@gmail.com>
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/model/queue-disc.cc ns-3.41/src/traffic-control/model/queue-disc.cc
--- ns-3.40/src/traffic-control/model/queue-disc.cc	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/model/queue-disc.cc	2024-02-09 14:44:08.537794600 -0800
@@ -512,7 +512,7 @@
 }
 
 QueueSize
-QueueDisc::GetCurrentSize()
+QueueDisc::GetCurrentSize() const
 {
     NS_LOG_FUNCTION(this);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/model/queue-disc.h ns-3.41/src/traffic-control/model/queue-disc.h
--- ns-3.40/src/traffic-control/model/queue-disc.h	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/model/queue-disc.h	2024-02-09 14:44:08.537794600 -0800
@@ -335,7 +335,7 @@
      *
      * \returns The queue disc size in bytes or packets.
      */
-    QueueSize GetCurrentSize();
+    QueueSize GetCurrentSize() const;
 
     /**
      * \brief Retrieve all the collected statistics.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/model/red-queue-disc.cc ns-3.41/src/traffic-control/model/red-queue-disc.cc
--- ns-3.40/src/traffic-control/model/red-queue-disc.cc	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/model/red-queue-disc.cc	2024-02-09 14:44:08.537794600 -0800
@@ -651,7 +651,7 @@
 }
 
 // Check if packet p needs to be dropped due to probability mark
-uint32_t
+bool
 RedQueueDisc::DropEarly(Ptr<QueueDiscItem> item, uint32_t qSize)
 {
     NS_LOG_FUNCTION(this << item << qSize);
@@ -673,7 +673,7 @@
         if ((double)qSize < fraction * m_qAvg)
         {
             // Queue could have been empty for 0.05 seconds
-            return 0;
+            return false;
         }
     }
 
@@ -706,10 +706,10 @@
         m_countBytes = 0;
         /// \todo Implement set bit to mark
 
-        return 1; // drop
+        return true; // drop
     }
 
-    return 0; // no drop/mark
+    return false; // no drop/mark
 }
 
 // Returns a probability using these function parameters for the DropEarly function
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/model/red-queue-disc.h ns-3.41/src/traffic-control/model/red-queue-disc.h
--- ns-3.40/src/traffic-control/model/red-queue-disc.h	2023-09-27 13:30:37.886839600 -0700
+++ ns-3.41/src/traffic-control/model/red-queue-disc.h	2024-02-09 14:44:08.537794600 -0800
@@ -244,9 +244,9 @@
      * \brief Check if a packet needs to be dropped due to probability mark
      * \param item queue item
      * \param qSize queue size
-     * \returns 0 for no drop/mark, 1 for drop
+     * \returns false for no drop/mark, true for drop
      */
-    uint32_t DropEarly(Ptr<QueueDiscItem> item, uint32_t qSize);
+    bool DropEarly(Ptr<QueueDiscItem> item, uint32_t qSize);
     /**
      * \brief Returns a probability using these function parameters for the DropEarly function
      * \returns Prob. of packet drop before "count"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/traffic-control/test/examples-to-run.py ns-3.41/src/traffic-control/test/examples-to-run.py
--- ns-3.40/src/traffic-control/test/examples-to-run.py	2023-09-27 13:30:37.890839800 -0700
+++ ns-3.41/src/traffic-control/test/examples-to-run.py	2024-02-09 14:44:08.537794600 -0800
@@ -21,7 +21,11 @@
     ("adaptive-red-tests --testNumber=13", "True", "True"),
     ("adaptive-red-tests --testNumber=14", "True", "True"),
     ("adaptive-red-tests --testNumber=15", "True", "True"),
-    ("codel-vs-pfifo-asymmetric --routerWanQueueDiscType=PfifoFast --simDuration=10", "True", "True"),
+    (
+        "codel-vs-pfifo-asymmetric --routerWanQueueDiscType=PfifoFast --simDuration=10",
+        "True",
+        "True",
+    ),
     ("codel-vs-pfifo-asymmetric --routerWanQueueDiscType=CoDel --simDuration=10", "True", "False"),
     ("codel-vs-pfifo-basic-test --queueDiscType=PfifoFast --simDuration=10", "True", "False"),
     ("codel-vs-pfifo-basic-test --queueDiscType=CoDel --simDuration=10", "True", "False"),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/uan/CMakeLists.txt ns-3.41/src/uan/CMakeLists.txt
--- ns-3.40/src/uan/CMakeLists.txt	2023-09-27 13:30:37.890839800 -0700
+++ ns-3.41/src/uan/CMakeLists.txt	2024-02-09 14:44:08.537794600 -0800
@@ -48,10 +48,8 @@
     model/uan-transducer-hd.h
     model/uan-transducer.h
     model/uan-tx-mode.h
-  LIBRARIES_TO_LINK
-    ${libnetwork}
-    ${libmobility}
-    ${libenergy}
+  LIBRARIES_TO_LINK ${libmobility}
+                    ${libenergy}
   TEST_SOURCES test/uan-energy-model-test.cc
                test/uan-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/uan/model/uan-mac-rc.h ns-3.41/src/uan/model/uan-mac-rc.h
--- ns-3.40/src/uan/model/uan-mac-rc.h	2023-09-27 13:30:37.890839800 -0700
+++ ns-3.41/src/uan/model/uan-mac-rc.h	2024-02-09 14:44:08.557794800 -0800
@@ -125,7 +125,7 @@
     /**
      * Set the reservation transmitted state.
      *
-     * \param t True if resevation has been transmitted.
+     * \param t True if reservation has been transmitted.
      */
     void SetTransmitted(bool t = true);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/CMakeLists.txt ns-3.41/src/visualizer/CMakeLists.txt
--- ns-3.40/src/visualizer/CMakeLists.txt	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/CMakeLists.txt	2024-02-09 14:44:08.561794800 -0800
@@ -18,15 +18,9 @@
   HEADER_FILES model/pyviz.h
   LIBRARIES_TO_LINK
     ${python_libraries}
-    ${libcore}
     ${libinternet}
     ${libwifi}
     ${libpoint-to-point}
-    ${libcsma}
-    ${libbridge}
-    ${libwimax}
-    ${liblte}
-    ${libmesh}
 )
 
 # move visualizer folder to build/bindings/python, which allows us to add only
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/model/visual-simulator-impl.cc ns-3.41/src/visualizer/model/visual-simulator-impl.cc
--- ns-3.40/src/visualizer/model/visual-simulator-impl.cc	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/model/visual-simulator-impl.cc	2024-02-09 14:44:08.561794800 -0800
@@ -140,10 +140,10 @@
     m_simulator->Stop();
 }
 
-void
+EventId
 VisualSimulatorImpl::Stop(const Time& delay)
 {
-    m_simulator->Stop(delay);
+    return m_simulator->Stop(delay);
 }
 
 //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/model/visual-simulator-impl.h ns-3.41/src/visualizer/model/visual-simulator-impl.h
--- ns-3.40/src/visualizer/model/visual-simulator-impl.h	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/model/visual-simulator-impl.h	2024-02-09 14:44:08.561794800 -0800
@@ -56,7 +56,7 @@
     void Destroy() override;
     bool IsFinished() const override;
     void Stop() override;
-    void Stop(const Time& delay) override;
+    EventId Stop(const Time& delay) override;
     EventId Schedule(const Time& delay, EventImpl* event) override;
     void ScheduleWithContext(uint32_t context, const Time& delay, EventImpl* event) override;
     EventId ScheduleNow(EventImpl* event) override;
@@ -81,11 +81,6 @@
     void NotifyConstructionCompleted() override;
 
   private:
-    /**
-     * Get the simulator implementation
-     * \return a pointer to the simulator implementation
-     */
-    Ptr<SimulatorImpl> GetSim();
     Ptr<SimulatorImpl> m_simulator;       ///< the simulator implementation
     ObjectFactory m_simulatorImplFactory; ///< simulator implementation factory
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/base.py ns-3.41/src/visualizer/visualizer/base.py
--- ns-3.40/src/visualizer/visualizer/base.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/base.py	2024-02-09 14:44:08.561794800 -0800
@@ -1,8 +1,10 @@
-from gi.repository import GObject
 import os.path
 import sys
 
-PIXELS_PER_METER = 3.0 # pixels-per-meter, at 100% zoom level
+from gi.repository import GObject
+
+PIXELS_PER_METER = 3.0  # pixels-per-meter, at 100% zoom level
+
 
 ## PyVizObject class
 class PyVizObject(GObject.GObject):
@@ -16,6 +18,7 @@
     def tooltip_query(self, tooltip):
         tooltip.set_text("TODO: tooltip for %r" % self)
 
+
 ## Link class
 class Link(PyVizObject):
     pass
@@ -29,6 +32,7 @@
     def update(self):
         raise NotImplementedError
 
+
 ## NetDeviceTraits class
 class NetDeviceTraits(object):
     ## class variables
@@ -47,6 +51,7 @@
         self.is_wireless = is_wireless
         self.is_virtual = is_virtual
 
+
 netdevice_traits = {
     ns.PointToPointNetDevice: NetDeviceTraits(is_wireless=False),
     ns.CsmaNetDevice: NetDeviceTraits(is_wireless=False),
@@ -60,36 +65,42 @@
     ns.LteEnbNetDevice: NetDeviceTraits(is_wireless=True),
 }
 
+
 def lookup_netdevice_traits(class_type):
     try:
         return netdevice_traits[class_type]
     except KeyError:
-        sys.stderr.write("WARNING: no NetDeviceTraits registered for device type %r; "
-                         "I will assume this is a non-virtual wireless device, "
-                         "but you should edit %r, variable 'netdevice_traits',"
-                         " to make sure.\n" % (class_type.__name__, __file__))
+        sys.stderr.write(
+            "WARNING: no NetDeviceTraits registered for device type %r; "
+            "I will assume this is a non-virtual wireless device, "
+            "but you should edit %r, variable 'netdevice_traits',"
+            " to make sure.\n" % (class_type.__name__, __file__)
+        )
         t = NetDeviceTraits(is_virtual=False, is_wireless=True)
         netdevice_traits[class_type] = t
         return t
 
+
 def transform_distance_simulation_to_canvas(d):
-    return d*PIXELS_PER_METER
+    return d * PIXELS_PER_METER
+
 
 def transform_point_simulation_to_canvas(x, y):
-    return x*PIXELS_PER_METER, y*PIXELS_PER_METER
+    return x * PIXELS_PER_METER, y * PIXELS_PER_METER
 
-def transform_distance_canvas_to_simulation(d):
-    return d/PIXELS_PER_METER
 
-def transform_point_canvas_to_simulation(x, y):
-    return x/PIXELS_PER_METER, y/PIXELS_PER_METER
+def transform_distance_canvas_to_simulation(d):
+    return d / PIXELS_PER_METER
 
 
+def transform_point_canvas_to_simulation(x, y):
+    return x / PIXELS_PER_METER, y / PIXELS_PER_METER
 
 
 plugins = []
 plugin_modules = {}
 
+
 def register_plugin(plugin_init_func, plugin_name=None, plugin_module=None):
     """
     Register a plugin.
@@ -102,18 +113,21 @@
     if plugin_module is not None:
         plugin_modules[plugin_name] = plugin_module
 
+
 plugins_loaded = False
+
+
 def load_plugins():
     global plugins_loaded
     if plugins_loaded:
         return
     plugins_loaded = True
-    plugins_dir = os.path.join(os.path.dirname(__file__), 'plugins')
+    plugins_dir = os.path.join(os.path.dirname(__file__), "plugins")
     old_path = list(sys.path)
     sys.path.insert(0, plugins_dir)
     for filename in os.listdir(plugins_dir):
         name, ext = os.path.splitext(filename)
-        if ext != '.py':
+        if ext != ".py":
             continue
         try:
             plugin_module = __import__(name)
@@ -125,7 +139,6 @@
         except AttributeError:
             print("Plugin %r has no 'register' function" % name, file=sys.stderr)
         else:
-            #print("Plugin %r registered" % name, file=sys.stderr)
+            # print("Plugin %r registered" % name, file=sys.stderr)
             register_plugin(plugin_func, name, plugin_module)
     sys.path = old_path
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/core.py ns-3.41/src/visualizer/visualizer/core.py
--- ns-3.40/src/visualizer/visualizer/core.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/core.py	2024-02-09 14:44:08.561794800 -0800
@@ -1,10 +1,12 @@
 # -*- Mode: python; coding: utf-8 -*-
 from ctypes import c_double
 
-LAYOUT_ALGORITHM = 'neato' # ['neato'|'dot'|'twopi'|'circo'|'fdp'|'nop']
+LAYOUT_ALGORITHM = "neato"  # ['neato'|'dot'|'twopi'|'circo'|'fdp'|'nop']
 REPRESENT_CHANNELS_AS_NODES = 1
-DEFAULT_NODE_SIZE = 1.0 # default node size in meters
-DEFAULT_TRANSMISSIONS_MEMORY = 5 # default number of of past intervals whose transmissions are remembered
+DEFAULT_NODE_SIZE = 1.0  # default node size in meters
+DEFAULT_TRANSMISSIONS_MEMORY = (
+    5  # default number of of past intervals whose transmissions are remembered
+)
 BITRATE_FONT_SIZE = 10
 
 # internal constants, normally not meant to be changed
@@ -12,8 +14,9 @@
 PRIORITY_UPDATE_MODEL = -100
 PRIORITY_UPDATE_VIEW = 200
 
-import warnings
 import platform
+import warnings
+
 if platform.system() == "Windows":
     SHELL_FONT = "Lucida Console 9"
 else:
@@ -52,16 +55,12 @@
     svgitem = None
 
 try:
-    gi.require_version('GooCanvas', '2.0')
-    gi.require_version('Gtk', '3.0')
-    gi.require_version('Gdk', '3.0')
+    gi.require_version("GooCanvas", "2.0")
+    gi.require_version("Gtk", "3.0")
+    gi.require_version("Gdk", "3.0")
     gi.require_foreign("cairo")
-    from gi.repository import GObject
-    from gi.repository import GLib
-    from gi.repository import Gtk
-    from gi.repository import Gdk
-    from gi.repository import Pango
-    from gi.repository import GooCanvas
+    from gi.repository import Gdk, GLib, GObject, GooCanvas, Gtk, Pango
+
     from . import hud
 except ImportError as e:
     _import_error = e
@@ -73,13 +72,24 @@
 except ImportError:
     ipython_view = None
 
-from .base import InformationWindow, PyVizObject, Link, lookup_netdevice_traits, PIXELS_PER_METER
-from .base import transform_distance_simulation_to_canvas, transform_point_simulation_to_canvas
-from .base import transform_distance_canvas_to_simulation, transform_point_canvas_to_simulation
-from .base import load_plugins, register_plugin, plugins
+from .base import (
+    PIXELS_PER_METER,
+    InformationWindow,
+    Link,
+    PyVizObject,
+    load_plugins,
+    lookup_netdevice_traits,
+    plugins,
+    register_plugin,
+    transform_distance_canvas_to_simulation,
+    transform_distance_simulation_to_canvas,
+    transform_point_canvas_to_simulation,
+    transform_point_simulation_to_canvas,
+)
+
+PI_OVER_2 = math.pi / 2
+PI_TIMES_2 = math.pi * 2
 
-PI_OVER_2 = math.pi/2
-PI_TIMES_2 = math.pi*2
 
 ## Node class
 class Node(PyVizObject):
@@ -117,12 +127,16 @@
     #  highlighted property
     ## @var selected
     #  selected property
+    ## @var on_enter_notify_event
+    #  on_enter_notify_event function
+    ## @var on_leave_notify_event
+    #  on_leave_notify_event function
 
     ## signal emitted whenever a tooltip is about to be shown for the node
     ## the first signal parameter is a python list of strings, to which
     ## information can be appended
     __gsignals__ = {
-        'query-extra-tooltip-info': (GObject.SignalFlags.RUN_LAST, None, (object,)),
+        "query-extra-tooltip-info": (GObject.SignalFlags.RUN_LAST, None, (object,)),
     }
 
     def __init__(self, visualizer, node_index):
@@ -141,7 +155,7 @@
         self._has_mobility = None
         self._selected = False
         self._highlighted = False
-        self._color = 0x808080ff
+        self._color = 0x808080FF
         self._size = DEFAULT_NODE_SIZE
         self.canvas_item.connect("enter-notify-event", self.on_enter_notify_event)
         self.canvas_item.connect("leave-notify-event", self.on_leave_notify_event)
@@ -152,7 +166,7 @@
         self._label = None
         self._label_canvas_item = None
 
-        self._update_appearance() # call this last
+        self._update_appearance()  # call this last
 
     def set_svg_icon(self, file_base_name, width=None, height=None, align_x=0.5, align_y=0.5):
         """!
@@ -191,9 +205,9 @@
         if height is not None:
             self.svg_item.props.height = transform_distance_simulation_to_canvas(height)
 
-        #threshold1 = 10.0/self.svg_item.props.height
-        #threshold2 = 10.0/self.svg_item.props.width
-        #self.svg_item.props.visibility_threshold = min(threshold1, threshold2)
+        # threshold1 = 10.0/self.svg_item.props.height
+        # threshold2 = 10.0/self.svg_item.props.width
+        # self.svg_item.props.visibility_threshold = min(threshold1, threshold2)
 
         self.svg_align_x = align_x
         self.svg_align_y = align_y
@@ -224,9 +238,9 @@
         """
         w = self.svg_item.width
         h = self.svg_item.height
-        self.svg_item.set_properties(x=(x - (1-self.svg_align_x)*w),
-                                     y=(y - (1-self.svg_align_y)*h))
-
+        self.svg_item.set_properties(
+            x=(x - (1 - self.svg_align_x) * w), y=(y - (1 - self.svg_align_y) * h)
+        )
 
     def tooltip_query(self, tooltip):
         """!
@@ -242,52 +256,62 @@
             ipv4 = ns.cppyy.gbl.getNodeIpv4(ns3_node)
             ipv6 = ns.cppyy.gbl.getNodeIpv6(ns3_node)
 
-            name = '<b><u>Node %i</u></b>' % self.node_index
-            node_name = ns.Names.FindName (ns3_node)
-            if len(node_name)!=0:
-                name += ' <b>(' + node_name + ')</b>'
+            name = "<b><u>Node %i</u></b>" % self.node_index
+            node_name = ns.Names.FindName(ns3_node)
+            if len(node_name) != 0:
+                name += " <b>(" + node_name + ")</b>"
 
             lines = [name]
-            lines.append('')
+            lines.append("")
 
             self.emit("query-extra-tooltip-info", lines)
 
             mob = ns.cppyy.gbl.hasMobilityModel(ns3_node)
             if mob:
                 mobility_model_name = ns.cppyy.gbl.getMobilityModelName(ns3_node)
-                lines.append('  <b>Mobility Model</b>: %s' % ns.cppyy.gbl.getMobilityModelName(ns3_node))
+                lines.append(
+                    "  <b>Mobility Model</b>: %s" % ns.cppyy.gbl.getMobilityModelName(ns3_node)
+                )
 
             for devI in range(ns3_node.GetNDevices()):
-                lines.append('')
-                lines.append('  <u>NetDevice %i:</u>' % devI)
+                lines.append("")
+                lines.append("  <u>NetDevice %i:</u>" % devI)
                 dev = ns3_node.GetDevice(devI)
                 name = ns.Names.FindName(dev)
                 if name:
-                    lines.append('    <b>Name:</b> %s' % name)
+                    lines.append("    <b>Name:</b> %s" % name)
                 devname = dev.GetInstanceTypeId().GetName()
-                lines.append('    <b>Type:</b> %s' % devname)
+                lines.append("    <b>Type:</b> %s" % devname)
 
                 if ipv4 is not None:
                     ipv4_idx = ipv4.GetInterfaceForDevice(dev)
                     if ipv4_idx != -1:
                         addresses = [
-                            '%s/%s' % (ipv4.GetAddress(ipv4_idx, i).GetLocal(),
-                                       ipv4.GetAddress(ipv4_idx, i).GetMask())
-                            for i in range(ipv4.GetNAddresses(ipv4_idx))]
-                        lines.append('    <b>IPv4 Addresses:</b> %s' % '; '.join(addresses))
+                            "%s/%s"
+                            % (
+                                ipv4.GetAddress(ipv4_idx, i).GetLocal(),
+                                ipv4.GetAddress(ipv4_idx, i).GetMask(),
+                            )
+                            for i in range(ipv4.GetNAddresses(ipv4_idx))
+                        ]
+                        lines.append("    <b>IPv4 Addresses:</b> %s" % "; ".join(addresses))
 
                 if ipv6 is not None:
                     ipv6_idx = ipv6.GetInterfaceForDevice(dev)
                     if ipv6_idx != -1:
                         addresses = [
-                            '%s/%s' % (ipv6.GetAddress(ipv6_idx, i).GetAddress(),
-                                       ipv6.GetAddress(ipv6_idx, i).GetPrefix())
-                            for i in range(ipv6.GetNAddresses(ipv6_idx))]
-                        lines.append('    <b>IPv6 Addresses:</b> %s' % '; '.join(addresses))
+                            "%s/%s"
+                            % (
+                                ipv6.GetAddress(ipv6_idx, i).GetAddress(),
+                                ipv6.GetAddress(ipv6_idx, i).GetPrefix(),
+                            )
+                            for i in range(ipv6.GetNAddresses(ipv6_idx))
+                        ]
+                        lines.append("    <b>IPv6 Addresses:</b> %s" % "; ".join(addresses))
 
-                lines.append('    <b>MAC Address:</b> %s' % (dev.GetAddress(),))
+                lines.append("    <b>MAC Address:</b> %s" % (dev.GetAddress(),))
 
-            tooltip.set_markup('\n'.join(lines))
+            tooltip.set_markup("\n".join(lines))
         finally:
             self.visualizer.simulation.lock.release()
 
@@ -327,6 +351,7 @@
         """
         self._selected = value
         self._update_appearance()
+
     def _get_selected(self):
         """!
         Get selected function.
@@ -348,6 +373,7 @@
         """
         self._highlighted = value
         self._update_appearance()
+
     def _get_highlighted(self):
         """!
         Get highlighted function.
@@ -382,33 +408,37 @@
         if self.svg_item is not None:
             alpha = 0x80
         else:
-            alpha = 0xff
-        fill_color_rgba = (self._color & 0xffffff00) | alpha
-        self.canvas_item.set_properties(radius_x=size, radius_y=size,
-                                        fill_color_rgba=fill_color_rgba)
+            alpha = 0xFF
+        fill_color_rgba = (self._color & 0xFFFFFF00) | alpha
+        self.canvas_item.set_properties(
+            radius_x=size, radius_y=size, fill_color_rgba=fill_color_rgba
+        )
         if self._selected:
-            line_width = size*.3
+            line_width = size * 0.3
         else:
-            line_width = size*.15
+            line_width = size * 0.15
         if self.highlighted:
-            stroke_color = 'yellow'
+            stroke_color = "yellow"
         else:
-            stroke_color = 'black'
+            stroke_color = "black"
         self.canvas_item.set_properties(line_width=line_width, stroke_color=stroke_color)
 
         if self._label is not None:
             if self._label_canvas_item is None:
-                self._label_canvas_item = GooCanvas.CanvasText(visibility_threshold=0.5,
-                                                               font="Sans Serif 10",
-                                                               fill_color_rgba=0x808080ff,
-                                                               alignment=Pango.Alignment.CENTER,
-                                                               anchor=GooCanvas.CanvasAnchorType.N,
-                                                               parent=self.visualizer.canvas.get_root_item(),
-                                                               pointer_events=GooCanvas.CanvasPointerEvents.NONE)
+                self._label_canvas_item = GooCanvas.CanvasText(
+                    visibility_threshold=0.5,
+                    font="Sans Serif 10",
+                    fill_color_rgba=0x808080FF,
+                    alignment=Pango.Alignment.CENTER,
+                    anchor=GooCanvas.CanvasAnchorType.N,
+                    parent=self.visualizer.canvas.get_root_item(),
+                    pointer_events=GooCanvas.CanvasPointerEvents.NONE,
+                )
                 self._label_canvas_item.lower(None)
 
-            self._label_canvas_item.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
-                                                   text=self._label)
+            self._label_canvas_item.set_properties(
+                visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD, text=self._label
+            )
             self._update_position()
 
     def set_position(self, x, y):
@@ -429,7 +459,7 @@
             link.update_points()
 
         if self._label_canvas_item is not None:
-            self._label_canvas_item.set_properties(x=x, y=(y+self._size*3))
+            self._label_canvas_item.set_properties(x=x, y=(y + self._size * 3))
 
         # If the location of the point is now beyond the bounds of the
         # canvas then those bounds now need to be increased
@@ -458,7 +488,10 @@
         @param self: class object.
         @return x and y position
         """
-        return (self.canvas_item.get_property("center_x"), self.canvas_item.get_property("center_y"))
+        return (
+            self.canvas_item.get_property("center_x"),
+            self.canvas_item.get_property("center_y"),
+        )
 
     def _update_position(self):
         """!
@@ -480,7 +513,12 @@
         """
         if isinstance(color, str):
             color = Gdk.color_parse(color)
-            color = ((color.red>>8) << 24) | ((color.green>>8) << 16) | ((color.blue>>8) << 8) | 0xff
+            color = (
+                ((color.red >> 8) << 24)
+                | ((color.green >> 8) << 16)
+                | ((color.blue >> 8) << 8)
+                | 0xFF
+            )
         self._color = color
         self._update_appearance()
 
@@ -537,11 +575,15 @@
         @param channel: channel.
         """
         self.channel = channel
-        self.canvas_item = GooCanvas.CanvasEllipse(radius_x=30, radius_y=30,
-                                                   fill_color="white",
-                                                   stroke_color="grey", line_width=2.0,
-                                                   line_dash=GooCanvas.CanvasLineDash.newv([10.0, 10.0 ]),
-                                                   visibility=GooCanvas.CanvasItemVisibility.VISIBLE)
+        self.canvas_item = GooCanvas.CanvasEllipse(
+            radius_x=30,
+            radius_y=30,
+            fill_color="white",
+            stroke_color="grey",
+            line_width=2.0,
+            line_dash=GooCanvas.CanvasLineDash.newv([10.0, 10.0]),
+            visibility=GooCanvas.CanvasItemVisibility.VISIBLE,
+        )
         self.canvas_item.pyviz_object = self
         self.links = []
 
@@ -567,7 +609,10 @@
         @param self: class object.
         @return x / y position.
         """
-        return (self.canvas_item.get_property("center_x"), self.canvas_item.get_property("center_y"))
+        return (
+            self.canvas_item.get_property("center_x"),
+            self.canvas_item.get_property("center_y"),
+        )
 
 
 ## WiredLink
@@ -633,11 +678,11 @@
         """
         super(SimulationThread, self).__init__()
         assert isinstance(viz, Visualizer)
-        self.viz = viz # Visualizer object
+        self.viz = viz  # Visualizer object
         self.lock = threading.Lock()
         self.go = threading.Event()
         self.go.clear()
-        self.target_time = 0 # in seconds
+        self.target_time = 0  # in seconds
         self.quit = False
         self.sim_helper = ns.PyViz()
         self.pause_messages = []
@@ -664,30 +709,31 @@
         @return none
         """
         while not self.quit:
-            #print "sim: Wait for go"
-            self.go.wait() # wait until the main (view) thread gives us the go signal
+            # print "sim: Wait for go"
+            self.go.wait()  # wait until the main (view) thread gives us the go signal
             self.go.clear()
             if self.quit:
                 break
-            #self.go.clear()
-            #print "sim: Acquire lock"
+            # self.go.clear()
+            # print "sim: Acquire lock"
             self.lock.acquire()
             try:
                 if 0:
                     if ns3.Simulator.IsFinished():
                         self.viz.play_button.set_sensitive(False)
                         break
-                #print "sim: Current time is %f; Run until: %f" % (ns3.Simulator.Now ().GetSeconds (), self.target_time)
-                #if ns3.Simulator.Now ().GetSeconds () > self.target_time:
+                # print "sim: Current time is %f; Run until: %f" % (ns3.Simulator.Now ().GetSeconds (), self.target_time)
+                # if ns3.Simulator.Now ().GetSeconds () > self.target_time:
                 #    print "skipping, model is ahead of view!"
                 self.sim_helper.SimulatorRunUntil(ns.Seconds(self.target_time))
-                #print "sim: Run until ended at current time: ", ns3.Simulator.Now ().GetSeconds ()
+                # print "sim: Run until ended at current time: ", ns3.Simulator.Now ().GetSeconds ()
                 self.pause_messages.extend(self.sim_helper.GetPauseMessages())
                 GLib.idle_add(self.viz.update_model, priority=PRIORITY_UPDATE_MODEL)
-                #print "sim: Run until: ", self.target_time, ": finished."
+                # print "sim: Run until: ", self.target_time, ": finished."
             finally:
                 self.lock.release()
-            #print "sim: Release lock, loop."
+            # print "sim: Release lock, loop."
+
 
 ## ShowTransmissionsMode
 class ShowTransmissionsMode(object):
@@ -700,10 +746,13 @@
 
     ## enumeration
     __slots__ = []
+
+
 ShowTransmissionsMode.ALL = ShowTransmissionsMode()
 ShowTransmissionsMode.NONE = ShowTransmissionsMode()
 ShowTransmissionsMode.SELECTED = ShowTransmissionsMode()
 
+
 ## Visualizer
 class Visualizer(GObject.GObject):
     ## @var INSTANCE
@@ -712,20 +761,22 @@
 
     if _import_error is None:
         __gsignals__ = {
-
             # signal emitted whenever a right-click-on-node popup menu is being constructed
-            'populate-node-menu': (GObject.SignalFlags.RUN_LAST, None, (object, Gtk.Menu,)),
-
+            "populate-node-menu": (
+                GObject.SignalFlags.RUN_LAST,
+                None,
+                (
+                    object,
+                    Gtk.Menu,
+                ),
+            ),
             # signal emitted after every simulation period (SAMPLE_PERIOD seconds of simulated time)
             # the simulation lock is acquired while the signal is emitted
-            'simulation-periodic-update': (GObject.SignalFlags.RUN_LAST, None, ()),
-
+            "simulation-periodic-update": (GObject.SignalFlags.RUN_LAST, None, ()),
             # signal emitted right after the topology is scanned
-            'topology-scanned': (GObject.SignalFlags.RUN_LAST, None, ()),
-
+            "topology-scanned": (GObject.SignalFlags.RUN_LAST, None, ()),
             # signal emitted when it's time to update the view objects
-            'update-view': (GObject.SignalFlags.RUN_LAST, None, ()),
-
+            "update-view": (GObject.SignalFlags.RUN_LAST, None, ()),
         }
 
     def __init__(self):
@@ -738,14 +789,14 @@
         assert Visualizer.INSTANCE is None
         Visualizer.INSTANCE = self
         super(Visualizer, self).__init__()
-        self.nodes =  {} # node index -> Node
-        self.channels = {} # id(ns3.Channel) -> Channel
-        self.window = None # toplevel window
-        self.canvas = None # GooCanvas.Canvas
-        self.time_label = None # Gtk.Label
-        self.play_button = None # Gtk.ToggleButton
-        self.zoom = None # Gtk.Adjustment
-        self._scrolled_window = None # Gtk.ScrolledWindow
+        self.nodes = {}  # node index -> Node
+        self.channels = {}  # id(ns3.Channel) -> Channel
+        self.window = None  # toplevel window
+        self.canvas = None  # GooCanvas.Canvas
+        self.time_label = None  # Gtk.Label
+        self.play_button = None  # Gtk.ToggleButton
+        self.zoom = None  # Gtk.Adjustment
+        self._scrolled_window = None  # Gtk.ScrolledWindow
 
         self.links_group = GooCanvas.CanvasGroup()
         self.channels_group = GooCanvas.CanvasGroup()
@@ -753,7 +804,7 @@
 
         self._update_timeout_id = None
         self.simulation = SimulationThread(self)
-        self.selected_node = None # node currently selected
+        self.selected_node = None  # node currently selected
         self.speed = 1.0
         self.information_windows = []
         self._transmission_arrows = []
@@ -811,9 +862,9 @@
         main_hbox1 = GObject.new(Gtk.HBox, border_width=8, visible=True)
         main_vbox.pack_start(main_hbox1, True, True, 0)
 
-        show_transmissions_group = GObject.new(Gtk.HeaderBar,
-                                               title="Show transmissions",
-                                               visible=True)
+        show_transmissions_group = GObject.new(
+            Gtk.HeaderBar, title="Show transmissions", visible=True
+        )
         main_hbox1.pack_start(show_transmissions_group, False, False, 8)
 
         vbox = Gtk.VBox(homogeneous=True, spacing=4)
@@ -841,16 +892,19 @@
         def toggled(radio):
             if radio.get_active():
                 self.set_show_transmissions_mode(ShowTransmissionsMode.ALL)
+
         all_nodes.connect("toggled", toggled)
 
         def toggled(radio):
             if radio.get_active():
                 self.set_show_transmissions_mode(ShowTransmissionsMode.NONE)
+
         no_node.connect("toggled", toggled)
 
         def toggled(radio):
             if radio.get_active():
                 self.set_show_transmissions_mode(ShowTransmissionsMode.SELECTED)
+
         selected_node.connect("toggled", toggled)
 
         # -- misc settings
@@ -866,9 +920,11 @@
         vbox.pack_start(GObject.new(Gtk.Label, label="Node Size", visible=True), True, True, 0)
         settings_hbox.pack_start(vbox, False, False, 6)
         self.node_size_adjustment = scale.get_adjustment()
+
         def node_size_changed(adj):
             for node in self.nodes.values():
                 node.set_size(adj.get_value())
+
         self.node_size_adjustment.connect("value-changed", node_size_changed)
         self.node_size_adjustment.set_lower(0.01)
         self.node_size_adjustment.set_upper(20)
@@ -879,14 +935,16 @@
         vbox = GObject.new(Gtk.VBox, border_width=0, visible=True)
         scale = GObject.new(Gtk.HScale, visible=True, digits=1)
         vbox.pack_start(scale, True, True, 0)
-        vbox.pack_start(GObject.new(Gtk.Label, label="Tx. Smooth Factor (s)", visible=True), True, True, 0)
+        vbox.pack_start(
+            GObject.new(Gtk.Label, label="Tx. Smooth Factor (s)", visible=True), True, True, 0
+        )
         settings_hbox.pack_start(vbox, False, False, 6)
         self.transmissions_smoothing_adjustment = scale.get_adjustment()
         adj = self.transmissions_smoothing_adjustment
         adj.set_lower(0.1)
         adj.set_upper(10)
         adj.set_step_increment(0.1)
-        adj.set_value(DEFAULT_TRANSMISSIONS_MEMORY*0.1)
+        adj.set_value(DEFAULT_TRANSMISSIONS_MEMORY * 0.1)
 
         return expander
 
@@ -894,7 +952,7 @@
     class _PanningState(object):
         ## @var __slots__
         #  internal variables
-        __slots__ = ['initial_mouse_pos', 'initial_canvas_pos', 'motion_signal']
+        __slots__ = ["initial_mouse_pos", "initial_canvas_pos", "motion_signal"]
 
     def _begin_panning(self, widget, event):
         """!
@@ -913,7 +971,9 @@
         x = self._scrolled_window.get_hadjustment().get_value()
         y = self._scrolled_window.get_vadjustment().get_value()
         self._panning_state.initial_canvas_pos = (x, y)
-        self._panning_state.motion_signal = self.canvas.connect("motion-notify-event", self._panning_motion)
+        self._panning_state.motion_signal = self.canvas.connect(
+            "motion-notify-event", self._panning_motion
+        )
 
     def _end_panning(self, event):
         """!
@@ -979,6 +1039,7 @@
 
     def get_hadjustment(self):
         return self._scrolled_window.get_hadjustment()
+
     def get_vadjustment(self):
         return self._scrolled_window.get_vadjustment()
 
@@ -996,7 +1057,8 @@
         self.canvas.props.has_tooltip = True
         self.canvas.connect("query-tooltip", self._canvas_tooltip_cb)
         self.canvas.show()
-        sw = Gtk.ScrolledWindow(); sw.show()
+        sw = Gtk.ScrolledWindow()
+        sw.show()
         self._scrolled_window = sw
         sw.add(self.canvas)
         vbox.pack_start(sw, True, True, 4)
@@ -1004,7 +1066,6 @@
         self.canvas.set_bounds(-10000, -10000, 10000, 10000)
         self.canvas.scroll_to(0, 0)
 
-
         self.canvas.get_root_item().add_child(self.links_group, -1)
         self.links_group.set_property("visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
 
@@ -1018,17 +1079,24 @@
 
         self.hud = hud.Axes(self)
 
-        hbox = Gtk.HBox(); hbox.show()
+        hbox = Gtk.HBox()
+        hbox.show()
         vbox.pack_start(hbox, False, False, 4)
 
         # zoom
-        zoom_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
-                                  step_increment=0.02,
-                                  page_increment=1.0,
-                                  page_size=1.0)
+        zoom_adj = Gtk.Adjustment(
+            value=1.0,
+            lower=0.01,
+            upper=10.0,
+            step_increment=0.02,
+            page_increment=1.0,
+            page_size=1.0,
+        )
         self.zoom = zoom_adj
+
         def _zoom_changed(adj):
             self.canvas.set_scale(adj.get_value())
+
         zoom_adj.connect("value-changed", _zoom_changed)
         zoom = Gtk.SpinButton.new(zoom_adj, 0.1, 1)
         zoom.set_digits(3)
@@ -1038,13 +1106,15 @@
         _zoom_changed(zoom_adj)
 
         # speed
-        speed_adj = Gtk.Adjustment(value=1.0, lower=0.01, upper=10.0,
-                                   step_increment=0.02,
-                                   page_increment=1.0, page_size=0)
+        speed_adj = Gtk.Adjustment(
+            value=1.0, lower=0.01, upper=10.0, step_increment=0.02, page_increment=1.0, page_size=0
+        )
+
         def _speed_changed(adj):
             self.speed = adj.get_value()
-            self.sample_period = SAMPLE_PERIOD*adj.get_value()
+            self.sample_period = SAMPLE_PERIOD * adj.get_value()
             self._start_update_timer()
+
         speed_adj.connect("value-changed", _speed_changed)
         speed = Gtk.SpinButton.new(speed_adj, 1, 0)
         speed.set_digits(3)
@@ -1059,10 +1129,13 @@
         hbox.pack_start(self.time_label, False, False, 4)
 
         # Screenshot button
-        screenshot_button = GObject.new(Gtk.Button,
-                                        label="Snapshot",
-                                        relief=Gtk.ReliefStyle.NONE, focus_on_click=False,
-                                        visible=True)
+        screenshot_button = GObject.new(
+            Gtk.Button,
+            label="Snapshot",
+            relief=Gtk.ReliefStyle.NONE,
+            focus_on_click=False,
+            visible=True,
+        )
         hbox.pack_start(screenshot_button, False, False, 4)
 
         def load_button_icon(button, icon_name):
@@ -1078,24 +1151,31 @@
 
         # Shell button
         if ipython_view is not None:
-            shell_button = GObject.new(Gtk.Button,
-                                       label="Shell",
-                                       relief=Gtk.ReliefStyle.NONE, focus_on_click=False,
-                                       visible=True)
+            shell_button = GObject.new(
+                Gtk.Button,
+                label="Shell",
+                relief=Gtk.ReliefStyle.NONE,
+                focus_on_click=False,
+                visible=True,
+            )
             hbox.pack_start(shell_button, False, False, 4)
             load_button_icon(shell_button, "gnome-terminal")
             shell_button.connect("clicked", self._start_shell)
 
         # Play button
-        self.play_button = GObject.new(Gtk.ToggleButton,
-                                       label="Simulate (F3)",
-                                       relief=Gtk.ReliefStyle.NONE, focus_on_click=False,
-                                       visible=True)
+        self.play_button = GObject.new(
+            Gtk.ToggleButton,
+            label="Simulate (F3)",
+            relief=Gtk.ReliefStyle.NONE,
+            focus_on_click=False,
+            visible=True,
+        )
         load_button_icon(self.play_button, "media-playback-start")
         accel_group = Gtk.AccelGroup()
         self.window.add_accel_group(accel_group)
-        self.play_button.add_accelerator("clicked", accel_group,
-                                         Gdk.KEY_F3, 0, Gtk.AccelFlags.VISIBLE)
+        self.play_button.add_accelerator(
+            "clicked", accel_group, Gdk.KEY_F3, 0, Gtk.AccelFlags.VISIBLE
+        )
         self.play_button.connect("toggled", self._on_play_button_toggled)
         hbox.pack_start(self.play_button, False, False, 4)
 
@@ -1125,7 +1205,10 @@
         for nodeI in range(ns.NodeList.GetNNodes()):
             seen_nodes += 1
             if seen_nodes == 100:
-                print("scan topology... %i nodes visited (%.1f%%)" % (nodeI, 100*nodeI/ns.NodeList.GetNNodes()))
+                print(
+                    "scan topology... %i nodes visited (%.1f%%)"
+                    % (nodeI, 100 * nodeI / ns.NodeList.GetNNodes())
+                )
                 seen_nodes = 0
             node = ns.NodeList.GetNode(nodeI)
             node_name = "Node %i" % nodeI
@@ -1136,7 +1219,7 @@
                 node_view.set_color("red")
                 pos = ns.cppyy.gbl.getNodePosition(node)
                 node_view.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
-                #print "node has mobility position -> ", "%f,%f" % (pos.x, pos.y)
+                # print "node has mobility position -> ", "%f,%f" % (pos.x, pos.y)
             else:
                 graph.add_node(node_name)
 
@@ -1181,12 +1264,12 @@
         print("scanning topology: calling graphviz layout")
         graph.layout(LAYOUT_ALGORITHM)
         for node in graph.iternodes():
-            #print node, "=>", node.attr['pos']
-            node_type, node_id = node.split(' ')
-            pos_x, pos_y = [float(s) for s in node.attr['pos'].split(',')]
-            if node_type == 'Node':
+            # print node, "=>", node.attr['pos']
+            node_type, node_id = node.split(" ")
+            pos_x, pos_y = [float(s) for s in node.attr["pos"].split(",")]
+            if node_type == "Node":
                 obj = self.nodes[int(node_id)]
-            elif node_type == 'Channel':
+            elif node_type == "Channel":
                 obj = self.channels[int(node_id)]
             obj.set_position(pos_x, pos_y)
 
@@ -1201,7 +1284,9 @@
             self.nodes[index] = node
             self.nodes_group.add_child(node.canvas_item, -1)
             node.canvas_item.connect("button-press-event", self.on_node_button_press_event, node)
-            node.canvas_item.connect("button-release-event", self.on_node_button_release_event, node)
+            node.canvas_item.connect(
+                "button-release-event", self.on_node_button_release_event, node
+            )
             return node
 
     def get_channel(self, ns3_channel):
@@ -1219,7 +1304,7 @@
         link.canvas_item.lower(None)
 
     def update_view(self):
-        #print "update_view"
+        # print "update_view"
 
         self.time_label.set_text("Time: %f s" % ns.Simulator.Now().GetSeconds())
 
@@ -1275,7 +1360,7 @@
             self.simulation.lock.release()
 
     def do_simulation_periodic_update(self):
-        smooth_factor = int(self.transmissions_smoothing_adjustment.get_value()*10)
+        smooth_factor = int(self.transmissions_smoothing_adjustment.get_value() * 10)
 
         transmissions = self.simulation.sim_helper.GetTransmissionSamples()
         self._last_transmissions.append(transmissions)
@@ -1291,17 +1376,13 @@
         hadj = self._scrolled_window.get_hadjustment()
         vadj = self._scrolled_window.get_vadjustment()
         bounds_x1, bounds_y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
-        bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(hadj.get_value() + hadj.get_page_size(),
-                                                               vadj.get_value() + vadj.get_page_size())
-        try:
-            pos1_x, pos1_y, pos2_x, pos2_y = ns.PyViz.LineClipping(bounds_x1, bounds_y1,
-                                                               bounds_x2, bounds_y2,
-                                                               pos1_x, pos1_y,
-                                                               pos2_x, pos2_y)
-        except:
-            res = (0,0,0,0)
-        pos1_x, pos1_y, pos2_x, pos2_y = res
-        return (pos1_x + pos2_x)/2, (pos1_y + pos2_y)/2
+        bounds_x2, bounds_y2 = self.canvas.convert_from_pixels(
+            hadj.get_value() + hadj.get_page_size(), vadj.get_value() + vadj.get_page_size()
+        )
+        ns.PyViz.LineClipping(
+            bounds_x1, bounds_y1, bounds_x2, bounds_y2, pos1_x, pos1_y, pos2_x, pos2_y
+        )
+        return (pos1_x.value + pos2_x.value) / 2, (pos1_y.value + pos2_y.value) / 2
 
     def _update_transmissions_view(self):
         transmissions_average = {}
@@ -1319,7 +1400,7 @@
             label.set_property("visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
         new_arrows = []
 
-        k = self.node_size_adjustment.get_value()/5
+        k = self.node_size_adjustment.get_value() / 5
 
         for (transmitter_id, receiver_id), (rx_bytes, rx_count) in transmissions_average.items():
             transmitter = self.get_node(transmitter_id)
@@ -1327,15 +1408,24 @@
             try:
                 arrow, label = old_arrows.pop()
             except IndexError:
-                arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0x00C000C0, close_path=False, end_arrow=True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
+                arrow = GooCanvas.CanvasPolyline(
+                    line_width=2.0,
+                    stroke_color_rgba=0x00C000C0,
+                    close_path=False,
+                    end_arrow=True,
+                    pointer_events=GooCanvas.CanvasPointerEvents.NONE,
+                )
                 arrow.set_property("parent", self.canvas.get_root_item())
                 arrow.raise_(None)
 
-                label = GooCanvas.CanvasText(parent=self.canvas.get_root_item(), pointer_events=GooCanvas.CanvasPointerEvents.NONE)
+                label = GooCanvas.CanvasText(
+                    parent=self.canvas.get_root_item(),
+                    pointer_events=GooCanvas.CanvasPointerEvents.NONE,
+                )
                 label.raise_(None)
 
             arrow.set_property("visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
-            line_width = max(0.1, math.log(float(rx_bytes)/rx_count/self.sample_period)*k)
+            line_width = max(0.1, math.log(float(rx_bytes) / rx_count / self.sample_period) * k)
             arrow.set_property("line-width", line_width)
 
             pos1_x, pos1_y = transmitter.get_position()
@@ -1345,40 +1435,49 @@
             points.set_point(1, pos2_x, pos2_y)
             arrow.set_property("points", points)
 
-            kbps = float(rx_bytes*8)/1e3/rx_count/self.sample_period
-            label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
-                                 visibility_threshold=0.5,
-                                 font=("Sans Serif %f" % int(1+BITRATE_FONT_SIZE*k)))
+            kbps = float(rx_bytes * 8) / 1e3 / rx_count / self.sample_period
+            label.set_properties(
+                visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
+                visibility_threshold=0.5,
+                font=("Sans Serif %f" % int(1 + BITRATE_FONT_SIZE * k)),
+            )
             angle = math.atan2((pos2_y - pos1_y), (pos2_x - pos1_x))
             if -PI_OVER_2 <= angle <= PI_OVER_2:
-                label.set_properties(text=("%.2f kbit/s →" % (kbps,)),
-                                     alignment=Pango.Alignment.CENTER,
-                                     anchor=GooCanvas.CanvasAnchorType.S,
-                                     x=0, y=-line_width/2)
+                label.set_properties(
+                    text=("%.2f kbit/s →" % (kbps,)),
+                    alignment=Pango.Alignment.CENTER,
+                    anchor=GooCanvas.CanvasAnchorType.S,
+                    x=0,
+                    y=-line_width / 2,
+                )
             else:
-                label.set_properties(text=("← %.2f kbit/s" % (kbps,)),
-                                     alignment=Pango.Alignment.CENTER,
-                                     anchor=GooCanvas.CanvasAnchorType.N,
-                                     x=0, y=line_width/2)
+                label.set_properties(
+                    text=("← %.2f kbit/s" % (kbps,)),
+                    alignment=Pango.Alignment.CENTER,
+                    anchor=GooCanvas.CanvasAnchorType.N,
+                    x=0,
+                    y=line_width / 2,
+                )
             M = cairo.Matrix()
-            lx, ly = self._get_label_over_line_position(c_double(pos1_x), c_double(pos1_y),
-                                                        c_double(pos2_x), c_double(pos2_y))
+            lx, ly = self._get_label_over_line_position(
+                c_double(pos1_x), c_double(pos1_y), c_double(pos2_x), c_double(pos2_y)
+            )
             M.translate(lx, ly)
             M.rotate(angle)
             try:
                 label.set_transform(M)
             except KeyError:
                 # https://gitlab.gnome.org/GNOME/pygobject/issues/16
-                warnings.warn("PyGobject bug causing label position error; "
-                              "should be fixed in PyGObject >= 3.29.1")
-                label.set_properties(x=(lx + label.props.x),
-                                     y=(ly + label.props.y))
+                warnings.warn(
+                    "PyGobject bug causing label position error; "
+                    "should be fixed in PyGObject >= 3.29.1"
+                )
+                label.set_properties(x=(lx + label.props.x), y=(ly + label.props.y))
 
             new_arrows.append((arrow, label))
 
         self._transmission_arrows = new_arrows + old_arrows
 
-
     def _update_drops_view(self):
         drops_average = {}
         for drop_set in self._last_drops:
@@ -1400,23 +1499,34 @@
         bottom_y = vadjustment.get_value() + vadjustment.get_page_size()
         dummy, edge_y = self.canvas.convert_from_pixels(0, bottom_y)
 
-        k = self.node_size_adjustment.get_value()/5
+        k = self.node_size_adjustment.get_value() / 5
 
         for transmitter_id, (drop_bytes, drop_count) in drops_average.items():
             transmitter = self.get_node(transmitter_id)
             try:
                 arrow, label = old_arrows.pop()
             except IndexError:
-                arrow = GooCanvas.CanvasPolyline(line_width=2.0, stroke_color_rgba=0xC00000C0, close_path=False, end_arrow=True, pointer_events=GooCanvas.CanvasPointerEvents.NONE)
+                arrow = GooCanvas.CanvasPolyline(
+                    line_width=2.0,
+                    stroke_color_rgba=0xC00000C0,
+                    close_path=False,
+                    end_arrow=True,
+                    pointer_events=GooCanvas.CanvasPointerEvents.NONE,
+                )
                 arrow.set_property("parent", self.canvas.get_root_item())
                 arrow.raise_(None)
 
-                label = GooCanvas.CanvasText(pointer_events=GooCanvas.CanvasPointerEvents.NONE)#, fill_color_rgba=0x00C000C0)
+                label = GooCanvas.CanvasText(
+                    pointer_events=GooCanvas.CanvasPointerEvents.NONE
+                )  # , fill_color_rgba=0x00C000C0)
                 label.set_property("parent", self.canvas.get_root_item())
                 label.raise_(None)
 
             arrow.set_property("visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
-            arrow.set_property("line-width", max(0.1, math.log(float(drop_bytes)/drop_count/self.sample_period)*k))
+            arrow.set_property(
+                "line-width",
+                max(0.1, math.log(float(drop_bytes) / drop_count / self.sample_period) * k),
+            )
             pos1_x, pos1_y = transmitter.get_position()
             pos2_x, pos2_y = pos1_x, edge_y
             points = GooCanvas.CanvasPoints.new(2)
@@ -1424,21 +1534,24 @@
             points.set_point(1, pos2_x, pos2_y)
             arrow.set_property("points", points)
 
-            label.set_properties(visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
-                                 visibility_threshold=0.5,
-                                 font=("Sans Serif %i" % int(1+BITRATE_FONT_SIZE*k)),
-                                 text=("%.2f kbit/s" % (float(drop_bytes*8)/1e3/drop_count/self.sample_period,)),
-                                 alignment=Pango.Alignment.CENTER,
-                                 x=(pos1_x + pos2_x)/2,
-                                 y=(pos1_y + pos2_y)/2)
+            label.set_properties(
+                visibility=GooCanvas.CanvasItemVisibility.VISIBLE_ABOVE_THRESHOLD,
+                visibility_threshold=0.5,
+                font=("Sans Serif %i" % int(1 + BITRATE_FONT_SIZE * k)),
+                text=(
+                    "%.2f kbit/s" % (float(drop_bytes * 8) / 1e3 / drop_count / self.sample_period,)
+                ),
+                alignment=Pango.Alignment.CENTER,
+                x=(pos1_x + pos2_x) / 2,
+                y=(pos1_y + pos2_y) / 2,
+            )
 
             new_arrows.append((arrow, label))
 
         self._drop_arrows = new_arrows + old_arrows
 
-
     def update_view_timeout(self):
-        #print "view: update_view_timeout called at real time ", time.time()
+        # print "view: update_view_timeout called at real time ", time.time()
 
         # while the simulator is busy, run the gtk event loop
         while not self.simulation.lock.acquire(False):
@@ -1448,15 +1561,20 @@
         self.simulation.pause_messages = []
         try:
             self.update_view()
-            self.simulation.target_time = ns.Simulator.Now ().GetSeconds () + self.sample_period
-            #print "view: target time set to %f" % self.simulation.target_time
+            self.simulation.target_time = ns.Simulator.Now().GetSeconds() + self.sample_period
+            # print "view: target time set to %f" % self.simulation.target_time
         finally:
             self.simulation.lock.release()
 
         if pause_messages:
-            #print pause_messages
-            dialog = Gtk.MessageDialog(parent=self.window, flags=0, type=Gtk.MessageType.WARNING, buttons=Gtk.ButtonsType.OK,
-                                       message_format='\n'.join(pause_messages))
+            # print pause_messages
+            dialog = Gtk.MessageDialog(
+                parent=self.window,
+                flags=0,
+                type=Gtk.MessageType.WARNING,
+                buttons=Gtk.ButtonsType.OK,
+                message_format="\n".join(pause_messages),
+            )
             dialog.connect("response", lambda d, r: d.destroy())
             dialog.show()
             self.play_button.set_active(False)
@@ -1466,18 +1584,20 @@
             self._update_timeout_id = None
             return False
 
-        #print "view: self.simulation.go.set()"
+        # print "view: self.simulation.go.set()"
         self.simulation.go.set()
-        #print "view: done."
+        # print "view: done."
         return True
 
     def _start_update_timer(self):
         if self._update_timeout_id is not None:
             GLib.source_remove(self._update_timeout_id)
-        #print "start_update_timer"
-        self._update_timeout_id = GLib.timeout_add(int(SAMPLE_PERIOD/min(self.speed, 1)*1e3),
-                                                   self.update_view_timeout,
-                                                   priority=PRIORITY_UPDATE_VIEW)
+        # print "start_update_timer"
+        self._update_timeout_id = GLib.timeout_add(
+            int(SAMPLE_PERIOD / min(self.speed, 1) * 1e3),
+            self.update_view_timeout,
+            priority=PRIORITY_UPDATE_VIEW,
+        )
 
     def _on_play_button_toggled(self, button):
         if button.get_active():
@@ -1503,15 +1623,18 @@
         # that is executed.
 
         original_runcode = self.ipython.runcode
+
         def runcode(ip, *args):
-            #print "lock"
+            # print "lock"
             self.simulation.lock.acquire()
             try:
                 return original_runcode(*args)
             finally:
-                #print "unlock"
+                # print "unlock"
                 self.simulation.lock.release()
+
         import types
+
         self.ipython.runcode = types.MethodType(runcode, self.ipython)
 
     def autoscale_view(self):
@@ -1519,8 +1642,8 @@
             return
         self._update_node_positions()
         positions = [node.get_position() for node in self.nodes.values()]
-        min_x, min_y = min(x for (x,y) in positions), min(y for (x,y) in positions)
-        max_x, max_y = max(x for (x,y) in positions), max(y for (x,y) in positions)
+        min_x, min_y = min(x for (x, y) in positions), min(y for (x, y) in positions)
+        max_x, max_y = max(x for (x, y) in positions), max(y for (x, y) in positions)
         min_x_px, min_y_px = self.canvas.convert_to_pixels(min_x, min_y)
         max_x_px, max_y_px = self.canvas.convert_to_pixels(max_x, max_y)
         dx = max_x - min_x
@@ -1529,31 +1652,30 @@
         dy_px = max_y_px - min_y_px
         hadj = self._scrolled_window.get_hadjustment()
         vadj = self._scrolled_window.get_vadjustment()
-        new_dx, new_dy = 1.5*dx_px, 1.5*dy_px
+        new_dx, new_dy = 1.5 * dx_px, 1.5 * dy_px
 
         if new_dx == 0 or new_dy == 0:
             return
 
-        self.zoom.set_value(min(hadj.get_page_size()/new_dx, vadj.get_page_size()/new_dy))
+        self.zoom.set_value(min(hadj.get_page_size() / new_dx, vadj.get_page_size() / new_dy))
 
         x1, y1 = self.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
-        x2, y2 = self.canvas.convert_from_pixels((hadj.get_value() +
-                                                  hadj.get_page_size()),
-                                                 (vadj.get_value() +
-                                                  vadj.get_page_size()))
+        x2, y2 = self.canvas.convert_from_pixels(
+            (hadj.get_value() + hadj.get_page_size()), (vadj.get_value() + vadj.get_page_size())
+        )
         width = x2 - x1
         height = y2 - y1
         center_x = (min_x + max_x) / 2
         center_y = (min_y + max_y) / 2
 
-        self.canvas.scroll_to(center_x - width/2, center_y - height/2)
+        self.canvas.scroll_to(center_x - width / 2, center_y - height / 2)
 
         return False
 
     def start(self):
         self.scan_topology()
         self.window.connect("delete-event", self._quit)
-        #self._start_update_timer()
+        # self._start_update_timer()
         GLib.timeout_add(200, self.autoscale_view)
         self.simulation.start()
 
@@ -1566,7 +1688,6 @@
 
         Gtk.main()
 
-
     def on_root_button_press_event(self, view, target, event):
         if event.button == 1:
             self.select_node(None)
@@ -1614,7 +1735,9 @@
         devpos = self.canvas.get_window().get_device_position(event.device)
         x0, y0 = self.canvas.convert_from_pixels(devpos.x, devpos.y)
         self.node_drag_state = self.NodeDragState(x0, y0, pos.x, pos.y)
-        self.node_drag_state.motion_signal = node.canvas_item.connect("motion-notify-event", self.node_drag_motion, node)
+        self.node_drag_state.motion_signal = node.canvas_item.connect(
+            "motion-notify-event", self.node_drag_motion, node
+        )
 
     def node_drag_motion(self, item, targe_item, event, node):
         self.simulation.lock.acquire()
@@ -1627,12 +1750,12 @@
                 return False
             devpos = self.canvas.get_window().get_device_position(event.device)
             canvas_x, canvas_y = self.canvas.convert_from_pixels(devpos.x, devpos.y)
-            dx = (canvas_x - self.node_drag_state.canvas_x0)
-            dy = (canvas_y - self.node_drag_state.canvas_y0)
+            dx = canvas_x - self.node_drag_state.canvas_x0
+            dy = canvas_y - self.node_drag_state.canvas_y0
             pos = mob.GetPosition()
             pos.x = self.node_drag_state.sim_x0 + transform_distance_canvas_to_simulation(dx)
             pos.y = self.node_drag_state.sim_y0 + transform_distance_canvas_to_simulation(dy)
-            #print "SetPosition(%G, %G)" % (pos.x, pos.y)
+            # print "SetPosition(%G, %G)" % (pos.x, pos.y)
             mob.SetPosition(pos)
             node.set_position(*transform_point_simulation_to_canvas(pos.x, pos.y))
         finally:
@@ -1667,8 +1790,7 @@
                     ns3_node = ns.NodeList.GetNode(self.selected_node.node_index)
                 finally:
                     self.simulation.lock.release()
-            self.ipython.updateNamespace({'selected_node': ns3_node})
-
+            self.ipython.updateNamespace({"selected_node": ns3_node})
 
     def select_node(self, node):
         if isinstance(node, ns.Node):
@@ -1699,7 +1821,6 @@
 
         self._update_ipython_selected_node()
 
-
     def add_information_window(self, info_win):
         self.information_windows.append(info_win)
         self.simulation.lock.acquire()
@@ -1712,12 +1833,12 @@
         self.information_windows.remove(info_win)
 
     def _canvas_tooltip_cb(self, canvas, x, y, keyboard_mode, tooltip):
-        #print "tooltip query: ", x, y
+        # print "tooltip query: ", x, y
         hadj = self._scrolled_window.get_hadjustment()
         vadj = self._scrolled_window.get_vadjustment()
         x, y = self.canvas.convert_from_pixels(hadj.get_value() + x, vadj.get_value() + y)
         item = self.canvas.get_item_at(x, y, True)
-        #print "items at (%f, %f): %r | keyboard_mode=%r" % (x, y, item, keyboard_mode)
+        # print "items at (%f, %f): %r | keyboard_mode=%r" % (x, y, item, keyboard_mode)
         if not item:
             return False
         while item is not None:
@@ -1729,10 +1850,9 @@
         return False
 
     def _get_export_file_name(self):
-        sel = Gtk.FileChooserNative.new("Save...", self.canvas.get_toplevel(),
-                                        Gtk.FileChooserAction.SAVE,
-                                        "_Save",
-                                        "_Cancel")
+        sel = Gtk.FileChooserNative.new(
+            "Save...", self.canvas.get_toplevel(), Gtk.FileChooserAction.SAVE, "_Save", "_Cancel"
+        )
         sel.set_local_only(True)
         sel.set_do_overwrite_confirmation(True)
         sel.set_current_name("Unnamed.pdf")
@@ -1762,7 +1882,7 @@
         return file_name
 
     def _take_screenshot(self, dummy_button):
-        #print "Cheese!"
+        # print "Cheese!"
         file_name = self._get_export_file_name()
         if file_name is None:
             return
@@ -1777,23 +1897,25 @@
         bounds.x2, bounds.y2 = self.canvas.convert_from_pixels(x2, y2)
         dest_width = bounds.x2 - bounds.x1
         dest_height = bounds.y2 - bounds.y1
-        #print bounds.x1, bounds.y1, " -> ", bounds.x2, bounds.y2
+        # print bounds.x1, bounds.y1, " -> ", bounds.x2, bounds.y2
 
         dummy, extension = os.path.splitext(file_name)
         extension = extension.lower()
-        if extension == '.eps':
+        if extension == ".eps":
             surface = cairo.PSSurface(file_name, dest_width, dest_height)
-        elif extension == '.pdf':
+        elif extension == ".pdf":
             surface = cairo.PDFSurface(file_name, dest_width, dest_height)
-        elif extension == '.svg':
+        elif extension == ".svg":
             surface = cairo.SVGSurface(file_name, dest_width, dest_height)
         else:
-            dialog = Gtk.MessageDialog(parent  = self.canvas.get_toplevel(),
-                                       flags   = Gtk.DialogFlags.DESTROY_WITH_PARENT,
-                                       type    = Gtk.MessageType.ERROR,
-                                       buttons = Gtk.ButtonsType.OK,
-                                       message_format = "Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
-                                                        % (extension,))
+            dialog = Gtk.MessageDialog(
+                parent=self.canvas.get_toplevel(),
+                flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                type=Gtk.MessageType.ERROR,
+                buttons=Gtk.ButtonsType.OK,
+                message_format="Unknown extension '%s' (valid extensions are '.eps', '.svg', and '.pdf')"
+                % (extension,),
+            )
             dialog.run()
             dialog.destroy()
             return
@@ -1816,11 +1938,10 @@
             return
 
         self.shell_window = Gtk.Window()
-        self.shell_window.set_size_request(750,550)
+        self.shell_window.set_size_request(750, 550)
         self.shell_window.set_resizable(True)
         scrolled_window = Gtk.ScrolledWindow()
-        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
-                                   Gtk.PolicyType.AUTOMATIC)
+        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
         self.ipython = ipython_view.IPythonView()
         self.ipython.modify_font(Pango.FontDescription(SHELL_FONT))
         self.ipython.set_wrap_mode(Gtk.WrapMode.CHAR)
@@ -1829,11 +1950,10 @@
         scrolled_window.show()
         self.shell_window.add(scrolled_window)
         self.shell_window.show()
-        self.shell_window.connect('destroy', self._on_shell_window_destroy)
+        self.shell_window.connect("destroy", self._on_shell_window_destroy)
 
         self._update_ipython_selected_node()
-        self.ipython.updateNamespace({'viz': self})
-
+        self.ipython.updateNamespace({"viz": self})
 
     def _on_shell_window_destroy(self, window):
         self.shell_window = None
@@ -1841,6 +1961,7 @@
 
 initialization_hooks = []
 
+
 def add_initialization_hook(hook, *args):
     """
     Adds a callback to be called after
@@ -1852,12 +1973,14 @@
 
 
 def set_bounds(x1, y1, x2, y2):
-    assert x2>x1
-    assert y2>y1
+    assert x2 > x1
+    assert y2 > y1
+
     def hook(viz):
         cx1, cy1 = transform_point_simulation_to_canvas(x1, y1)
         cx2, cy2 = transform_point_simulation_to_canvas(x2, y2)
         viz.canvas.set_bounds(cx1, cy1, cx2, cy2)
+
     add_initialization_hook(hook)
 
 
@@ -1865,8 +1988,8 @@
     assert Visualizer.INSTANCE is None
     if _import_error is not None:
         import sys
-        print("No visualization support (%s)." % (str(_import_error),),
-              file=sys.stderr)
+
+        print("No visualization support (%s)." % (str(_import_error),), file=sys.stderr)
         ns.Simulator.Run()
         return
     load_plugins()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/hud.py ns-3.41/src/visualizer/visualizer/hud.py
--- ns-3.40/src/visualizer/visualizer/hud.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/hud.py	2024-02-09 14:44:08.561794800 -0800
@@ -1,8 +1,8 @@
 import math
+
+from gi.repository import GooCanvas, Gtk, Pango
+
 from .base import PIXELS_PER_METER
-from gi.repository import Pango
-from gi.repository import Gtk
-from gi.repository import GooCanvas
 
 
 ## Axes class
@@ -28,16 +28,22 @@
         """
         self.viz = viz
         self.color = 0x8080C0FF
-        self.hlines = GooCanvas.CanvasPath(parent=viz.canvas.get_root_item(), stroke_color_rgba=self.color)
+        self.hlines = GooCanvas.CanvasPath(
+            parent=viz.canvas.get_root_item(), stroke_color_rgba=self.color
+        )
         self.hlines.lower(None)
-        self.vlines = GooCanvas.CanvasPath(parent=viz.canvas.get_root_item(), stroke_color_rgba=self.color)
+        self.vlines = GooCanvas.CanvasPath(
+            parent=viz.canvas.get_root_item(), stroke_color_rgba=self.color
+        )
         self.vlines.lower(None)
         self.labels = []
         hadj = self.viz.get_hadjustment()
         vadj = self.viz.get_vadjustment()
+
         def update(adj):
             if self.visible:
                 self.update_view()
+
         hadj.connect("value-changed", update)
         vadj.connect("value-changed", update)
         hadj.connect("changed", update)
@@ -76,7 +82,7 @@
         dx = xf - xi
         size = dx
         ndiv = 5
-        text_width = dx/ndiv/2
+        text_width = dx / ndiv / 2
 
         def rint(x):
             """!
@@ -85,23 +91,25 @@
             @param x: x
             @return x rounded up
             """
-            return math.floor(x+0.5)
+            return math.floor(x + 0.5)
 
         dx_over_ndiv = dx / ndiv
-        for n in range(5): # iterate 5 times to find optimum division size
-            #/* div: length of each division */
-            tbe = math.log10(dx_over_ndiv)#;   /* looking for approx. 'ndiv' divisions in a length 'dx' */
-            div = pow(10, rint(tbe))#;         /* div: power of 10 closest to dx/ndiv */
-            if math.fabs(div/2 - dx_over_ndiv) < math.fabs(div - dx_over_ndiv): #/* test if div/2 is closer to dx/ndiv */
+        for n in range(5):  # iterate 5 times to find optimum division size
+            # div: length of each division
+            # looking for approx. 'ndiv' divisions in a length 'dx'
+            tbe = math.log10(dx_over_ndiv)
+            # div: power of 10 closest to dx/ndiv
+            div = pow(10, rint(tbe))
+            # test if div/2 is closer to dx/ndiv
+            if math.fabs(div / 2 - dx_over_ndiv) < math.fabs(div - dx_over_ndiv):
                 div /= 2
-            elif math.fabs(div*2 - dx_over_ndiv) < math.fabs(div - dx_over_ndiv):
-                div *= 2 # /* test if div*2 is closer to dx/ndiv */
-            x0 = div*math.ceil(xi / div) - div
+            elif math.fabs(div * 2 - dx_over_ndiv) < math.fabs(div - dx_over_ndiv):
+                div *= 2  # test if div*2 is closer to dx/ndiv
+            x0 = div * math.ceil(xi / div) - div
             if n > 1:
                 ndiv = rint(size / text_width)
         return x0, div
 
-
     def update_view(self):
         """!
         Update view function
@@ -116,6 +124,7 @@
         self.labels = []
         for label in unused_labels:
             label.set_property("visibility", GooCanvas.CanvasItemVisibility.HIDDEN)
+
         def get_label():
             """!
             Get label function
@@ -126,7 +135,9 @@
             try:
                 label = unused_labels.pop(0)
             except IndexError:
-                label = GooCanvas.CanvasText(parent=self.viz.canvas.get_root_item(), stroke_color_rgba=self.color)
+                label = GooCanvas.CanvasText(
+                    parent=self.viz.canvas.get_root_item(), stroke_color_rgba=self.color
+                )
             else:
                 label.set_property("visibility", GooCanvas.CanvasItemVisibility.VISIBLE)
                 label.lower(None)
@@ -136,31 +147,37 @@
         hadj = self.viz.get_hadjustment()
         vadj = self.viz.get_vadjustment()
         zoom = self.viz.zoom.get_value()
-        offset = 10/zoom
+        offset = 10 / zoom
 
         x1, y1 = self.viz.canvas.convert_from_pixels(hadj.get_value(), vadj.get_value())
-        x2, y2 = self.viz.canvas.convert_from_pixels(hadj.get_value() + hadj.get_page_size(), vadj.get_value() + vadj.get_page_size())
-        line_width = 5.0/self.viz.zoom.get_value()
+        x2, y2 = self.viz.canvas.convert_from_pixels(
+            hadj.get_value() + hadj.get_page_size(), vadj.get_value() + vadj.get_page_size()
+        )
+        line_width = 5.0 / self.viz.zoom.get_value()
 
         # draw the horizontal axis
         self.hlines.set_property("line-width", line_width)
-        yc = y2 - line_width/2
+        yc = y2 - line_width / 2
 
-        sim_x1 = x1/PIXELS_PER_METER
-        sim_x2 = x2/PIXELS_PER_METER
+        sim_x1 = x1 / PIXELS_PER_METER
+        sim_x2 = x2 / PIXELS_PER_METER
         x0, xdiv = self._compute_divisions(sim_x1, sim_x2)
         path = ["M %r %r L %r %r" % (x1, yc, x2, yc)]
         x = x0
         while x < sim_x2:
-            path.append("M %r %r L %r %r" % (PIXELS_PER_METER*x, yc - offset, PIXELS_PER_METER*x, yc))
+            path.append(
+                "M %r %r L %r %r" % (PIXELS_PER_METER * x, yc - offset, PIXELS_PER_METER * x, yc)
+            )
             label = get_label()
-            label.set_properties(font=("Sans Serif %f" % int(12/zoom)),
-                                 text=("%G" % x),
-                                 fill_color_rgba=self.color,
-                                 alignment=Pango.Alignment.CENTER,
-                                 # anchor=Gtk.Widget.ANCHOR_S,
-                                 x=PIXELS_PER_METER*x,
-                                 y=(yc - offset))
+            label.set_properties(
+                font=("Sans Serif %f" % int(12 / zoom)),
+                text=("%G" % x),
+                fill_color_rgba=self.color,
+                alignment=Pango.Alignment.CENTER,
+                # anchor=Gtk.Widget.ANCHOR_S,
+                x=PIXELS_PER_METER * x,
+                y=(yc - offset),
+            )
             x += xdiv
         del x
 
@@ -168,29 +185,30 @@
 
         # draw the vertical axis
         self.vlines.set_property("line-width", line_width)
-        xc = x1 + line_width/2
-
-        sim_y1 = y1/PIXELS_PER_METER
-        sim_y2 = y2/PIXELS_PER_METER
+        xc = x1 + line_width / 2
 
+        sim_y1 = y1 / PIXELS_PER_METER
+        sim_y2 = y2 / PIXELS_PER_METER
 
         y0, ydiv = self._compute_divisions(sim_y1, sim_y2)
         path = ["M %r %r L %r %r" % (xc, y1, xc, y2)]
         y = y0
         while y < sim_y2:
-            path.append("M %r %r L %r %r" % (xc, PIXELS_PER_METER*y, xc + offset, PIXELS_PER_METER*y))
+            path.append(
+                "M %r %r L %r %r" % (xc, PIXELS_PER_METER * y, xc + offset, PIXELS_PER_METER * y)
+            )
             label = get_label()
-            label.set_properties(font=("Sans Serif %f" % int(12/zoom)),
-                                 text=("%G" % y),
-                                 fill_color_rgba=self.color,
-                                 alignment=Pango.Alignment.LEFT,
-                                 # anchor=Gtk.ANCHOR_W,
-                                 x=xc + offset,
-                                 y=PIXELS_PER_METER*y)
+            label.set_properties(
+                font=("Sans Serif %f" % int(12 / zoom)),
+                text=("%G" % y),
+                fill_color_rgba=self.color,
+                alignment=Pango.Alignment.LEFT,
+                # anchor=Gtk.ANCHOR_W,
+                x=xc + offset,
+                y=PIXELS_PER_METER * y,
+            )
             y += ydiv
 
         self.vlines.set_property("data", " ".join(path))
 
-
-
         self.labels.extend(unused_labels)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/__init__.py ns-3.41/src/visualizer/visualizer/__init__.py
--- ns-3.40/src/visualizer/visualizer/__init__.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/__init__.py	2024-02-09 14:44:08.561794800 -0800
@@ -2,14 +2,27 @@
 
 # Some useful tricks for visualizer
 # we need to check if the node has a mobility model, but we can't pass Ptr<MobilityModel> to python
-ns.cppyy.cppdef("""using namespace ns3; bool hasMobilityModel(Ptr<Node> node){ return !(node->GetObject<MobilityModel>() == 0); };""")
-ns.cppyy.cppdef("""using namespace ns3; Vector3D getNodePosition(Ptr<Node> node){ return node->GetObject<MobilityModel>()->GetPosition(); };""")
-ns.cppyy.cppdef("""using namespace ns3; Ptr<Ipv4> getNodeIpv4(Ptr<Node> node){ return node->GetObject<Ipv4>(); };""")
-ns.cppyy.cppdef("""using namespace ns3; Ptr<Ipv6> getNodeIpv6(Ptr<Node> node){ return node->GetObject<Ipv6>(); };""")
-ns.cppyy.cppdef("""using namespace ns3; std::string getMobilityModelName(Ptr<Node> node){ return node->GetObject<MobilityModel>()->GetInstanceTypeId().GetName(); };""")
-ns.cppyy.cppdef("""using namespace ns3; bool hasOlsr(Ptr<Node> node){ return !(node->GetObject<olsr::RoutingProtocol>() == 0); };""")
-ns.cppyy.cppdef("""using namespace ns3; Ptr<olsr::RoutingProtocol> getNodeOlsr(Ptr<Node> node){ return node->GetObject<olsr::RoutingProtocol>(); };""")
+ns.cppyy.cppdef(
+    """using namespace ns3; bool hasMobilityModel(Ptr<Node> node){ return !(node->GetObject<MobilityModel>() == 0); };"""
+)
+ns.cppyy.cppdef(
+    """using namespace ns3; Vector3D getNodePosition(Ptr<Node> node){ return node->GetObject<MobilityModel>()->GetPosition(); };"""
+)
+ns.cppyy.cppdef(
+    """using namespace ns3; Ptr<Ipv4> getNodeIpv4(Ptr<Node> node){ return node->GetObject<Ipv4>(); };"""
+)
+ns.cppyy.cppdef(
+    """using namespace ns3; Ptr<Ipv6> getNodeIpv6(Ptr<Node> node){ return node->GetObject<Ipv6>(); };"""
+)
+ns.cppyy.cppdef(
+    """using namespace ns3; std::string getMobilityModelName(Ptr<Node> node){ return node->GetObject<MobilityModel>()->GetInstanceTypeId().GetName(); };"""
+)
+ns.cppyy.cppdef(
+    """using namespace ns3; bool hasOlsr(Ptr<Node> node){ return !(node->GetObject<olsr::RoutingProtocol>() == 0); };"""
+)
+ns.cppyy.cppdef(
+    """using namespace ns3; Ptr<olsr::RoutingProtocol> getNodeOlsr(Ptr<Node> node){ return node->GetObject<olsr::RoutingProtocol>(); };"""
+)
 
 
-from .core import start, register_plugin, set_bounds, add_initialization_hook
-
+from .core import add_initialization_hook, register_plugin, set_bounds, start
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/ipython_view.py ns-3.41/src/visualizer/visualizer/ipython_view.py
--- ns-3.40/src/visualizer/visualizer/ipython_view.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/ipython_view.py	2024-02-09 14:44:08.561794800 -0800
@@ -10,702 +10,736 @@
 available under the terms of the BSD which accompanies this distribution, and
 is available at U{http://www.opensource.org/licenses/bsd-license.php}
 """
+
 # this file is a modified version of source code from the Accerciser project
 # https://wiki.gnome.org/Apps/Accerciser
 
 import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk
-from gi.repository import Gdk
-from gi.repository import GLib
-from gi.repository import Pango
-
-from pkg_resources import parse_version
 
+gi.require_version("Gtk", "3.0")
+import os
 import re
 import sys
-import os
-
-from io import StringIO
 from functools import reduce
+from io import StringIO
+
+from gi.repository import Gdk, GLib, Gtk, Pango
+from pkg_resources import parse_version
 
 ## Try to import IPython
 try:
-  import IPython
+    import IPython
 except ImportError:
-  ##@ var IPython
-  #
-  IPython = None
+    ##@ var IPython
+    #
+    IPython = None
+
 
 ## IterableIPShell class
 class IterableIPShell:
-  ## @var IP
-  #  IP
-  ## @var iter_more
-  #  iterate more
-  ## @var history_level
-  #  history level
-  ## @var complete_sep
-  #  separators
-  ## @var no_input_splitter
-  # no input splitter
-  ## @var lines
-  # lines
-  ## @var indent_spaces
-  # indent spaces
-  ## @var prompt
-  #  prompt
-  ## @var header
-  #  header
-  ## @var config
-  #  config
-  ## @var colors
-  #  colors
-  ## @var raw_input
-  #  raw input
-  ## Constructor
-  def __init__(self, argv=[], user_ns=None, user_global_ns=None,
-               cin=None, cout=None, cerr=None, input_func=None):
-    """! Initializer
-
-    @param self: this object
-    @param argv: Command line options for IPython
-    @param user_ns: User namespace.
-    @param user_global_ns: User global namespace.
-    @param cin: Console standard input.
-    @param cout: Console standard output.
-    @param cerr: Console standard error.
-    @param input_func: Replacement for builtin raw_input()
-    """
-    io = IPython.utils.io
-    if input_func:
-      IPython.terminal.interactiveshell.raw_input_original = input_func
-    if IPython.version_info < (8,):
-      if cin:
-        io.stdin = io.IOStream(cin)
-      if cout:
-        io.stdout = io.IOStream(cout)
-      if cerr:
-        io.stderr = io.IOStream(cerr)
-    else:
-      if cin:
-        sys.stdin = cin
-      if cout:
-        sys.stdout = cout
-      if cerr:
-        sys.stderr = cerr
-
-    # This is to get rid of the blockage that occurs during
-    # IPython.Shell.InteractiveShell.user_setup()
-    io.raw_input = lambda x: None
-
-    os.environ['TERM'] = 'dumb'
-    excepthook = sys.excepthook
-
-    from traitlets.config.loader import Config
-    cfg = Config()
-    cfg.InteractiveShell.colors = "Linux"
-    cfg.Completer.use_jedi = False
-
-    if IPython.version_info < (8,):
-      # InteractiveShell's __init__ overwrites io.stdout,io.stderr with
-      # sys.stdout, sys.stderr, this makes sure they are right
-      old_stdout, old_stderr = sys.stdout, sys.stderr
-      sys.stdout, sys.stderr = io.stdout.stream, io.stderr.stream
+    ## @var IP
+    #  IP
+    ## @var iter_more
+    #  iterate more
+    ## @var history_level
+    #  history level
+    ## @var complete_sep
+    #  separators
+    ## @var no_input_splitter
+    # no input splitter
+    ## @var lines
+    # lines
+    ## @var indent_spaces
+    # indent spaces
+    ## @var prompt
+    #  prompt
+    ## @var header
+    #  header
+    ## @var config
+    #  config
+    ## @var colors
+    #  colors
+    ## @var raw_input
+    #  raw input
+
+    def __init__(
+        self,
+        argv=[],
+        user_ns=None,
+        user_global_ns=None,
+        cin=None,
+        cout=None,
+        cerr=None,
+        input_func=None,
+    ):
+        """!
+        Constructor for the IterableIPShell class
+        @param self: this object
+        @param argv: Command line options for IPython
+        @param user_ns: User namespace.
+        @param user_global_ns: User global namespace.
+        @param cin: Console standard input.
+        @param cout: Console standard output.
+        @param cerr: Console standard error.
+        @param input_func: Replacement for builtin raw_input()
+        """
+        io = IPython.utils.io
+        if input_func:
+            IPython.terminal.interactiveshell.raw_input_original = input_func
+        if IPython.version_info < (8,):
+            if cin:
+                io.stdin = io.IOStream(cin)
+            if cout:
+                io.stdout = io.IOStream(cout)
+            if cerr:
+                io.stderr = io.IOStream(cerr)
+        else:
+            if cin:
+                sys.stdin = cin
+            if cout:
+                sys.stdout = cout
+            if cerr:
+                sys.stderr = cerr
+
+        # This is to get rid of the blockage that occurs during
+        # IPython.Shell.InteractiveShell.user_setup()
+        io.raw_input = lambda x: None
+
+        os.environ["TERM"] = "dumb"
+        excepthook = sys.excepthook
+
+        from traitlets.config.loader import Config
+
+        cfg = Config()
+        cfg.InteractiveShell.colors = "Linux"
+        cfg.Completer.use_jedi = False
+
+        if IPython.version_info < (8,):
+            # InteractiveShell's __init__ overwrites io.stdout,io.stderr with
+            # sys.stdout, sys.stderr, this makes sure they are right
+            old_stdout, old_stderr = sys.stdout, sys.stderr
+            sys.stdout, sys.stderr = io.stdout.stream, io.stderr.stream
+
+        # InteractiveShell inherits from SingletonConfigurable, so use instance()
+        #
+        self.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(config=cfg, user_ns=user_ns)
+
+        if IPython.version_info < (8,):
+            sys.stdout, sys.stderr = old_stdout, old_stderr
+
+        self.IP.system = lambda cmd: self.shell(
+            self.IP.var_expand(cmd), header="IPython system call: "
+        )
+        #                                            local_ns=user_ns)
+        # global_ns=user_global_ns)
+        # verbose=self.IP.rc.system_verbose)
+
+        self.IP.raw_input = input_func
+        sys.excepthook = excepthook
+        self.iter_more = 0
+        self.history_level = 0
+        self.complete_sep = re.compile("[\s\{\}\[\]\(\)]")
+        self.updateNamespace({"exit": lambda: None})
+        self.updateNamespace({"quit": lambda: None})
+        # Workaround for updating namespace with sys.modules
+        #
+        self.__update_namespace()
+
+        # Avoid using input splitter when not really needed.
+        # Perhaps it could work even before 5.8.0
+        # But it definitely does not work any more with >= 7.0.0
+        self.no_input_splitter = parse_version(IPython.release.version) >= parse_version("5.8.0")
+        self.lines = []
+        self.indent_spaces = ""
 
-    # InteractiveShell inherits from SingletonConfigurable, so use instance()
-    #
-    self.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(\
-              config=cfg, user_ns=user_ns)
+    def __update_namespace(self):
+        """!
+        Update self.IP namespace for autocompletion with sys.modules
+        @return none
+        """
+        for k, v in list(sys.modules.items()):
+            if not "." in k:
+                self.IP.user_ns.update({k: v})
 
-    if IPython.version_info < (8,):
-      sys.stdout, sys.stderr = old_stdout, old_stderr
+    def execute(self):
+        """!
+        Executes the current line provided by the shell object.
+        @return none
+        """
+        self.history_level = 0
 
-    self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
-                                            header='IPython system call: ')
-#                                            local_ns=user_ns)
-                                            #global_ns=user_global_ns)
-                                            #verbose=self.IP.rc.system_verbose)
-
-    self.IP.raw_input = input_func
-    sys.excepthook = excepthook
-    self.iter_more = 0
-    self.history_level = 0
-    self.complete_sep =  re.compile('[\s\{\}\[\]\(\)]')
-    self.updateNamespace({'exit':lambda:None})
-    self.updateNamespace({'quit':lambda:None})
-    # Workaround for updating namespace with sys.modules
-    #
-    self.__update_namespace()
+        if IPython.version_info < (8,):
+            # this is needed because some functions in IPython use 'print' to print
+            # output (like 'who')
+
+            orig_stdout = sys.stdout
+            sys.stdout = IPython.utils.io.stdout
+
+            orig_stdin = sys.stdin
+            sys.stdin = IPython.utils.io.stdin
+
+        self.prompt = self.generatePrompt(self.iter_more)
+
+        self.IP.hooks.pre_prompt_hook()
+        if self.iter_more:
+            try:
+                self.prompt = self.generatePrompt(True)
+            except:
+                self.IP.showtraceback()
+            if self.IP.autoindent:
+                self.IP.rl_do_indent = True
 
-    # Avoid using input splitter when not really needed.
-    # Perhaps it could work even before 5.8.0
-    # But it definitely does not work any more with >= 7.0.0
-    self.no_input_splitter = parse_version(IPython.release.version) >= parse_version('5.8.0')
-    self.lines = []
-    self.indent_spaces = ''
-
-  def __update_namespace(self):
-    """!
-    Update self.IP namespace for autocompletion with sys.modules
-    @return none
-    """
-    for k, v in list(sys.modules.items()):
-        if not '.' in k:
-          self.IP.user_ns.update({k:v})
-
-  def execute(self):
-    """!
-    Executes the current line provided by the shell object.
-    @return none
-    """
-    self.history_level = 0
+        try:
+            line = self.IP.raw_input(self.prompt)
+        except KeyboardInterrupt:
+            self.IP.write("\nKeyboardInterrupt\n")
+            if self.no_input_splitter:
+                self.lines = []
+            else:
+                self.IP.input_splitter.reset()
+        except:
+            self.IP.showtraceback()
+        else:
+            if self.no_input_splitter:
+                self.lines.append(line)
+                (status, self.indent_spaces) = self.IP.check_complete("\n".join(self.lines))
+                self.iter_more = status == "incomplete"
+            else:
+                self.IP.input_splitter.push(line)
+                self.iter_more = self.IP.input_splitter.push_accepts_more()
+            if not self.iter_more:
+                if self.no_input_splitter:
+                    source_raw = "\n".join(self.lines)
+                    self.lines = []
+                else:
+                    source_raw = self.IP.input_splitter.raw_reset()
+                self.IP.run_cell(source_raw, store_history=True)
+                self.IP.rl_do_indent = False
+            else:
+                # TODO: Auto-indent
+                #
+                self.IP.rl_do_indent = True
+                pass
+            self.prompt = self.generatePrompt(self.iter_more)
+
+        if IPython.version_info < (8,):
+            sys.stdout = orig_stdout
+            sys.stdin = orig_stdin
 
-    if IPython.version_info < (8,):
-      # this is needed because some functions in IPython use 'print' to print
-      # output (like 'who')
+    def generatePrompt(self, is_continuation):
+        """!
+        Generate prompt depending on is_continuation value
 
-      orig_stdout = sys.stdout
-      sys.stdout = IPython.utils.io.stdout
+        @param is_continuation
+        @return: The prompt string representation
 
-      orig_stdin = sys.stdin
-      sys.stdin = IPython.utils.io.stdin
+        """
 
-    self.prompt = self.generatePrompt(self.iter_more)
+        if is_continuation:
+            prompt = "... "
+        else:
+            prompt = ">>> "
+        return prompt
 
-    self.IP.hooks.pre_prompt_hook()
-    if self.iter_more:
-        try:
-            self.prompt = self.generatePrompt(True)
-        except:
-            self.IP.showtraceback()
-        if self.IP.autoindent:
-            self.IP.rl_do_indent = True
+    def historyBack(self):
+        """!
+        Provides one history command back.
 
-    try:
-      line = self.IP.raw_input(self.prompt)
-    except KeyboardInterrupt:
-      self.IP.write('\nKeyboardInterrupt\n')
-      if self.no_input_splitter:
-        self.lines = []
-      else:
-        self.IP.input_splitter.reset()
-    except:
-      self.IP.showtraceback()
-    else:
-      if self.no_input_splitter:
-        self.lines.append(line)
-        (status, self.indent_spaces) = self.IP.check_complete('\n'.join(self.lines))
-        self.iter_more = status == 'incomplete'
-      else:
-        self.IP.input_splitter.push(line)
-        self.iter_more = self.IP.input_splitter.push_accepts_more()
-      if not self.iter_more:
-          if self.no_input_splitter:
-            source_raw = '\n'.join(self.lines)
-            self.lines = []
-          else:
-            source_raw = self.IP.input_splitter.raw_reset()
-          self.IP.run_cell(source_raw, store_history=True)
-          self.IP.rl_do_indent = False
-      else:
-          # TODO: Auto-indent
-          #
-          self.IP.rl_do_indent = True
-          pass
-      self.prompt = self.generatePrompt(self.iter_more)
-
-    if IPython.version_info < (8,):
-      sys.stdout = orig_stdout
-      sys.stdin = orig_stdin
-
-  def generatePrompt(self, is_continuation):
-    """!
-    Generate prompt depending on is_continuation value
+        @param self this object
+        @return: The command string.
+        """
+        self.history_level -= 1
+        if not self._getHistory():
+            self.history_level += 1
+        return self._getHistory()
 
-    @param is_continuation
-    @return: The prompt string representation
+    def historyForward(self):
+        """!
+        Provides one history command forward.
 
-    """
+        @param self this object
+        @return: The command string.
+        """
+        if self.history_level < 0:
+            self.history_level += 1
+        return self._getHistory()
 
-    if is_continuation:
-      prompt = '... '
-    else:
-      prompt = '>>> '
-    return prompt
+    def _getHistory(self):
+        """!
+        Gets the command string of the current history level.
 
+        @param self this object
+        @return: Historic command string.
+        """
+        try:
+            rv = self.IP.user_ns["In"][self.history_level].strip("\n")
+        except IndexError:
+            rv = ""
+        return rv
 
-  def historyBack(self):
-    """!
-    Provides one history command back.
+    def updateNamespace(self, ns_dict):
+        """!
+        Add the current dictionary to the shell namespace.
 
-    @param self this object
-    @return: The command string.
-    """
-    self.history_level -= 1
-    if not self._getHistory():
-      self.history_level +=1
-    return self._getHistory()
-
-  def historyForward(self):
-    """!
-    Provides one history command forward.
+        @param ns_dict: A dictionary of symbol-values.
+        @return none
+        """
+        self.IP.user_ns.update(ns_dict)
 
-    @param self this object
-    @return: The command string.
-    """
-    if self.history_level < 0:
-      self.history_level += 1
-    return self._getHistory()
-
-  def _getHistory(self):
-    """!
-    Gets the command string of the current history level.
+    def complete(self, line):
+        """!
+        Returns an auto completed line and/or possibilities for completion.
 
-    @param self this object
-    @return: Historic command string.
-    """
-    try:
-      rv = self.IP.user_ns['In'][self.history_level].strip('\n')
-    except IndexError:
-      rv = ''
-    return rv
-
-  def updateNamespace(self, ns_dict):
-    """!
-    Add the current dictionary to the shell namespace.
+        @param line: Given line so far.
+        @return: Line completed as for as possible, and possible further completions.
+        """
+        split_line = self.complete_sep.split(line)
+        if split_line[-1]:
+            possibilities = self.IP.complete(split_line[-1])
+        else:
+            completed = line
+            possibilities = ["", []]
+        if possibilities:
+
+            def _commonPrefix(str1, str2):
+                """!
+                Reduction function. returns common prefix of two given strings.
+
+                @param str1: First string.
+                @param str2: Second string
+                @return: Common prefix to both strings.
+                """
+                for i in range(len(str1)):
+                    if not str2.startswith(str1[: i + 1]):
+                        return str1[:i]
+                return str1
+
+            if possibilities[1]:
+                common_prefix = reduce(_commonPrefix, possibilities[1]) or split_line[-1]
+                completed = line[: -len(split_line[-1])] + common_prefix
+            else:
+                completed = line
+        else:
+            completed = line
+        return completed, possibilities[1]
 
-    @param ns_dict: A dictionary of symbol-values.
-    @return none
-    """
-    self.IP.user_ns.update(ns_dict)
+    def shell(self, cmd, verbose=0, debug=0, header=""):
+        """!
+        Replacement method to allow shell commands without them blocking.
 
-  def complete(self, line):
-    """!
-    Returns an auto completed line and/or possibilities for completion.
+        @param cmd: Shell command to execute.
+        @param verbose: Verbosity
+        @param debug: Debug level
+        @param header: Header to be printed before output
+        @return none
+        """
+        stat = 0
+        if verbose or debug:
+            print(header + cmd)
+        # flush stdout so we don't mangle python's buffering
+        if not debug:
+            input, output = os.popen4(cmd)
+            print(output.read())
+            output.close()
+            input.close()
 
-    @param line: Given line so far.
-    @return: Line completed as for as possible, and possible further completions.
-    """
-    split_line = self.complete_sep.split(line)
-    if split_line[-1]:
-      possibilities = self.IP.complete(split_line[-1])
-    else:
-      completed = line
-      possibilities = ['', []]
-    if possibilities:
-      def _commonPrefix(str1, str2):
-        """!
-        Reduction function. returns common prefix of two given strings.
-
-        @param str1: First string.
-        @param str2: Second string
-        @return: Common prefix to both strings.
-        """
-        for i in range(len(str1)):
-          if not str2.startswith(str1[:i+1]):
-            return str1[:i]
-        return str1
-      if possibilities[1]:
-        common_prefix = reduce(_commonPrefix, possibilities[1]) or split_line[-1]
-        completed = line[:-len(split_line[-1])]+common_prefix
-      else:
-        completed = line
-    else:
-      completed = line
-    return completed, possibilities[1]
-
-
-  def shell(self, cmd,verbose=0,debug=0,header=''):
-    """!
-    Replacement method to allow shell commands without them blocking.
-
-    @param cmd: Shell command to execute.
-    @param verbose: Verbosity
-    @param debug: Debug level
-    @param header: Header to be printed before output
-    @return none
-    """
-    stat = 0
-    if verbose or debug: print(header+cmd)
-    # flush stdout so we don't mangle python's buffering
-    if not debug:
-      input, output = os.popen4(cmd)
-      print(output.read())
-      output.close()
-      input.close()
 
 ## ConsoleView class
 class ConsoleView(Gtk.TextView):
-  ## @var ANSI_COLORS
-  #  color list
-  ## @var text_buffer
-  #  text buffer
-  ## @var mark
-  #  scroll mark
-  ## @var color_pat
-  #  color pattern
-  ## @var line_start
-  #  line start
-  """
-  Specialized text view for console-like workflow.
-
-  @cvar ANSI_COLORS: Mapping of terminal control sequence values to
-                     tuples containing foreground and background color names.
-  @type ANSI_COLORS: dictionary
-
-  @ivar text_buffer: Widget's text buffer.
-  @type text_buffer: Gtk.TextBuffer
-  @ivar color_pat: Regex of terminal color pattern
-  @type color_pat: _sre.SRE_Pattern
-  @ivar mark: Scroll mark for automatic scrolling on input.
-  @type mark: Gtk.TextMark
-  @ivar line_start: Start of command line mark.
-  @type line_start: Gtk.TextMark
-  """
-  ANSI_COLORS = {'0;30': ('Black', None),
-                 '0;31': ('Red', None),
-                 '0;32': ('Green', None),
-                 '0;33': ('Brown', None),
-                 '0;34': ('Blue', None),
-                 '0;35': ('Purple', None),
-                 '0;36': ('Cyan', None),
-                 '0;37': ('LightGray', None),
-                 '1;30': ('DarkGray', None),
-                 '1;31': ('DarkRed', None),
-                 '1;32': ('SeaGreen', None),
-                 '1;33': ('Yellow', None),
-                 '1;34': ('LightBlue', None),
-                 '1;35': ('MediumPurple', None),
-                 '1;36': ('LightCyan', None),
-                 '1;37': ('White', None),
-                 '38;5;124;43': ('DarkRed', 'Yellow'),
-                 '38;5;241': ('Gray', None),
-                 '38;5;241;43': ('Gray', 'Yellow'),
-                 '39': ('Black', None),
-                 '39;49': ('Red', 'White'),
-                 '43': (None, 'Yellow'),
-                 '49': (None, 'White')}
+    ## @var ANSI_COLORS
+    #  color list
+    ## @var text_buffer
+    #  text buffer
+    ## @var mark
+    #  scroll mark
+    ## @var color_pat
+    #  color pattern
+    ## @var line_start
+    #  line start
+    ## @var onKeyPress
+    #  onKeyPress function
+    ## @var _write
+    #  _write function
+    ## @var _showPrompt
+    #  _showPrompt function
+    ## @var _changeLine
+    #  _changeLine function
+    ## @var _showReturned
+    #  _showReturned function
+    ## @var prompt
+    #  prompt function
+
+    """
+    Specialized text view for console-like workflow.
+
+    @cvar ANSI_COLORS: Mapping of terminal control sequence values to
+                       tuples containing foreground and background color names.
+    @type ANSI_COLORS: dictionary
+
+    @ivar text_buffer: Widget's text buffer.
+    @type text_buffer: Gtk.TextBuffer
+    @ivar color_pat: Regex of terminal color pattern
+    @type color_pat: _sre.SRE_Pattern
+    @ivar mark: Scroll mark for automatic scrolling on input.
+    @type mark: Gtk.TextMark
+    @ivar line_start: Start of command line mark.
+    @type line_start: Gtk.TextMark
+    """
+    ANSI_COLORS = {
+        "0;30": ("Black", None),
+        "0;31": ("Red", None),
+        "0;32": ("Green", None),
+        "0;33": ("Brown", None),
+        "0;34": ("Blue", None),
+        "0;35": ("Purple", None),
+        "0;36": ("Cyan", None),
+        "0;37": ("LightGray", None),
+        "1;30": ("DarkGray", None),
+        "1;31": ("DarkRed", None),
+        "1;32": ("SeaGreen", None),
+        "1;33": ("Yellow", None),
+        "1;34": ("LightBlue", None),
+        "1;35": ("MediumPurple", None),
+        "1;36": ("LightCyan", None),
+        "1;37": ("White", None),
+        "38;5;124;43": ("DarkRed", "Yellow"),
+        "38;5;241": ("Gray", None),
+        "38;5;241;43": ("Gray", "Yellow"),
+        "39": ("Black", None),
+        "39;49": ("Red", "White"),
+        "43": (None, "Yellow"),
+        "49": (None, "White"),
+    }
 
-  def __init__(self):
-    """
-    Initialize console view.
-    """
-    Gtk.TextView.__init__(self)
-    self.modify_font(Pango.FontDescription('Mono'))
-    self.set_cursor_visible(True)
-    self.text_buffer = self.get_buffer()
-    self.mark = self.text_buffer.create_mark('scroll_mark',
-                                             self.text_buffer.get_end_iter(),
-                                             False)
-    for code in self.ANSI_COLORS:
-      self.text_buffer.create_tag(code,
-                                  foreground=self.ANSI_COLORS[code][0],
-                                  background=self.ANSI_COLORS[code][1],
-                                  weight=700)
-    self.text_buffer.create_tag('0')
-    self.text_buffer.create_tag('notouch', editable=False)
-    self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
-    self.line_start = \
-        self.text_buffer.create_mark('line_start',
-                                     self.text_buffer.get_end_iter(), True)
-    self.connect('key-press-event', self.onKeyPress)
-
-  def write(self, text, editable=False):
-    """!
-    Write given text to buffer.
-
-    @param text: Text to append.
-    @param editable: If true, added text is editable.
-    @return none
-    """
-    GLib.idle_add(self._write, text, editable)
+    def __init__(self):
+        """
+        Initialize console view.
+        """
+        Gtk.TextView.__init__(self)
+        self.modify_font(Pango.FontDescription("Mono"))
+        self.set_cursor_visible(True)
+        self.text_buffer = self.get_buffer()
+        self.mark = self.text_buffer.create_mark(
+            "scroll_mark", self.text_buffer.get_end_iter(), False
+        )
+        for code in self.ANSI_COLORS:
+            self.text_buffer.create_tag(
+                code,
+                foreground=self.ANSI_COLORS[code][0],
+                background=self.ANSI_COLORS[code][1],
+                weight=700,
+            )
+        self.text_buffer.create_tag("0")
+        self.text_buffer.create_tag("notouch", editable=False)
+        self.color_pat = re.compile("\x01?\x1b\[(.*?)m\x02?")
+        self.line_start = self.text_buffer.create_mark(
+            "line_start", self.text_buffer.get_end_iter(), True
+        )
+        self.connect("key-press-event", self.onKeyPress)
 
-  def _write(self, text, editable=False):
-    """!
-    Write given text to buffer.
-
-    @param text: Text to append.
-    @param editable: If true, added text is editable.
-    @return none
-    """
-    segments = self.color_pat.split(text)
-    segment = segments.pop(0)
-    start_mark = self.text_buffer.create_mark(None,
-                                              self.text_buffer.get_end_iter(),
-                                              True)
-    self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)
-
-    if segments:
-      ansi_tags = self.color_pat.findall(text)
-      for tag in ansi_tags:
-        i = segments.index(tag)
-        self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),
-                                                  segments[i+1], str(tag))
-        segments.pop(i)
-    if not editable:
-      self.text_buffer.apply_tag_by_name('notouch',
-                                         self.text_buffer.get_iter_at_mark(start_mark),
-                                         self.text_buffer.get_end_iter())
-    self.text_buffer.delete_mark(start_mark)
-    self.scroll_mark_onscreen(self.mark)
-
-  def showPrompt(self, prompt):
-    """!
-    Prints prompt at start of line.
+    def write(self, text, editable=False):
+        """!
+        Write given text to buffer.
 
-    @param prompt: Prompt to print.
-    @return none
-    """
-    GLib.idle_add(self._showPrompt, prompt)
+        @param text: Text to append.
+        @param editable: If true, added text is editable.
+        @return none
+        """
+        GLib.idle_add(self._write, text, editable)
 
-  def _showPrompt(self, prompt):
-    """!
-    Prints prompt at start of line.
+    def _write(self, text, editable=False):
+        """!
+        Write given text to buffer.
 
-    @param prompt: Prompt to print.
-    @return none
-    """
-    self._write(prompt)
-    self.text_buffer.move_mark(self.line_start,
-                               self.text_buffer.get_end_iter())
-
-  def changeLine(self, text):
-    """!
-    Replace currently entered command line with given text.
+        @param text: Text to append.
+        @param editable: If true, added text is editable.
+        @return none
+        """
+        segments = self.color_pat.split(text)
+        segment = segments.pop(0)
+        start_mark = self.text_buffer.create_mark(None, self.text_buffer.get_end_iter(), True)
+        self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)
+
+        if segments:
+            ansi_tags = self.color_pat.findall(text)
+            for tag in ansi_tags:
+                i = segments.index(tag)
+                self.text_buffer.insert_with_tags_by_name(
+                    self.text_buffer.get_end_iter(), segments[i + 1], str(tag)
+                )
+                segments.pop(i)
+        if not editable:
+            self.text_buffer.apply_tag_by_name(
+                "notouch",
+                self.text_buffer.get_iter_at_mark(start_mark),
+                self.text_buffer.get_end_iter(),
+            )
+        self.text_buffer.delete_mark(start_mark)
+        self.scroll_mark_onscreen(self.mark)
 
-    @param text: Text to use as replacement.
-    @return none
-    """
-    GLib.idle_add(self._changeLine, text)
+    def showPrompt(self, prompt):
+        """!
+        Prints prompt at start of line.
 
-  def _changeLine(self, text):
-    """!
-    Replace currently entered command line with given text.
+        @param prompt: Prompt to print.
+        @return none
+        """
+        GLib.idle_add(self._showPrompt, prompt)
 
-    @param text: Text to use as replacement.
-    @return none
-    """
-    iter = self.text_buffer.get_iter_at_mark(self.line_start)
-    iter.forward_to_line_end()
-    self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter)
-    self._write(text, True)
-
-  def getCurrentLine(self):
-    """!
-    Get text in current command line.
+    def _showPrompt(self, prompt):
+        """!
+        Prints prompt at start of line.
 
-    @return Text of current command line.
-    """
-    rv = self.text_buffer.get_slice(
-      self.text_buffer.get_iter_at_mark(self.line_start),
-      self.text_buffer.get_end_iter(), False)
-    return rv
-
-  def showReturned(self, text):
-    """!
-    Show returned text from last command and print new prompt.
+        @param prompt: Prompt to print.
+        @return none
+        """
+        self._write(prompt)
+        self.text_buffer.move_mark(self.line_start, self.text_buffer.get_end_iter())
 
-    @param text: Text to show.
-    @return none
-    """
-    GLib.idle_add(self._showReturned, text)
+    def changeLine(self, text):
+        """!
+        Replace currently entered command line with given text.
+
+        @param text: Text to use as replacement.
+        @return none
+        """
+        GLib.idle_add(self._changeLine, text)
 
-  def _showReturned(self, text):
-    """!
-    Show returned text from last command and print new prompt.
+    def _changeLine(self, text):
+        """!
+        Replace currently entered command line with given text.
 
-    @param text: Text to show.
-    @return none
-    """
-    iter = self.text_buffer.get_iter_at_mark(self.line_start)
-    iter.forward_to_line_end()
-    self.text_buffer.apply_tag_by_name(
-      'notouch',
-      self.text_buffer.get_iter_at_mark(self.line_start),
-      iter)
-    self._write('\n'+text)
-    if text:
-      self._write('\n')
-    self._showPrompt(self.prompt)
-    self.text_buffer.move_mark(self.line_start, self.text_buffer.get_end_iter())
-    self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
-
-    if self.IP.rl_do_indent:
-      if self.no_input_splitter:
-        indentation = self.indent_spaces
-      else:
-        indentation = self.IP.input_splitter.indent_spaces * ' '
-      self.text_buffer.insert_at_cursor(indentation)
-
-  def onKeyPress(self, widget, event):
-    """!
-    Key press callback used for correcting behavior for console-like
-    interfaces. For example 'home' should go to prompt, not to beginning of
-    line.
-
-    @param widget: Widget that key press accored in.
-    @param event: Event object
-    @return Return True if event should not trickle.
-    """
-    insert_mark = self.text_buffer.get_insert()
-    insert_iter = self.text_buffer.get_iter_at_mark(insert_mark)
-    selection_mark = self.text_buffer.get_selection_bound()
-    selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
-    start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
-    if event.keyval == Gdk.KEY_Home:
-      if event.state & Gdk.ModifierType.CONTROL_MASK or event.state & Gdk.ModifierType.MOD1_MASK:
+        @param text: Text to use as replacement.
+        @return none
+        """
+        iter = self.text_buffer.get_iter_at_mark(self.line_start)
+        iter.forward_to_line_end()
+        self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter)
+        self._write(text, True)
+
+    def getCurrentLine(self):
+        """!
+        Get text in current command line.
+
+        @return Text of current command line.
+        """
+        rv = self.text_buffer.get_slice(
+            self.text_buffer.get_iter_at_mark(self.line_start),
+            self.text_buffer.get_end_iter(),
+            False,
+        )
+        return rv
+
+    def showReturned(self, text):
+        """!
+        Show returned text from last command and print new prompt.
+
+        @param text: Text to show.
+        @return none
+        """
+        GLib.idle_add(self._showReturned, text)
+
+    def _showReturned(self, text):
+        """!
+        Show returned text from last command and print new prompt.
+
+        @param text: Text to show.
+        @return none
+        """
+        iter = self.text_buffer.get_iter_at_mark(self.line_start)
+        iter.forward_to_line_end()
+        self.text_buffer.apply_tag_by_name(
+            "notouch", self.text_buffer.get_iter_at_mark(self.line_start), iter
+        )
+        self._write("\n" + text)
+        if text:
+            self._write("\n")
+        self._showPrompt(self.prompt)
+        self.text_buffer.move_mark(self.line_start, self.text_buffer.get_end_iter())
+        self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
+
+        if self.IP.rl_do_indent:
+            if self.no_input_splitter:
+                indentation = self.indent_spaces
+            else:
+                indentation = self.IP.input_splitter.indent_spaces * " "
+            self.text_buffer.insert_at_cursor(indentation)
+
+    def onKeyPress(self, widget, event):
+        """!
+        Key press callback used for correcting behavior for console-like
+        interfaces. For example 'home' should go to prompt, not to beginning of
+        line.
+
+        @param widget: Widget that key press accored in.
+        @param event: Event object
+        @return Return True if event should not trickle.
+        """
+        insert_mark = self.text_buffer.get_insert()
+        insert_iter = self.text_buffer.get_iter_at_mark(insert_mark)
+        selection_mark = self.text_buffer.get_selection_bound()
+        selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
+        start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
+        if event.keyval == Gdk.KEY_Home:
+            if (
+                event.state & Gdk.ModifierType.CONTROL_MASK
+                or event.state & Gdk.ModifierType.MOD1_MASK
+            ):
+                pass
+            elif event.state & Gdk.ModifierType.SHIFT_MASK:
+                self.text_buffer.move_mark(insert_mark, start_iter)
+                return True
+            else:
+                self.text_buffer.place_cursor(start_iter)
+                return True
+        elif event.keyval == Gdk.KEY_Left:
+            insert_iter.backward_cursor_position()
+            if not insert_iter.editable(True):
+                return True
+        elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval in [ord("L"), ord("l")]:
+            # clear previous output on Ctrl+L, but remember current input line + cursor position
+            cursor_offset = self.text_buffer.get_property("cursor-position")
+            cursor_pos_in_line = cursor_offset - start_iter.get_offset() + len(self.prompt)
+            current_input = self.text_buffer.get_text(
+                start_iter, self.text_buffer.get_end_iter(), False
+            )
+            self.text_buffer.set_text(self.prompt + current_input)
+            self.text_buffer.move_mark(
+                self.line_start, self.text_buffer.get_iter_at_offset(len(self.prompt))
+            )
+            self.text_buffer.place_cursor(self.text_buffer.get_iter_at_offset(cursor_pos_in_line))
+            return True
+        elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval in [Gdk.KEY_k, Gdk.KEY_K]:
+            # clear text after input cursor on Ctrl+K
+            if insert_iter.editable(True):
+                self.text_buffer.delete(insert_iter, self.text_buffer.get_end_iter())
+            return True
+        elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_C:
+            # copy selection on Ctrl+C (upper-case 'C' only)
+            self.text_buffer.copy_clipboard(Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD))
+            return True
+        elif not event.string:
+            pass
+        elif start_iter.compare(insert_iter) <= 0 and start_iter.compare(selection_iter) <= 0:
+            pass
+        elif start_iter.compare(insert_iter) > 0 and start_iter.compare(selection_iter) > 0:
+            self.text_buffer.place_cursor(start_iter)
+        elif insert_iter.compare(selection_iter) < 0:
+            self.text_buffer.move_mark(insert_mark, start_iter)
+        elif insert_iter.compare(selection_iter) > 0:
+            self.text_buffer.move_mark(selection_mark, start_iter)
+
+        return self.onKeyPressExtend(event)
+
+    def onKeyPressExtend(self, event):
+        """!
+        For some reason we can't extend onKeyPress directly (bug #500900).
+        @param event key press
+        @return none
+        """
         pass
-      elif event.state & Gdk.ModifierType.SHIFT_MASK:
-        self.text_buffer.move_mark(insert_mark, start_iter)
-        return True
-      else:
-        self.text_buffer.place_cursor(start_iter)
-        return True
-    elif event.keyval == Gdk.KEY_Left:
-      insert_iter.backward_cursor_position()
-      if not insert_iter.editable(True):
-        return True
-    elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval in [ord('L'), ord('l')]:
-        # clear previous output on Ctrl+L, but remember current input line + cursor position
-        cursor_offset = self.text_buffer.get_property('cursor-position')
-        cursor_pos_in_line = cursor_offset - start_iter.get_offset() + len(self.prompt)
-        current_input = self.text_buffer.get_text(start_iter, self.text_buffer.get_end_iter(), False)
-        self.text_buffer.set_text(self.prompt + current_input)
-        self.text_buffer.move_mark(self.line_start, self.text_buffer.get_iter_at_offset(len(self.prompt)))
-        self.text_buffer.place_cursor(self.text_buffer.get_iter_at_offset(cursor_pos_in_line))
-        return True
-    elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval in [Gdk.KEY_k, Gdk.KEY_K]:
-      # clear text after input cursor on Ctrl+K
-      if insert_iter.editable(True):
-        self.text_buffer.delete(insert_iter, self.text_buffer.get_end_iter())
-      return True
-    elif event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_C:
-      # copy selection on Ctrl+C (upper-case 'C' only)
-      self.text_buffer.copy_clipboard(Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD))
-      return True
-    elif not event.string:
-      pass
-    elif start_iter.compare(insert_iter) <= 0 and \
-          start_iter.compare(selection_iter) <= 0:
-      pass
-    elif start_iter.compare(insert_iter) > 0 and \
-          start_iter.compare(selection_iter) > 0:
-      self.text_buffer.place_cursor(start_iter)
-    elif insert_iter.compare(selection_iter) < 0:
-      self.text_buffer.move_mark(insert_mark, start_iter)
-    elif insert_iter.compare(selection_iter) > 0:
-      self.text_buffer.move_mark(selection_mark, start_iter)
-
-    return self.onKeyPressExtend(event)
-
-  def onKeyPressExtend(self, event):
-    """!
-    For some reason we can't extend onKeyPress directly (bug #500900).
-    @param event key press
-    @return none
-    """
-    pass
+
 
 ## IPythonView class
 class IPythonView(ConsoleView, IterableIPShell):
-  ## @var cout
-  #  cout
-  ## @var interrupt
-  #  interrupt
-  ## @var execute
-  #  execute
-  ## @var prompt
-  #  prompt
-  ## @var showPrompt
-  #  show prompt
-  ## @var history_pos
-  #  history list
-  ## @var window
-  #  GTK Window
-  """
-  Sub-class of both modified IPython shell and L{ConsoleView} this makes
-  a GTK+ IPython console.
-  """
-  def __init__(self):
+    ## @var cout
+    #  cout
+    ## @var interrupt
+    #  interrupt
+    ## @var execute
+    #  execute
+    ## @var prompt
+    #  prompt
+    ## @var showPrompt
+    #  show prompt
+    ## @var history_pos
+    #  history list
+    ## @var window
+    #  GTK Window
     """
-    Initialize. Redirect I/O to console.
+    Sub-class of both modified IPython shell and L{ConsoleView} this makes
+    a GTK+ IPython console.
     """
-    ConsoleView.__init__(self)
-    self.cout = StringIO()
-    IterableIPShell.__init__(self, cout=self.cout, cerr=self.cout,
-                             input_func=self.raw_input)
-    self.interrupt = False
-    self.execute()
-    self.prompt = self.generatePrompt(False)
-    self.cout.truncate(0)
-    self.showPrompt(self.prompt)
-
-  def raw_input(self, prompt=''):
-    """!
-    Custom raw_input() replacement. Gets current line from console buffer.
 
-    @param prompt: Prompt to print. Here for compatibility as replacement.
-    @return The current command line text.
-    """
-    if self.interrupt:
-      self.interrupt = False
-      raise KeyboardInterrupt
-    return self.getCurrentLine()
-
-  def onKeyPressExtend(self, event):
-    """!
-    Key press callback with plenty of shell goodness, like history,
-    autocompletions, etc.
+    def __init__(self):
+        """
+        Initialize. Redirect I/O to console.
+        """
+        ConsoleView.__init__(self)
+        self.cout = StringIO()
+        IterableIPShell.__init__(self, cout=self.cout, cerr=self.cout, input_func=self.raw_input)
+        self.interrupt = False
+        self.execute()
+        self.prompt = self.generatePrompt(False)
+        self.cout.truncate(0)
+        self.showPrompt(self.prompt)
 
-    @param event: Event object.
-    @return True if event should not trickle.
-    """
+    def raw_input(self, prompt=""):
+        """!
+        Custom raw_input() replacement. Gets current line from console buffer.
 
-    if event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == 99:
-      self.interrupt = True
-      self._processLine()
-      return True
-    elif event.keyval == Gdk.KEY_Return:
-      self._processLine()
-      return True
-    elif event.keyval == Gdk.KEY_Up:
-      self.changeLine(self.historyBack())
-      return True
-    elif event.keyval == Gdk.KEY_Down:
-      self.changeLine(self.historyForward())
-      return True
-    elif event.keyval == Gdk.KEY_Tab:
-      if not self.getCurrentLine().strip():
-        return False
-      completed, possibilities = self.complete(self.getCurrentLine())
-      if len(possibilities) > 1:
-        slice = self.getCurrentLine()
-        self.write('\n')
-        for symbol in possibilities:
-          self.write(symbol+'\n')
-        self.showPrompt(self.prompt)
-      self.changeLine(completed or slice)
-      return True
+        @param prompt: Prompt to print. Here for compatibility as replacement.
+        @return The current command line text.
+        """
+        if self.interrupt:
+            self.interrupt = False
+            raise KeyboardInterrupt
+        return self.getCurrentLine()
 
-  def _processLine(self):
-    """!
-    Process current command line.
-    @return none
-    """
-    self.history_pos = 0
-    self.execute()
-    rv = self.cout.getvalue()
-    if rv: rv = rv.strip('\n')
-    self.showReturned(rv)
-    self.cout.truncate(0)
-    self.cout.seek(0)
+    def onKeyPressExtend(self, event):
+        """!
+        Key press callback with plenty of shell goodness, like history,
+        autocompletions, etc.
 
-if __name__ == "__main__":
-  window = Gtk.Window()
-  window.set_default_size(640, 320)
-  window.connect('delete-event', lambda x, y: Gtk.main_quit())
-  window.add(IPythonView())
-  window.show_all()
-  Gtk.main()
+        @param event: Event object.
+        @return True if event should not trickle.
+        """
+
+        if event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == 99:
+            self.interrupt = True
+            self._processLine()
+            return True
+        elif event.keyval == Gdk.KEY_Return:
+            self._processLine()
+            return True
+        elif event.keyval == Gdk.KEY_Up:
+            self.changeLine(self.historyBack())
+            return True
+        elif event.keyval == Gdk.KEY_Down:
+            self.changeLine(self.historyForward())
+            return True
+        elif event.keyval == Gdk.KEY_Tab:
+            if not self.getCurrentLine().strip():
+                return False
+            completed, possibilities = self.complete(self.getCurrentLine())
+            if len(possibilities) > 1:
+                slice = self.getCurrentLine()
+                self.write("\n")
+                for symbol in possibilities:
+                    self.write(symbol + "\n")
+                self.showPrompt(self.prompt)
+            self.changeLine(completed or slice)
+            return True
 
+    def _processLine(self):
+        """!
+        Process current command line.
+        @return none
+        """
+        self.history_pos = 0
+        self.execute()
+        rv = self.cout.getvalue()
+        if rv:
+            rv = rv.strip("\n")
+        self.showReturned(rv)
+        self.cout.truncate(0)
+        self.cout.seek(0)
+
+
+if __name__ == "__main__":
+    window = Gtk.Window()
+    window.set_default_size(640, 320)
+    window.connect("delete-event", lambda x, y: Gtk.main_quit())
+    window.add(IPythonView())
+    window.show_all()
+    Gtk.main()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/plugins/interface_statistics.py ns-3.41/src/visualizer/visualizer/plugins/interface_statistics.py
--- ns-3.40/src/visualizer/visualizer/plugins/interface_statistics.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/plugins/interface_statistics.py	2024-02-09 14:44:08.561794800 -0800
@@ -13,6 +13,7 @@
     """
     Collects interface statistics for all nodes.
     """
+
     ## @var node_statistics
     #  node statistics
     ## @var visualizer
@@ -21,8 +22,16 @@
     ## NetDevStats class
     class NetDevStats(object):
         ## class members
-        __slots__ = ['rxPackets', 'rxBytes', 'txPackets', 'txBytes',
-                     'rxPacketRate', 'rxBitRate', 'txPacketRate', 'txBitRate']
+        __slots__ = [
+            "rxPackets",
+            "rxBytes",
+            "txPackets",
+            "txBytes",
+            "rxPacketRate",
+            "rxBitRate",
+            "txPacketRate",
+            "txBitRate",
+        ]
 
     def __init__(self, visualizer):
         """!
@@ -30,7 +39,7 @@
         @param self this object
         @param visualizer visualizer object
         """
-        self.node_statistics = {} # nodeid -> list(raw statistics)
+        self.node_statistics = {}  # nodeid -> list(raw statistics)
         self.visualizer = visualizer
 
     def simulation_periodic_update(self, viz):
@@ -66,7 +75,7 @@
         if len(raw_stats_list) < NODE_STATISTICS_MEMORY:
             return []
         assert len(raw_stats_list) == NODE_STATISTICS_MEMORY
-        tx_packets1 = [] # transmitted packets, one value per interface
+        tx_packets1 = []  # transmitted packets, one value per interface
         rx_packets1 = []
         tx_bytes1 = []
         rx_bytes1 = []
@@ -78,7 +87,7 @@
 
         retval = []
 
-        k = self.visualizer.sample_period*(NODE_STATISTICS_MEMORY-1)
+        k = self.visualizer.sample_period * (NODE_STATISTICS_MEMORY - 1)
         for iface, stats in enumerate(raw_stats_list[-1]):
             outStat = self.NetDevStats()
             outStat.txPackets = stats.transmittedPackets
@@ -86,10 +95,10 @@
             outStat.rxPackets = stats.receivedPackets
             outStat.rxBytes = stats.receivedBytes
 
-            outStat.txPacketRate = (stats.transmittedPackets - tx_packets1[iface])/k
-            outStat.rxPacketRate = (stats.receivedPackets - rx_packets1[iface])/k
-            outStat.txBitRate = (stats.transmittedBytes - tx_bytes1[iface])*8/k
-            outStat.rxBitRate = (stats.receivedBytes - rx_bytes1[iface])*8/k
+            outStat.txPacketRate = (stats.transmittedPackets - tx_packets1[iface]) / k
+            outStat.rxPacketRate = (stats.receivedPackets - rx_packets1[iface]) / k
+            outStat.txBitRate = (stats.transmittedBytes - tx_bytes1[iface]) * 8 / k
+            outStat.rxBitRate = (stats.receivedBytes - rx_bytes1[iface]) * 8 / k
             retval.append(outStat)
         return retval
 
@@ -108,19 +117,36 @@
     #  visualizer node
     ## @var table_model
     #  table model
+    ## @var _response_cb
+    #  _response_cb function
+    ## @var COLUMN_INTERFACE
+    #  COLUMN_INTERFACE constant
+    ## @var COLUMN_TX_PACKETS
+    #  COLUMN_TX_PACKETS constant
+    ## @var COLUMN_TX_BYTES
+    #  COLUMN_TX_BYTES constant
+    ## @var COLUMN_TX_PACKET_RATE
+    #  COLUMN_TX_PACKET_RATE constant
+    ## @var COLUMN_TX_BIT_RATE
+    #  COLUMN_TX_BIT_RATE constant
+    ## @var COLUMN_RX_PACKETS
+    #  COLUMN_RX_PACKETS constant
+    ## @var COLUMN_RX_BYTES
+    #  COLUMN_RX_BYTES constant
+    ## @var COLUMN_RX_PACKET_RATE
+    #  COLUMN_RX_PACKET_RATE constant
+    ## @var COLUMN_RX_BIT_RATE
+    #  COLUMN_RX_BIT_RATE constant
     (
         COLUMN_INTERFACE,
-
         COLUMN_TX_PACKETS,
         COLUMN_TX_BYTES,
         COLUMN_TX_PACKET_RATE,
         COLUMN_TX_BIT_RATE,
-
         COLUMN_RX_PACKETS,
         COLUMN_RX_BYTES,
         COLUMN_RX_PACKET_RATE,
         COLUMN_RX_BIT_RATE,
-
     ) = range(9)
 
     def __init__(self, visualizer, node_index, statistics_collector):
@@ -132,9 +158,11 @@
         @param statistics_collector statistics collector class
         """
         InformationWindow.__init__(self)
-        self.win = Gtk.Dialog(parent=visualizer.window,
-                              flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
-                              buttons=("_Close", Gtk.ResponseType.CLOSE))
+        self.win = Gtk.Dialog(
+            parent=visualizer.window,
+            flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
+            buttons=("_Close", Gtk.ResponseType.CLOSE),
+        )
         self.win.connect("response", self._response_cb)
         self.win.set_title("Statistics for node %i" % node_index)
         self.visualizer = visualizer
@@ -142,7 +170,7 @@
         self.node_index = node_index
         self.viz_node = visualizer.get_node(node_index)
 
-        self.table_model = Gtk.ListStore(*([str]*13))
+        self.table_model = Gtk.ListStore(*([str] * 13))
 
         treeview = Gtk.TreeView(self.table_model)
         treeview.show()
@@ -193,23 +221,30 @@
             interface_name = ns.Names.FindName(netdevice)
             if not interface_name:
                 interface_name = "(interface %i)" % iface
-            self.table_model.set(tree_iter,
-                                 self.COLUMN_INTERFACE, interface_name,
-
-                                 self.COLUMN_TX_PACKETS, str(stats.txPackets),
-                                 self.COLUMN_TX_BYTES, str(stats.txBytes),
-                                 self.COLUMN_TX_PACKET_RATE, str(stats.txPacketRate),
-                                 self.COLUMN_TX_BIT_RATE, str(stats.txBitRate),
-
-                                 self.COLUMN_RX_PACKETS, str(stats.rxPackets),
-                                 self.COLUMN_RX_BYTES, str(stats.rxBytes),
-                                 self.COLUMN_RX_PACKET_RATE, str(stats.rxPacketRate),
-                                 self.COLUMN_RX_BIT_RATE, str(stats.rxBitRate)
-                                 )
+            self.table_model.set(
+                tree_iter,
+                self.COLUMN_INTERFACE,
+                interface_name,
+                self.COLUMN_TX_PACKETS,
+                str(stats.txPackets),
+                self.COLUMN_TX_BYTES,
+                str(stats.txBytes),
+                self.COLUMN_TX_PACKET_RATE,
+                str(stats.txPacketRate),
+                self.COLUMN_TX_BIT_RATE,
+                str(stats.txBitRate),
+                self.COLUMN_RX_PACKETS,
+                str(stats.rxPackets),
+                self.COLUMN_RX_BYTES,
+                str(stats.rxBytes),
+                self.COLUMN_RX_PACKET_RATE,
+                str(stats.rxPacketRate),
+                self.COLUMN_RX_BIT_RATE,
+                str(stats.rxBitRate),
+            )
 
 
 def populate_node_menu(viz, node, menu, statistics_collector):
-
     menu_item = Gtk.MenuItem("Show Interface Statistics")
     menu_item.show()
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/plugins/ipv4_routing_table.py ns-3.41/src/visualizer/visualizer/plugins/ipv4_routing_table.py
--- ns-3.40/src/visualizer/visualizer/plugins/ipv4_routing_table.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/plugins/ipv4_routing_table.py	2024-02-09 14:44:08.561794800 -0800
@@ -5,6 +5,7 @@
 except ModuleNotFoundError:
     from visualizer.base import InformationWindow
 
+
 ## ShowIpv4RoutingTable class
 class ShowIpv4RoutingTable(InformationWindow):
     ## @var win
@@ -15,12 +16,24 @@
     #  node index
     ## @var table_model
     #  table model
+    ## @var _response_cb
+    #  _response_cb function
+    ## @var COLUMN_DESTINATION
+    #  COLUMN_DESTINATION constant
+    ## @var COLUMN_NEXT_HOP
+    #  COLUMN_NEXT_HOP constant
+    ## @var COLUMN_INTERFACE
+    #  COLUMN_INTERFACE constant
+    ## @var COLUMN_TYPE
+    #  COLUMN_TYPE constant
+    ## @var COLUMN_PRIO
+    #  COLUMN_PRIO constant
     (
         COLUMN_DESTINATION,
         COLUMN_NEXT_HOP,
         COLUMN_INTERFACE,
         COLUMN_TYPE,
-        COLUMN_PRIO
+        COLUMN_PRIO,
     ) = range(5)
 
     def __init__(self, visualizer, node_index):
@@ -31,9 +44,11 @@
         @param node_index the node index
         """
         InformationWindow.__init__(self)
-        self.win = Gtk.Dialog(parent=visualizer.window,
-                              flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
-                              buttons=("_Close", Gtk.ResponseType.CLOSE))
+        self.win = Gtk.Dialog(
+            parent=visualizer.window,
+            flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
+            buttons=("_Close", Gtk.ResponseType.CLOSE),
+        )
         self.win.connect("response", self._response_cb)
         self.win.set_title("IPv4 routing table for node %i" % node_index)
         self.visualizer = visualizer
@@ -44,36 +59,34 @@
         treeview = Gtk.TreeView(self.table_model)
         treeview.show()
         sw = Gtk.ScrolledWindow()
-        sw.set_properties(hscrollbar_policy=Gtk.PolicyType.AUTOMATIC,
-                          vscrollbar_policy=Gtk.PolicyType.AUTOMATIC)
+        sw.set_properties(
+            hscrollbar_policy=Gtk.PolicyType.AUTOMATIC, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC
+        )
         sw.show()
         sw.add(treeview)
         self.win.vbox.add(sw)
         self.win.set_default_size(600, 300)
 
         # Dest.
-        column = Gtk.TreeViewColumn('Destination', Gtk.CellRendererText(),
-                                    text=self.COLUMN_DESTINATION)
+        column = Gtk.TreeViewColumn(
+            "Destination", Gtk.CellRendererText(), text=self.COLUMN_DESTINATION
+        )
         treeview.append_column(column)
 
         # Next hop
-        column = Gtk.TreeViewColumn('Next hop', Gtk.CellRendererText(),
-                                    text=self.COLUMN_NEXT_HOP)
+        column = Gtk.TreeViewColumn("Next hop", Gtk.CellRendererText(), text=self.COLUMN_NEXT_HOP)
         treeview.append_column(column)
 
         # Interface
-        column = Gtk.TreeViewColumn('Interface', Gtk.CellRendererText(),
-                                    text=self.COLUMN_INTERFACE)
+        column = Gtk.TreeViewColumn("Interface", Gtk.CellRendererText(), text=self.COLUMN_INTERFACE)
         treeview.append_column(column)
 
         # Type
-        column = Gtk.TreeViewColumn('Type', Gtk.CellRendererText(),
-                                    text=self.COLUMN_TYPE)
+        column = Gtk.TreeViewColumn("Type", Gtk.CellRendererText(), text=self.COLUMN_TYPE)
         treeview.append_column(column)
 
         # Prio
-        column = Gtk.TreeViewColumn('Prio', Gtk.CellRendererText(),
-                                    text=self.COLUMN_PRIO)
+        column = Gtk.TreeViewColumn("Prio", Gtk.CellRendererText(), text=self.COLUMN_PRIO)
         treeview.append_column(column)
 
         self.visualizer.add_information_window(self)
@@ -102,7 +115,7 @@
         if routing is None:
             return
 
-        routing_protocols = [] # list of (protocol, type_string, priority)
+        routing_protocols = []  # list of (protocol, type_string, priority)
 
         if isinstance(routing, ns.Ipv4StaticRouting):
             ipv4_routing = routing_protocols.append((routing, "static", 0))
@@ -124,17 +137,24 @@
                 tree_iter = self.table_model.append()
                 netdevice = ipv4.GetNetDevice(route.GetInterface())
                 if netdevice is None:
-                    interface_name = 'lo'
+                    interface_name = "lo"
                 else:
                     interface_name = ns.Names.FindName(netdevice)
                     if not interface_name:
                         interface_name = "(interface %i)" % route.GetInterface()
-                self.table_model.set(tree_iter,
-                                     self.COLUMN_DESTINATION, str(route.GetDest()),
-                                     self.COLUMN_NEXT_HOP, str(route.GetGateway()),
-                                     self.COLUMN_INTERFACE, interface_name,
-                                     self.COLUMN_TYPE, type_string,
-                                     self.COLUMN_PRIO, prio)
+                self.table_model.set(
+                    tree_iter,
+                    self.COLUMN_DESTINATION,
+                    str(route.GetDest()),
+                    self.COLUMN_NEXT_HOP,
+                    str(route.GetGateway()),
+                    self.COLUMN_INTERFACE,
+                    interface_name,
+                    self.COLUMN_TYPE,
+                    type_string,
+                    self.COLUMN_PRIO,
+                    prio,
+                )
 
 
 def populate_node_menu(viz, node, menu):
@@ -147,5 +167,6 @@
     menu_item.connect("activate", _show_ipv4_routing_table)
     menu.add(menu_item)
 
+
 def register(viz):
     viz.connect("populate-node-menu", populate_node_menu)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/plugins/olsr.py ns-3.41/src/visualizer/visualizer/plugins/olsr.py
--- ns-3.40/src/visualizer/visualizer/plugins/olsr.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/plugins/olsr.py	2024-02-09 14:44:08.561794800 -0800
@@ -1,11 +1,11 @@
-from gi.repository import Gtk
-from gi.repository import Gdk
+from gi.repository import Gdk, Gtk
 
 try:
     from ns3.visualizer.base import InformationWindow
 except ModuleNotFoundError:
     from visualizer.base import InformationWindow
 
+
 ## ShowOlsrRoutingTable class
 class ShowOlsrRoutingTable(InformationWindow):
     ## @var win
@@ -16,6 +16,16 @@
     #  node index
     ## @var table_model
     #  table model
+    ## @var _response_cb
+    #  _response_cb function
+    ## @var COLUMN_DESTINATION
+    #  COLUMN_DESTINATION constant
+    ## @var COLUMN_NEXT_HOP
+    #  COLUMN_NEXT_HOP constant
+    ## @var COLUMN_INTERFACE
+    #  COLUMN_INTERFACE constant
+    ## @var COLUMN_NUM_HOPS
+    #  COLUMN_NUM_HOPS constant
     (
         COLUMN_DESTINATION,
         COLUMN_NEXT_HOP,
@@ -31,10 +41,12 @@
         @param node_index the node index
         """
         InformationWindow.__init__(self)
-        self.win = Gtk.Dialog(parent=visualizer.window,
-                              flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
-                              buttons=("_Close", Gtk.ResponseType.CLOSE))
-        self.win.set_default_size(Gdk.Screen.width()/2, Gdk.Screen.height()/2)
+        self.win = Gtk.Dialog(
+            parent=visualizer.window,
+            flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
+            buttons=("_Close", Gtk.ResponseType.CLOSE),
+        )
+        self.win.set_default_size(Gdk.Screen.width() / 2, Gdk.Screen.height() / 2)
         self.win.connect("response", self._response_cb)
         self.win.set_title("OLSR routing table for node %i" % node_index)
         self.visualizer = visualizer
@@ -45,30 +57,29 @@
         treeview = Gtk.TreeView(self.table_model)
         treeview.show()
         sw = Gtk.ScrolledWindow()
-        sw.set_properties(hscrollbar_policy=Gtk.PolicyType.AUTOMATIC,
-                          vscrollbar_policy=Gtk.PolicyType.AUTOMATIC)
+        sw.set_properties(
+            hscrollbar_policy=Gtk.PolicyType.AUTOMATIC, vscrollbar_policy=Gtk.PolicyType.AUTOMATIC
+        )
         sw.show()
         sw.add(treeview)
         self.win.vbox.add(sw)
 
         # Dest.
-        column = Gtk.TreeViewColumn('Destination', Gtk.CellRendererText(),
-                                    text=self.COLUMN_DESTINATION)
+        column = Gtk.TreeViewColumn(
+            "Destination", Gtk.CellRendererText(), text=self.COLUMN_DESTINATION
+        )
         treeview.append_column(column)
 
         # Next hop
-        column = Gtk.TreeViewColumn('Next hop', Gtk.CellRendererText(),
-                                    text=self.COLUMN_NEXT_HOP)
+        column = Gtk.TreeViewColumn("Next hop", Gtk.CellRendererText(), text=self.COLUMN_NEXT_HOP)
         treeview.append_column(column)
 
         # Interface
-        column = Gtk.TreeViewColumn('Interface', Gtk.CellRendererText(),
-                                    text=self.COLUMN_INTERFACE)
+        column = Gtk.TreeViewColumn("Interface", Gtk.CellRendererText(), text=self.COLUMN_INTERFACE)
         treeview.append_column(column)
 
         # Num. Hops
-        column = Gtk.TreeViewColumn('Num. Hops', Gtk.CellRendererText(),
-                                    text=self.COLUMN_NUM_HOPS)
+        column = Gtk.TreeViewColumn("Num. Hops", Gtk.CellRendererText(), text=self.COLUMN_NUM_HOPS)
         treeview.append_column(column)
 
         self.visualizer.add_information_window(self)
@@ -102,16 +113,22 @@
             tree_iter = self.table_model.append()
             netdevice = ipv4.GetNetDevice(route.interface)
             if netdevice is None:
-                interface_name = 'lo'
+                interface_name = "lo"
             else:
                 interface_name = ns.Names.FindName(netdevice)
                 if not interface_name:
                     interface_name = "(interface %i)" % route.interface
-            self.table_model.set(tree_iter,
-                                 self.COLUMN_DESTINATION, str(route.destAddr),
-                                 self.COLUMN_NEXT_HOP, str(route.nextAddr),
-                                 self.COLUMN_INTERFACE, interface_name,
-                                 self.COLUMN_NUM_HOPS, route.distance)
+            self.table_model.set(
+                tree_iter,
+                self.COLUMN_DESTINATION,
+                str(route.destAddr),
+                self.COLUMN_NEXT_HOP,
+                str(route.nextAddr),
+                self.COLUMN_INTERFACE,
+                interface_name,
+                self.COLUMN_NUM_HOPS,
+                route.distance,
+            )
 
 
 def populate_node_menu(viz, node, menu):
@@ -129,5 +146,6 @@
     menu_item.connect("activate", _show_ipv4_routing_table)
     menu.add(menu_item)
 
+
 def register(viz):
     viz.connect("populate-node-menu", populate_node_menu)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/plugins/show_last_packets.py ns-3.41/src/visualizer/visualizer/plugins/show_last_packets.py
--- ns-3.40/src/visualizer/visualizer/plugins/show_last_packets.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/plugins/show_last_packets.py	2024-02-09 14:44:08.561794800 -0800
@@ -1,14 +1,21 @@
-from gi.repository import GObject
-from gi.repository import Gtk
+from gi.repository import GObject, Gtk
 
-from ns import ns
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 
 try:
     from ns3.visualizer.base import InformationWindow
 except ModuleNotFoundError:
     from visualizer.base import InformationWindow
 
-from kiwi.ui.objectlist import ObjectList, Column
+from kiwi.ui.objectlist import Column, ObjectList
+
 
 ## ShowLastPackets class
 class ShowLastPackets(InformationWindow):
@@ -36,18 +43,30 @@
     #  AND button
     ## @var op_OR_button
     #  OR button
+    ## @var _response_cb
+    #  _response_cb function
+
     class PacketList(Gtk.ScrolledWindow):
         """
         PacketList class
         """
+
         ## @var table_model
         #  table model
+        ## @var COLUMN_TIME
+        #  COLUMN_TIME constant
+        ## @var COLUMN_INTERFACE
+        #  COLUMN_INTERFACE constant
+        ## @var COLUMN_SIZE
+        #  COLUMN_SIZE constant
+        ## @var COLUMN_CONTENTS
+        #  COLUMN_CONTENTS constant
         (
             COLUMN_TIME,
             COLUMN_INTERFACE,
             COLUMN_SIZE,
             COLUMN_CONTENTS,
-            ) = range(4)
+        ) = range(4)
 
         def __init__(self):
             """
@@ -55,9 +74,11 @@
             @param self this object
             """
             super(ShowLastPackets.PacketList, self).__init__()
-            self.set_properties(hscrollbar_policy=Gtk.PolicyType.AUTOMATIC,
-                                vscrollbar_policy=Gtk.PolicyType.AUTOMATIC)
-            self.table_model = Gtk.ListStore(*([str]*4))
+            self.set_properties(
+                hscrollbar_policy=Gtk.PolicyType.AUTOMATIC,
+                vscrollbar_policy=Gtk.PolicyType.AUTOMATIC,
+            )
+            self.table_model = Gtk.ListStore(*([str] * 4))
             treeview = Gtk.TreeView(self.table_model)
             treeview.show()
             self.add(treeview)
@@ -88,13 +109,17 @@
                     interface_name = ns.core.Names.FindName(sample.device)
                     if not interface_name:
                         interface_name = "(interface %i)" % sample.device.GetIfIndex()
-                self.table_model.set(tree_iter,
-                                     self.COLUMN_TIME, str(sample.time.GetSeconds()),
-                                     self.COLUMN_INTERFACE, interface_name,
-                                     self.COLUMN_SIZE, str(sample.packet.GetSize ()),
-                                     self.COLUMN_CONTENTS, str(sample.packet)
-                                     )
-
+                self.table_model.set(
+                    tree_iter,
+                    self.COLUMN_TIME,
+                    str(sample.time.GetSeconds()),
+                    self.COLUMN_INTERFACE,
+                    interface_name,
+                    self.COLUMN_SIZE,
+                    str(sample.packet.GetSize()),
+                    self.COLUMN_CONTENTS,
+                    str(sample.packet),
+                )
 
     def __init__(self, visualizer, node_index):
         """!
@@ -104,9 +129,11 @@
         @param node_index the node index
         """
         InformationWindow.__init__(self)
-        self.win = Gtk.Dialog(parent=visualizer.window,
-                              flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
-                              buttons=("_Close", Gtk.ResponseType.CLOSE))
+        self.win = Gtk.Dialog(
+            parent=visualizer.window,
+            flags=Gtk.DialogFlags.DESTROY_WITH_PARENT,
+            buttons=("_Close", Gtk.ResponseType.CLOSE),
+        )
         self.win.connect("response", self._response_cb)
         self.win.set_title("Last packets for node %i" % node_index)
         self.visualizer = visualizer
@@ -115,9 +142,13 @@
 
         def smart_expand(expander, vbox):
             if expander.get_expanded():
-                vbox.set_child_packing(expander, expand=True, fill=True, padding=0, pack_type=Gtk.PACK_START)
+                vbox.set_child_packing(
+                    expander, expand=True, fill=True, padding=0, pack_type=Gtk.PACK_START
+                )
             else:
-                vbox.set_child_packing(expander, expand=False, fill=False, padding=0, pack_type=Gtk.PACK_START)
+                vbox.set_child_packing(
+                    expander, expand=False, fill=False, padding=0, pack_type=Gtk.PACK_START
+                )
 
         main_hbox = Gtk.HBox(False, 4)
         main_hbox.show()
@@ -150,7 +181,6 @@
         main_vbox.pack_start(group, expand=False, fill=False)
         group.connect_after("activate", smart_expand, main_vbox)
 
-
         # Packet Filter
 
         # - options
@@ -169,17 +199,20 @@
         sel_buttons_box.add(select_all_button)
         sel_buttons_box.add(select_none_button)
 
-        self.packet_filter_widget = ObjectList([
-                Column('selected', title="Sel.", data_type=bool, editable=True),
-                Column('name', title="Header"),
-                ], sortable=True)
+        self.packet_filter_widget = ObjectList(
+            [
+                Column("selected", title="Sel.", data_type=bool, editable=True),
+                Column("name", title="Header"),
+            ],
+            sortable=True,
+        )
         self.packet_filter_widget.show()
         packet_filter_vbox.pack_start(self.packet_filter_widget, True, True, 4)
 
         class TypeIdConfig(object):
-            __slots__ = ['name', 'selected', 'typeid']
+            __slots__ = ["name", "selected", "typeid"]
 
-        self.packet_filter_list = [] # list of TypeIdConfig instances
+        self.packet_filter_list = []  # list of TypeIdConfig instances
 
         Header = ns.core.TypeId.LookupByName("ns3::Header")
         Trailer = ns.core.TypeId.LookupByName("ns3::Trailer")
@@ -209,15 +242,22 @@
 
         def update_capture_options():
             if self.op_AND_button.props.active:
-                self.packet_capture_options.mode = ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_AND
+                self.packet_capture_options.mode = (
+                    ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_AND
+                )
             else:
-                self.packet_capture_options.mode = ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_OR
+                self.packet_capture_options.mode = (
+                    ns.visualizer.PyViz.PACKET_CAPTURE_FILTER_HEADERS_OR
+                )
             self.packet_capture_options.numLastPackets = 100
-            self.packet_capture_options.headers = [c.typeid for c in self.packet_filter_list if c.selected]
+            self.packet_capture_options.headers = [
+                c.typeid for c in self.packet_filter_list if c.selected
+            ]
             self.visualizer.simulation.lock.acquire()
             try:
                 self.visualizer.simulation.sim_helper.SetPacketCaptureOptions(
-                    self.node.GetId(), self.packet_capture_options)
+                    self.node.GetId(), self.packet_capture_options
+                )
             finally:
                 self.visualizer.simulation.lock.release()
 
@@ -240,7 +280,9 @@
         op_buttons_box.show()
         packet_filter_vbox.pack_start(op_buttons_box, False, False, 4)
         self.op_AND_button = GObject.new(Gtk.RadioButton, label="AND", visible=True)
-        self.op_OR_button = GObject.new(Gtk.RadioButton, label="OR", visible=True, group=self.op_AND_button)
+        self.op_OR_button = GObject.new(
+            Gtk.RadioButton, label="OR", visible=True, group=self.op_AND_button
+        )
         op_buttons_box.add(self.op_AND_button)
         op_buttons_box.add(self.op_OR_button)
         self.op_OR_button.props.active = True
@@ -249,6 +291,7 @@
 
         def cell_edited(l, obj, attribute):
             update_capture_options()
+
         self.packet_filter_widget.connect("cell-edited", cell_edited)
 
         update_capture_options()
@@ -291,5 +334,6 @@
     menu_item.connect("activate", _show_it)
     menu.add(menu_item)
 
+
 def register(viz):
     viz.connect("populate-node-menu", populate_node_menu)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py ns-3.41/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py
--- ns-3.40/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py	2024-02-09 14:44:08.561794800 -0800
@@ -1,5 +1,13 @@
 import math
-from ns import ns
+
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 from gi.repository import GooCanvas
 
 try:
@@ -7,6 +15,7 @@
 except ModuleNotFoundError:
     from visualizer.base import Link, transform_distance_canvas_to_simulation
 
+
 ## WifiLink class
 class WifiLink(Link):
     ## @var node1
@@ -31,15 +40,19 @@
         super(WifiLink, self).__init__()
         self.node1 = sta
         self.dev = dev
-        self.node2 = None # ap
+        self.node2 = None  # ap
         self.canvas_item = GooCanvas.CanvasGroup(parent=parent_canvas_item)
-        self.invisible_line = GooCanvas.CanvasPolyline(parent=self.canvas_item,
-                                                 line_width=25.0,
-                                                 visibility=GooCanvas.CanvasItemVisibility.HIDDEN)
-        self.visible_line = GooCanvas.CanvasPolyline(parent=self.canvas_item,
-                                              line_width=1.0,
-                                              stroke_color_rgba=0xC00000FF,
-                                              line_dash=GooCanvas.CanvasLineDash.newv([2.0, 2.0 ]))
+        self.invisible_line = GooCanvas.CanvasPolyline(
+            parent=self.canvas_item,
+            line_width=25.0,
+            visibility=GooCanvas.CanvasItemVisibility.HIDDEN,
+        )
+        self.visible_line = GooCanvas.CanvasPolyline(
+            parent=self.canvas_item,
+            line_width=1.0,
+            stroke_color_rgba=0xC00000FF,
+            line_dash=GooCanvas.CanvasLineDash.newv([2.0, 2.0]),
+        )
         # self.invisible_line.set_property("pointer-events", (GooCanvas.CanvasPointerEvents.STROKE_MASK
         #                                             |GooCanvas.CanvasPointerEvents.FILL_MASK
         #                                             |GooCanvas.CanvasPointerEvents.PAINTED_MASK))
@@ -99,13 +112,17 @@
         pos2_x, pos2_y = self.node2.get_position()
         dx = pos2_x - pos1_x
         dy = pos2_y - pos1_y
-        d = transform_distance_canvas_to_simulation(math.sqrt(dx*dx + dy*dy))
+        d = transform_distance_canvas_to_simulation(math.sqrt(dx * dx + dy * dy))
         mac = self.dev.GetMac()
-        tooltip.set_text(("WiFi link between STA Node %i and AP Node %i; distance=%.2f m.\n"
-                          "SSID: %s\n"
-                          "BSSID: %s")
-                         % (self.node1.node_index, self.node2.node_index, d,
-                            mac.GetSsid(), mac.GetBssid()))
+        tooltip.set_text(
+            (
+                "WiFi link between STA Node %i and AP Node %i; distance=%.2f m.\n"
+                "SSID: %s\n"
+                "BSSID: %s"
+            )
+            % (self.node1.node_index, self.node2.node_index, d, mac.GetSsid(), mac.GetBssid())
+        )
+
 
 ## WifiLinkMonitor class
 class WifiLinkMonitor(object):
@@ -118,8 +135,8 @@
         @param self The object pointer.
         @param dummy_viz A dummy visualizer
         """
-        self.access_points = {} # bssid -> node
-        self.stations = [] # list of (sta_netdevice, viz_node, wifi_link)
+        self.access_points = {}  # bssid -> node
+        self.stations = []  # list of (sta_netdevice, viz_node, wifi_link)
 
     def scan_nodes(self, viz):
         """! Scan nodes function.
@@ -127,7 +144,7 @@
         @param viz The visualizer object
         @return none
         """
-        for (sta_netdevice, viz_node, wifi_link) in self.stations:
+        for sta_netdevice, viz_node, wifi_link in self.stations:
             wifi_link.destroy()
 
         self.access_points = {}
@@ -146,8 +163,8 @@
                 elif isinstance(wifi_mac, ns.wifi.ApWifiMac):
                     bssid = ns.network.Mac48Address.ConvertFrom(dev.GetAddress())
                     self.access_points[str(bssid)] = node
-        #print "APs: ", self.access_points
-        #print "STAs: ", self.stations
+        # print "APs: ", self.access_points
+        # print "STAs: ", self.stations
 
     def simulation_periodic_update(self, viz):
         """! Simulation Periodic Update function.
@@ -155,12 +172,12 @@
         @param viz The visualizer object
         @return none
         """
-        for (sta_netdevice, viz_node, wifi_link) in self.stations:
+        for sta_netdevice, viz_node, wifi_link in self.stations:
             if not sta_netdevice.IsLinkUp():
                 wifi_link.set_ap(None)
                 continue
             bssid = str(sta_netdevice.GetMac().GetBssid())
-            if bssid == '00:00:00:00:00:00':
+            if bssid == "00:00:00:00:00:00":
                 wifi_link.set_ap(None)
                 continue
             ap = self.access_points[bssid]
@@ -172,7 +189,7 @@
         @param viz The visualizer object
         @return none
         """
-        for (dummy_sta_netdevice, dummy_viz_node, wifi_link) in self.stations:
+        for dummy_sta_netdevice, dummy_viz_node, wifi_link in self.stations:
             if wifi_link is not None:
                 wifi_link.update_points()
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/visualizer/visualizer/svgitem.py ns-3.41/src/visualizer/visualizer/svgitem.py
--- ns-3.40/src/visualizer/visualizer/svgitem.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/visualizer/visualizer/svgitem.py	2024-02-09 14:44:08.561794800 -0800
@@ -1,8 +1,9 @@
-from gi.repository import GObject, GooCanvas
-import rsvg
-#import cairo
+# import cairo
 import os.path
 
+import rsvg
+from gi.repository import GObject, GooCanvas
+
 
 ## SvgItem class
 class SvgItem(GooCanvas.ItemSimple):
@@ -35,38 +36,43 @@
 
     ## setup our custom properties
     __gproperties__ = {
-        'x': (float,                                # property type
-              'X',                                  # property nick name
-              'The x coordinate of a SVG image',    # property description
-              -10e6,                                # property minimum value
-              10e6,                                 # property maximum value
-              0,                                    # property default value
-              GObject.PARAM_READWRITE),             # property flags
-
-        'y': (float,
-              'Y',
-              'The y coordinate of a SVG image',
-              -10e6,
-              10e6,
-              0,
-              GObject.PARAM_READWRITE),
-
-        'width': (float,
-                  'Width',
-                  'The width of the SVG Image',
-                  0,
-                  10e6,
-                  0,
-                  GObject.PARAM_READWRITE),
-
-        'height': (float,
-                   'Height',
-                   'The width of the SVG Image',
-                   0,
-                   10e6,
-                   0,
-                   GObject.PARAM_READWRITE),
-        }
+        "x": (
+            float,  # property type
+            "X",  # property nick name
+            "The x coordinate of a SVG image",  # property description
+            -10e6,  # property minimum value
+            10e6,  # property maximum value
+            0,  # property default value
+            GObject.PARAM_READWRITE,
+        ),  # property flags
+        "y": (
+            float,
+            "Y",
+            "The y coordinate of a SVG image",
+            -10e6,
+            10e6,
+            0,
+            GObject.PARAM_READWRITE,
+        ),
+        "width": (
+            float,
+            "Width",
+            "The width of the SVG Image",
+            0,
+            10e6,
+            0,
+            GObject.PARAM_READWRITE,
+        ),
+        "height": (
+            float,
+            "Height",
+            "The width of the SVG Image",
+            0,
+            10e6,
+            0,
+            GObject.PARAM_READWRITE,
+        ),
+    }
 
     def __init__(self, x, y, rsvg_handle, **kwargs):
         """!
@@ -97,26 +103,26 @@
         @param value property value
         @return exception if unknown property
         """
-        if pspec.name == 'x':
+        if pspec.name == "x":
             self.x = value
 
             # make sure we update the display
             self.changed(True)
 
-        elif pspec.name == 'y':
+        elif pspec.name == "y":
             self.y = value
 
             # make sure we update the display
             self.changed(True)
 
-        elif pspec.name == 'width':
+        elif pspec.name == "width":
             self.custom_width = value
             self._size_changed()
 
             # make sure we update the display
             self.changed(True)
 
-        elif pspec.name == 'height':
+        elif pspec.name == "height":
             self.custom_height = value
             self._size_changed()
 
@@ -124,7 +130,7 @@
             self.changed(True)
 
         else:
-            raise AttributeError('unknown property %s' % pspec.name)
+            raise AttributeError("unknown property %s" % pspec.name)
 
     def _size_changed(self):
         """!
@@ -141,12 +147,12 @@
             self.width = self.custom_width
             self.sx = self.custom_width / self.handle.props.width
             self.sy = self.sx
-            self.height = self.handle.props.height*self.sy
+            self.height = self.handle.props.height * self.sy
         elif self.custom_width is None and self.custom_height is not None:
             self.height = self.custom_height
             self.sy = self.custom_height / self.handle.props.height
-            self.sx  = self.sy
-            self.width = self.handle.props.width*self.sx
+            self.sx = self.sy
+            self.width = self.handle.props.width * self.sx
         else:
             self.width = self.custom_width
             self.height = self.custom_height
@@ -160,23 +166,23 @@
         @param pspec property name
         @return property value or exception if unknown property
         """
-        if pspec.name == 'x':
+        if pspec.name == "x":
             return self.x
 
-        elif pspec.name == 'y':
+        elif pspec.name == "y":
             return self.y
 
-        elif pspec.name == 'width':
+        elif pspec.name == "width":
             self.width = self.handle.props.width
             self.height = self.handle.props.height
 
             return self.width
 
-        elif pspec.name == 'height':
+        elif pspec.name == "height":
             return self.height
 
         else:
-            raise AttributeError('unknown property %s' % pspec.name)
+            raise AttributeError("unknown property %s" % pspec.name)
 
     def do_simple_paint(self, cr, bounds):
         """!
@@ -212,7 +218,9 @@
         @param is_pointer_event is the event a pointer event
         @return true if at or false if not
         """
-        if ((x < self.x) or (x > self.x + self.width)) or ((y < self.y) or (y > self.y + self.height)):
+        if ((x < self.x) or (x > self.x + self.width)) or (
+            (y < self.y) or (y > self.y + self.height)
+        ):
             return False
         else:
             return True
@@ -220,12 +228,12 @@
 
 _rsvg_cache = dict()
 
+
 def rsvg_handle_factory(base_file_name):
     try:
         return _rsvg_cache[base_file_name]
     except KeyError:
-        full_path = os.path.join(os.path.dirname(__file__), 'resource', base_file_name)
+        full_path = os.path.join(os.path.dirname(__file__), "resource", base_file_name)
         rsvg_handle = rsvg.Handle(full_path)
         _rsvg_cache[base_file_name] = rsvg_handle
         return rsvg_handle
-
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/CMakeLists.txt ns-3.41/src/wifi/CMakeLists.txt
--- ns-3.40/src/wifi/CMakeLists.txt	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/wifi/CMakeLists.txt	2024-02-09 14:44:08.561794800 -0800
@@ -12,6 +12,7 @@
     helper/wifi-mac-helper.cc
     helper/wifi-radio-energy-model-helper.cc
     helper/yans-wifi-helper.cc
+    model/addba-extension.cc
     model/adhoc-wifi-mac.cc
     model/ampdu-subframe-header.cc
     model/ampdu-tag.cc
@@ -62,6 +63,7 @@
     model/interference-helper.cc
     model/mac-rx-middle.cc
     model/mac-tx-middle.cc
+    model/mgt-action-headers.cc
     model/mgt-headers.cc
     model/mpdu-aggregator.cc
     model/msdu-aggregator.cc
@@ -163,6 +165,7 @@
     helper/wifi-mac-helper.h
     helper/wifi-radio-energy-model-helper.h
     helper/yans-wifi-helper.h
+    model/addba-extension.h
     model/adhoc-wifi-mac.h
     model/ampdu-subframe-header.h
     model/ampdu-tag.h
@@ -213,6 +216,7 @@
     model/interference-helper.h
     model/mac-rx-middle.h
     model/mac-tx-middle.h
+    model/mgt-action-headers.h
     model/mgt-headers.h
     model/mpdu-aggregator.h
     model/msdu-aggregator.h
@@ -320,12 +324,8 @@
   SOURCE_FILES ${source_files}
   HEADER_FILES ${header_files}
   LIBRARIES_TO_LINK
-    ${libnetwork}
-    ${libpropagation}
     ${libenergy}
     ${libspectrum}
-    ${libantenna}
-    ${libmobility}
     ${gsl_libraries}
   TEST_SOURCES
     test/block-ack-test-suite.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/doc/Makefile ns-3.41/src/wifi/doc/Makefile
--- ns-3.40/src/wifi/doc/Makefile	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/wifi/doc/Makefile	2024-02-09 14:44:08.561794800 -0800
@@ -25,6 +25,8 @@
 	$(FIGURES)/FemHierarchy.eps \
         $(FIGURES)/assoc-manager.eps \
         $(FIGURES)/clear-channel.eps \
+        $(FIGURES)/emlsr-dl-txop.eps \
+        $(FIGURES)/emlsr-ul-txop.eps \
         $(FIGURES)/nist-frame-success-rate.eps \
         $(FIGURES)/nist-frame-success-rate-n.eps \
         $(FIGURES)/nist-frame-success-rate-ac.eps \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/doc/source/conf.py ns-3.41/src/wifi/doc/source/conf.py
--- ns-3.40/src/wifi/doc/source/conf.py	2023-09-27 13:30:37.894839800 -0700
+++ ns-3.41/src/wifi/doc/source/conf.py	2024-02-09 14:44:08.565794700 -0800
@@ -11,202 +11,208 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os
+import os
+import sys
 
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration -----------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.imgmath']
+extensions = ["sphinx.ext.imgmath"]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
 
 # The suffix of source filenames.
-source_suffix = '.rst'
+source_suffix = ".rst"
 
 # The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = 'wifi'
+master_doc = "wifi"
 
 # General information about the project.
-project = u'ns-3'
-copyright = u'ns-3 project'
+project = "ns-3"
+copyright = "ns-3 project"
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3-dev'
+version = "ns-3-dev"
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3-dev'
+release = "ns-3-dev"
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
-#language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
-#today = ''
+# today = ''
 # Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
 exclude_patterns = []
 
 # The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
+# default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
-#show_authors = False
+# show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
 
 # A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
 
 
 # -- Options for HTML output ---------------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = "default"
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
-#html_theme_options = {}
+# html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".
-#html_title = None
+# html_title = None
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
-#html_logo = None
+# html_logo = None
 
 # The name of an image file (within the static path) to use as favicon of the
 # docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
-#html_favicon = None
+# html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-#html_static_path = ['_static']
+# html_static_path = ['_static']
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
 
 # If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
 
 # If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
 
 # Output file base name for HTML help builder.
-#htmlhelp_basename = 'ns-3doc'
+# htmlhelp_basename = 'ns-3doc'
 
 
 # -- Options for LaTeX output --------------------------------------------------
 
 # The paper size ('letter' or 'a4').
-#latex_paper_size = 'letter'
+# latex_paper_size = 'letter'
 
 # The font size ('10pt', '11pt' or '12pt').
-#latex_font_size = '10pt'
+# latex_font_size = '10pt'
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-#  ('wifi-testing', 'wifi-doc-testing.tex', u'Wi-Fi Testing Documentation', u'ns-3 project', 'manual'),
-#  ('wifi-design', 'wifi-doc-design.tex', u'Wi-Fi Design Documentation', u'ns-3 project', 'manual'),
-#  ('wifi-user', 'wifi-doc-user.tex', u'Wi-Fi User Documentation', u'ns-3 project', 'manual'),
-  ('wifi', 'wifi-module-doc.tex', u'The ns-3 Wi-Fi Module Documentation', u'ns-3 project', 'manual'),
+    #  ('wifi-testing', 'wifi-doc-testing.tex', u'Wi-Fi Testing Documentation', u'ns-3 project', 'manual'),
+    #  ('wifi-design', 'wifi-doc-design.tex', u'Wi-Fi Design Documentation', u'ns-3 project', 'manual'),
+    #  ('wifi-user', 'wifi-doc-user.tex', u'Wi-Fi User Documentation', u'ns-3 project', 'manual'),
+    (
+        "wifi",
+        "wifi-module-doc.tex",
+        "The ns-3 Wi-Fi Module Documentation",
+        "ns-3 project",
+        "manual",
+    ),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
-#latex_logo = None
+# latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
 
 # If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
 
 # Additional stuff for the LaTeX preamble.
-#latex_preamble = ''
+# latex_preamble = ''
 
 # Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
 
 # If false, no module index is generated.
-#latex_domain_indices = True
-
+# latex_domain_indices = True
 
 
 # add page breaks in the pdf. Level 1 is for top-level sections, level 2 for subsections, and so on.
@@ -217,7 +223,4 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'ns-3-model-library', u'ns-3 Model Library',
-     [u'ns-3 project'], 1)
-]
+man_pages = [("index", "ns-3-model-library", "ns-3 Model Library", ["ns-3 project"], 1)]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/doc/source/figures/emlsr-dl-txop.eps ns-3.41/src/wifi/doc/source/figures/emlsr-dl-txop.eps
--- ns-3.40/src/wifi/doc/source/figures/emlsr-dl-txop.eps	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/wifi/doc/source/figures/emlsr-dl-txop.eps	2024-02-09 14:44:08.569795000 -0800
@@ -0,0 +1,2189 @@
+%!PS-Adobe-3.0 EPSF-3.0 
+%%BoundingBox: 0 0 1077 575
+%%Pages: 0
+%%Creator: LibreOffice 7.5
+%%Title: none
+%%CreationDate: none
+%%LanguageLevel: 2
+%%EndComments
+%%BeginProlog
+%%BeginResource: procset SDRes-Prolog 1.0 0
+/b4_inc_state save def
+/dict_count countdictstack def
+/op_count count 1 sub def
+userdict begin
+0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit[] 0 setdash newpath
+/languagelevel where {pop languagelevel 1 ne {false setstrokeadjust false setoverprint} if} if
+/bdef {bind def} bind def
+/c {setrgbcolor} bdef
+/l {neg lineto} bdef
+/rl {neg rlineto} bdef
+/lc {setlinecap} bdef
+/lj {setlinejoin} bdef
+/lw {setlinewidth} bdef
+/ml {setmiterlimit} bdef
+/ld {setdash} bdef
+/m {neg moveto} bdef
+/ct {6 2 roll neg 6 2 roll neg 6 2 roll neg curveto} bdef
+/r {rotate} bdef
+/t {neg translate} bdef
+/s {scale} bdef
+/sw {show} bdef
+/gs {gsave} bdef
+/gr {grestore} bdef
+/f {findfont dup length dict begin
+{1 index /FID ne {def} {pop pop} ifelse} forall /Encoding ISOLatin1Encoding def
+currentdict end /NFont exch definefont pop /NFont findfont} bdef
+/p {closepath} bdef
+/sf {scalefont setfont} bdef
+/ef {eofill}bdef
+/pc {closepath stroke}bdef
+/ps {stroke}bdef
+/pum {matrix currentmatrix}bdef
+/pom {setmatrix}bdef
+/bs {/aString exch def /nXOfs exch def /nWidth exch def currentpoint nXOfs 0 rmoveto pum nWidth aString stringwidth pop div 1 scale aString show pom moveto} bdef
+%%EndResource
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+%%EndPageSetup
+pum
+0.02834 0.02834 s 
+0 -20289 t
+/tm matrix currentmatrix def
+tm setmatrix
+-2305 -2051 t 
+1 1 s 
+gs
+0 lw 1 lj 0.003 0.003 0.003 c 2291 7066 m  37533 7066 l  ps
+gr
+pum
+2542 6732 t
+0.003 0.003 0.003 c 362 0 m  312 -128 l  113 -128 l  63 0 l  1 0 l  180 -438 l 
+247 -438 l  422 0 l  362 0 l  p
+212 -393 m  210 -384 l  204 -367 197 -345 187 -318 ct 131 -174 l  294 -174 l 
+238 -319 l  232 -333 227 -349 221 -367 ct 212 -393 l  p ef
+813 -305 m  813 -264 800 -231 773 -207 ct 746 -182 709 -170 663 -170 ct 534 -170 l 
+534 1 l  475 1 l  475 -437 l  659 -437 l  708 -437 746 -426 773 -403 ct 800 -380 813 -347 813 -305 ct 
+p
+754 -305 m  754 -361 720 -390 652 -390 ct 534 -390 l  534 -217 l  654 -217 l 
+720 -217 754 -246 754 -305 ct p ef
+1436 0 m  1436 -292 l  1436 -324 1436 -355 1438 -385 ct 1428 -348 1419 -319 1411 -298 ct 
+1298 0 l  1257 0 l  1142 -298 l  1125 -351 l  1115 -385 l  1116 -351 l 
+1117 -292 l  1117 0 l  1064 0 l  1064 -438 l  1142 -438 l  1258 -134 l 
+1262 -122 1266 -109 1270 -95 ct 1274 -81 1276 -71 1278 -65 ct 1279 -73 1283 -85 1288 -102 ct 
+1293 -119 1297 -130 1299 -134 ct 1413 -438 l  1489 -438 l  1489 0 l  1436 0 l 
+p ef
+1593 0 m  1593 -438 l  1652 -438 l  1652 -48 l  1873 -48 l  1873 0 l  1593 0 l 
+p ef
+2320 -223 m  2320 -178 2311 -139 2294 -105 ct 2276 -71 2251 -45 2219 -27 ct 
+2187 -9 2150 0 2107 0 ct 1944 0 l  1944 -437 l  2089 -437 l  2163 -437 2220 -419 2260 -382 ct 
+2300 -345 2320 -292 2320 -223 ct p
+2261 -223 m  2261 -277 2246 -319 2216 -347 ct 2187 -376 2144 -390 2087 -390 ct 
+2003 -390 l  2003 -47 l  2101 -47 l  2133 -47 2161 -55 2185 -69 ct 2209 -83 2228 -103 2241 -129 ct 
+2254 -156 2261 -187 2261 -223 ct p ef
+pom
+pum
+2542 8032 t
+52 0 m  52 -437 l  384 -437 l  384 -389 l  111 -389 l  111 -249 l  365 -249 l 
+365 -201 l  111 -201 l  111 -48 l  396 -48 l  396 0 l  52 0 l  p ef
+847 0 m  847 -292 l  847 -324 847 -355 849 -385 ct 839 -348 830 -319 822 -298 ct 
+709 0 l  668 0 l  553 -298 l  536 -351 l  526 -385 l  527 -351 l  528 -292 l 
+528 0 l  475 0 l  475 -438 l  553 -438 l  669 -134 l  673 -122 677 -109 681 -95 ct 
+685 -81 687 -71 689 -65 ct 690 -73 694 -85 699 -102 ct 704 -119 708 -130 710 -134 ct 
+824 -438 l  900 -438 l  900 0 l  847 0 l  p ef
+1005 0 m  1005 -438 l  1064 -438 l  1064 -48 l  1285 -48 l  1285 0 l  1005 0 l 
+p ef
+1698 -121 m  1698 -81 1683 -49 1651 -27 ct 1620 -5 1575 6 1518 6 ct 1411 6 1350 -31 1333 -105 ct 
+1390 -117 l  1397 -90 1411 -71 1432 -59 ct 1454 -46 1483 -40 1520 -40 ct 1558 -40 1588 -47 1609 -60 ct 
+1629 -73 1640 -92 1640 -118 ct 1640 -132 1637 -144 1630 -153 ct 1624 -161 1614 -169 1603 -175 ct 
+1591 -180 1577 -185 1560 -189 ct 1544 -193 1526 -197 1506 -202 ct 1472 -210 1445 -217 1428 -225 ct 
+1410 -233 1396 -241 1385 -250 ct 1375 -260 1367 -271 1362 -284 ct 1356 -296 1353 -311 1353 -327 ct 
+1353 -364 1368 -393 1396 -414 ct 1425 -434 1466 -444 1519 -444 ct 1569 -444 1607 -436 1633 -421 ct 
+1659 -406 1678 -380 1688 -343 ct 1630 -333 l  1623 -356 1611 -373 1593 -384 ct 
+1575 -394 1550 -399 1519 -399 ct 1484 -399 1457 -394 1439 -382 ct 1420 -370 1411 -353 1411 -330 ct 
+1411 -317 1415 -306 1422 -297 ct 1429 -288 1439 -281 1453 -274 ct 1466 -268 1493 -261 1533 -252 ct 
+1546 -249 1560 -246 1573 -242 ct 1586 -239 1599 -235 1611 -231 ct 1623 -226 1635 -221 1646 -215 ct 
+1656 -209 1665 -202 1673 -193 ct 1681 -185 1687 -174 1692 -163 ct 1696 -151 1698 -137 1698 -121 ct 
+p ef
+2088 -1 m  1974 -182 l  1838 -182 l  1838 -1 l  1779 -1 l  1779 -437 l 
+1985 -437 l  2034 -437 2072 -426 2099 -404 ct 2125 -382 2139 -352 2139 -313 ct 
+2139 -280 2129 -253 2110 -231 ct 2091 -209 2065 -195 2032 -189 ct 2156 -1 l 
+2088 -1 l  p
+2079 -312 m  2079 -337 2071 -357 2053 -370 ct 2036 -383 2011 -390 1979 -390 ct 
+1838 -390 l  1838 -229 l  1981 -229 l  2012 -229 2037 -236 2054 -251 ct 2071 -265 2079 -286 2079 -312 ct 
+p ef
+pom
+pum
+2896 8743 t
+85 -170 m  85 -125 92 -92 106 -70 ct 120 -49 142 -38 170 -38 ct 190 -38 206 -43 220 -54 ct 
+233 -65 241 -81 244 -104 ct 301 -100 l  296 -68 283 -42 260 -23 ct 236 -4 207 6 171 6 ct 
+125 6 89 -9 64 -39 ct 39 -68 27 -111 27 -168 ct 27 -225 39 -268 64 -297 ct 89 -327 125 -342 171 -342 ct 
+205 -342 234 -333 256 -315 ct 279 -298 293 -273 299 -242 ct 242 -237 l  239 -256 231 -271 220 -282 ct 
+208 -293 191 -298 169 -298 ct 140 -298 118 -288 105 -269 ct 92 -249 85 -216 85 -170 ct 
+p ef
+361 1 m  361 -460 l  417 -460 l  417 1 l  361 1 l  p ef
+499 -407 m  499 -460 l  555 -460 l  555 -407 l  499 -407 l  p
+499 1 m  499 -335 l  555 -335 l  555 1 l  499 1 l  p ef
+683 -156 m  683 -118 691 -88 706 -67 ct 722 -46 746 -36 776 -36 ct 800 -36 820 -41 834 -50 ct 
+849 -60 859 -72 864 -87 ct 913 -73 l  893 -20 847 6 776 6 ct 727 6 689 -9 663 -38 ct 
+637 -68 624 -112 624 -170 ct 624 -226 637 -268 663 -298 ct 689 -327 726 -342 774 -342 ct 
+873 -342 922 -283 922 -164 ct 922 -156 l  683 -156 l  p
+864 -199 m  861 -234 852 -260 837 -277 ct 822 -293 801 -301 773 -301 ct 746 -301 725 -292 709 -274 ct 
+693 -256 684 -231 683 -199 ct 864 -199 l  p ef
+1204 0 m  1204 -213 l  1204 -235 1202 -252 1197 -264 ct 1193 -277 1186 -285 1177 -291 ct 
+1167 -296 1153 -299 1135 -299 ct 1108 -299 1087 -290 1071 -271 ct 1056 -253 1048 -227 1048 -195 ct 
+1048 0 l  992 0 l  992 -264 l  992 -303 991 -327 990 -336 ct 1043 -336 l 
+1043 -335 1043 -332 1044 -327 ct 1044 -323 1044 -318 1044 -312 ct 1045 -306 1045 -295 1045 -278 ct 
+1046 -278 l  1059 -302 1074 -318 1091 -328 ct 1108 -337 1129 -342 1154 -342 ct 
+1190 -342 1217 -333 1234 -315 ct 1251 -296 1260 -266 1260 -224 ct 1260 0 l  1204 0 l 
+p ef
+1472 -2 m  1453 3 1435 5 1415 5 ct 1371 5 1348 -20 1348 -71 ct 1348 -295 l  1310 -295 l 
+1310 -336 l  1351 -336 l  1367 -411 l  1404 -411 l  1404 -336 l  1466 -336 l 
+1466 -295 l  1404 -295 l  1404 -83 l  1404 -67 1407 -56 1412 -49 ct 1417 -43 1427 -39 1440 -39 ct 
+1447 -39 1458 -41 1472 -44 ct 1472 -2 l  p ef
+pom
+pum
+2542 14233 t
+362 0 m  312 -128 l  113 -128 l  63 0 l  1 0 l  180 -438 l  247 -438 l 
+422 0 l  362 0 l  p
+212 -393 m  210 -384 l  204 -367 197 -345 187 -318 ct 131 -174 l  294 -174 l 
+238 -319 l  232 -333 227 -349 221 -367 ct 212 -393 l  p ef
+813 -305 m  813 -264 800 -231 773 -207 ct 746 -182 709 -170 663 -170 ct 534 -170 l 
+534 1 l  475 1 l  475 -437 l  659 -437 l  708 -437 746 -426 773 -403 ct 800 -380 813 -347 813 -305 ct 
+p
+754 -305 m  754 -361 720 -390 652 -390 ct 534 -390 l  534 -217 l  654 -217 l 
+720 -217 754 -246 754 -305 ct p ef
+1436 0 m  1436 -292 l  1436 -324 1436 -355 1438 -385 ct 1428 -348 1419 -319 1411 -298 ct 
+1298 0 l  1257 0 l  1142 -298 l  1125 -351 l  1115 -385 l  1116 -351 l 
+1117 -292 l  1117 0 l  1064 0 l  1064 -438 l  1142 -438 l  1258 -134 l 
+1262 -122 1266 -109 1270 -95 ct 1274 -81 1276 -71 1278 -65 ct 1279 -73 1283 -85 1288 -102 ct 
+1293 -119 1297 -130 1299 -134 ct 1413 -438 l  1489 -438 l  1489 0 l  1436 0 l 
+p ef
+1593 0 m  1593 -438 l  1652 -438 l  1652 -48 l  1873 -48 l  1873 0 l  1593 0 l 
+p ef
+2320 -223 m  2320 -178 2311 -139 2294 -105 ct 2276 -71 2251 -45 2219 -27 ct 
+2187 -9 2150 0 2107 0 ct 1944 0 l  1944 -437 l  2089 -437 l  2163 -437 2220 -419 2260 -382 ct 
+2300 -345 2320 -292 2320 -223 ct p
+2261 -223 m  2261 -277 2246 -319 2216 -347 ct 2187 -376 2144 -390 2087 -390 ct 
+2003 -390 l  2003 -47 l  2101 -47 l  2133 -47 2161 -55 2185 -69 ct 2209 -83 2228 -103 2241 -129 ct 
+2254 -156 2261 -187 2261 -223 ct p ef
+pom
+pum
+2542 15532 t
+52 0 m  52 -437 l  384 -437 l  384 -389 l  111 -389 l  111 -249 l  365 -249 l 
+365 -201 l  111 -201 l  111 -48 l  396 -48 l  396 0 l  52 0 l  p ef
+847 0 m  847 -292 l  847 -324 847 -355 849 -385 ct 839 -348 830 -319 822 -298 ct 
+709 0 l  668 0 l  553 -298 l  536 -351 l  526 -385 l  527 -351 l  528 -292 l 
+528 0 l  475 0 l  475 -438 l  553 -438 l  669 -134 l  673 -122 677 -109 681 -95 ct 
+685 -81 687 -71 689 -65 ct 690 -73 694 -85 699 -102 ct 704 -119 708 -130 710 -134 ct 
+824 -438 l  900 -438 l  900 0 l  847 0 l  p ef
+1005 0 m  1005 -438 l  1064 -438 l  1064 -48 l  1285 -48 l  1285 0 l  1005 0 l 
+p ef
+1698 -121 m  1698 -81 1683 -49 1651 -27 ct 1620 -5 1575 6 1518 6 ct 1411 6 1350 -31 1333 -105 ct 
+1390 -117 l  1397 -90 1411 -71 1432 -59 ct 1454 -46 1483 -40 1520 -40 ct 1558 -40 1588 -47 1609 -60 ct 
+1629 -73 1640 -92 1640 -118 ct 1640 -132 1637 -144 1630 -153 ct 1624 -161 1614 -169 1603 -175 ct 
+1591 -180 1577 -185 1560 -189 ct 1544 -193 1526 -197 1506 -202 ct 1472 -210 1445 -217 1428 -225 ct 
+1410 -233 1396 -241 1385 -250 ct 1375 -260 1367 -271 1362 -284 ct 1356 -296 1353 -311 1353 -327 ct 
+1353 -364 1368 -393 1396 -414 ct 1425 -434 1466 -444 1519 -444 ct 1569 -444 1607 -436 1633 -421 ct 
+1659 -406 1678 -380 1688 -343 ct 1630 -333 l  1623 -356 1611 -373 1593 -384 ct 
+1575 -394 1550 -399 1519 -399 ct 1484 -399 1457 -394 1439 -382 ct 1420 -370 1411 -353 1411 -330 ct 
+1411 -317 1415 -306 1422 -297 ct 1429 -288 1439 -281 1453 -274 ct 1466 -268 1493 -261 1533 -252 ct 
+1546 -249 1560 -246 1573 -242 ct 1586 -239 1599 -235 1611 -231 ct 1623 -226 1635 -221 1646 -215 ct 
+1656 -209 1665 -202 1673 -193 ct 1681 -185 1687 -174 1692 -163 ct 1696 -151 1698 -137 1698 -121 ct 
+p ef
+2088 -1 m  1974 -182 l  1838 -182 l  1838 -1 l  1779 -1 l  1779 -437 l 
+1985 -437 l  2034 -437 2072 -426 2099 -404 ct 2125 -382 2139 -352 2139 -313 ct 
+2139 -280 2129 -253 2110 -231 ct 2091 -209 2065 -195 2032 -189 ct 2156 -1 l 
+2088 -1 l  p
+2079 -312 m  2079 -337 2071 -357 2053 -370 ct 2036 -383 2011 -390 1979 -390 ct 
+1838 -390 l  1838 -229 l  1981 -229 l  2012 -229 2037 -236 2054 -251 ct 2071 -265 2079 -286 2079 -312 ct 
+p ef
+pom
+pum
+2896 16243 t
+85 -170 m  85 -125 92 -92 106 -70 ct 120 -49 142 -38 170 -38 ct 190 -38 206 -43 220 -54 ct 
+233 -65 241 -81 244 -104 ct 301 -100 l  296 -68 283 -42 260 -23 ct 236 -4 207 6 171 6 ct 
+125 6 89 -9 64 -39 ct 39 -68 27 -111 27 -168 ct 27 -225 39 -268 64 -297 ct 89 -327 125 -342 171 -342 ct 
+205 -342 234 -333 256 -315 ct 279 -298 293 -273 299 -242 ct 242 -237 l  239 -256 231 -271 220 -282 ct 
+208 -293 191 -298 169 -298 ct 140 -298 118 -288 105 -269 ct 92 -249 85 -216 85 -170 ct 
+p ef
+361 1 m  361 -460 l  417 -460 l  417 1 l  361 1 l  p ef
+499 -407 m  499 -460 l  555 -460 l  555 -407 l  499 -407 l  p
+499 1 m  499 -335 l  555 -335 l  555 1 l  499 1 l  p ef
+683 -156 m  683 -118 691 -88 706 -67 ct 722 -46 746 -36 776 -36 ct 800 -36 820 -41 834 -50 ct 
+849 -60 859 -72 864 -87 ct 913 -73 l  893 -20 847 6 776 6 ct 727 6 689 -9 663 -38 ct 
+637 -68 624 -112 624 -170 ct 624 -226 637 -268 663 -298 ct 689 -327 726 -342 774 -342 ct 
+873 -342 922 -283 922 -164 ct 922 -156 l  683 -156 l  p
+864 -199 m  861 -234 852 -260 837 -277 ct 822 -293 801 -301 773 -301 ct 746 -301 725 -292 709 -274 ct 
+693 -256 684 -231 683 -199 ct 864 -199 l  p ef
+1204 0 m  1204 -213 l  1204 -235 1202 -252 1197 -264 ct 1193 -277 1186 -285 1177 -291 ct 
+1167 -296 1153 -299 1135 -299 ct 1108 -299 1087 -290 1071 -271 ct 1056 -253 1048 -227 1048 -195 ct 
+1048 0 l  992 0 l  992 -264 l  992 -303 991 -327 990 -336 ct 1043 -336 l 
+1043 -335 1043 -332 1044 -327 ct 1044 -323 1044 -318 1044 -312 ct 1045 -306 1045 -295 1045 -278 ct 
+1046 -278 l  1059 -302 1074 -318 1091 -328 ct 1108 -337 1129 -342 1154 -342 ct 
+1190 -342 1217 -333 1234 -315 ct 1251 -296 1260 -266 1260 -224 ct 1260 0 l  1204 0 l 
+p ef
+1472 -2 m  1453 3 1435 5 1415 5 ct 1371 5 1348 -20 1348 -71 ct 1348 -295 l  1310 -295 l 
+1310 -336 l  1351 -336 l  1367 -411 l  1404 -411 l  1404 -336 l  1466 -336 l 
+1466 -295 l  1404 -295 l  1404 -83 l  1404 -67 1407 -56 1412 -49 ct 1417 -43 1427 -39 1440 -39 ct 
+1447 -39 1458 -41 1472 -44 ct 1472 -2 l  p ef
+pom
+pum
+38348 7285 t
+52 0 m  52 -438 l  111 -438 l  111 -48 l  332 -48 l  332 0 l  52 0 l  p ef
+393 -407 m  393 -460 l  449 -460 l  449 -407 l  393 -407 l  p
+393 1 m  393 -335 l  449 -335 l  449 1 l  393 1 l  p ef
+747 0 m  747 -213 l  747 -235 745 -252 740 -264 ct 736 -277 729 -285 720 -291 ct 
+710 -296 696 -299 678 -299 ct 651 -299 630 -290 614 -271 ct 599 -253 591 -227 591 -195 ct 
+591 0 l  535 0 l  535 -264 l  535 -303 534 -327 533 -336 ct 586 -336 l  586 -335 586 -332 587 -327 ct 
+587 -323 587 -318 587 -312 ct 588 -306 588 -295 588 -278 ct 589 -278 l  602 -302 617 -318 634 -328 ct 
+651 -337 672 -342 697 -342 ct 733 -342 760 -333 777 -315 ct 794 -296 803 -266 803 -224 ct 
+803 0 l  747 0 l  p ef
+1095 1 m  982 -153 l  941 -119 l  941 1 l  885 1 l  885 -460 l  941 -460 l 
+941 -172 l  1088 -335 l  1153 -335 l  1017 -191 l  1160 1 l  1095 1 l 
+p ef
+1666 -219 m  1666 -146 1653 -90 1628 -52 ct 1602 -13 1564 6 1514 6 ct 1464 6 1426 -13 1401 -51 ct 
+1375 -90 1363 -146 1363 -219 ct 1363 -294 1375 -350 1400 -388 ct 1424 -425 1463 -444 1516 -444 ct 
+1567 -444 1605 -425 1630 -387 ct 1654 -349 1666 -293 1666 -219 ct p
+1610 -219 m  1610 -282 1602 -328 1588 -356 ct 1573 -385 1549 -399 1516 -399 ct 
+1481 -399 1457 -385 1442 -357 ct 1427 -329 1419 -283 1419 -219 ct 1419 -157 1427 -112 1442 -83 ct 
+1457 -54 1481 -40 1514 -40 ct 1547 -40 1571 -54 1587 -84 ct 1602 -113 1610 -158 1610 -219 ct 
+p ef
+pom
+pum
+38349 14885 t
+52 0 m  52 -438 l  111 -438 l  111 -48 l  332 -48 l  332 0 l  52 0 l  p ef
+393 -407 m  393 -460 l  449 -460 l  449 -407 l  393 -407 l  p
+393 1 m  393 -335 l  449 -335 l  449 1 l  393 1 l  p ef
+747 0 m  747 -213 l  747 -235 745 -252 740 -264 ct 736 -277 729 -285 720 -291 ct 
+710 -296 696 -299 678 -299 ct 651 -299 630 -290 614 -271 ct 599 -253 591 -227 591 -195 ct 
+591 0 l  535 0 l  535 -264 l  535 -303 534 -327 533 -336 ct 586 -336 l  586 -335 586 -332 587 -327 ct 
+587 -323 587 -318 587 -312 ct 588 -306 588 -295 588 -278 ct 589 -278 l  602 -302 617 -318 634 -328 ct 
+651 -337 672 -342 697 -342 ct 733 -342 760 -333 777 -315 ct 794 -296 803 -266 803 -224 ct 
+803 0 l  747 0 l  p ef
+1095 1 m  982 -153 l  941 -119 l  941 1 l  885 1 l  885 -460 l  941 -460 l 
+941 -172 l  1088 -335 l  1153 -335 l  1017 -191 l  1160 1 l  1095 1 l 
+p ef
+1386 0 m  1386 -47 l  1498 -47 l  1498 -384 l  1399 -314 l  1399 -366 l 
+1502 -438 l  1554 -438 l  1554 -47 l  1660 -47 l  1660 0 l  1386 0 l  p ef
+pom
+gs
+1.000 1.000 0.843 c 3968 22179 m  2856 22179 l  2856 21544 l  5079 21544 l 
+5079 22179 l  3968 22179 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 3967 22178 m  2855 22178 l  2855 21543 l 
+5078 21543 l  5078 22178 l  3967 22178 l  pc
+gr
+pum
+5665 22064 t
+238 0 m  238 -213 l  238 -245 234 -268 225 -280 ct 216 -293 200 -299 177 -299 ct 
+153 -299 134 -290 120 -272 ct 106 -253 100 -228 100 -195 ct 100 0 l  44 0 l 
+44 -264 l  44 -303 43 -327 42 -336 ct 95 -336 l  95 -335 95 -332 96 -327 ct 
+96 -323 96 -318 96 -312 ct 97 -306 97 -295 97 -278 ct 98 -278 l  110 -302 124 -319 140 -328 ct 
+155 -337 174 -342 196 -342 ct 222 -342 242 -337 257 -327 ct 271 -317 282 -301 287 -278 ct 
+288 -278 l  300 -301 314 -317 330 -327 ct 347 -337 367 -342 390 -342 ct 424 -342 449 -333 464 -314 ct 
+479 -296 487 -266 487 -224 ct 487 0 l  432 0 l  432 -213 l  432 -245 427 -268 419 -280 ct 
+410 -293 394 -299 371 -299 ct 346 -299 327 -290 314 -272 ct 300 -254 293 -228 293 -195 ct 
+293 0 l  238 0 l  p ef
+657 6 m  624 6 598 -3 581 -21 ct 564 -38 556 -63 556 -94 ct 556 -129 567 -155 590 -174 ct 
+613 -192 650 -202 701 -204 ct 776 -205 l  776 -223 l  776 -250 770 -270 759 -282 ct 
+747 -294 729 -299 704 -299 ct 679 -299 661 -295 650 -287 ct 638 -278 631 -265 629 -246 ct 
+571 -251 l  580 -312 625 -342 705 -342 ct 748 -342 779 -332 801 -313 ct 822 -294 833 -266 833 -229 ct 
+833 -85 l  833 -68 835 -55 839 -47 ct 843 -39 852 -35 864 -35 ct 869 -35 875 -35 882 -37 ct 
+882 -2 l  868 1 854 3 839 3 ct 818 3 803 -2 794 -13 ct 785 -24 779 -41 778 -64 ct 
+776 -64 l  762 -39 745 -21 726 -10 ct 707 1 684 6 657 6 ct p
+670 -36 m  691 -36 709 -41 725 -50 ct 741 -59 753 -72 762 -88 ct 772 -104 776 -121 776 -138 ct 
+776 -166 l  715 -165 l  689 -164 669 -161 655 -157 ct 642 -152 631 -144 624 -134 ct 
+617 -123 613 -110 613 -93 ct 613 -75 618 -61 628 -51 ct 638 -41 652 -36 670 -36 ct 
+p ef
+923 -407 m  923 -460 l  979 -460 l  979 -407 l  923 -407 l  p
+923 1 m  923 -335 l  979 -335 l  979 1 l  923 1 l  p ef
+1276 0 m  1276 -213 l  1276 -235 1274 -252 1269 -264 ct 1265 -277 1258 -285 1249 -291 ct 
+1239 -296 1225 -299 1207 -299 ct 1180 -299 1159 -290 1143 -271 ct 1128 -253 1120 -227 1120 -195 ct 
+1120 0 l  1064 0 l  1064 -264 l  1064 -303 1063 -327 1062 -336 ct 1115 -336 l 
+1115 -335 1115 -332 1116 -327 ct 1116 -323 1116 -318 1116 -312 ct 1117 -306 1117 -295 1117 -278 ct 
+1118 -278 l  1131 -302 1146 -318 1163 -328 ct 1180 -337 1201 -342 1226 -342 ct 
+1262 -342 1289 -333 1306 -315 ct 1323 -296 1332 -266 1332 -224 ct 1332 0 l  1276 0 l 
+p ef
+1939 -305 m  1939 -264 1926 -231 1899 -207 ct 1872 -182 1835 -170 1789 -170 ct 
+1660 -170 l  1660 1 l  1601 1 l  1601 -437 l  1785 -437 l  1834 -437 1872 -426 1899 -403 ct 
+1926 -380 1939 -347 1939 -305 ct p
+1880 -305 m  1880 -361 1846 -390 1778 -390 ct 1660 -390 l  1660 -217 l  1780 -217 l 
+1846 -217 1880 -246 1880 -305 ct p ef
+2321 -1 m  2321 -203 l  2084 -203 l  2084 -1 l  2025 -1 l  2025 -438 l 
+2084 -438 l  2084 -253 l  2321 -253 l  2321 -438 l  2380 -438 l  2380 -1 l 
+2321 -1 l  p ef
+2671 -181 m  2671 0 l  2612 0 l  2612 -181 l  2444 -438 l  2509 -438 l 
+2642 -229 l  2774 -438 l  2840 -438 l  2671 -181 l  p ef
+pom
+gs
+0.871 0.902 0.937 c 13067 22179 m  11955 22179 l  11955 21544 l  14178 21544 l 
+14178 22179 l  13067 22179 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 13067 22178 m  11955 22178 l  11955 21543 l 
+14178 21543 l  14178 22178 l  13067 22178 l  pc
+gr
+pum
+14764 22064 t
+128 6 m  95 6 69 -3 52 -21 ct 35 -38 27 -63 27 -94 ct 27 -129 38 -155 61 -174 ct 
+84 -192 121 -202 172 -204 ct 247 -205 l  247 -223 l  247 -250 241 -270 230 -282 ct 
+218 -294 200 -299 175 -299 ct 150 -299 132 -295 121 -287 ct 109 -278 102 -265 100 -246 ct 
+42 -251 l  51 -312 96 -342 176 -342 ct 219 -342 250 -332 272 -313 ct 293 -294 304 -266 304 -229 ct 
+304 -85 l  304 -68 306 -55 310 -47 ct 314 -39 323 -35 335 -35 ct 340 -35 346 -35 353 -37 ct 
+353 -2 l  339 1 325 3 310 3 ct 289 3 274 -2 265 -13 ct 256 -24 250 -41 249 -64 ct 
+247 -64 l  233 -39 216 -21 197 -10 ct 178 1 155 6 128 6 ct p
+141 -36 m  162 -36 180 -41 196 -50 ct 212 -59 224 -72 233 -88 ct 243 -104 247 -121 247 -138 ct 
+247 -166 l  186 -165 l  160 -164 140 -161 126 -157 ct 113 -152 102 -144 95 -134 ct 
+88 -123 84 -110 84 -93 ct 84 -75 89 -61 99 -51 ct 109 -41 123 -36 141 -36 ct p ef
+448 -336 m  448 -123 l  448 -101 451 -84 455 -72 ct 459 -59 466 -51 476 -45 ct 
+485 -40 499 -37 518 -37 ct 544 -37 566 -46 581 -65 ct 597 -83 604 -109 604 -141 ct 
+604 -336 l  660 -336 l  660 -72 l  660 -33 661 -9 662 0 ct 609 0 l  609 -1 609 -4 609 -9 ct 
+608 -13 608 -18 608 -24 ct 608 -30 607 -41 607 -58 ct 606 -58 l  593 -34 578 -18 561 -8 ct 
+545 1 524 6 499 6 ct 462 6 435 -3 418 -21 ct 401 -40 392 -70 392 -112 ct 392 -336 l 
+448 -336 l  p ef
+951 0 m  861 -138 l  770 0 l  710 0 l  830 -173 l  716 -336 l  777 -336 l 
+861 -205 l  944 -336 l  1007 -336 l  893 -173 l  1014 0 l  951 0 l  p ef
+1588 -305 m  1588 -264 1575 -231 1548 -207 ct 1521 -182 1484 -170 1438 -170 ct 
+1309 -170 l  1309 1 l  1250 1 l  1250 -437 l  1434 -437 l  1483 -437 1521 -426 1548 -403 ct 
+1575 -380 1588 -347 1588 -305 ct p
+1529 -305 m  1529 -361 1495 -390 1427 -390 ct 1309 -390 l  1309 -217 l  1429 -217 l 
+1495 -217 1529 -246 1529 -305 ct p ef
+1969 -1 m  1969 -203 l  1732 -203 l  1732 -1 l  1673 -1 l  1673 -438 l 
+1732 -438 l  1732 -253 l  1969 -253 l  1969 -438 l  2028 -438 l  2028 -1 l 
+1969 -1 l  p ef
+2320 -181 m  2320 0 l  2261 0 l  2261 -181 l  2093 -438 l  2158 -438 l 
+2291 -229 l  2423 -438 l  2489 -438 l  2320 -181 l  p ef
+pom
+gs
+0.871 0.902 0.937 c 8890 8121 m  5080 8121 l  5080 7169 l  12699 7169 l 
+12699 8121 l  8890 8121 l  p ef
+gr
+gs
+1.000 1.000 0.843 c 8890 15718 m  5080 15718 l  5080 14769 l  12699 14769 l 
+12699 15718 l  8890 15718 l  p ef
+gr
+gs
+1.000 0.714 0.425 c 9310 6969 m  7720 6969 l  7720 5699 l  10899 5699 l 
+10899 6969 l  9310 6969 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 9309 6969 m  7719 6969 l  7719 5699 l  10899 5699 l 
+10899 6969 l  9309 6969 l  pc
+gr
+pum
+8797 6554 t
+59 0 m  59 -438 l  118 -438 l  118 0 l  59 0 l  p ef
+424 -396 m  375 -396 338 -380 311 -349 ct 284 -318 270 -275 270 -221 ct 270 -167 284 -124 312 -92 ct 
+340 -59 378 -43 426 -43 ct 487 -43 533 -73 564 -134 ct 612 -109 l  594 -72 569 -43 537 -23 ct 
+504 -4 466 6 423 6 ct 379 6 341 -3 309 -21 ct 277 -40 252 -66 236 -100 ct 219 -134 210 -174 210 -221 ct 
+210 -291 229 -345 267 -385 ct 304 -424 356 -444 423 -444 ct 469 -444 508 -435 540 -417 ct 
+571 -398 594 -371 608 -336 ct 552 -317 l  542 -343 526 -362 503 -375 ct 481 -389 454 -396 424 -396 ct 
+p ef
+746 -389 m  746 -226 l  990 -226 l  990 -177 l  746 -177 l  746 0 l  687 0 l 
+687 -437 l  997 -437 l  997 -389 l  746 -389 l  p ef
+pom
+gs
+0.933 0.933 0.933 c 9259 9841 m  9260 9841 l  9079 9841 8901 9858 8744 9890 ct 
+8588 9923 8458 9969 8367 10026 ct 8277 10082 8229 10145 8229 10210 ct 8229 10486 l 
+8229 10763 l  8229 11139 l  8229 11416 l  8229 11692 l  8229 11693 l  8229 11758 8277 11821 8367 11877 ct 
+8458 11934 8588 11980 8744 12013 ct 8901 12045 9079 12062 9260 12062 ct 10030 12062 l 
+10803 12062 l  11853 12062 l  12626 12062 l  13398 12062 l  13398 12062 l 
+13579 12062 13757 12045 13914 12013 ct 14070 11980 14200 11934 14291 11877 ct 
+14381 11821 14429 11758 14429 11693 ct 14429 11416 l  14429 11139 l  14429 10763 l 
+14429 10486 l  14429 10210 l  14429 10210 l  14429 10210 l  14429 10145 14381 10082 14291 10026 ct 
+14200 9969 14070 9923 13914 9890 ct 13757 9858 13579 9841 13398 9841 ct 12676 8374 l 
+11853 9841 l  10803 9841 l  10030 9841 l  9259 9841 l  p ef
+gr
+gs
+0 lw 1 lj 9258 9841 m  9259 9841 l  9078 9841 8900 9858 8743 9890 ct 8587 9923 8457 9969 8366 10026 ct 
+8276 10082 8228 10144 8228 10209 ct 8228 10485 l  8228 10762 l  8228 11138 l 
+8228 11415 l  8228 11691 l  8228 11692 l  8228 11757 8276 11820 8366 11876 ct 
+8457 11933 8587 11979 8743 12012 ct 8900 12044 9078 12061 9259 12061 ct 10030 12061 l 
+10803 12061 l  11853 12061 l  12626 12061 l  13398 12061 l  13398 12061 l 
+13579 12061 13757 12044 13914 12012 ct 14070 11979 14200 11933 14291 11876 ct 
+14381 11820 14429 11757 14429 11692 ct 14429 11415 l  14429 11138 l  14429 10762 l 
+14429 10485 l  14429 10209 l  14429 10209 l  14429 10209 l  14429 10144 14381 10082 14291 10026 ct 
+14200 9969 14070 9923 13914 9890 ct 13757 9858 13579 9841 13398 9841 ct 12676 8374 l 
+11853 9841 l  10803 9841 l  10030 9841 l  9258 9841 l  pc
+gr
+pum
+8903 10518 t
+376 0 m  376 -259 l  376 -288 377 -315 379 -342 ct 370 -309 362 -283 354 -265 ct 
+254 0 l  217 0 l  116 -265 l  100 -312 l  91 -342 l  92 -311 l  93 -259 l 
+93 0 l  46 0 l  46 -389 l  115 -389 l  219 -119 l  222 -108 226 -97 229 -84 ct 
+233 -72 235 -63 236 -57 ct 237 -65 241 -76 245 -91 ct 250 -106 253 -115 255 -119 ct 
+356 -389 l  424 -389 l  424 0 l  376 0 l  p ef
+584 6 m  554 6 532 -2 517 -18 ct 501 -34 494 -55 494 -83 ct 494 -114 504 -138 524 -154 ct 
+545 -171 577 -180 623 -181 ct 689 -182 l  689 -198 l  689 -222 684 -240 674 -250 ct 
+664 -261 648 -266 626 -266 ct 603 -266 587 -262 577 -255 ct 567 -247 561 -235 559 -219 ct 
+507 -223 l  516 -277 555 -304 627 -304 ct 664 -304 692 -295 711 -278 ct 730 -261 740 -236 740 -203 ct 
+740 -74 l  740 -60 742 -49 745 -41 ct 749 -34 757 -30 767 -30 ct 772 -30 778 -31 784 -32 ct 
+784 -1 l  771 2 758 3 745 3 ct 727 3 714 -1 705 -11 ct 697 -21 692 -36 691 -57 ct 
+689 -57 l  677 -34 662 -18 645 -8 ct 628 1 608 6 584 6 ct p
+595 -31 m  613 -31 630 -35 644 -44 ct 658 -52 669 -63 677 -78 ct 685 -92 689 -107 689 -122 ct 
+689 -147 l  635 -146 l  612 -145 594 -143 582 -139 ct 570 -134 561 -127 555 -118 ct 
+548 -109 545 -97 545 -82 ct 545 -66 549 -53 558 -44 ct 567 -36 579 -31 595 -31 ct 
+p ef
+821 -362 m  821 -409 l  870 -409 l  870 -362 l  821 -362 l  p
+821 0 m  821 -298 l  870 -298 l  870 0 l  821 0 l  p ef
+1137 0 m  1137 -189 l  1137 -209 1135 -224 1131 -235 ct 1128 -246 1121 -254 1113 -258 ct 
+1105 -263 1092 -266 1076 -266 ct 1052 -266 1033 -257 1019 -241 ct 1006 -225 999 -202 999 -173 ct 
+999 0 l  949 0 l  949 -235 l  949 -270 949 -291 947 -298 ct 994 -298 l  994 -298 995 -295 995 -291 ct 
+995 -287 995 -282 996 -277 ct 996 -272 996 -262 996 -247 ct 997 -247 l  1009 -268 1022 -283 1037 -291 ct 
+1052 -300 1070 -304 1093 -304 ct 1125 -304 1149 -296 1164 -280 ct 1179 -263 1187 -236 1187 -199 ct 
+1187 0 l  1137 0 l  p ef
+1726 -271 m  1726 -234 1714 -205 1691 -184 ct 1667 -162 1634 -151 1593 -151 ct 
+1479 -151 l  1479 1 l  1426 1 l  1426 -388 l  1590 -388 l  1633 -388 1667 -378 1691 -358 ct 
+1715 -337 1726 -308 1726 -271 ct p
+1674 -271 m  1674 -321 1643 -346 1583 -346 ct 1479 -346 l  1479 -193 l  1585 -193 l 
+1644 -193 1674 -219 1674 -271 ct p ef
+2066 0 m  2066 -180 l  1856 -180 l  1856 0 l  1803 0 l  1803 -389 l  1856 -389 l 
+1856 -224 l  2066 -224 l  2066 -389 l  2118 -389 l  2118 0 l  2066 0 l 
+p ef
+2377 -161 m  2377 0 l  2325 0 l  2325 -161 l  2175 -389 l  2233 -389 l 
+2351 -203 l  2469 -389 l  2527 -389 l  2377 -161 l  p ef
+2946 -82 m  2946 -54 2935 -32 2914 -17 ct 2893 -2 2863 6 2825 6 ct 2788 6 2759 0 2739 -12 ct 
+2719 -25 2706 -44 2700 -70 ct 2743 -78 l  2748 -62 2756 -50 2770 -43 ct 2783 -36 2801 -32 2825 -32 ct 
+2850 -32 2868 -36 2880 -43 ct 2892 -51 2897 -63 2897 -78 ct 2897 -90 2893 -99 2885 -107 ct 
+2877 -114 2864 -120 2846 -125 ct 2811 -134 l  2782 -142 2762 -149 2750 -156 ct 
+2738 -163 2729 -172 2722 -182 ct 2715 -192 2712 -204 2712 -219 ct 2712 -246 2721 -267 2741 -281 ct 
+2760 -295 2788 -303 2825 -303 ct 2858 -303 2884 -297 2904 -285 ct 2923 -274 2935 -255 2940 -229 ct 
+2896 -224 l  2893 -237 2886 -247 2874 -254 ct 2862 -261 2845 -265 2825 -265 ct 
+2803 -265 2786 -262 2776 -255 ct 2765 -248 2760 -238 2760 -224 ct 2760 -215 2762 -208 2766 -203 ct 
+2771 -197 2777 -193 2786 -189 ct 2795 -185 2813 -180 2840 -173 ct 2867 -166 2886 -160 2897 -155 ct 
+2909 -149 2918 -143 2925 -136 ct 2931 -129 2936 -121 2940 -112 ct 2944 -103 2946 -93 2946 -82 ct 
+p ef
+3291 0 m  3234 0 l  3182 -211 l  3172 -258 l  3170 -249 3168 -237 3164 -222 ct 
+3161 -206 3142 -132 3108 0 ct 3051 0 l  2967 -299 l  3016 -299 l  3067 -96 l 
+3068 -91 3072 -73 3078 -41 ct 3083 -62 l  3145 -299 l  3199 -299 l  3251 -94 l 
+3263 -41 l  3272 -79 l  3328 -299 l  3377 -299 l  3291 0 l  p ef
+3412 -362 m  3412 -409 l  3461 -409 l  3461 -362 l  3412 -362 l  p
+3412 0 m  3412 -298 l  3461 -298 l  3461 0 l  3412 0 l  p ef
+3654 -3 m  3637 2 3621 4 3603 4 ct 3564 4 3544 -19 3544 -64 ct 3544 -263 l  3510 -263 l 
+3510 -299 l  3546 -299 l  3560 -366 l  3594 -366 l  3594 -299 l  3649 -299 l 
+3649 -263 l  3594 -263 l  3594 -74 l  3594 -60 3596 -50 3601 -44 ct 3605 -38 3613 -35 3625 -35 ct 
+3632 -35 3641 -37 3654 -39 ct 3654 -3 l  p ef
+3734 -150 m  3734 -111 3740 -81 3752 -62 ct 3765 -43 3784 -33 3809 -33 ct 3827 -33 3841 -38 3853 -48 ct 
+3865 -57 3872 -72 3875 -92 ct 3925 -88 l  3921 -60 3909 -37 3889 -20 ct 3868 -3 3842 6 3810 6 ct 
+3769 6 3737 -7 3715 -34 ct 3693 -60 3682 -99 3682 -149 ct 3682 -200 3693 -238 3715 -264 ct 
+3737 -291 3769 -304 3810 -304 ct 3840 -304 3866 -296 3886 -280 ct 3906 -264 3918 -243 3923 -215 ct 
+3873 -211 l  3870 -227 3863 -241 3853 -250 ct 3843 -260 3828 -265 3808 -265 ct 
+3782 -265 3763 -256 3751 -239 ct 3740 -221 3734 -192 3734 -150 ct p ef
+4028 -247 m  4039 -266 4052 -281 4067 -290 ct 4082 -299 4101 -303 4124 -303 ct 
+4156 -303 4180 -295 4195 -279 ct 4210 -263 4218 -236 4218 -198 ct 4218 1 l  4168 1 l 
+4168 -189 l  4168 -210 4166 -225 4162 -235 ct 4159 -246 4152 -253 4143 -258 ct 
+4135 -263 4122 -265 4107 -265 ct 4083 -265 4065 -257 4051 -241 ct 4037 -225 4030 -203 4030 -175 ct 
+4030 1 l  3980 1 l  3980 -409 l  4030 -409 l  4030 -302 l  4030 -291 4029 -279 4029 -267 ct 
+4028 -256 4028 -249 4027 -247 ct 4028 -247 l  p ef
+4331 -138 m  4331 -104 4338 -78 4352 -59 ct 4366 -41 4387 -31 4414 -31 ct 4436 -31 4453 -36 4466 -44 ct 
+4479 -53 4488 -64 4492 -77 ct 4536 -65 l  4518 -18 4477 6 4414 6 ct 4370 6 4337 -7 4314 -33 ct 
+4290 -60 4279 -99 4279 -151 ct 4279 -200 4290 -238 4314 -264 ct 4337 -291 4369 -304 4412 -304 ct 
+4500 -304 4544 -251 4544 -145 ct 4544 -138 l  4331 -138 l  p
+4492 -176 m  4490 -208 4482 -231 4468 -245 ct 4455 -260 4436 -267 4411 -267 ct 
+4387 -267 4368 -259 4354 -243 ct 4340 -227 4333 -204 4332 -176 ct 4492 -176 l 
+p ef
+4830 -82 m  4830 -54 4819 -32 4798 -17 ct 4777 -2 4747 6 4709 6 ct 4672 6 4643 0 4623 -12 ct 
+4603 -25 4590 -44 4584 -70 ct 4627 -78 l  4632 -62 4640 -50 4654 -43 ct 4667 -36 4685 -32 4709 -32 ct 
+4734 -32 4752 -36 4764 -43 ct 4776 -51 4781 -63 4781 -78 ct 4781 -90 4777 -99 4769 -107 ct 
+4761 -114 4748 -120 4730 -125 ct 4695 -134 l  4666 -142 4646 -149 4634 -156 ct 
+4622 -163 4613 -172 4606 -182 ct 4599 -192 4596 -204 4596 -219 ct 4596 -246 4605 -267 4625 -281 ct 
+4644 -295 4672 -303 4709 -303 ct 4742 -303 4768 -297 4788 -285 ct 4807 -274 4819 -255 4824 -229 ct 
+4780 -224 l  4777 -237 4770 -247 4758 -254 ct 4746 -261 4729 -265 4709 -265 ct 
+4687 -265 4670 -262 4660 -255 ct 4649 -248 4644 -238 4644 -224 ct 4644 -215 4646 -208 4650 -203 ct 
+4655 -197 4661 -193 4670 -189 ct 4679 -185 4697 -180 4724 -173 ct 4751 -166 4770 -160 4781 -155 ct 
+4793 -149 4802 -143 4809 -136 ct 4815 -129 4820 -121 4824 -112 ct 4828 -103 4830 -93 4830 -82 ct 
+p ef
+pom
+pum
+8710 11145 t
+153 -3 m  136 2 120 4 102 4 ct 63 4 43 -19 43 -64 ct 43 -263 l  9 -263 l  9 -299 l 
+45 -299 l  59 -366 l  93 -366 l  93 -299 l  148 -299 l  148 -263 l  93 -263 l 
+93 -74 l  93 -60 95 -50 100 -44 ct 104 -38 112 -35 124 -35 ct 131 -35 140 -37 153 -39 ct 
+153 -3 l  p ef
+447 -149 m  447 -97 436 -58 413 -32 ct 390 -7 356 6 313 6 ct 269 6 236 -7 214 -34 ct 
+192 -60 181 -99 181 -149 ct 181 -252 225 -304 314 -304 ct 360 -304 393 -291 415 -266 ct 
+436 -241 447 -202 447 -149 ct p
+395 -149 m  395 -190 389 -220 377 -239 ct 364 -258 344 -267 315 -267 ct 286 -267 265 -257 252 -238 ct 
+239 -219 233 -190 233 -149 ct 233 -110 239 -80 252 -60 ct 265 -41 285 -31 312 -31 ct 
+342 -31 363 -40 376 -59 ct 389 -79 395 -108 395 -149 ct p ef
+917 -149 m  917 -97 906 -58 883 -32 ct 860 -7 826 6 783 6 ct 739 6 706 -7 684 -34 ct 
+662 -60 651 -99 651 -149 ct 651 -252 695 -304 784 -304 ct 830 -304 863 -291 885 -266 ct 
+906 -241 917 -202 917 -149 ct p
+865 -149 m  865 -190 859 -220 847 -239 ct 834 -258 814 -267 785 -267 ct 756 -267 735 -257 722 -238 ct 
+709 -219 703 -190 703 -149 ct 703 -110 709 -80 722 -60 ct 735 -41 755 -31 782 -31 ct 
+812 -31 833 -40 846 -59 ct 859 -79 865 -108 865 -149 ct p ef
+1230 -151 m  1230 -47 1193 5 1120 5 ct 1074 5 1044 -12 1028 -47 ct 1026 -47 l 
+1027 -45 1028 -30 1028 0 ct 1028 117 l  978 117 l  978 -238 l  978 -269 977 -289 976 -299 ct 
+1024 -299 l  1024 -298 1025 -295 1025 -291 ct 1025 -286 1026 -279 1026 -270 ct 
+1027 -261 1027 -254 1027 -251 ct 1028 -251 l  1037 -269 1049 -283 1063 -291 ct 
+1078 -300 1097 -304 1120 -304 ct 1157 -304 1185 -292 1203 -267 ct 1221 -242 1230 -204 1230 -151 ct 
+p
+1178 -150 m  1178 -191 1172 -221 1161 -239 ct 1150 -257 1132 -266 1108 -266 ct 
+1088 -266 1073 -262 1062 -253 ct 1050 -245 1042 -232 1036 -215 ct 1030 -197 1028 -174 1028 -146 ct 
+1028 -107 1034 -78 1046 -60 ct 1059 -41 1079 -32 1107 -32 ct 1132 -32 1150 -41 1161 -59 ct 
+1172 -77 1178 -107 1178 -150 ct p ef
+1329 -138 m  1329 -104 1336 -78 1350 -59 ct 1364 -41 1385 -31 1412 -31 ct 1434 -31 1451 -36 1464 -44 ct 
+1477 -53 1486 -64 1490 -77 ct 1534 -65 l  1516 -18 1475 6 1412 6 ct 1368 6 1335 -7 1312 -33 ct 
+1288 -60 1277 -99 1277 -151 ct 1277 -200 1288 -238 1312 -264 ct 1335 -291 1367 -304 1410 -304 ct 
+1498 -304 1542 -251 1542 -145 ct 1542 -138 l  1329 -138 l  p
+1490 -176 m  1488 -208 1480 -231 1466 -245 ct 1453 -260 1434 -267 1409 -267 ct 
+1385 -267 1366 -259 1352 -243 ct 1338 -227 1331 -204 1330 -176 ct 1490 -176 l 
+p ef
+1605 0 m  1605 -229 l  1605 -250 1605 -273 1603 -298 ct 1650 -298 l  1652 -265 1652 -244 1652 -238 ct 
+1654 -238 l  1661 -263 1671 -281 1681 -290 ct 1691 -299 1706 -304 1724 -304 ct 
+1731 -304 1738 -303 1744 -301 ct 1744 -256 l  1738 -258 1729 -258 1718 -258 ct 
+1697 -258 1682 -250 1671 -232 ct 1660 -214 1655 -189 1655 -156 ct 1655 0 l  1605 0 l 
+p ef
+1867 6 m  1837 6 1815 -2 1800 -18 ct 1784 -34 1777 -55 1777 -83 ct 1777 -114 1787 -138 1807 -154 ct 
+1828 -171 1860 -180 1906 -181 ct 1972 -182 l  1972 -198 l  1972 -222 1967 -240 1957 -250 ct 
+1947 -261 1931 -266 1909 -266 ct 1886 -266 1870 -262 1860 -255 ct 1850 -247 1844 -235 1842 -219 ct 
+1790 -223 l  1799 -277 1838 -304 1910 -304 ct 1947 -304 1975 -295 1994 -278 ct 
+2013 -261 2023 -236 2023 -203 ct 2023 -74 l  2023 -60 2025 -49 2028 -41 ct 2032 -34 2040 -30 2050 -30 ct 
+2055 -30 2061 -31 2067 -32 ct 2067 -1 l  2054 2 2041 3 2028 3 ct 2010 3 1997 -1 1988 -11 ct 
+1980 -21 1975 -36 1974 -57 ct 1972 -57 l  1960 -34 1945 -18 1928 -8 ct 1911 1 1891 6 1867 6 ct 
+p
+1878 -31 m  1896 -31 1913 -35 1927 -44 ct 1941 -52 1952 -63 1960 -78 ct 1968 -92 1972 -107 1972 -122 ct 
+1972 -147 l  1918 -146 l  1895 -145 1877 -143 1865 -139 ct 1853 -134 1844 -127 1838 -118 ct 
+1831 -109 1828 -97 1828 -82 ct 1828 -66 1832 -53 1841 -44 ct 1850 -36 1862 -31 1878 -31 ct 
+p ef
+2219 -3 m  2202 2 2186 4 2168 4 ct 2129 4 2109 -19 2109 -64 ct 2109 -263 l  2075 -263 l 
+2075 -299 l  2111 -299 l  2125 -366 l  2159 -366 l  2159 -299 l  2214 -299 l 
+2214 -263 l  2159 -263 l  2159 -74 l  2159 -60 2161 -50 2166 -44 ct 2170 -38 2178 -35 2190 -35 ct 
+2197 -35 2206 -37 2219 -39 ct 2219 -3 l  p ef
+2261 -362 m  2261 -409 l  2310 -409 l  2310 -362 l  2261 -362 l  p
+2261 0 m  2261 -298 l  2310 -298 l  2310 0 l  2261 0 l  p ef
+2577 0 m  2577 -189 l  2577 -209 2575 -224 2571 -235 ct 2568 -246 2561 -254 2553 -258 ct 
+2545 -263 2532 -266 2516 -266 ct 2492 -266 2473 -257 2459 -241 ct 2446 -225 2439 -202 2439 -173 ct 
+2439 0 l  2389 0 l  2389 -235 l  2389 -270 2389 -291 2387 -298 ct 2434 -298 l 
+2434 -298 2435 -295 2435 -291 ct 2435 -287 2435 -282 2436 -277 ct 2436 -272 2436 -262 2436 -247 ct 
+2437 -247 l  2449 -268 2462 -283 2477 -291 ct 2492 -300 2510 -304 2533 -304 ct 
+2565 -304 2589 -296 2604 -280 ct 2619 -263 2627 -236 2627 -199 ct 2627 0 l  2577 0 l 
+p ef
+2814 117 m  2781 117 2756 111 2736 98 ct 2717 85 2705 67 2699 43 ct 2749 36 l 
+2752 50 2760 61 2771 68 ct 2782 75 2797 79 2815 79 ct 2865 79 2889 50 2889 -8 ct 
+2889 -56 l  2889 -56 l  2879 -37 2867 -22 2850 -12 ct 2834 -3 2815 2 2793 2 ct 
+2756 2 2730 -10 2712 -34 ct 2695 -59 2687 -97 2687 -149 ct 2687 -201 2696 -240 2714 -265 ct 
+2733 -290 2761 -303 2799 -303 ct 2820 -303 2838 -298 2853 -289 ct 2869 -279 2881 -265 2889 -247 ct 
+2890 -247 l  2890 -253 2890 -262 2891 -276 ct 2892 -290 2893 -297 2893 -298 ct 
+2940 -298 l  2939 -288 2939 -268 2939 -237 ct 2939 -9 l  2939 75 2897 117 2814 117 ct 
+p
+2889 -149 m  2889 -173 2886 -194 2879 -212 ct 2873 -229 2864 -243 2852 -252 ct 
+2839 -261 2826 -266 2811 -266 ct 2785 -266 2767 -257 2755 -238 ct 2744 -220 2738 -190 2738 -149 ct 
+2738 -108 2743 -79 2754 -61 ct 2765 -44 2784 -35 2810 -35 ct 2825 -35 2839 -39 2851 -48 ct 
+2863 -58 2873 -71 2879 -88 ct 2886 -105 2889 -126 2889 -149 ct p ef
+3423 -149 m  3423 -97 3412 -58 3389 -32 ct 3366 -7 3332 6 3289 6 ct 3245 6 3212 -7 3190 -34 ct 
+3168 -60 3157 -99 3157 -149 ct 3157 -252 3201 -304 3290 -304 ct 3336 -304 3369 -291 3391 -266 ct 
+3412 -241 3423 -202 3423 -149 ct p
+3371 -149 m  3371 -190 3365 -220 3353 -239 ct 3340 -258 3320 -267 3291 -267 ct 
+3262 -267 3241 -257 3228 -238 ct 3215 -219 3209 -190 3209 -149 ct 3209 -110 3215 -80 3228 -60 ct 
+3241 -41 3261 -31 3288 -31 ct 3318 -31 3339 -40 3352 -59 ct 3365 -79 3371 -108 3371 -149 ct 
+p ef
+3673 0 m  3673 -189 l  3673 -209 3671 -224 3667 -235 ct 3664 -246 3657 -254 3649 -258 ct 
+3641 -263 3628 -266 3612 -266 ct 3588 -266 3569 -257 3555 -241 ct 3542 -225 3535 -202 3535 -173 ct 
+3535 0 l  3485 0 l  3485 -235 l  3485 -270 3485 -291 3483 -298 ct 3530 -298 l 
+3530 -298 3531 -295 3531 -291 ct 3531 -287 3531 -282 3532 -277 ct 3532 -272 3532 -262 3532 -247 ct 
+3533 -247 l  3545 -268 3558 -283 3573 -291 ct 3588 -300 3606 -304 3629 -304 ct 
+3661 -304 3685 -296 3700 -280 ct 3715 -263 3723 -236 3723 -199 ct 3723 0 l  3673 0 l 
+p ef
+3954 1 m  3954 -409 l  4004 -409 l  4004 1 l  3954 1 l  p ef
+4081 -362 m  4081 -409 l  4130 -409 l  4130 -362 l  4081 -362 l  p
+4081 0 m  4081 -298 l  4130 -298 l  4130 0 l  4081 0 l  p ef
+4397 0 m  4397 -189 l  4397 -209 4395 -224 4391 -235 ct 4388 -246 4381 -254 4373 -258 ct 
+4365 -263 4352 -266 4336 -266 ct 4312 -266 4293 -257 4279 -241 ct 4266 -225 4259 -202 4259 -173 ct 
+4259 0 l  4209 0 l  4209 -235 l  4209 -270 4209 -291 4207 -298 ct 4254 -298 l 
+4254 -298 4255 -295 4255 -291 ct 4255 -287 4255 -282 4256 -277 ct 4256 -272 4256 -262 4256 -247 ct 
+4257 -247 l  4269 -268 4282 -283 4297 -291 ct 4312 -300 4330 -304 4353 -304 ct 
+4385 -304 4409 -296 4424 -280 ct 4439 -263 4447 -236 4447 -199 ct 4447 0 l  4397 0 l 
+p ef
+4708 0 m  4607 -136 l  4571 -106 l  4571 0 l  4521 0 l  4521 -409 l  4571 -409 l 
+4571 -153 l  4701 -298 l  4760 -298 l  4639 -170 l  4766 0 l  4708 0 l 
+p ef
+5215 -194 m  5215 -129 5203 -80 5180 -45 ct 5157 -11 5124 6 5079 6 ct 5035 6 5001 -11 4979 -45 ct 
+4956 -79 4945 -129 4945 -194 ct 4945 -261 4956 -311 4978 -344 ct 4999 -377 5034 -394 5081 -394 ct 
+5127 -394 5160 -377 5182 -344 ct 5204 -310 5215 -260 5215 -194 ct p
+5164 -194 m  5164 -250 5158 -291 5145 -316 ct 5132 -341 5111 -354 5081 -354 ct 
+5050 -354 5028 -341 5015 -316 ct 5002 -292 4995 -251 4995 -194 ct 4995 -139 5002 -98 5015 -73 ct 
+5029 -47 5050 -35 5080 -35 ct 5109 -35 5130 -48 5144 -74 ct 5157 -100 5164 -140 5164 -194 ct 
+p ef
+pom
+pum
+9385 11772 t
+114 6 m  84 6 62 -2 47 -18 ct 31 -34 24 -55 24 -83 ct 24 -114 34 -138 54 -154 ct 
+75 -171 107 -180 153 -181 ct 219 -182 l  219 -198 l  219 -222 214 -240 204 -250 ct 
+194 -261 178 -266 156 -266 ct 133 -266 117 -262 107 -255 ct 97 -247 91 -235 89 -219 ct 
+37 -223 l  46 -277 85 -304 157 -304 ct 194 -304 222 -295 241 -278 ct 260 -261 270 -236 270 -203 ct 
+270 -74 l  270 -60 272 -49 275 -41 ct 279 -34 287 -30 297 -30 ct 302 -30 308 -31 314 -32 ct 
+314 -1 l  301 2 288 3 275 3 ct 257 3 244 -1 235 -11 ct 227 -21 222 -36 221 -57 ct 
+219 -57 l  207 -34 192 -18 175 -8 ct 158 1 138 6 114 6 ct p
+125 -31 m  143 -31 160 -35 174 -44 ct 188 -52 199 -63 207 -78 ct 215 -92 219 -107 219 -122 ct 
+219 -147 l  165 -146 l  142 -145 124 -143 112 -139 ct 100 -134 91 -127 85 -118 ct 
+78 -109 75 -97 75 -82 ct 75 -66 79 -53 88 -44 ct 97 -36 109 -31 125 -31 ct p ef
+540 0 m  540 -189 l  540 -209 538 -224 534 -235 ct 531 -246 524 -254 516 -258 ct 
+508 -263 495 -266 479 -266 ct 455 -266 436 -257 422 -241 ct 409 -225 402 -202 402 -173 ct 
+402 0 l  352 0 l  352 -235 l  352 -270 352 -291 350 -298 ct 397 -298 l  397 -298 398 -295 398 -291 ct 
+398 -287 398 -282 399 -277 ct 399 -272 399 -262 399 -247 ct 400 -247 l  412 -268 425 -283 440 -291 ct 
+455 -300 473 -304 496 -304 ct 528 -304 552 -296 567 -280 ct 582 -263 590 -236 590 -199 ct 
+590 0 l  540 0 l  p ef
+853 -48 m  844 -28 832 -15 817 -6 ct 801 2 783 6 760 6 ct 723 6 695 -7 677 -32 ct 
+660 -57 651 -96 651 -147 ct 651 -252 687 -304 760 -304 ct 783 -304 802 -300 817 -291 ct 
+832 -283 844 -270 853 -252 ct 854 -252 l  853 -285 l  853 -409 l  903 -409 l 
+903 -61 l  903 -30 903 -9 904 0 ct 857 0 l  856 -2 856 -9 855 -20 ct 855 -31 854 -40 854 -48 ct 
+853 -48 l  p
+703 -149 m  703 -107 708 -77 719 -59 ct 730 -41 748 -32 773 -32 ct 801 -32 821 -42 834 -62 ct 
+847 -81 853 -111 853 -152 ct 853 -192 847 -221 834 -239 ct 821 -258 801 -267 774 -267 ct 
+749 -267 731 -258 719 -239 ct 708 -221 703 -191 703 -149 ct p ef
+1358 -82 m  1358 -54 1347 -32 1326 -17 ct 1305 -2 1275 6 1237 6 ct 1200 6 1171 0 1151 -12 ct 
+1131 -25 1118 -44 1112 -70 ct 1155 -78 l  1160 -62 1168 -50 1182 -43 ct 1195 -36 1213 -32 1237 -32 ct 
+1262 -32 1280 -36 1292 -43 ct 1304 -51 1309 -63 1309 -78 ct 1309 -90 1305 -99 1297 -107 ct 
+1289 -114 1276 -120 1258 -125 ct 1223 -134 l  1194 -142 1174 -149 1162 -156 ct 
+1150 -163 1141 -172 1134 -182 ct 1127 -192 1124 -204 1124 -219 ct 1124 -246 1133 -267 1153 -281 ct 
+1172 -295 1200 -303 1237 -303 ct 1270 -303 1296 -297 1316 -285 ct 1335 -274 1347 -255 1352 -229 ct 
+1308 -224 l  1305 -237 1298 -247 1286 -254 ct 1274 -261 1257 -265 1237 -265 ct 
+1215 -265 1198 -262 1188 -255 ct 1177 -248 1172 -238 1172 -224 ct 1172 -215 1174 -208 1178 -203 ct 
+1183 -197 1189 -193 1198 -189 ct 1207 -185 1225 -180 1252 -173 ct 1279 -166 1298 -160 1309 -155 ct 
+1321 -149 1330 -143 1337 -136 ct 1343 -129 1348 -121 1352 -112 ct 1356 -103 1358 -93 1358 -82 ct 
+p ef
+1456 -138 m  1456 -104 1463 -78 1477 -59 ct 1491 -41 1512 -31 1539 -31 ct 1561 -31 1578 -36 1591 -44 ct 
+1604 -53 1613 -64 1617 -77 ct 1661 -65 l  1643 -18 1602 6 1539 6 ct 1495 6 1462 -7 1439 -33 ct 
+1415 -60 1404 -99 1404 -151 ct 1404 -200 1415 -238 1439 -264 ct 1462 -291 1494 -304 1537 -304 ct 
+1625 -304 1669 -251 1669 -145 ct 1669 -138 l  1456 -138 l  p
+1617 -176 m  1615 -208 1607 -231 1593 -245 ct 1580 -260 1561 -267 1536 -267 ct 
+1512 -267 1493 -259 1479 -243 ct 1465 -227 1458 -204 1457 -176 ct 1617 -176 l 
+p ef
+1920 0 m  1920 -189 l  1920 -209 1918 -224 1914 -235 ct 1911 -246 1904 -254 1896 -258 ct 
+1888 -263 1875 -266 1859 -266 ct 1835 -266 1816 -257 1802 -241 ct 1789 -225 1782 -202 1782 -173 ct 
+1782 0 l  1732 0 l  1732 -235 l  1732 -270 1732 -291 1730 -298 ct 1777 -298 l 
+1777 -298 1778 -295 1778 -291 ct 1778 -287 1778 -282 1779 -277 ct 1779 -272 1779 -262 1779 -247 ct 
+1780 -247 l  1792 -268 1805 -283 1820 -291 ct 1835 -300 1853 -304 1876 -304 ct 
+1908 -304 1932 -296 1947 -280 ct 1962 -263 1970 -236 1970 -199 ct 1970 0 l  1920 0 l 
+p ef
+2233 -48 m  2224 -28 2212 -15 2197 -6 ct 2181 2 2163 6 2140 6 ct 2103 6 2075 -7 2057 -32 ct 
+2040 -57 2031 -96 2031 -147 ct 2031 -252 2067 -304 2140 -304 ct 2163 -304 2182 -300 2197 -291 ct 
+2212 -283 2224 -270 2233 -252 ct 2234 -252 l  2233 -285 l  2233 -409 l  2283 -409 l 
+2283 -61 l  2283 -30 2283 -9 2284 0 ct 2237 0 l  2236 -2 2236 -9 2235 -20 ct 
+2235 -31 2234 -40 2234 -48 ct 2233 -48 l  p
+2083 -149 m  2083 -107 2088 -77 2099 -59 ct 2110 -41 2128 -32 2153 -32 ct 2181 -32 2201 -42 2214 -62 ct 
+2227 -81 2233 -111 2233 -152 ct 2233 -192 2227 -221 2214 -239 ct 2201 -258 2181 -267 2154 -267 ct 
+2129 -267 2111 -258 2099 -239 ct 2088 -221 2083 -191 2083 -149 ct p ef
+2582 -82 m  2582 -54 2571 -32 2550 -17 ct 2529 -2 2499 6 2461 6 ct 2424 6 2395 0 2375 -12 ct 
+2355 -25 2342 -44 2336 -70 ct 2379 -78 l  2384 -62 2392 -50 2406 -43 ct 2419 -36 2437 -32 2461 -32 ct 
+2486 -32 2504 -36 2516 -43 ct 2528 -51 2533 -63 2533 -78 ct 2533 -90 2529 -99 2521 -107 ct 
+2513 -114 2500 -120 2482 -125 ct 2447 -134 l  2418 -142 2398 -149 2386 -156 ct 
+2374 -163 2365 -172 2358 -182 ct 2351 -192 2348 -204 2348 -219 ct 2348 -246 2357 -267 2377 -281 ct 
+2396 -295 2424 -303 2461 -303 ct 2494 -303 2520 -297 2540 -285 ct 2559 -274 2571 -255 2576 -229 ct 
+2532 -224 l  2529 -237 2522 -247 2510 -254 ct 2498 -261 2481 -265 2461 -265 ct 
+2439 -265 2422 -262 2412 -255 ct 2401 -248 2396 -238 2396 -224 ct 2396 -215 2398 -208 2402 -203 ct 
+2407 -197 2413 -193 2422 -189 ct 2431 -185 2449 -180 2476 -173 ct 2503 -166 2522 -160 2533 -155 ct 
+2545 -149 2554 -143 2561 -136 ct 2567 -129 2572 -121 2576 -112 ct 2580 -103 2582 -93 2582 -82 ct 
+p ef
+2978 -351 m  2935 -351 2902 -337 2878 -309 ct 2854 -282 2842 -244 2842 -196 ct 
+2842 -148 2855 -110 2879 -81 ct 2904 -52 2938 -37 2980 -37 ct 3035 -37 3076 -64 3103 -118 ct 
+3146 -97 l  3130 -63 3107 -38 3078 -20 ct 3050 -3 3016 6 2978 6 ct 2939 6 2905 -2 2876 -18 ct 
+2848 -35 2826 -58 2811 -88 ct 2796 -118 2789 -154 2789 -196 ct 2789 -258 2805 -306 2839 -341 ct 
+2872 -376 2918 -394 2978 -394 ct 3019 -394 3053 -386 3081 -370 ct 3109 -354 3129 -330 3142 -298 ct 
+3092 -281 l  3083 -304 3069 -321 3049 -333 ct 3029 -345 3005 -351 2978 -351 ct 
+p ef
+3365 -346 m  3365 -1 l  3313 -1 l  3313 -346 l  3180 -346 l  3180 -389 l 
+3499 -389 l  3499 -346 l  3365 -346 l  p ef
+3859 -107 m  3859 -71 3845 -43 3817 -24 ct 3789 -4 3750 6 3699 6 ct 3604 6 3550 -27 3535 -93 ct 
+3586 -103 l  3591 -80 3604 -62 3623 -52 ct 3642 -41 3668 -35 3701 -35 ct 3735 -35 3761 -41 3780 -53 ct 
+3798 -64 3807 -81 3807 -104 ct 3807 -117 3804 -127 3799 -135 ct 3793 -143 3785 -149 3774 -155 ct 
+3764 -160 3751 -164 3737 -168 ct 3722 -171 3706 -175 3689 -179 ct 3658 -186 3635 -192 3619 -199 ct 
+3603 -206 3590 -214 3581 -222 ct 3572 -230 3565 -240 3560 -251 ct 3555 -263 3553 -275 3553 -290 ct 
+3553 -323 3566 -349 3591 -367 ct 3616 -385 3653 -394 3700 -394 ct 3744 -394 3778 -387 3801 -374 ct 
+3825 -360 3841 -337 3850 -305 ct 3798 -296 l  3793 -316 3782 -331 3766 -340 ct 
+3750 -350 3728 -354 3700 -354 ct 3669 -354 3645 -349 3629 -339 ct 3612 -329 3604 -313 3604 -293 ct 
+3604 -281 3607 -271 3614 -263 ct 3620 -255 3629 -249 3641 -243 ct 3653 -238 3677 -231 3712 -223 ct 
+3724 -220 3736 -218 3748 -215 ct 3760 -212 3771 -209 3782 -205 ct 3793 -201 3803 -196 3812 -191 ct 
+3822 -185 3830 -179 3837 -171 ct 3844 -163 3849 -154 3853 -144 ct 3857 -133 3859 -121 3859 -107 ct 
+p ef
+pom
+gs
+0.933 0.933 0.933 c 5207 2038 m  5207 2038 l  5034 2038 4864 2055 4714 2088 ct 
+4564 2120 4439 2167 4352 2223 ct 4266 2279 4220 2343 4220 2408 ct 4220 2684 l 
+4220 2961 l  4220 3338 l  4220 3615 l  4220 3892 l  4220 3892 l  4220 3957 4266 4021 4352 4077 ct 
+4439 4133 4564 4180 4714 4212 ct 4864 4245 5034 4262 5207 4262 ct 5947 4262 l 
+6686 4262 l  7693 4262 l  7727 5598 l  9172 4262 l  9173 4262 l  9346 4262 9515 4245 9665 4212 ct 
+9815 4180 9940 4133 10027 4077 ct 10113 4021 10159 3957 10159 3892 ct 10159 3615 l 
+10159 3338 l  10159 2961 l  10159 2684 l  10159 2407 l  10159 2408 l  10159 2408 l 
+10159 2343 10113 2279 10027 2223 ct 9940 2167 9815 2120 9665 2088 ct 9515 2055 9346 2038 9173 2038 ct 
+8433 2038 l  7693 2038 l  6686 2038 l  5947 2038 l  5207 2038 l  p ef
+gr
+gs
+0 lw 1 lj 5206 2037 m  5206 2037 l  5033 2037 4863 2054 4713 2087 ct 4563 2119 4438 2166 4351 2222 ct 
+4265 2278 4219 2342 4219 2407 ct 4219 2683 l  4219 2960 l  4219 3337 l  4219 3614 l 
+4219 3891 l  4219 3891 l  4219 3956 4265 4020 4351 4076 ct 4438 4132 4563 4179 4713 4211 ct 
+4863 4244 5033 4261 5206 4261 ct 5946 4261 l  6685 4261 l  7692 4261 l  7726 5598 l 
+9171 4261 l  9172 4261 l  9345 4261 9515 4244 9665 4211 ct 9815 4179 9940 4132 10027 4076 ct 
+10113 4020 10159 3956 10159 3891 ct 10159 3614 l  10159 3337 l  10159 2960 l 
+10159 2683 l  10159 2406 l  10159 2407 l  10159 2407 l  10159 2342 10113 2278 10027 2222 ct 
+9940 2166 9815 2119 9665 2087 ct 9515 2054 9345 2037 9172 2037 ct 8432 2037 l 
+7692 2037 l  6685 2037 l  5946 2037 l  5206 2037 l  pc
+gr
+pum
+4717 2717 t
+198 -346 m  198 -1 l  146 -1 l  146 -346 l  13 -346 l  13 -389 l  332 -389 l 
+332 -346 l  198 -346 l  p ef
+430 -247 m  441 -266 454 -281 469 -290 ct 484 -299 503 -303 526 -303 ct 558 -303 582 -295 597 -279 ct 
+612 -263 620 -236 620 -198 ct 620 1 l  570 1 l  570 -189 l  570 -210 568 -225 564 -235 ct 
+561 -246 554 -253 545 -258 ct 537 -263 524 -265 509 -265 ct 485 -265 467 -257 453 -241 ct 
+439 -225 432 -203 432 -175 ct 432 1 l  382 1 l  382 -409 l  432 -409 l  432 -302 l 
+432 -291 431 -279 431 -267 ct 430 -256 430 -249 429 -247 ct 430 -247 l  p ef
+732 -138 m  732 -104 739 -78 753 -59 ct 767 -41 788 -31 815 -31 ct 837 -31 854 -36 867 -44 ct 
+880 -53 889 -64 893 -77 ct 937 -65 l  919 -18 878 6 815 6 ct 771 6 738 -7 715 -33 ct 
+691 -60 680 -99 680 -151 ct 680 -200 691 -238 715 -264 ct 738 -291 770 -304 813 -304 ct 
+901 -304 945 -251 945 -145 ct 945 -138 l  732 -138 l  p
+893 -176 m  891 -208 883 -231 869 -245 ct 856 -260 837 -267 812 -267 ct 788 -267 769 -259 755 -243 ct 
+741 -227 734 -204 733 -176 ct 893 -176 l  p ef
+1417 -1 m  1373 -114 l  1196 -114 l  1152 -1 l  1097 -1 l  1255 -389 l 
+1315 -389 l  1471 -1 l  1417 -1 l  p
+1285 -349 m  1282 -342 l  1278 -326 1271 -307 1262 -283 ct 1212 -155 l  1357 -155 l 
+1308 -283 l  1302 -296 1297 -310 1292 -326 ct 1285 -349 l  p ef
+1819 -271 m  1819 -234 1807 -205 1784 -184 ct 1760 -162 1727 -151 1686 -151 ct 
+1572 -151 l  1572 1 l  1519 1 l  1519 -388 l  1683 -388 l  1726 -388 1760 -378 1784 -358 ct 
+1808 -337 1819 -308 1819 -271 ct p
+1767 -271 m  1767 -321 1736 -346 1676 -346 ct 1572 -346 l  1572 -193 l  1678 -193 l 
+1737 -193 1767 -219 1767 -271 ct p ef
+2370 0 m  2370 -259 l  2370 -288 2371 -315 2373 -342 ct 2364 -309 2356 -283 2348 -265 ct 
+2248 0 l  2211 0 l  2110 -265 l  2094 -312 l  2085 -342 l  2086 -311 l 
+2087 -259 l  2087 0 l  2040 0 l  2040 -389 l  2109 -389 l  2213 -119 l 
+2216 -108 2220 -97 2223 -84 ct 2227 -72 2229 -63 2230 -57 ct 2231 -65 2235 -76 2239 -91 ct 
+2244 -106 2247 -115 2249 -119 ct 2350 -389 l  2418 -389 l  2418 0 l  2370 0 l 
+p ef
+2510 0 m  2510 -389 l  2563 -389 l  2563 -43 l  2759 -43 l  2759 0 l  2510 0 l 
+p ef
+3157 -199 m  3157 -158 3150 -123 3134 -93 ct 3118 -63 3096 -40 3068 -24 ct 3039 -8 3006 0 2968 0 ct 
+2823 0 l  2823 -389 l  2952 -389 l  3017 -389 3068 -372 3104 -339 ct 3139 -306 3157 -259 3157 -199 ct 
+p
+3104 -199 m  3104 -247 3091 -284 3065 -309 ct 3039 -334 3000 -347 2951 -347 ct 
+2876 -347 l  2876 -42 l  2962 -42 l  2991 -42 3016 -48 3037 -61 ct 3059 -73 3076 -92 3087 -115 ct 
+3099 -139 3104 -166 3104 -199 ct p ef
+3630 -150 m  3630 -46 3593 6 3520 6 ct 3498 6 3479 2 3464 -6 ct 3449 -14 3437 -28 3428 -46 ct 
+3427 -46 l  3427 -40 3427 -31 3426 -20 ct 3425 -8 3425 -1 3424 0 ct 3376 0 l 
+3377 -9 3378 -30 3378 -61 ct 3378 -409 l  3428 -409 l  3428 -292 l  3428 -280 3427 -266 3426 -250 ct 
+3428 -250 l  3437 -269 3449 -283 3464 -291 ct 3479 -299 3498 -303 3520 -303 ct 
+3558 -303 3586 -291 3603 -265 ct 3621 -240 3630 -202 3630 -150 ct p
+3578 -148 m  3578 -190 3572 -220 3561 -238 ct 3550 -256 3533 -265 3508 -265 ct 
+3480 -265 3459 -255 3447 -236 ct 3434 -217 3428 -187 3428 -145 ct 3428 -106 3434 -77 3446 -59 ct 
+3459 -40 3479 -31 3507 -31 ct 3532 -31 3550 -40 3561 -58 ct 3572 -77 3578 -107 3578 -148 ct 
+p ef
+3691 1 m  3691 -409 l  3741 -409 l  3741 1 l  3691 1 l  p ef
+4070 -149 m  4070 -97 4059 -58 4036 -32 ct 4013 -7 3979 6 3936 6 ct 3892 6 3859 -7 3837 -34 ct 
+3815 -60 3804 -99 3804 -149 ct 3804 -252 3848 -304 3937 -304 ct 3983 -304 4016 -291 4038 -266 ct 
+4059 -241 4070 -202 4070 -149 ct p
+4018 -149 m  4018 -190 4012 -220 4000 -239 ct 3987 -258 3967 -267 3938 -267 ct 
+3909 -267 3888 -257 3875 -238 ct 3862 -219 3856 -190 3856 -149 ct 3856 -110 3862 -80 3875 -60 ct 
+3888 -41 3908 -31 3935 -31 ct 3965 -31 3986 -40 3999 -59 ct 4012 -79 4018 -108 4018 -149 ct 
+p ef
+4170 -150 m  4170 -111 4176 -81 4188 -62 ct 4201 -43 4220 -33 4245 -33 ct 4263 -33 4277 -38 4289 -48 ct 
+4301 -57 4308 -72 4311 -92 ct 4361 -88 l  4357 -60 4345 -37 4325 -20 ct 4304 -3 4278 6 4246 6 ct 
+4205 6 4173 -7 4151 -34 ct 4129 -60 4118 -99 4118 -149 ct 4118 -200 4129 -238 4151 -264 ct 
+4173 -291 4205 -304 4246 -304 ct 4276 -304 4302 -296 4322 -280 ct 4342 -264 4354 -243 4359 -215 ct 
+4309 -211 l  4306 -227 4299 -241 4289 -250 ct 4279 -260 4264 -265 4244 -265 ct 
+4218 -265 4199 -256 4187 -239 ct 4176 -221 4170 -192 4170 -150 ct p ef
+4602 0 m  4501 -136 l  4465 -106 l  4465 0 l  4415 0 l  4415 -409 l  4465 -409 l 
+4465 -153 l  4595 -298 l  4654 -298 l  4533 -170 l  4660 0 l  4602 0 l 
+p ef
+4923 -82 m  4923 -54 4912 -32 4891 -17 ct 4870 -2 4840 6 4802 6 ct 4765 6 4736 0 4716 -12 ct 
+4696 -25 4683 -44 4677 -70 ct 4720 -78 l  4725 -62 4733 -50 4747 -43 ct 4760 -36 4778 -32 4802 -32 ct 
+4827 -32 4845 -36 4857 -43 ct 4869 -51 4874 -63 4874 -78 ct 4874 -90 4870 -99 4862 -107 ct 
+4854 -114 4841 -120 4823 -125 ct 4788 -134 l  4759 -142 4739 -149 4727 -156 ct 
+4715 -163 4706 -172 4699 -182 ct 4692 -192 4689 -204 4689 -219 ct 4689 -246 4698 -267 4718 -281 ct 
+4737 -295 4765 -303 4802 -303 ct 4835 -303 4861 -297 4881 -285 ct 4900 -274 4912 -255 4917 -229 ct 
+4873 -224 l  4870 -237 4863 -247 4851 -254 ct 4839 -261 4822 -265 4802 -265 ct 
+4780 -265 4763 -262 4753 -255 ct 4742 -248 4737 -238 4737 -224 ct 4737 -215 4739 -208 4743 -203 ct 
+4748 -197 4754 -193 4763 -189 ct 4772 -185 4790 -180 4817 -173 ct 4844 -166 4863 -160 4874 -155 ct 
+4886 -149 4895 -143 4902 -136 ct 4908 -129 4913 -121 4917 -112 ct 4921 -103 4923 -93 4923 -82 ct 
+p ef
+pom
+pum
+5070 3344 t
+153 -3 m  136 2 120 4 102 4 ct 63 4 43 -19 43 -64 ct 43 -263 l  9 -263 l  9 -299 l 
+45 -299 l  59 -366 l  93 -366 l  93 -299 l  148 -299 l  148 -263 l  93 -263 l 
+93 -74 l  93 -60 95 -50 100 -44 ct 104 -38 112 -35 124 -35 ct 131 -35 140 -37 153 -39 ct 
+153 -3 l  p ef
+196 0 m  196 -229 l  196 -250 196 -273 194 -298 ct 241 -298 l  243 -265 243 -244 243 -238 ct 
+245 -238 l  252 -263 262 -281 272 -290 ct 282 -299 297 -304 315 -304 ct 322 -304 329 -303 335 -301 ct 
+335 -256 l  329 -258 320 -258 309 -258 ct 288 -258 273 -250 262 -232 ct 251 -214 246 -189 246 -156 ct 
+246 0 l  196 0 l  p ef
+457 6 m  427 6 405 -2 390 -18 ct 374 -34 367 -55 367 -83 ct 367 -114 377 -138 397 -154 ct 
+418 -171 450 -180 496 -181 ct 562 -182 l  562 -198 l  562 -222 557 -240 547 -250 ct 
+537 -261 521 -266 499 -266 ct 476 -266 460 -262 450 -255 ct 440 -247 434 -235 432 -219 ct 
+380 -223 l  389 -277 428 -304 500 -304 ct 537 -304 565 -295 584 -278 ct 603 -261 613 -236 613 -203 ct 
+613 -74 l  613 -60 615 -49 618 -41 ct 622 -34 630 -30 640 -30 ct 645 -30 651 -31 657 -32 ct 
+657 -1 l  644 2 631 3 618 3 ct 600 3 587 -1 578 -11 ct 570 -21 565 -36 564 -57 ct 
+562 -57 l  550 -34 535 -18 518 -8 ct 501 1 481 6 457 6 ct p
+468 -31 m  486 -31 503 -35 517 -44 ct 531 -52 542 -63 550 -78 ct 558 -92 562 -107 562 -122 ct 
+562 -147 l  508 -146 l  485 -145 467 -143 455 -139 ct 443 -134 434 -127 428 -118 ct 
+421 -109 418 -97 418 -82 ct 418 -66 422 -53 431 -44 ct 440 -36 452 -31 468 -31 ct 
+p ef
+883 0 m  883 -189 l  883 -209 881 -224 877 -235 ct 874 -246 867 -254 859 -258 ct 
+851 -263 838 -266 822 -266 ct 798 -266 779 -257 765 -241 ct 752 -225 745 -202 745 -173 ct 
+745 0 l  695 0 l  695 -235 l  695 -270 695 -291 693 -298 ct 740 -298 l  740 -298 741 -295 741 -291 ct 
+741 -287 741 -282 742 -277 ct 742 -272 742 -262 742 -247 ct 743 -247 l  755 -268 768 -283 783 -291 ct 
+798 -300 816 -304 839 -304 ct 871 -304 895 -296 910 -280 ct 925 -263 933 -236 933 -199 ct 
+933 0 l  883 0 l  p ef
+1231 -82 m  1231 -54 1220 -32 1199 -17 ct 1178 -2 1148 6 1110 6 ct 1073 6 1044 0 1024 -12 ct 
+1004 -25 991 -44 985 -70 ct 1028 -78 l  1033 -62 1041 -50 1055 -43 ct 1068 -36 1086 -32 1110 -32 ct 
+1135 -32 1153 -36 1165 -43 ct 1177 -51 1182 -63 1182 -78 ct 1182 -90 1178 -99 1170 -107 ct 
+1162 -114 1149 -120 1131 -125 ct 1096 -134 l  1067 -142 1047 -149 1035 -156 ct 
+1023 -163 1014 -172 1007 -182 ct 1000 -192 997 -204 997 -219 ct 997 -246 1006 -267 1026 -281 ct 
+1045 -295 1073 -303 1110 -303 ct 1143 -303 1169 -297 1189 -285 ct 1208 -274 1220 -255 1225 -229 ct 
+1181 -224 l  1178 -237 1171 -247 1159 -254 ct 1147 -261 1130 -265 1110 -265 ct 
+1088 -265 1071 -262 1061 -255 ct 1050 -248 1045 -238 1045 -224 ct 1045 -215 1047 -208 1051 -203 ct 
+1056 -197 1062 -193 1071 -189 ct 1080 -185 1098 -180 1125 -173 ct 1152 -166 1171 -160 1182 -155 ct 
+1194 -149 1203 -143 1210 -136 ct 1216 -129 1221 -121 1225 -112 ct 1229 -103 1231 -93 1231 -82 ct 
+p ef
+1465 0 m  1465 -189 l  1465 -218 1461 -238 1453 -249 ct 1445 -260 1431 -266 1410 -266 ct 
+1389 -266 1372 -258 1360 -241 ct 1348 -225 1341 -202 1341 -173 ct 1341 0 l  1292 0 l 
+1292 -235 l  1292 -270 1292 -291 1290 -298 ct 1337 -298 l  1337 -298 1338 -295 1338 -291 ct 
+1338 -287 1338 -282 1339 -277 ct 1339 -272 1339 -262 1339 -247 ct 1340 -247 l 
+1351 -269 1363 -283 1377 -292 ct 1391 -300 1408 -304 1427 -304 ct 1450 -304 1468 -300 1481 -290 ct 
+1494 -281 1503 -267 1508 -247 ct 1509 -247 l  1519 -268 1532 -282 1546 -291 ct 
+1561 -300 1579 -304 1599 -304 ct 1630 -304 1651 -296 1665 -279 ct 1679 -263 1686 -236 1686 -199 ct 
+1686 0 l  1637 0 l  1637 -189 l  1637 -218 1633 -238 1625 -249 ct 1617 -260 1603 -266 1582 -266 ct 
+1560 -266 1544 -258 1532 -242 ct 1520 -225 1514 -203 1514 -173 ct 1514 0 l  1465 0 l 
+p ef
+1761 -362 m  1761 -409 l  1810 -409 l  1810 -362 l  1761 -362 l  p
+1761 0 m  1761 -298 l  1810 -298 l  1810 0 l  1761 0 l  p ef
+2112 -82 m  2112 -54 2101 -32 2080 -17 ct 2059 -2 2029 6 1991 6 ct 1954 6 1925 0 1905 -12 ct 
+1885 -25 1872 -44 1866 -70 ct 1909 -78 l  1914 -62 1922 -50 1936 -43 ct 1949 -36 1967 -32 1991 -32 ct 
+2016 -32 2034 -36 2046 -43 ct 2058 -51 2063 -63 2063 -78 ct 2063 -90 2059 -99 2051 -107 ct 
+2043 -114 2030 -120 2012 -125 ct 1977 -134 l  1948 -142 1928 -149 1916 -156 ct 
+1904 -163 1895 -172 1888 -182 ct 1881 -192 1878 -204 1878 -219 ct 1878 -246 1887 -267 1907 -281 ct 
+1926 -295 1954 -303 1991 -303 ct 2024 -303 2050 -297 2070 -285 ct 2089 -274 2101 -255 2106 -229 ct 
+2062 -224 l  2059 -237 2052 -247 2040 -254 ct 2028 -261 2011 -265 1991 -265 ct 
+1969 -265 1952 -262 1942 -255 ct 1931 -248 1926 -238 1926 -224 ct 1926 -215 1928 -208 1932 -203 ct 
+1937 -197 1943 -193 1952 -189 ct 1961 -185 1979 -180 2006 -173 ct 2033 -166 2052 -160 2063 -155 ct 
+2075 -149 2084 -143 2091 -136 ct 2097 -129 2102 -121 2106 -112 ct 2110 -103 2112 -93 2112 -82 ct 
+p ef
+2396 -82 m  2396 -54 2385 -32 2364 -17 ct 2343 -2 2313 6 2275 6 ct 2238 6 2209 0 2189 -12 ct 
+2169 -25 2156 -44 2150 -70 ct 2193 -78 l  2198 -62 2206 -50 2220 -43 ct 2233 -36 2251 -32 2275 -32 ct 
+2300 -32 2318 -36 2330 -43 ct 2342 -51 2347 -63 2347 -78 ct 2347 -90 2343 -99 2335 -107 ct 
+2327 -114 2314 -120 2296 -125 ct 2261 -134 l  2232 -142 2212 -149 2200 -156 ct 
+2188 -163 2179 -172 2172 -182 ct 2165 -192 2162 -204 2162 -219 ct 2162 -246 2171 -267 2191 -281 ct 
+2210 -295 2238 -303 2275 -303 ct 2308 -303 2334 -297 2354 -285 ct 2373 -274 2385 -255 2390 -229 ct 
+2346 -224 l  2343 -237 2336 -247 2324 -254 ct 2312 -261 2295 -265 2275 -265 ct 
+2253 -265 2236 -262 2226 -255 ct 2215 -248 2210 -238 2210 -224 ct 2210 -215 2212 -208 2216 -203 ct 
+2221 -197 2227 -193 2236 -189 ct 2245 -185 2263 -180 2290 -173 ct 2317 -166 2336 -160 2347 -155 ct 
+2359 -149 2368 -143 2375 -136 ct 2381 -129 2386 -121 2390 -112 ct 2394 -103 2396 -93 2396 -82 ct 
+p ef
+2455 -362 m  2455 -409 l  2504 -409 l  2504 -362 l  2455 -362 l  p
+2455 0 m  2455 -298 l  2504 -298 l  2504 0 l  2455 0 l  p ef
+2834 -149 m  2834 -97 2823 -58 2800 -32 ct 2777 -7 2743 6 2700 6 ct 2656 6 2623 -7 2601 -34 ct 
+2579 -60 2568 -99 2568 -149 ct 2568 -252 2612 -304 2701 -304 ct 2747 -304 2780 -291 2802 -266 ct 
+2823 -241 2834 -202 2834 -149 ct p
+2782 -149 m  2782 -190 2776 -220 2764 -239 ct 2751 -258 2731 -267 2702 -267 ct 
+2673 -267 2652 -257 2639 -238 ct 2626 -219 2620 -190 2620 -149 ct 2620 -110 2626 -80 2639 -60 ct 
+2652 -41 2672 -31 2699 -31 ct 2729 -31 2750 -40 2763 -59 ct 2776 -79 2782 -108 2782 -149 ct 
+p ef
+3085 0 m  3085 -189 l  3085 -209 3083 -224 3079 -235 ct 3076 -246 3069 -254 3061 -258 ct 
+3053 -263 3040 -266 3024 -266 ct 3000 -266 2981 -257 2967 -241 ct 2954 -225 2947 -202 2947 -173 ct 
+2947 0 l  2897 0 l  2897 -235 l  2897 -270 2897 -291 2895 -298 ct 2942 -298 l 
+2942 -298 2943 -295 2943 -291 ct 2943 -287 2943 -282 2944 -277 ct 2944 -272 2944 -262 2944 -247 ct 
+2945 -247 l  2957 -268 2970 -283 2985 -291 ct 3000 -300 3018 -304 3041 -304 ct 
+3073 -304 3097 -296 3112 -280 ct 3127 -263 3135 -236 3135 -199 ct 3135 0 l  3085 0 l 
+p ef
+3433 -82 m  3433 -54 3422 -32 3401 -17 ct 3380 -2 3350 6 3312 6 ct 3275 6 3246 0 3226 -12 ct 
+3206 -25 3193 -44 3187 -70 ct 3230 -78 l  3235 -62 3243 -50 3257 -43 ct 3270 -36 3288 -32 3312 -32 ct 
+3337 -32 3355 -36 3367 -43 ct 3379 -51 3384 -63 3384 -78 ct 3384 -90 3380 -99 3372 -107 ct 
+3364 -114 3351 -120 3333 -125 ct 3298 -134 l  3269 -142 3249 -149 3237 -156 ct 
+3225 -163 3216 -172 3209 -182 ct 3202 -192 3199 -204 3199 -219 ct 3199 -246 3208 -267 3228 -281 ct 
+3247 -295 3275 -303 3312 -303 ct 3345 -303 3371 -297 3391 -285 ct 3410 -274 3422 -255 3427 -229 ct 
+3383 -224 l  3380 -237 3373 -247 3361 -254 ct 3349 -261 3332 -265 3312 -265 ct 
+3290 -265 3273 -262 3263 -255 ct 3252 -248 3247 -238 3247 -224 ct 3247 -215 3249 -208 3253 -203 ct 
+3258 -197 3264 -193 3273 -189 ct 3282 -185 3300 -180 3327 -173 ct 3354 -166 3373 -160 3384 -155 ct 
+3396 -149 3405 -143 3412 -136 ct 3418 -129 3423 -121 3427 -112 ct 3431 -103 3433 -93 3433 -82 ct 
+p ef
+3901 -149 m  3901 -97 3890 -58 3867 -32 ct 3844 -7 3810 6 3767 6 ct 3723 6 3690 -7 3668 -34 ct 
+3646 -60 3635 -99 3635 -149 ct 3635 -252 3679 -304 3768 -304 ct 3814 -304 3847 -291 3869 -266 ct 
+3890 -241 3901 -202 3901 -149 ct p
+3849 -149 m  3849 -190 3843 -220 3831 -239 ct 3818 -258 3798 -267 3769 -267 ct 
+3740 -267 3719 -257 3706 -238 ct 3693 -219 3687 -190 3687 -149 ct 3687 -110 3693 -80 3706 -60 ct 
+3719 -41 3739 -31 3766 -31 ct 3796 -31 3817 -40 3830 -59 ct 3843 -79 3849 -108 3849 -149 ct 
+p ef
+4151 0 m  4151 -189 l  4151 -209 4149 -224 4145 -235 ct 4142 -246 4135 -254 4127 -258 ct 
+4119 -263 4106 -266 4090 -266 ct 4066 -266 4047 -257 4033 -241 ct 4020 -225 4013 -202 4013 -173 ct 
+4013 0 l  3963 0 l  3963 -235 l  3963 -270 3963 -291 3961 -298 ct 4008 -298 l 
+4008 -298 4009 -295 4009 -291 ct 4009 -287 4009 -282 4010 -277 ct 4010 -272 4010 -262 4010 -247 ct 
+4011 -247 l  4023 -268 4036 -283 4051 -291 ct 4066 -300 4084 -304 4107 -304 ct 
+4139 -304 4163 -296 4178 -280 ct 4193 -263 4201 -236 4201 -199 ct 4201 0 l  4151 0 l 
+p ef
+pom
+pum
+4852 3971 t
+290 -149 m  290 -97 279 -58 256 -32 ct 233 -7 199 6 156 6 ct 112 6 79 -7 57 -34 ct 
+35 -60 24 -99 24 -149 ct 24 -252 68 -304 157 -304 ct 203 -304 236 -291 258 -266 ct 
+279 -241 290 -202 290 -149 ct p
+238 -149 m  238 -190 232 -220 220 -239 ct 207 -258 187 -267 158 -267 ct 129 -267 108 -257 95 -238 ct 
+82 -219 76 -190 76 -149 ct 76 -110 82 -80 95 -60 ct 108 -41 128 -31 155 -31 ct 
+185 -31 206 -40 219 -59 ct 232 -79 238 -108 238 -149 ct p ef
+466 -3 m  449 2 433 4 415 4 ct 376 4 356 -19 356 -64 ct 356 -263 l  322 -263 l 
+322 -299 l  358 -299 l  372 -366 l  406 -366 l  406 -299 l  461 -299 l 
+461 -263 l  406 -263 l  406 -74 l  406 -60 408 -50 413 -44 ct 417 -38 425 -35 437 -35 ct 
+444 -35 453 -37 466 -39 ct 466 -3 l  p ef
+557 -247 m  568 -266 581 -281 596 -290 ct 611 -299 630 -303 653 -303 ct 685 -303 709 -295 724 -279 ct 
+739 -263 747 -236 747 -198 ct 747 1 l  697 1 l  697 -189 l  697 -210 695 -225 691 -235 ct 
+688 -246 681 -253 672 -258 ct 664 -263 651 -265 636 -265 ct 612 -265 594 -257 580 -241 ct 
+566 -225 559 -203 559 -175 ct 559 1 l  509 1 l  509 -409 l  559 -409 l  559 -302 l 
+559 -291 558 -279 558 -267 ct 557 -256 557 -249 556 -247 ct 557 -247 l  p ef
+859 -138 m  859 -104 866 -78 880 -59 ct 894 -41 915 -31 942 -31 ct 964 -31 981 -36 994 -44 ct 
+1007 -53 1016 -64 1020 -77 ct 1064 -65 l  1046 -18 1005 6 942 6 ct 898 6 865 -7 842 -33 ct 
+818 -60 807 -99 807 -151 ct 807 -200 818 -238 842 -264 ct 865 -291 897 -304 940 -304 ct 
+1028 -304 1072 -251 1072 -145 ct 1072 -138 l  859 -138 l  p
+1020 -176 m  1018 -208 1010 -231 996 -245 ct 983 -260 964 -267 939 -267 ct 915 -267 896 -259 882 -243 ct 
+868 -227 861 -204 860 -176 ct 1020 -176 l  p ef
+1135 0 m  1135 -229 l  1135 -250 1135 -273 1133 -298 ct 1180 -298 l  1182 -265 1182 -244 1182 -238 ct 
+1184 -238 l  1191 -263 1201 -281 1211 -290 ct 1221 -299 1236 -304 1254 -304 ct 
+1261 -304 1268 -303 1274 -301 ct 1274 -256 l  1268 -258 1259 -258 1248 -258 ct 
+1227 -258 1212 -250 1201 -232 ct 1190 -214 1185 -189 1185 -156 ct 1185 0 l  1135 0 l 
+p ef
+1485 0 m  1485 -389 l  1780 -389 l  1780 -346 l  1538 -346 l  1538 -221 l 
+1763 -221 l  1763 -179 l  1538 -179 l  1538 -43 l  1791 -43 l  1791 0 l 
+1485 0 l  p ef
+2192 0 m  2192 -259 l  2192 -288 2193 -315 2195 -342 ct 2186 -309 2178 -283 2170 -265 ct 
+2070 0 l  2033 0 l  1932 -265 l  1916 -312 l  1907 -342 l  1908 -311 l 
+1909 -259 l  1909 0 l  1862 0 l  1862 -389 l  1931 -389 l  2035 -119 l 
+2038 -108 2042 -97 2045 -84 ct 2049 -72 2051 -63 2052 -57 ct 2053 -65 2057 -76 2061 -91 ct 
+2066 -106 2069 -115 2071 -119 ct 2172 -389 l  2240 -389 l  2240 0 l  2192 0 l 
+p ef
+2332 0 m  2332 -389 l  2385 -389 l  2385 -43 l  2581 -43 l  2581 0 l  2332 0 l 
+p ef
+2949 -107 m  2949 -71 2935 -43 2907 -24 ct 2879 -4 2840 6 2789 6 ct 2694 6 2640 -27 2625 -93 ct 
+2676 -103 l  2681 -80 2694 -62 2713 -52 ct 2732 -41 2758 -35 2791 -35 ct 2825 -35 2851 -41 2870 -53 ct 
+2888 -64 2897 -81 2897 -104 ct 2897 -117 2894 -127 2889 -135 ct 2883 -143 2875 -149 2864 -155 ct 
+2854 -160 2841 -164 2827 -168 ct 2812 -171 2796 -175 2779 -179 ct 2748 -186 2725 -192 2709 -199 ct 
+2693 -206 2680 -214 2671 -222 ct 2662 -230 2655 -240 2650 -251 ct 2645 -263 2643 -275 2643 -290 ct 
+2643 -323 2656 -349 2681 -367 ct 2706 -385 2743 -394 2790 -394 ct 2834 -394 2868 -387 2891 -374 ct 
+2915 -360 2931 -337 2940 -305 ct 2888 -296 l  2883 -316 2872 -331 2856 -340 ct 
+2840 -350 2818 -354 2790 -354 ct 2759 -354 2735 -349 2719 -339 ct 2702 -329 2694 -313 2694 -293 ct 
+2694 -281 2697 -271 2704 -263 ct 2710 -255 2719 -249 2731 -243 ct 2743 -238 2767 -231 2802 -223 ct 
+2814 -220 2826 -218 2838 -215 ct 2850 -212 2861 -209 2872 -205 ct 2883 -201 2893 -196 2902 -191 ct 
+2912 -185 2920 -179 2927 -171 ct 2934 -163 2939 -154 2943 -144 ct 2947 -133 2949 -121 2949 -107 ct 
+p ef
+3297 1 m  3196 -161 l  3075 -161 l  3075 1 l  3022 1 l  3022 -388 l  3205 -388 l 
+3249 -388 3282 -378 3306 -359 ct 3330 -339 3342 -312 3342 -277 ct 3342 -248 3333 -224 3317 -204 ct 
+3300 -185 3277 -172 3247 -167 ct 3357 1 l  3297 1 l  p
+3289 -277 m  3289 -299 3281 -316 3266 -328 ct 3251 -340 3228 -346 3200 -346 ct 
+3075 -346 l  3075 -203 l  3202 -203 l  3230 -203 3251 -209 3266 -222 ct 3281 -235 3289 -253 3289 -277 ct 
+p ef
+3577 1 m  3577 -409 l  3627 -409 l  3627 1 l  3577 1 l  p ef
+3704 -362 m  3704 -409 l  3753 -409 l  3753 -362 l  3704 -362 l  p
+3704 0 m  3704 -298 l  3753 -298 l  3753 0 l  3704 0 l  p ef
+4020 0 m  4020 -189 l  4020 -209 4018 -224 4014 -235 ct 4011 -246 4004 -254 3996 -258 ct 
+3988 -263 3975 -266 3959 -266 ct 3935 -266 3916 -257 3902 -241 ct 3889 -225 3882 -202 3882 -173 ct 
+3882 0 l  3832 0 l  3832 -235 l  3832 -270 3832 -291 3830 -298 ct 3877 -298 l 
+3877 -298 3878 -295 3878 -291 ct 3878 -287 3878 -282 3879 -277 ct 3879 -272 3879 -262 3879 -247 ct 
+3880 -247 l  3892 -268 3905 -283 3920 -291 ct 3935 -300 3953 -304 3976 -304 ct 
+4008 -304 4032 -296 4047 -280 ct 4062 -263 4070 -236 4070 -199 ct 4070 0 l  4020 0 l 
+p ef
+4331 0 m  4230 -136 l  4194 -106 l  4194 0 l  4144 0 l  4144 -409 l  4194 -409 l 
+4194 -153 l  4324 -298 l  4383 -298 l  4262 -170 l  4389 0 l  4331 0 l 
+p ef
+4652 -82 m  4652 -54 4641 -32 4620 -17 ct 4599 -2 4569 6 4531 6 ct 4494 6 4465 0 4445 -12 ct 
+4425 -25 4412 -44 4406 -70 ct 4449 -78 l  4454 -62 4462 -50 4476 -43 ct 4489 -36 4507 -32 4531 -32 ct 
+4556 -32 4574 -36 4586 -43 ct 4598 -51 4603 -63 4603 -78 ct 4603 -90 4599 -99 4591 -107 ct 
+4583 -114 4570 -120 4552 -125 ct 4517 -134 l  4488 -142 4468 -149 4456 -156 ct 
+4444 -163 4435 -172 4428 -182 ct 4421 -192 4418 -204 4418 -219 ct 4418 -246 4427 -267 4447 -281 ct 
+4466 -295 4494 -303 4531 -303 ct 4564 -303 4590 -297 4610 -285 ct 4629 -274 4641 -255 4646 -229 ct 
+4602 -224 l  4599 -237 4592 -247 4580 -254 ct 4568 -261 4551 -265 4531 -265 ct 
+4509 -265 4492 -262 4482 -255 ct 4471 -248 4466 -238 4466 -224 ct 4466 -215 4468 -208 4472 -203 ct 
+4477 -197 4483 -193 4492 -189 ct 4501 -185 4519 -180 4546 -173 ct 4573 -166 4592 -160 4603 -155 ct 
+4615 -149 4624 -143 4631 -136 ct 4637 -129 4642 -121 4646 -112 ct 4650 -103 4652 -93 4652 -82 ct 
+p ef
+pom
+gs
+0.933 0.933 0.933 c 19165 2307 m  19165 2307 l  19017 2307 18872 2324 18743 2357 ct 
+18615 2389 18508 2436 18434 2492 ct 18360 2548 18321 2612 18321 2677 ct 18321 2953 l 
+18321 3230 l  18321 3607 l  18321 3884 l  18321 4161 l  18321 4161 l  18321 4226 18360 4290 18434 4346 ct 
+18508 4402 18615 4449 18743 4481 ct 18872 4514 19017 4531 19165 4531 ct 19798 4531 l 
+20431 4531 l  21291 4531 l  23066 6037 l  22557 4531 l  22558 4531 l  22706 4531 22851 4514 22980 4481 ct 
+23108 4449 23215 4402 23289 4346 ct 23363 4290 23402 4226 23402 4161 ct 23402 3884 l 
+23402 3607 l  23402 3230 l  23402 2953 l  23402 2676 l  23402 2677 l  23402 2677 l 
+23402 2612 23363 2548 23289 2492 ct 23215 2436 23108 2389 22980 2357 ct 22851 2324 22706 2307 22558 2307 ct 
+21924 2307 l  21291 2307 l  20431 2307 l  19798 2307 l  19165 2307 l  p ef
+gr
+gs
+0 lw 1 lj 19164 2306 m  19164 2306 l  19016 2306 18872 2323 18743 2356 ct 
+18615 2388 18508 2435 18434 2491 ct 18360 2547 18321 2611 18321 2676 ct 18321 2952 l 
+18321 3229 l  18321 3606 l  18321 3883 l  18321 4160 l  18321 4160 l  18321 4225 18360 4289 18434 4345 ct 
+18508 4401 18615 4448 18743 4480 ct 18872 4513 19016 4530 19164 4530 ct 19797 4530 l 
+20430 4530 l  21290 4530 l  23065 6037 l  22556 4530 l  22557 4530 l  22705 4530 22850 4513 22979 4480 ct 
+23107 4448 23214 4401 23288 4345 ct 23362 4289 23401 4225 23401 4160 ct 23401 3883 l 
+23401 3606 l  23401 3229 l  23401 2952 l  23401 2675 l  23401 2676 l  23401 2676 l 
+23401 2611 23362 2547 23288 2491 ct 23214 2435 23107 2388 22979 2356 ct 22850 2323 22705 2306 22557 2306 ct 
+21923 2306 l  21290 2306 l  20430 2306 l  19797 2306 l  19164 2306 l  pc
+gr
+pum
+19269 2986 t
+198 -346 m  198 -1 l  146 -1 l  146 -346 l  13 -346 l  13 -389 l  332 -389 l 
+332 -346 l  198 -346 l  p ef
+430 -247 m  441 -266 454 -281 469 -290 ct 484 -299 503 -303 526 -303 ct 558 -303 582 -295 597 -279 ct 
+612 -263 620 -236 620 -198 ct 620 1 l  570 1 l  570 -189 l  570 -210 568 -225 564 -235 ct 
+561 -246 554 -253 545 -258 ct 537 -263 524 -265 509 -265 ct 485 -265 467 -257 453 -241 ct 
+439 -225 432 -203 432 -175 ct 432 1 l  382 1 l  382 -409 l  432 -409 l  432 -302 l 
+432 -291 431 -279 431 -267 ct 430 -256 430 -249 429 -247 ct 430 -247 l  p ef
+732 -138 m  732 -104 739 -78 753 -59 ct 767 -41 788 -31 815 -31 ct 837 -31 854 -36 867 -44 ct 
+880 -53 889 -64 893 -77 ct 937 -65 l  919 -18 878 6 815 6 ct 771 6 738 -7 715 -33 ct 
+691 -60 680 -99 680 -151 ct 680 -200 691 -238 715 -264 ct 738 -291 770 -304 813 -304 ct 
+901 -304 945 -251 945 -145 ct 945 -138 l  732 -138 l  p
+893 -176 m  891 -208 883 -231 869 -245 ct 856 -260 837 -267 812 -267 ct 788 -267 769 -259 755 -243 ct 
+741 -227 734 -204 733 -176 ct 893 -176 l  p ef
+1417 -1 m  1373 -114 l  1196 -114 l  1152 -1 l  1097 -1 l  1255 -389 l 
+1315 -389 l  1471 -1 l  1417 -1 l  p
+1285 -349 m  1282 -342 l  1278 -326 1271 -307 1262 -283 ct 1212 -155 l  1357 -155 l 
+1308 -283 l  1302 -296 1297 -310 1292 -326 ct 1285 -349 l  p ef
+1819 -271 m  1819 -234 1807 -205 1784 -184 ct 1760 -162 1727 -151 1686 -151 ct 
+1572 -151 l  1572 1 l  1519 1 l  1519 -388 l  1683 -388 l  1726 -388 1760 -378 1784 -358 ct 
+1808 -337 1819 -308 1819 -271 ct p
+1767 -271 m  1767 -321 1736 -346 1676 -346 ct 1572 -346 l  1572 -193 l  1678 -193 l 
+1737 -193 1767 -219 1767 -271 ct p ef
+2370 0 m  2370 -259 l  2370 -288 2371 -315 2373 -342 ct 2364 -309 2356 -283 2348 -265 ct 
+2248 0 l  2211 0 l  2110 -265 l  2094 -312 l  2085 -342 l  2086 -311 l 
+2087 -259 l  2087 0 l  2040 0 l  2040 -389 l  2109 -389 l  2213 -119 l 
+2216 -108 2220 -97 2223 -84 ct 2227 -72 2229 -63 2230 -57 ct 2231 -65 2235 -76 2239 -91 ct 
+2244 -106 2247 -115 2249 -119 ct 2350 -389 l  2418 -389 l  2418 0 l  2370 0 l 
+p ef
+2510 0 m  2510 -389 l  2563 -389 l  2563 -43 l  2759 -43 l  2759 0 l  2510 0 l 
+p ef
+3157 -199 m  3157 -158 3150 -123 3134 -93 ct 3118 -63 3096 -40 3068 -24 ct 3039 -8 3006 0 2968 0 ct 
+2823 0 l  2823 -389 l  2952 -389 l  3017 -389 3068 -372 3104 -339 ct 3139 -306 3157 -259 3157 -199 ct 
+p
+3104 -199 m  3104 -247 3091 -284 3065 -309 ct 3039 -334 3000 -347 2951 -347 ct 
+2876 -347 l  2876 -42 l  2962 -42 l  2991 -42 3016 -48 3037 -61 ct 3059 -73 3076 -92 3087 -115 ct 
+3099 -139 3104 -166 3104 -199 ct p ef
+pom
+pum
+19226 3613 t
+290 -150 m  290 -46 253 6 180 6 ct 158 6 139 2 124 -6 ct 109 -14 97 -28 88 -46 ct 
+87 -46 l  87 -40 87 -31 86 -20 ct 85 -8 85 -1 84 0 ct 36 0 l  37 -9 38 -30 38 -61 ct 
+38 -409 l  88 -409 l  88 -292 l  88 -280 87 -266 86 -250 ct 88 -250 l  97 -269 109 -283 124 -291 ct 
+139 -299 158 -303 180 -303 ct 218 -303 246 -291 263 -265 ct 281 -240 290 -202 290 -150 ct 
+p
+238 -148 m  238 -190 232 -220 221 -238 ct 210 -256 193 -265 168 -265 ct 140 -265 119 -255 107 -236 ct 
+94 -217 88 -187 88 -145 ct 88 -106 94 -77 106 -59 ct 119 -40 139 -31 167 -31 ct 
+192 -31 210 -40 221 -58 ct 232 -77 238 -107 238 -148 ct p ef
+351 1 m  351 -409 l  401 -409 l  401 1 l  351 1 l  p ef
+730 -149 m  730 -97 719 -58 696 -32 ct 673 -7 639 6 596 6 ct 552 6 519 -7 497 -34 ct 
+475 -60 464 -99 464 -149 ct 464 -252 508 -304 597 -304 ct 643 -304 676 -291 698 -266 ct 
+719 -241 730 -202 730 -149 ct p
+678 -149 m  678 -190 672 -220 660 -239 ct 647 -258 627 -267 598 -267 ct 569 -267 548 -257 535 -238 ct 
+522 -219 516 -190 516 -149 ct 516 -110 522 -80 535 -60 ct 548 -41 568 -31 595 -31 ct 
+625 -31 646 -40 659 -59 ct 672 -79 678 -108 678 -149 ct p ef
+830 -150 m  830 -111 836 -81 848 -62 ct 861 -43 880 -33 905 -33 ct 923 -33 937 -38 949 -48 ct 
+961 -57 968 -72 971 -92 ct 1021 -88 l  1017 -60 1005 -37 985 -20 ct 964 -3 938 6 906 6 ct 
+865 6 833 -7 811 -34 ct 789 -60 778 -99 778 -149 ct 778 -200 789 -238 811 -264 ct 
+833 -291 865 -304 906 -304 ct 936 -304 962 -296 982 -280 ct 1002 -264 1014 -243 1019 -215 ct 
+969 -211 l  966 -227 959 -241 949 -250 ct 939 -260 924 -265 904 -265 ct 878 -265 859 -256 847 -239 ct 
+836 -221 830 -192 830 -150 ct p ef
+1262 0 m  1161 -136 l  1125 -106 l  1125 0 l  1075 0 l  1075 -409 l  1125 -409 l 
+1125 -153 l  1255 -298 l  1314 -298 l  1193 -170 l  1320 0 l  1262 0 l 
+p ef
+1583 -82 m  1583 -54 1572 -32 1551 -17 ct 1530 -2 1500 6 1462 6 ct 1425 6 1396 0 1376 -12 ct 
+1356 -25 1343 -44 1337 -70 ct 1380 -78 l  1385 -62 1393 -50 1407 -43 ct 1420 -36 1438 -32 1462 -32 ct 
+1487 -32 1505 -36 1517 -43 ct 1529 -51 1534 -63 1534 -78 ct 1534 -90 1530 -99 1522 -107 ct 
+1514 -114 1501 -120 1483 -125 ct 1448 -134 l  1419 -142 1399 -149 1387 -156 ct 
+1375 -163 1366 -172 1359 -182 ct 1352 -192 1349 -204 1349 -219 ct 1349 -246 1358 -267 1378 -281 ct 
+1397 -295 1425 -303 1462 -303 ct 1495 -303 1521 -297 1541 -285 ct 1560 -274 1572 -255 1577 -229 ct 
+1533 -224 l  1530 -237 1523 -247 1511 -254 ct 1499 -261 1482 -265 1462 -265 ct 
+1440 -265 1423 -262 1413 -255 ct 1402 -248 1397 -238 1397 -224 ct 1397 -215 1399 -208 1403 -203 ct 
+1408 -197 1414 -193 1423 -189 ct 1432 -185 1450 -180 1477 -173 ct 1504 -166 1523 -160 1534 -155 ct 
+1546 -149 1555 -143 1562 -136 ct 1568 -129 1573 -121 1577 -112 ct 1581 -103 1583 -93 1583 -82 ct 
+p ef
+1875 6 m  1845 6 1823 -2 1808 -18 ct 1792 -34 1785 -55 1785 -83 ct 1785 -114 1795 -138 1815 -154 ct 
+1836 -171 1868 -180 1914 -181 ct 1980 -182 l  1980 -198 l  1980 -222 1975 -240 1965 -250 ct 
+1955 -261 1939 -266 1917 -266 ct 1894 -266 1878 -262 1868 -255 ct 1858 -247 1852 -235 1850 -219 ct 
+1798 -223 l  1807 -277 1846 -304 1918 -304 ct 1955 -304 1983 -295 2002 -278 ct 
+2021 -261 2031 -236 2031 -203 ct 2031 -74 l  2031 -60 2033 -49 2036 -41 ct 2040 -34 2048 -30 2058 -30 ct 
+2063 -30 2069 -31 2075 -32 ct 2075 -1 l  2062 2 2049 3 2036 3 ct 2018 3 2005 -1 1996 -11 ct 
+1988 -21 1983 -36 1982 -57 ct 1980 -57 l  1968 -34 1953 -18 1936 -8 ct 1919 1 1899 6 1875 6 ct 
+p
+1886 -31 m  1904 -31 1921 -35 1935 -44 ct 1949 -52 1960 -63 1968 -78 ct 1976 -92 1980 -107 1980 -122 ct 
+1980 -147 l  1926 -146 l  1903 -145 1885 -143 1873 -139 ct 1861 -134 1852 -127 1846 -118 ct 
+1839 -109 1836 -97 1836 -82 ct 1836 -66 1840 -53 1849 -44 ct 1858 -36 1870 -31 1886 -31 ct 
+p ef
+2112 1 m  2112 -409 l  2162 -409 l  2162 1 l  2112 1 l  p ef
+2239 1 m  2239 -409 l  2289 -409 l  2289 1 l  2239 1 l  p ef
+2638 -3 m  2621 2 2605 4 2587 4 ct 2548 4 2528 -19 2528 -64 ct 2528 -263 l  2494 -263 l 
+2494 -299 l  2530 -299 l  2544 -366 l  2578 -366 l  2578 -299 l  2633 -299 l 
+2633 -263 l  2578 -263 l  2578 -74 l  2578 -60 2580 -50 2585 -44 ct 2589 -38 2597 -35 2609 -35 ct 
+2616 -35 2625 -37 2638 -39 ct 2638 -3 l  p ef
+2729 -247 m  2740 -266 2753 -281 2768 -290 ct 2783 -299 2802 -303 2825 -303 ct 
+2857 -303 2881 -295 2896 -279 ct 2911 -263 2919 -236 2919 -198 ct 2919 1 l  2869 1 l 
+2869 -189 l  2869 -210 2867 -225 2863 -235 ct 2860 -246 2853 -253 2844 -258 ct 
+2836 -263 2823 -265 2808 -265 ct 2784 -265 2766 -257 2752 -241 ct 2738 -225 2731 -203 2731 -175 ct 
+2731 1 l  2681 1 l  2681 -409 l  2731 -409 l  2731 -302 l  2731 -291 2730 -279 2730 -267 ct 
+2729 -256 2729 -249 2728 -247 ct 2729 -247 l  p ef
+3031 -138 m  3031 -104 3038 -78 3052 -59 ct 3066 -41 3087 -31 3114 -31 ct 3136 -31 3153 -36 3166 -44 ct 
+3179 -53 3188 -64 3192 -77 ct 3236 -65 l  3218 -18 3177 6 3114 6 ct 3070 6 3037 -7 3014 -33 ct 
+2990 -60 2979 -99 2979 -151 ct 2979 -200 2990 -238 3014 -264 ct 3037 -291 3069 -304 3112 -304 ct 
+3200 -304 3244 -251 3244 -145 ct 3244 -138 l  3031 -138 l  p
+3192 -176 m  3190 -208 3182 -231 3168 -245 ct 3155 -260 3136 -267 3111 -267 ct 
+3087 -267 3068 -259 3054 -243 ct 3040 -227 3033 -204 3032 -176 ct 3192 -176 l 
+p ef
+pom
+pum
+20293 4240 t
+38 1 m  38 -409 l  88 -409 l  88 1 l  38 1 l  p ef
+165 -362 m  165 -409 l  214 -409 l  214 -362 l  165 -362 l  p
+165 0 m  165 -298 l  214 -298 l  214 0 l  165 0 l  p ef
+481 0 m  481 -189 l  481 -209 479 -224 475 -235 ct 472 -246 465 -254 457 -258 ct 
+449 -263 436 -266 420 -266 ct 396 -266 377 -257 363 -241 ct 350 -225 343 -202 343 -173 ct 
+343 0 l  293 0 l  293 -235 l  293 -270 293 -291 291 -298 ct 338 -298 l  338 -298 339 -295 339 -291 ct 
+339 -287 339 -282 340 -277 ct 340 -272 340 -262 340 -247 ct 341 -247 l  353 -268 366 -283 381 -291 ct 
+396 -300 414 -304 437 -304 ct 469 -304 493 -296 508 -280 ct 523 -263 531 -236 531 -199 ct 
+531 0 l  481 0 l  p ef
+792 0 m  691 -136 l  655 -106 l  655 0 l  605 0 l  605 -409 l  655 -409 l 
+655 -153 l  785 -298 l  844 -298 l  723 -170 l  850 0 l  792 0 l  p ef
+1113 -82 m  1113 -54 1102 -32 1081 -17 ct 1060 -2 1030 6 992 6 ct 955 6 926 0 906 -12 ct 
+886 -25 873 -44 867 -70 ct 910 -78 l  915 -62 923 -50 937 -43 ct 950 -36 968 -32 992 -32 ct 
+1017 -32 1035 -36 1047 -43 ct 1059 -51 1064 -63 1064 -78 ct 1064 -90 1060 -99 1052 -107 ct 
+1044 -114 1031 -120 1013 -125 ct 978 -134 l  949 -142 929 -149 917 -156 ct 905 -163 896 -172 889 -182 ct 
+882 -192 879 -204 879 -219 ct 879 -246 888 -267 908 -281 ct 927 -295 955 -303 992 -303 ct 
+1025 -303 1051 -297 1071 -285 ct 1090 -274 1102 -255 1107 -229 ct 1063 -224 l 
+1060 -237 1053 -247 1041 -254 ct 1029 -261 1012 -265 992 -265 ct 970 -265 953 -262 943 -255 ct 
+932 -248 927 -238 927 -224 ct 927 -215 929 -208 933 -203 ct 938 -197 944 -193 953 -189 ct 
+962 -185 980 -180 1007 -173 ct 1034 -166 1053 -160 1064 -155 ct 1076 -149 1085 -143 1092 -136 ct 
+1098 -129 1103 -121 1107 -112 ct 1111 -103 1113 -93 1113 -82 ct p ef
+pom
+gs
+0.933 0.933 0.933 c 27866 2276 m  27866 2276 l  27718 2276 27573 2293 27444 2326 ct 
+27316 2358 27209 2405 27135 2461 ct 27061 2517 27022 2581 27022 2646 ct 27022 2922 l 
+27022 3199 l  27022 3576 l  27022 3853 l  27022 4130 l  27022 4130 l  27022 4195 27061 4259 27135 4315 ct 
+27209 4371 27316 4418 27444 4450 ct 27573 4483 27718 4500 27866 4500 ct 27136 5934 l 
+29131 4500 l  29991 4500 l  30624 4500 l  31257 4500 l  31258 4500 l  31406 4500 31551 4483 31680 4450 ct 
+31808 4418 31915 4371 31989 4315 ct 32063 4259 32102 4195 32102 4130 ct 32102 3853 l 
+32102 3576 l  32102 3199 l  32102 2922 l  32102 2645 l  32102 2646 l  32102 2646 l 
+32102 2581 32063 2517 31989 2461 ct 31915 2405 31808 2358 31680 2326 ct 31551 2293 31406 2276 31258 2276 ct 
+30624 2276 l  29991 2276 l  29131 2276 l  28499 2276 l  27866 2276 l  p ef
+gr
+gs
+0 lw 1 lj 27865 2275 m  27865 2275 l  27717 2275 27572 2292 27443 2325 ct 
+27315 2357 27208 2404 27134 2460 ct 27060 2516 27021 2580 27021 2645 ct 27021 2921 l 
+27021 3198 l  27021 3575 l  27021 3852 l  27021 4129 l  27021 4129 l  27021 4194 27060 4258 27134 4314 ct 
+27208 4370 27315 4417 27443 4449 ct 27572 4482 27717 4499 27865 4499 ct 27135 5934 l 
+29131 4499 l  29991 4499 l  30624 4499 l  31257 4499 l  31258 4499 l  31406 4499 31551 4482 31680 4449 ct 
+31808 4417 31915 4370 31989 4314 ct 32063 4258 32102 4194 32102 4129 ct 32102 3852 l 
+32102 3575 l  32102 3198 l  32102 2921 l  32102 2644 l  32102 2645 l  32102 2645 l 
+32102 2580 32063 2516 31989 2460 ct 31915 2404 31808 2357 31680 2325 ct 31551 2292 31406 2275 31258 2275 ct 
+30624 2275 l  29991 2275 l  29131 2275 l  28498 2275 l  27865 2275 l  pc
+gr
+pum
+27970 2955 t
+198 -346 m  198 -1 l  146 -1 l  146 -346 l  13 -346 l  13 -389 l  332 -389 l 
+332 -346 l  198 -346 l  p ef
+430 -247 m  441 -266 454 -281 469 -290 ct 484 -299 503 -303 526 -303 ct 558 -303 582 -295 597 -279 ct 
+612 -263 620 -236 620 -198 ct 620 1 l  570 1 l  570 -189 l  570 -210 568 -225 564 -235 ct 
+561 -246 554 -253 545 -258 ct 537 -263 524 -265 509 -265 ct 485 -265 467 -257 453 -241 ct 
+439 -225 432 -203 432 -175 ct 432 1 l  382 1 l  382 -409 l  432 -409 l  432 -302 l 
+432 -291 431 -279 431 -267 ct 430 -256 430 -249 429 -247 ct 430 -247 l  p ef
+732 -138 m  732 -104 739 -78 753 -59 ct 767 -41 788 -31 815 -31 ct 837 -31 854 -36 867 -44 ct 
+880 -53 889 -64 893 -77 ct 937 -65 l  919 -18 878 6 815 6 ct 771 6 738 -7 715 -33 ct 
+691 -60 680 -99 680 -151 ct 680 -200 691 -238 715 -264 ct 738 -291 770 -304 813 -304 ct 
+901 -304 945 -251 945 -145 ct 945 -138 l  732 -138 l  p
+893 -176 m  891 -208 883 -231 869 -245 ct 856 -260 837 -267 812 -267 ct 788 -267 769 -259 755 -243 ct 
+741 -227 734 -204 733 -176 ct 893 -176 l  p ef
+1417 -1 m  1373 -114 l  1196 -114 l  1152 -1 l  1097 -1 l  1255 -389 l 
+1315 -389 l  1471 -1 l  1417 -1 l  p
+1285 -349 m  1282 -342 l  1278 -326 1271 -307 1262 -283 ct 1212 -155 l  1357 -155 l 
+1308 -283 l  1302 -296 1297 -310 1292 -326 ct 1285 -349 l  p ef
+1819 -271 m  1819 -234 1807 -205 1784 -184 ct 1760 -162 1727 -151 1686 -151 ct 
+1572 -151 l  1572 1 l  1519 1 l  1519 -388 l  1683 -388 l  1726 -388 1760 -378 1784 -358 ct 
+1808 -337 1819 -308 1819 -271 ct p
+1767 -271 m  1767 -321 1736 -346 1676 -346 ct 1572 -346 l  1572 -193 l  1678 -193 l 
+1737 -193 1767 -219 1767 -271 ct p ef
+2370 0 m  2370 -259 l  2370 -288 2371 -315 2373 -342 ct 2364 -309 2356 -283 2348 -265 ct 
+2248 0 l  2211 0 l  2110 -265 l  2094 -312 l  2085 -342 l  2086 -311 l 
+2087 -259 l  2087 0 l  2040 0 l  2040 -389 l  2109 -389 l  2213 -119 l 
+2216 -108 2220 -97 2223 -84 ct 2227 -72 2229 -63 2230 -57 ct 2231 -65 2235 -76 2239 -91 ct 
+2244 -106 2247 -115 2249 -119 ct 2350 -389 l  2418 -389 l  2418 0 l  2370 0 l 
+p ef
+2510 0 m  2510 -389 l  2563 -389 l  2563 -43 l  2759 -43 l  2759 0 l  2510 0 l 
+p ef
+3157 -199 m  3157 -158 3150 -123 3134 -93 ct 3118 -63 3096 -40 3068 -24 ct 3039 -8 3006 0 2968 0 ct 
+2823 0 l  2823 -389 l  2952 -389 l  3017 -389 3068 -372 3104 -339 ct 3139 -306 3157 -259 3157 -199 ct 
+p
+3104 -199 m  3104 -247 3091 -284 3065 -309 ct 3039 -334 3000 -347 2951 -347 ct 
+2876 -347 l  2876 -42 l  2962 -42 l  2991 -42 3016 -48 3037 -61 ct 3059 -73 3076 -92 3087 -115 ct 
+3099 -139 3104 -166 3104 -199 ct p ef
+pom
+pum
+27614 3582 t
+86 -298 m  86 -109 l  86 -89 88 -74 92 -63 ct 96 -52 102 -44 111 -40 ct 119 -35 132 -32 148 -32 ct 
+172 -32 191 -41 204 -57 ct 218 -73 225 -96 225 -125 ct 225 -298 l  275 -298 l 
+275 -63 l  275 -28 275 -7 276 0 ct 229 0 l  229 0 229 -3 229 -7 ct 229 -11 228 -16 228 -21 ct 
+228 -26 228 -36 227 -51 ct 226 -51 l  215 -30 202 -15 187 -7 ct 172 2 153 6 131 6 ct 
+98 6 74 -2 59 -18 ct 44 -35 37 -62 37 -99 ct 37 -298 l  86 -298 l  p ef
+540 0 m  540 -189 l  540 -209 538 -224 534 -235 ct 531 -246 524 -254 516 -258 ct 
+508 -263 495 -266 479 -266 ct 455 -266 436 -257 422 -241 ct 409 -225 402 -202 402 -173 ct 
+402 0 l  352 0 l  352 -235 l  352 -270 352 -291 350 -298 ct 397 -298 l  397 -298 398 -295 398 -291 ct 
+398 -287 398 -282 399 -277 ct 399 -272 399 -262 399 -247 ct 400 -247 l  412 -268 425 -283 440 -291 ct 
+455 -300 473 -304 496 -304 ct 528 -304 552 -296 567 -280 ct 582 -263 590 -236 590 -199 ct 
+590 0 l  540 0 l  p ef
+917 -150 m  917 -46 880 6 807 6 ct 785 6 766 2 751 -6 ct 736 -14 724 -28 715 -46 ct 
+714 -46 l  714 -40 714 -31 713 -20 ct 712 -8 712 -1 711 0 ct 663 0 l  664 -9 665 -30 665 -61 ct 
+665 -409 l  715 -409 l  715 -292 l  715 -280 714 -266 713 -250 ct 715 -250 l 
+724 -269 736 -283 751 -291 ct 766 -299 785 -303 807 -303 ct 845 -303 873 -291 890 -265 ct 
+908 -240 917 -202 917 -150 ct p
+865 -148 m  865 -190 859 -220 848 -238 ct 837 -256 820 -265 795 -265 ct 767 -265 746 -255 734 -236 ct 
+721 -217 715 -187 715 -145 ct 715 -106 721 -77 733 -59 ct 746 -40 766 -31 794 -31 ct 
+819 -31 837 -40 848 -58 ct 859 -77 865 -107 865 -148 ct p ef
+978 1 m  978 -409 l  1028 -409 l  1028 1 l  978 1 l  p ef
+1357 -149 m  1357 -97 1346 -58 1323 -32 ct 1300 -7 1266 6 1223 6 ct 1179 6 1146 -7 1124 -34 ct 
+1102 -60 1091 -99 1091 -149 ct 1091 -252 1135 -304 1224 -304 ct 1270 -304 1303 -291 1325 -266 ct 
+1346 -241 1357 -202 1357 -149 ct p
+1305 -149 m  1305 -190 1299 -220 1287 -239 ct 1274 -258 1254 -267 1225 -267 ct 
+1196 -267 1175 -257 1162 -238 ct 1149 -219 1143 -190 1143 -149 ct 1143 -110 1149 -80 1162 -60 ct 
+1175 -41 1195 -31 1222 -31 ct 1252 -31 1273 -40 1286 -59 ct 1299 -79 1305 -108 1305 -149 ct 
+p ef
+1456 -150 m  1456 -111 1462 -81 1474 -62 ct 1487 -43 1506 -33 1531 -33 ct 1549 -33 1563 -38 1575 -48 ct 
+1587 -57 1594 -72 1597 -92 ct 1647 -88 l  1643 -60 1631 -37 1611 -20 ct 1590 -3 1564 6 1532 6 ct 
+1491 6 1459 -7 1437 -34 ct 1415 -60 1404 -99 1404 -149 ct 1404 -200 1415 -238 1437 -264 ct 
+1459 -291 1491 -304 1532 -304 ct 1562 -304 1588 -296 1608 -280 ct 1628 -264 1640 -243 1645 -215 ct 
+1595 -211 l  1592 -227 1585 -241 1575 -250 ct 1565 -260 1550 -265 1530 -265 ct 
+1504 -265 1485 -256 1473 -239 ct 1462 -221 1456 -192 1456 -150 ct p ef
+1889 0 m  1788 -136 l  1752 -106 l  1752 0 l  1702 0 l  1702 -409 l  1752 -409 l 
+1752 -153 l  1882 -298 l  1941 -298 l  1820 -170 l  1947 0 l  1889 0 l 
+p ef
+2209 -82 m  2209 -54 2198 -32 2177 -17 ct 2156 -2 2126 6 2088 6 ct 2051 6 2022 0 2002 -12 ct 
+1982 -25 1969 -44 1963 -70 ct 2006 -78 l  2011 -62 2019 -50 2033 -43 ct 2046 -36 2064 -32 2088 -32 ct 
+2113 -32 2131 -36 2143 -43 ct 2155 -51 2160 -63 2160 -78 ct 2160 -90 2156 -99 2148 -107 ct 
+2140 -114 2127 -120 2109 -125 ct 2074 -134 l  2045 -142 2025 -149 2013 -156 ct 
+2001 -163 1992 -172 1985 -182 ct 1978 -192 1975 -204 1975 -219 ct 1975 -246 1984 -267 2004 -281 ct 
+2023 -295 2051 -303 2088 -303 ct 2121 -303 2147 -297 2167 -285 ct 2186 -274 2198 -255 2203 -229 ct 
+2159 -224 l  2156 -237 2149 -247 2137 -254 ct 2125 -261 2108 -265 2088 -265 ct 
+2066 -265 2049 -262 2039 -255 ct 2028 -248 2023 -238 2023 -224 ct 2023 -215 2025 -208 2029 -203 ct 
+2034 -197 2040 -193 2049 -189 ct 2058 -185 2076 -180 2103 -173 ct 2130 -166 2149 -160 2160 -155 ct 
+2172 -149 2181 -143 2188 -136 ct 2194 -129 2199 -121 2203 -112 ct 2207 -103 2209 -93 2209 -82 ct 
+p ef
+2502 6 m  2472 6 2450 -2 2435 -18 ct 2419 -34 2412 -55 2412 -83 ct 2412 -114 2422 -138 2442 -154 ct 
+2463 -171 2495 -180 2541 -181 ct 2607 -182 l  2607 -198 l  2607 -222 2602 -240 2592 -250 ct 
+2582 -261 2566 -266 2544 -266 ct 2521 -266 2505 -262 2495 -255 ct 2485 -247 2479 -235 2477 -219 ct 
+2425 -223 l  2434 -277 2473 -304 2545 -304 ct 2582 -304 2610 -295 2629 -278 ct 
+2648 -261 2658 -236 2658 -203 ct 2658 -74 l  2658 -60 2660 -49 2663 -41 ct 2667 -34 2675 -30 2685 -30 ct 
+2690 -30 2696 -31 2702 -32 ct 2702 -1 l  2689 2 2676 3 2663 3 ct 2645 3 2632 -1 2623 -11 ct 
+2615 -21 2610 -36 2609 -57 ct 2607 -57 l  2595 -34 2580 -18 2563 -8 ct 2546 1 2526 6 2502 6 ct 
+p
+2513 -31 m  2531 -31 2548 -35 2562 -44 ct 2576 -52 2587 -63 2595 -78 ct 2603 -92 2607 -107 2607 -122 ct 
+2607 -147 l  2553 -146 l  2530 -145 2512 -143 2500 -139 ct 2488 -134 2479 -127 2473 -118 ct 
+2466 -109 2463 -97 2463 -82 ct 2463 -66 2467 -53 2476 -44 ct 2485 -36 2497 -31 2513 -31 ct 
+p ef
+2739 1 m  2739 -409 l  2789 -409 l  2789 1 l  2739 1 l  p ef
+2866 1 m  2866 -409 l  2916 -409 l  2916 1 l  2866 1 l  p ef
+3265 -3 m  3248 2 3232 4 3214 4 ct 3175 4 3155 -19 3155 -64 ct 3155 -263 l  3121 -263 l 
+3121 -299 l  3157 -299 l  3171 -366 l  3205 -366 l  3205 -299 l  3260 -299 l 
+3260 -263 l  3205 -263 l  3205 -74 l  3205 -60 3207 -50 3212 -44 ct 3216 -38 3224 -35 3236 -35 ct 
+3243 -35 3252 -37 3265 -39 ct 3265 -3 l  p ef
+3355 -247 m  3366 -266 3379 -281 3394 -290 ct 3409 -299 3428 -303 3451 -303 ct 
+3483 -303 3507 -295 3522 -279 ct 3537 -263 3545 -236 3545 -198 ct 3545 1 l  3495 1 l 
+3495 -189 l  3495 -210 3493 -225 3489 -235 ct 3486 -246 3479 -253 3470 -258 ct 
+3462 -263 3449 -265 3434 -265 ct 3410 -265 3392 -257 3378 -241 ct 3364 -225 3357 -203 3357 -175 ct 
+3357 1 l  3307 1 l  3307 -409 l  3357 -409 l  3357 -302 l  3357 -291 3356 -279 3356 -267 ct 
+3355 -256 3355 -249 3354 -247 ct 3355 -247 l  p ef
+3657 -138 m  3657 -104 3664 -78 3678 -59 ct 3692 -41 3713 -31 3740 -31 ct 3762 -31 3779 -36 3792 -44 ct 
+3805 -53 3814 -64 3818 -77 ct 3862 -65 l  3844 -18 3803 6 3740 6 ct 3696 6 3663 -7 3640 -33 ct 
+3616 -60 3605 -99 3605 -151 ct 3605 -200 3616 -238 3640 -264 ct 3663 -291 3695 -304 3738 -304 ct 
+3826 -304 3870 -251 3870 -145 ct 3870 -138 l  3657 -138 l  p
+3818 -176 m  3816 -208 3808 -231 3794 -245 ct 3781 -260 3762 -267 3737 -267 ct 
+3713 -267 3694 -259 3680 -243 ct 3666 -227 3659 -204 3658 -176 ct 3818 -176 l 
+p ef
+pom
+pum
+28993 4209 t
+38 1 m  38 -409 l  88 -409 l  88 1 l  38 1 l  p ef
+165 -362 m  165 -409 l  214 -409 l  214 -362 l  165 -362 l  p
+165 0 m  165 -298 l  214 -298 l  214 0 l  165 0 l  p ef
+481 0 m  481 -189 l  481 -209 479 -224 475 -235 ct 472 -246 465 -254 457 -258 ct 
+449 -263 436 -266 420 -266 ct 396 -266 377 -257 363 -241 ct 350 -225 343 -202 343 -173 ct 
+343 0 l  293 0 l  293 -235 l  293 -270 293 -291 291 -298 ct 338 -298 l  338 -298 339 -295 339 -291 ct 
+339 -287 339 -282 340 -277 ct 340 -272 340 -262 340 -247 ct 341 -247 l  353 -268 366 -283 381 -291 ct 
+396 -300 414 -304 437 -304 ct 469 -304 493 -296 508 -280 ct 523 -263 531 -236 531 -199 ct 
+531 0 l  481 0 l  p ef
+792 0 m  691 -136 l  655 -106 l  655 0 l  605 0 l  605 -409 l  655 -409 l 
+655 -153 l  785 -298 l  844 -298 l  723 -170 l  850 0 l  792 0 l  p ef
+1113 -82 m  1113 -54 1102 -32 1081 -17 ct 1060 -2 1030 6 992 6 ct 955 6 926 0 906 -12 ct 
+886 -25 873 -44 867 -70 ct 910 -78 l  915 -62 923 -50 937 -43 ct 950 -36 968 -32 992 -32 ct 
+1017 -32 1035 -36 1047 -43 ct 1059 -51 1064 -63 1064 -78 ct 1064 -90 1060 -99 1052 -107 ct 
+1044 -114 1031 -120 1013 -125 ct 978 -134 l  949 -142 929 -149 917 -156 ct 905 -163 896 -172 889 -182 ct 
+882 -192 879 -204 879 -219 ct 879 -246 888 -267 908 -281 ct 927 -295 955 -303 992 -303 ct 
+1025 -303 1051 -297 1071 -285 ct 1090 -274 1102 -255 1107 -229 ct 1063 -224 l 
+1060 -237 1053 -247 1041 -254 ct 1029 -261 1012 -265 992 -265 ct 970 -265 953 -262 943 -255 ct 
+932 -248 927 -238 927 -224 ct 927 -215 929 -208 933 -203 ct 938 -197 944 -193 953 -189 ct 
+962 -185 980 -180 1007 -173 ct 1034 -166 1053 -160 1064 -155 ct 1076 -149 1085 -143 1092 -136 ct 
+1098 -129 1103 -121 1107 -112 ct 1111 -103 1113 -93 1113 -82 ct p ef
+pom
+gs
+1.000 0.714 0.425 c 18499 6969 m  16899 6969 l  16899 5699 l  20099 5699 l 
+20099 6969 l  18499 6969 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 18499 6969 m  16899 6969 l  16899 5699 l 
+20098 5699 l  20098 6969 l  18499 6969 l  pc
+gr
+pum
+17829 6554 t
+428 -223 m  428 -178 419 -139 402 -105 ct 384 -71 359 -45 327 -27 ct 295 -9 258 0 215 0 ct 
+52 0 l  52 -437 l  197 -437 l  271 -437 328 -419 368 -382 ct 408 -345 428 -292 428 -223 ct 
+p
+369 -223 m  369 -277 354 -319 324 -347 ct 295 -376 252 -390 195 -390 ct 111 -390 l 
+111 -47 l  209 -47 l  241 -47 269 -55 293 -69 ct 317 -83 336 -103 349 -129 ct 
+362 -156 369 -187 369 -223 ct p ef
+585 6 m  552 6 526 -3 509 -21 ct 492 -38 484 -63 484 -94 ct 484 -129 495 -155 518 -174 ct 
+541 -192 578 -202 629 -204 ct 704 -205 l  704 -223 l  704 -250 698 -270 687 -282 ct 
+675 -294 657 -299 632 -299 ct 607 -299 589 -295 578 -287 ct 566 -278 559 -265 557 -246 ct 
+499 -251 l  508 -312 553 -342 633 -342 ct 676 -342 707 -332 729 -313 ct 750 -294 761 -266 761 -229 ct 
+761 -85 l  761 -68 763 -55 767 -47 ct 771 -39 780 -35 792 -35 ct 797 -35 803 -35 810 -37 ct 
+810 -2 l  796 1 782 3 767 3 ct 746 3 731 -2 722 -13 ct 713 -24 707 -41 706 -64 ct 
+704 -64 l  690 -39 673 -21 654 -10 ct 635 1 612 6 585 6 ct p
+598 -36 m  619 -36 637 -41 653 -50 ct 669 -59 681 -72 690 -88 ct 700 -104 704 -121 704 -138 ct 
+704 -166 l  643 -165 l  617 -164 597 -161 583 -157 ct 570 -152 559 -144 552 -134 ct 
+545 -123 541 -110 541 -93 ct 541 -75 546 -61 556 -51 ct 566 -41 580 -36 598 -36 ct 
+p ef
+981 -2 m  962 3 944 5 924 5 ct 880 5 857 -20 857 -71 ct 857 -295 l  819 -295 l 
+819 -336 l  860 -336 l  876 -411 l  913 -411 l  913 -336 l  975 -336 l 
+975 -295 l  913 -295 l  913 -83 l  913 -67 916 -56 921 -49 ct 926 -43 936 -39 949 -39 ct 
+956 -39 967 -41 981 -44 ct 981 -2 l  p ef
+1114 6 m  1081 6 1055 -3 1038 -21 ct 1021 -38 1013 -63 1013 -94 ct 1013 -129 1024 -155 1047 -174 ct 
+1070 -192 1107 -202 1158 -204 ct 1233 -205 l  1233 -223 l  1233 -250 1227 -270 1216 -282 ct 
+1204 -294 1186 -299 1161 -299 ct 1136 -299 1118 -295 1107 -287 ct 1095 -278 1088 -265 1086 -246 ct 
+1028 -251 l  1037 -312 1082 -342 1162 -342 ct 1205 -342 1236 -332 1258 -313 ct 
+1279 -294 1290 -266 1290 -229 ct 1290 -85 l  1290 -68 1292 -55 1296 -47 ct 1300 -39 1309 -35 1321 -35 ct 
+1326 -35 1332 -35 1339 -37 ct 1339 -2 l  1325 1 1311 3 1296 3 ct 1275 3 1260 -2 1251 -13 ct 
+1242 -24 1236 -41 1235 -64 ct 1233 -64 l  1219 -39 1202 -21 1183 -10 ct 1164 1 1141 6 1114 6 ct 
+p
+1127 -36 m  1148 -36 1166 -41 1182 -50 ct 1198 -59 1210 -72 1219 -88 ct 1229 -104 1233 -121 1233 -138 ct 
+1233 -166 l  1172 -165 l  1146 -164 1126 -161 1112 -157 ct 1099 -152 1088 -144 1081 -134 ct 
+1074 -123 1070 -110 1070 -93 ct 1070 -75 1075 -61 1085 -51 ct 1095 -41 1109 -36 1127 -36 ct 
+p ef
+pom
+gs
+1.000 0.714 0.425 c 11799 6969 m  10899 6969 l  10899 5699 l  12699 5699 l 
+12699 6969 l  11799 6969 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 11799 6969 m  10899 6969 l  10899 5699 l 
+12699 5699 l  12699 6969 l  11799 6969 l  pc
+gr
+pum
+11235 6554 t
+390 -305 m  390 -264 377 -231 350 -207 ct 323 -182 286 -170 240 -170 ct 111 -170 l 
+111 1 l  52 1 l  52 -437 l  236 -437 l  285 -437 323 -426 350 -403 ct 377 -380 390 -347 390 -305 ct 
+p
+331 -305 m  331 -361 297 -390 229 -390 ct 111 -390 l  111 -217 l  231 -217 l 
+297 -217 331 -246 331 -305 ct p ef
+551 6 m  518 6 492 -3 475 -21 ct 458 -38 450 -63 450 -94 ct 450 -129 461 -155 484 -174 ct 
+507 -192 544 -202 595 -204 ct 670 -205 l  670 -223 l  670 -250 664 -270 653 -282 ct 
+641 -294 623 -299 598 -299 ct 573 -299 555 -295 544 -287 ct 532 -278 525 -265 523 -246 ct 
+465 -251 l  474 -312 519 -342 599 -342 ct 642 -342 673 -332 695 -313 ct 716 -294 727 -266 727 -229 ct 
+727 -85 l  727 -68 729 -55 733 -47 ct 737 -39 746 -35 758 -35 ct 763 -35 769 -35 776 -37 ct 
+776 -2 l  762 1 748 3 733 3 ct 712 3 697 -2 688 -13 ct 679 -24 673 -41 672 -64 ct 
+670 -64 l  656 -39 639 -21 620 -10 ct 601 1 578 6 551 6 ct p
+564 -36 m  585 -36 603 -41 619 -50 ct 635 -59 647 -72 656 -88 ct 666 -104 670 -121 670 -138 ct 
+670 -166 l  609 -165 l  583 -164 563 -161 549 -157 ct 536 -152 525 -144 518 -134 ct 
+511 -123 507 -110 507 -93 ct 507 -75 512 -61 522 -51 ct 532 -41 546 -36 564 -36 ct 
+p ef
+1030 -53 m  1019 -32 1006 -16 988 -7 ct 971 2 950 7 925 7 ct 883 7 852 -7 832 -36 ct 
+812 -64 802 -108 802 -166 ct 802 -283 843 -341 925 -341 ct 950 -341 972 -337 989 -327 ct 
+1006 -318 1019 -303 1030 -283 ct 1030 -283 l  1030 -321 l  1030 -460 l  1085 -460 l 
+1085 -68 l  1085 -33 1086 -10 1087 1 ct 1034 1 l  1033 -3 1033 -10 1032 -22 ct 
+1031 -34 1031 -45 1031 -53 ct 1030 -53 l  p
+860 -167 m  860 -120 866 -87 879 -67 ct 891 -46 911 -36 939 -36 ct 971 -36 994 -47 1008 -69 ct 
+1022 -91 1030 -125 1030 -171 ct 1030 -216 1022 -248 1008 -269 ct 994 -290 971 -300 940 -300 ct 
+912 -300 892 -290 879 -269 ct 867 -248 860 -214 860 -167 ct p ef
+pom
+gs
+1.000 1.000 0.843 c 23009 8119 m  12699 8119 l  12699 7169 l  33318 7169 l 
+33318 8119 l  23009 8119 l  p ef
+gr
+gs
+0.507 0.832 0.105 c 14799 8439 m  13699 8439 l  13699 7169 l  15899 7169 l 
+15899 8439 l  14799 8439 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 14799 8439 m  13699 8439 l  13699 7169 l 
+15899 7169 l  15899 8439 l  14799 8439 l  pc
+gr
+pum
+14163 8024 t
+246 -396 m  197 -396 160 -380 133 -349 ct 106 -318 92 -275 92 -221 ct 92 -167 106 -124 134 -92 ct 
+162 -59 200 -43 248 -43 ct 309 -43 355 -73 386 -134 ct 434 -109 l  416 -72 391 -43 359 -23 ct 
+326 -4 288 6 245 6 ct 201 6 163 -3 131 -21 ct 99 -40 74 -66 58 -100 ct 41 -134 32 -174 32 -221 ct 
+32 -291 51 -345 89 -385 ct 126 -424 178 -444 245 -444 ct 291 -444 330 -435 362 -417 ct 
+393 -398 416 -371 430 -336 ct 374 -317 l  364 -343 348 -362 325 -375 ct 303 -389 276 -396 246 -396 ct 
+p ef
+680 -389 m  680 0 l  621 0 l  621 -389 l  471 -389 l  471 -437 l  830 -437 l 
+830 -389 l  680 -389 l  p ef
+1241 -121 m  1241 -81 1226 -49 1194 -27 ct 1163 -5 1118 6 1061 6 ct 954 6 893 -31 876 -105 ct 
+933 -117 l  940 -90 954 -71 975 -59 ct 997 -46 1026 -40 1063 -40 ct 1101 -40 1131 -47 1152 -60 ct 
+1172 -73 1183 -92 1183 -118 ct 1183 -132 1180 -144 1173 -153 ct 1167 -161 1157 -169 1146 -175 ct 
+1134 -180 1120 -185 1103 -189 ct 1087 -193 1069 -197 1049 -202 ct 1015 -210 988 -217 971 -225 ct 
+953 -233 939 -241 928 -250 ct 918 -260 910 -271 905 -284 ct 899 -296 896 -311 896 -327 ct 
+896 -364 911 -393 939 -414 ct 968 -434 1009 -444 1062 -444 ct 1112 -444 1150 -436 1176 -421 ct 
+1202 -406 1221 -380 1231 -343 ct 1173 -333 l  1166 -356 1154 -373 1136 -384 ct 
+1118 -394 1093 -399 1062 -399 ct 1027 -399 1000 -394 982 -382 ct 963 -370 954 -353 954 -330 ct 
+954 -317 958 -306 965 -297 ct 972 -288 982 -281 996 -274 ct 1009 -268 1036 -261 1076 -252 ct 
+1089 -249 1103 -246 1116 -242 ct 1129 -239 1142 -235 1154 -231 ct 1166 -226 1178 -221 1189 -215 ct 
+1199 -209 1208 -202 1216 -193 ct 1224 -185 1230 -174 1235 -163 ct 1239 -151 1241 -137 1241 -121 ct 
+p ef
+pom
+gs
+0.507 0.832 0.105 c 22149 8439 m  21099 8439 l  21099 7169 l  23199 7169 l 
+23199 8439 l  22149 8439 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 22148 8439 m  21098 8439 l  21098 7169 l 
+23198 7169 l  23198 8439 l  22148 8439 l  pc
+gr
+pum
+21619 8024 t
+362 0 m  312 -128 l  113 -128 l  63 0 l  1 0 l  180 -438 l  247 -438 l 
+422 0 l  362 0 l  p
+212 -393 m  210 -384 l  204 -367 197 -345 187 -318 ct 131 -174 l  294 -174 l 
+238 -319 l  232 -333 227 -349 221 -367 ct 212 -393 l  p ef
+508 -170 m  508 -125 515 -92 529 -70 ct 543 -49 565 -38 593 -38 ct 613 -38 629 -43 643 -54 ct 
+656 -65 664 -81 667 -104 ct 724 -100 l  719 -68 706 -42 683 -23 ct 659 -4 630 6 594 6 ct 
+548 6 512 -9 487 -39 ct 462 -68 450 -111 450 -168 ct 450 -225 462 -268 487 -297 ct 
+512 -327 548 -342 594 -342 ct 628 -342 657 -333 679 -315 ct 702 -298 716 -273 722 -242 ct 
+665 -237 l  662 -256 654 -271 643 -282 ct 631 -293 614 -298 592 -298 ct 563 -298 541 -288 528 -269 ct 
+515 -249 508 -216 508 -170 ct p ef
+994 1 m  881 -153 l  840 -119 l  840 1 l  784 1 l  784 -460 l  840 -460 l 
+840 -172 l  987 -335 l  1052 -335 l  916 -191 l  1059 1 l  994 1 l  p ef
+pom
+gs
+0.871 0.902 0.937 c 23009 15718 m  12699 15718 l  12699 14769 l  33318 14769 l 
+33318 15718 l  23009 15718 l  p ef
+gr
+gs
+0.933 0.933 0.933 c 9260 17407 m  9261 17407 l  9080 17407 8902 17424 8745 17456 ct 
+8589 17489 8459 17535 8368 17592 ct 8278 17648 8230 17711 8230 17776 ct 8230 18052 l 
+8230 18329 l  8230 18705 l  8230 18982 l  8230 19258 l  8230 19259 l  8230 19324 8278 19387 8368 19443 ct 
+8459 19500 8589 19546 8745 19579 ct 8902 19611 9080 19628 9261 19628 ct 10031 19628 l 
+10804 19628 l  11854 19628 l  12627 19628 l  13399 19628 l  13399 19628 l 
+13580 19628 13758 19611 13915 19579 ct 14071 19546 14201 19500 14292 19443 ct 
+14382 19387 14430 19324 14430 19259 ct 14430 18982 l  14430 18705 l  14430 18329 l 
+14430 18052 l  14430 17776 l  14430 17776 l  14430 17776 l  14430 17711 14382 17648 14292 17592 ct 
+14201 17535 14071 17489 13915 17456 ct 13758 17424 13580 17407 13399 17407 ct 
+12677 15940 l  11854 17407 l  10804 17407 l  10031 17407 l  9260 17407 l 
+p ef
+gr
+gs
+0 lw 1 lj 9259 17407 m  9260 17407 l  9079 17407 8901 17424 8744 17456 ct 
+8588 17489 8458 17535 8367 17592 ct 8277 17648 8229 17711 8229 17776 ct 8229 18052 l 
+8229 18329 l  8229 18705 l  8229 18982 l  8229 19258 l  8229 19259 l  8229 19324 8277 19387 8367 19443 ct 
+8458 19500 8588 19546 8744 19579 ct 8901 19611 9079 19628 9260 19628 ct 10031 19628 l 
+10804 19628 l  11854 19628 l  12627 19628 l  13399 19628 l  13399 19628 l 
+13580 19628 13758 19611 13915 19579 ct 14071 19546 14201 19500 14292 19443 ct 
+14382 19387 14430 19324 14430 19259 ct 14430 18982 l  14430 18705 l  14430 18329 l 
+14430 18052 l  14430 17776 l  14430 17776 l  14430 17776 l  14430 17711 14382 17648 14292 17592 ct 
+14201 17535 14071 17489 13915 17456 ct 13758 17424 13580 17407 13399 17407 ct 
+12677 15940 l  11854 17407 l  10804 17407 l  10031 17407 l  9259 17407 l 
+pc
+gr
+pum
+8715 18398 t
+321 -1 m  277 -114 l  100 -114 l  56 -1 l  1 -1 l  159 -389 l  219 -389 l 
+375 -1 l  321 -1 l  p
+189 -349 m  186 -342 l  182 -326 175 -307 166 -283 ct 116 -155 l  261 -155 l 
+212 -283 l  206 -296 201 -310 196 -326 ct 189 -349 l  p ef
+463 -298 m  463 -109 l  463 -89 465 -74 469 -63 ct 473 -52 479 -44 488 -40 ct 
+496 -35 509 -32 525 -32 ct 549 -32 568 -41 581 -57 ct 595 -73 602 -96 602 -125 ct 
+602 -298 l  652 -298 l  652 -63 l  652 -28 652 -7 653 0 ct 606 0 l  606 0 606 -3 606 -7 ct 
+606 -11 605 -16 605 -21 ct 605 -26 605 -36 604 -51 ct 603 -51 l  592 -30 579 -15 564 -7 ct 
+549 2 530 6 508 6 ct 475 6 451 -2 436 -18 ct 421 -35 414 -62 414 -99 ct 414 -298 l 
+463 -298 l  p ef
+911 0 m  830 -122 l  750 0 l  696 0 l  802 -153 l  701 -298 l  756 -298 l 
+830 -182 l  904 -298 l  960 -298 l  859 -154 l  966 0 l  911 0 l  p ef
+1476 -271 m  1476 -234 1464 -205 1441 -184 ct 1417 -162 1384 -151 1343 -151 ct 
+1229 -151 l  1229 1 l  1176 1 l  1176 -388 l  1340 -388 l  1383 -388 1417 -378 1441 -358 ct 
+1465 -337 1476 -308 1476 -271 ct p
+1424 -271 m  1424 -321 1393 -346 1333 -346 ct 1229 -346 l  1229 -193 l  1335 -193 l 
+1394 -193 1424 -219 1424 -271 ct p ef
+1816 0 m  1816 -180 l  1606 -180 l  1606 0 l  1553 0 l  1553 -389 l  1606 -389 l 
+1606 -224 l  1816 -224 l  1816 -389 l  1868 -389 l  1868 0 l  1816 0 l 
+p ef
+2127 -161 m  2127 0 l  2075 0 l  2075 -161 l  1925 -389 l  1983 -389 l 
+2101 -203 l  2219 -389 l  2277 -389 l  2127 -161 l  p ef
+2696 -82 m  2696 -54 2685 -32 2664 -17 ct 2643 -2 2613 6 2575 6 ct 2538 6 2509 0 2489 -12 ct 
+2469 -25 2456 -44 2450 -70 ct 2493 -78 l  2498 -62 2506 -50 2520 -43 ct 2533 -36 2551 -32 2575 -32 ct 
+2600 -32 2618 -36 2630 -43 ct 2642 -51 2647 -63 2647 -78 ct 2647 -90 2643 -99 2635 -107 ct 
+2627 -114 2614 -120 2596 -125 ct 2561 -134 l  2532 -142 2512 -149 2500 -156 ct 
+2488 -163 2479 -172 2472 -182 ct 2465 -192 2462 -204 2462 -219 ct 2462 -246 2471 -267 2491 -281 ct 
+2510 -295 2538 -303 2575 -303 ct 2608 -303 2634 -297 2654 -285 ct 2673 -274 2685 -255 2690 -229 ct 
+2646 -224 l  2643 -237 2636 -247 2624 -254 ct 2612 -261 2595 -265 2575 -265 ct 
+2553 -265 2536 -262 2526 -255 ct 2515 -248 2510 -238 2510 -224 ct 2510 -215 2512 -208 2516 -203 ct 
+2521 -197 2527 -193 2536 -189 ct 2545 -185 2563 -180 2590 -173 ct 2617 -166 2636 -160 2647 -155 ct 
+2659 -149 2668 -143 2675 -136 ct 2681 -129 2686 -121 2690 -112 ct 2694 -103 2696 -93 2696 -82 ct 
+p ef
+3041 0 m  2984 0 l  2932 -211 l  2922 -258 l  2920 -249 2918 -237 2914 -222 ct 
+2911 -206 2892 -132 2858 0 ct 2801 0 l  2717 -299 l  2766 -299 l  2817 -96 l 
+2818 -91 2822 -73 2828 -41 ct 2833 -62 l  2895 -299 l  2949 -299 l  3001 -94 l 
+3013 -41 l  3022 -79 l  3078 -299 l  3127 -299 l  3041 0 l  p ef
+3162 -362 m  3162 -409 l  3211 -409 l  3211 -362 l  3162 -362 l  p
+3162 0 m  3162 -298 l  3211 -298 l  3211 0 l  3162 0 l  p ef
+3404 -3 m  3387 2 3371 4 3353 4 ct 3314 4 3294 -19 3294 -64 ct 3294 -263 l  3260 -263 l 
+3260 -299 l  3296 -299 l  3310 -366 l  3344 -366 l  3344 -299 l  3399 -299 l 
+3399 -263 l  3344 -263 l  3344 -74 l  3344 -60 3346 -50 3351 -44 ct 3355 -38 3363 -35 3375 -35 ct 
+3382 -35 3391 -37 3404 -39 ct 3404 -3 l  p ef
+3484 -150 m  3484 -111 3490 -81 3502 -62 ct 3515 -43 3534 -33 3559 -33 ct 3577 -33 3591 -38 3603 -48 ct 
+3615 -57 3622 -72 3625 -92 ct 3675 -88 l  3671 -60 3659 -37 3639 -20 ct 3618 -3 3592 6 3560 6 ct 
+3519 6 3487 -7 3465 -34 ct 3443 -60 3432 -99 3432 -149 ct 3432 -200 3443 -238 3465 -264 ct 
+3487 -291 3519 -304 3560 -304 ct 3590 -304 3616 -296 3636 -280 ct 3656 -264 3668 -243 3673 -215 ct 
+3623 -211 l  3620 -227 3613 -241 3603 -250 ct 3593 -260 3578 -265 3558 -265 ct 
+3532 -265 3513 -256 3501 -239 ct 3490 -221 3484 -192 3484 -150 ct p ef
+3778 -247 m  3789 -266 3802 -281 3817 -290 ct 3832 -299 3851 -303 3874 -303 ct 
+3906 -303 3930 -295 3945 -279 ct 3960 -263 3968 -236 3968 -198 ct 3968 1 l  3918 1 l 
+3918 -189 l  3918 -210 3916 -225 3912 -235 ct 3909 -246 3902 -253 3893 -258 ct 
+3885 -263 3872 -265 3857 -265 ct 3833 -265 3815 -257 3801 -241 ct 3787 -225 3780 -203 3780 -175 ct 
+3780 1 l  3730 1 l  3730 -409 l  3780 -409 l  3780 -302 l  3780 -291 3779 -279 3779 -267 ct 
+3778 -256 3778 -249 3777 -247 ct 3778 -247 l  p ef
+4081 -138 m  4081 -104 4088 -78 4102 -59 ct 4116 -41 4137 -31 4164 -31 ct 4186 -31 4203 -36 4216 -44 ct 
+4229 -53 4238 -64 4242 -77 ct 4286 -65 l  4268 -18 4227 6 4164 6 ct 4120 6 4087 -7 4064 -33 ct 
+4040 -60 4029 -99 4029 -151 ct 4029 -200 4040 -238 4064 -264 ct 4087 -291 4119 -304 4162 -304 ct 
+4250 -304 4294 -251 4294 -145 ct 4294 -138 l  4081 -138 l  p
+4242 -176 m  4240 -208 4232 -231 4218 -245 ct 4205 -260 4186 -267 4161 -267 ct 
+4137 -267 4118 -259 4104 -243 ct 4090 -227 4083 -204 4082 -176 ct 4242 -176 l 
+p ef
+4580 -82 m  4580 -54 4569 -32 4548 -17 ct 4527 -2 4497 6 4459 6 ct 4422 6 4393 0 4373 -12 ct 
+4353 -25 4340 -44 4334 -70 ct 4377 -78 l  4382 -62 4390 -50 4404 -43 ct 4417 -36 4435 -32 4459 -32 ct 
+4484 -32 4502 -36 4514 -43 ct 4526 -51 4531 -63 4531 -78 ct 4531 -90 4527 -99 4519 -107 ct 
+4511 -114 4498 -120 4480 -125 ct 4445 -134 l  4416 -142 4396 -149 4384 -156 ct 
+4372 -163 4363 -172 4356 -182 ct 4349 -192 4346 -204 4346 -219 ct 4346 -246 4355 -267 4375 -281 ct 
+4394 -295 4422 -303 4459 -303 ct 4492 -303 4518 -297 4538 -285 ct 4557 -274 4569 -255 4574 -229 ct 
+4530 -224 l  4527 -237 4520 -247 4508 -254 ct 4496 -261 4479 -265 4459 -265 ct 
+4437 -265 4420 -262 4410 -255 ct 4399 -248 4394 -238 4394 -224 ct 4394 -215 4396 -208 4400 -203 ct 
+4405 -197 4411 -193 4420 -189 ct 4429 -185 4447 -180 4474 -173 ct 4501 -166 4520 -160 4531 -155 ct 
+4543 -149 4552 -143 4559 -136 ct 4565 -129 4570 -121 4574 -112 ct 4578 -103 4580 -93 4580 -82 ct 
+p ef
+4911 -3 m  4894 2 4878 4 4860 4 ct 4821 4 4801 -19 4801 -64 ct 4801 -263 l  4767 -263 l 
+4767 -299 l  4803 -299 l  4817 -366 l  4851 -366 l  4851 -299 l  4906 -299 l 
+4906 -263 l  4851 -263 l  4851 -74 l  4851 -60 4853 -50 4858 -44 ct 4862 -38 4870 -35 4882 -35 ct 
+4889 -35 4898 -37 4911 -39 ct 4911 -3 l  p ef
+5205 -149 m  5205 -97 5194 -58 5171 -32 ct 5148 -7 5114 6 5071 6 ct 5027 6 4994 -7 4972 -34 ct 
+4950 -60 4939 -99 4939 -149 ct 4939 -252 4983 -304 5072 -304 ct 5118 -304 5151 -291 5173 -266 ct 
+5194 -241 5205 -202 5205 -149 ct p
+5153 -149 m  5153 -190 5147 -220 5135 -239 ct 5122 -258 5102 -267 5073 -267 ct 
+5044 -267 5023 -257 5010 -238 ct 4997 -219 4991 -190 4991 -149 ct 4991 -110 4997 -80 5010 -60 ct 
+5023 -41 5043 -31 5070 -31 ct 5100 -31 5121 -40 5134 -59 ct 5147 -79 5153 -108 5153 -149 ct 
+p ef
+pom
+pum
+9024 19025 t
+290 -149 m  290 -97 279 -58 256 -32 ct 233 -7 199 6 156 6 ct 112 6 79 -7 57 -34 ct 
+35 -60 24 -99 24 -149 ct 24 -252 68 -304 157 -304 ct 203 -304 236 -291 258 -266 ct 
+279 -241 290 -202 290 -149 ct p
+238 -149 m  238 -190 232 -220 220 -239 ct 207 -258 187 -267 158 -267 ct 129 -267 108 -257 95 -238 ct 
+82 -219 76 -190 76 -149 ct 76 -110 82 -80 95 -60 ct 108 -41 128 -31 155 -31 ct 
+185 -31 206 -40 219 -59 ct 232 -79 238 -108 238 -149 ct p ef
+603 -151 m  603 -47 566 5 493 5 ct 447 5 417 -12 401 -47 ct 399 -47 l  400 -45 401 -30 401 0 ct 
+401 117 l  351 117 l  351 -238 l  351 -269 350 -289 349 -299 ct 397 -299 l 
+397 -298 398 -295 398 -291 ct 398 -286 399 -279 399 -270 ct 400 -261 400 -254 400 -251 ct 
+401 -251 l  410 -269 422 -283 436 -291 ct 451 -300 470 -304 493 -304 ct 530 -304 558 -292 576 -267 ct 
+594 -242 603 -204 603 -151 ct p
+551 -150 m  551 -191 545 -221 534 -239 ct 523 -257 505 -266 481 -266 ct 461 -266 446 -262 435 -253 ct 
+423 -245 415 -232 409 -215 ct 403 -197 401 -174 401 -146 ct 401 -107 407 -78 419 -60 ct 
+432 -41 452 -32 480 -32 ct 505 -32 523 -41 534 -59 ct 545 -77 551 -107 551 -150 ct 
+p ef
+703 -138 m  703 -104 710 -78 724 -59 ct 738 -41 759 -31 786 -31 ct 808 -31 825 -36 838 -44 ct 
+851 -53 860 -64 864 -77 ct 908 -65 l  890 -18 849 6 786 6 ct 742 6 709 -7 686 -33 ct 
+662 -60 651 -99 651 -151 ct 651 -200 662 -238 686 -264 ct 709 -291 741 -304 784 -304 ct 
+872 -304 916 -251 916 -145 ct 916 -138 l  703 -138 l  p
+864 -176 m  862 -208 854 -231 840 -245 ct 827 -260 808 -267 783 -267 ct 759 -267 740 -259 726 -243 ct 
+712 -227 705 -204 704 -176 ct 864 -176 l  p ef
+979 0 m  979 -229 l  979 -250 979 -273 977 -298 ct 1024 -298 l  1026 -265 1026 -244 1026 -238 ct 
+1028 -238 l  1035 -263 1045 -281 1055 -290 ct 1065 -299 1080 -304 1098 -304 ct 
+1105 -304 1112 -303 1118 -301 ct 1118 -256 l  1112 -258 1103 -258 1092 -258 ct 
+1071 -258 1056 -250 1045 -232 ct 1034 -214 1029 -189 1029 -156 ct 1029 0 l  979 0 l 
+p ef
+1240 6 m  1210 6 1188 -2 1173 -18 ct 1157 -34 1150 -55 1150 -83 ct 1150 -114 1160 -138 1180 -154 ct 
+1201 -171 1233 -180 1279 -181 ct 1345 -182 l  1345 -198 l  1345 -222 1340 -240 1330 -250 ct 
+1320 -261 1304 -266 1282 -266 ct 1259 -266 1243 -262 1233 -255 ct 1223 -247 1217 -235 1215 -219 ct 
+1163 -223 l  1172 -277 1211 -304 1283 -304 ct 1320 -304 1348 -295 1367 -278 ct 
+1386 -261 1396 -236 1396 -203 ct 1396 -74 l  1396 -60 1398 -49 1401 -41 ct 1405 -34 1413 -30 1423 -30 ct 
+1428 -30 1434 -31 1440 -32 ct 1440 -1 l  1427 2 1414 3 1401 3 ct 1383 3 1370 -1 1361 -11 ct 
+1353 -21 1348 -36 1347 -57 ct 1345 -57 l  1333 -34 1318 -18 1301 -8 ct 1284 1 1264 6 1240 6 ct 
+p
+1251 -31 m  1269 -31 1286 -35 1300 -44 ct 1314 -52 1325 -63 1333 -78 ct 1341 -92 1345 -107 1345 -122 ct 
+1345 -147 l  1291 -146 l  1268 -145 1250 -143 1238 -139 ct 1226 -134 1217 -127 1211 -118 ct 
+1204 -109 1201 -97 1201 -82 ct 1201 -66 1205 -53 1214 -44 ct 1223 -36 1235 -31 1251 -31 ct 
+p ef
+1592 -3 m  1575 2 1559 4 1541 4 ct 1502 4 1482 -19 1482 -64 ct 1482 -263 l  1448 -263 l 
+1448 -299 l  1484 -299 l  1498 -366 l  1532 -366 l  1532 -299 l  1587 -299 l 
+1587 -263 l  1532 -263 l  1532 -74 l  1532 -60 1534 -50 1539 -44 ct 1543 -38 1551 -35 1563 -35 ct 
+1570 -35 1579 -37 1592 -39 ct 1592 -3 l  p ef
+1634 -362 m  1634 -409 l  1683 -409 l  1683 -362 l  1634 -362 l  p
+1634 0 m  1634 -298 l  1683 -298 l  1683 0 l  1634 0 l  p ef
+1950 0 m  1950 -189 l  1950 -209 1948 -224 1944 -235 ct 1941 -246 1934 -254 1926 -258 ct 
+1918 -263 1905 -266 1889 -266 ct 1865 -266 1846 -257 1832 -241 ct 1819 -225 1812 -202 1812 -173 ct 
+1812 0 l  1762 0 l  1762 -235 l  1762 -270 1762 -291 1760 -298 ct 1807 -298 l 
+1807 -298 1808 -295 1808 -291 ct 1808 -287 1808 -282 1809 -277 ct 1809 -272 1809 -262 1809 -247 ct 
+1810 -247 l  1822 -268 1835 -283 1850 -291 ct 1865 -300 1883 -304 1906 -304 ct 
+1938 -304 1962 -296 1977 -280 ct 1992 -263 2000 -236 2000 -199 ct 2000 0 l  1950 0 l 
+p ef
+2187 117 m  2154 117 2129 111 2109 98 ct 2090 85 2078 67 2072 43 ct 2122 36 l 
+2125 50 2133 61 2144 68 ct 2155 75 2170 79 2188 79 ct 2238 79 2262 50 2262 -8 ct 
+2262 -56 l  2262 -56 l  2252 -37 2240 -22 2223 -12 ct 2207 -3 2188 2 2166 2 ct 
+2129 2 2103 -10 2085 -34 ct 2068 -59 2060 -97 2060 -149 ct 2060 -201 2069 -240 2087 -265 ct 
+2106 -290 2134 -303 2172 -303 ct 2193 -303 2211 -298 2226 -289 ct 2242 -279 2254 -265 2262 -247 ct 
+2263 -247 l  2263 -253 2263 -262 2264 -276 ct 2265 -290 2266 -297 2266 -298 ct 
+2313 -298 l  2312 -288 2312 -268 2312 -237 ct 2312 -9 l  2312 75 2270 117 2187 117 ct 
+p
+2262 -149 m  2262 -173 2259 -194 2252 -212 ct 2246 -229 2237 -243 2225 -252 ct 
+2212 -261 2199 -266 2184 -266 ct 2158 -266 2140 -257 2128 -238 ct 2117 -220 2111 -190 2111 -149 ct 
+2111 -108 2116 -79 2127 -61 ct 2138 -44 2157 -35 2183 -35 ct 2198 -35 2212 -39 2224 -48 ct 
+2236 -58 2246 -71 2252 -88 ct 2259 -105 2262 -126 2262 -149 ct p ef
+2796 -149 m  2796 -97 2785 -58 2762 -32 ct 2739 -7 2705 6 2662 6 ct 2618 6 2585 -7 2563 -34 ct 
+2541 -60 2530 -99 2530 -149 ct 2530 -252 2574 -304 2663 -304 ct 2709 -304 2742 -291 2764 -266 ct 
+2785 -241 2796 -202 2796 -149 ct p
+2744 -149 m  2744 -190 2738 -220 2726 -239 ct 2713 -258 2693 -267 2664 -267 ct 
+2635 -267 2614 -257 2601 -238 ct 2588 -219 2582 -190 2582 -149 ct 2582 -110 2588 -80 2601 -60 ct 
+2614 -41 2634 -31 2661 -31 ct 2691 -31 2712 -40 2725 -59 ct 2738 -79 2744 -108 2744 -149 ct 
+p ef
+3046 0 m  3046 -189 l  3046 -209 3044 -224 3040 -235 ct 3037 -246 3030 -254 3022 -258 ct 
+3014 -263 3001 -266 2985 -266 ct 2961 -266 2942 -257 2928 -241 ct 2915 -225 2908 -202 2908 -173 ct 
+2908 0 l  2858 0 l  2858 -235 l  2858 -270 2858 -291 2856 -298 ct 2903 -298 l 
+2903 -298 2904 -295 2904 -291 ct 2904 -287 2904 -282 2905 -277 ct 2905 -272 2905 -262 2905 -247 ct 
+2906 -247 l  2918 -268 2931 -283 2946 -291 ct 2961 -300 2979 -304 3002 -304 ct 
+3034 -304 3058 -296 3073 -280 ct 3088 -263 3096 -236 3096 -199 ct 3096 0 l  3046 0 l 
+p ef
+3327 1 m  3327 -409 l  3377 -409 l  3377 1 l  3327 1 l  p ef
+3454 -362 m  3454 -409 l  3503 -409 l  3503 -362 l  3454 -362 l  p
+3454 0 m  3454 -298 l  3503 -298 l  3503 0 l  3454 0 l  p ef
+3770 0 m  3770 -189 l  3770 -209 3768 -224 3764 -235 ct 3761 -246 3754 -254 3746 -258 ct 
+3738 -263 3725 -266 3709 -266 ct 3685 -266 3666 -257 3652 -241 ct 3639 -225 3632 -202 3632 -173 ct 
+3632 0 l  3582 0 l  3582 -235 l  3582 -270 3582 -291 3580 -298 ct 3627 -298 l 
+3627 -298 3628 -295 3628 -291 ct 3628 -287 3628 -282 3629 -277 ct 3629 -272 3629 -262 3629 -247 ct 
+3630 -247 l  3642 -268 3655 -283 3670 -291 ct 3685 -300 3703 -304 3726 -304 ct 
+3758 -304 3782 -296 3797 -280 ct 3812 -263 3820 -236 3820 -199 ct 3820 0 l  3770 0 l 
+p ef
+4082 0 m  3981 -136 l  3945 -106 l  3945 0 l  3895 0 l  3895 -409 l  3945 -409 l 
+3945 -153 l  4075 -298 l  4134 -298 l  4013 -170 l  4140 0 l  4082 0 l 
+p ef
+4340 0 m  4340 -42 l  4439 -42 l  4439 -341 l  4351 -278 l  4351 -325 l 
+4443 -389 l  4489 -389 l  4489 -42 l  4583 -42 l  4583 0 l  4340 0 l  p ef
+pom
+gs
+0 lw 1 lj 2292 14665 m  37534 14665 l  ps
+gr
+gs
+1 lj 23536 13369 m  26772 13369 l  ps
+gr
+gs
+23249 13369 m  23549 13469 l  23549 13269 l  23249 13369 l  p ef
+gr
+gs
+27059 13369 m  26759 13269 l  26759 13469 l  27059 13369 l  p ef
+gr
+gs
+0 lw 1 lj 23248 14068 m  23248 13168 l  ps
+gr
+gs
+0 lw 1 lj 27058 14068 m  27058 13168 l  ps
+gr
+pum
+23882 12476 t
+172 -2 m  153 3 135 5 115 5 ct 71 5 48 -20 48 -71 ct 48 -295 l  10 -295 l  10 -336 l 
+51 -336 l  67 -411 l  104 -411 l  104 -336 l  166 -336 l  166 -295 l  104 -295 l 
+104 -83 l  104 -67 107 -56 112 -49 ct 117 -43 127 -39 140 -39 ct 147 -39 158 -41 172 -44 ct 
+172 -2 l  p ef
+222 0 m  222 -258 l  222 -281 221 -307 220 -336 ct 273 -336 l  275 -298 275 -275 275 -267 ct 
+277 -267 l  285 -296 296 -316 307 -326 ct 319 -337 335 -342 356 -342 ct 364 -342 371 -341 379 -339 ct 
+379 -288 l  371 -290 362 -291 349 -291 ct 326 -291 308 -281 296 -261 ct 284 -241 278 -212 278 -175 ct 
+278 0 l  222 0 l  p ef
+517 6 m  484 6 458 -3 441 -21 ct 424 -38 416 -63 416 -94 ct 416 -129 427 -155 450 -174 ct 
+473 -192 510 -202 561 -204 ct 636 -205 l  636 -223 l  636 -250 630 -270 619 -282 ct 
+607 -294 589 -299 564 -299 ct 539 -299 521 -295 510 -287 ct 498 -278 491 -265 489 -246 ct 
+431 -251 l  440 -312 485 -342 565 -342 ct 608 -342 639 -332 661 -313 ct 682 -294 693 -266 693 -229 ct 
+693 -85 l  693 -68 695 -55 699 -47 ct 703 -39 712 -35 724 -35 ct 729 -35 735 -35 742 -37 ct 
+742 -2 l  728 1 714 3 699 3 ct 678 3 663 -2 654 -13 ct 645 -24 639 -41 638 -64 ct 
+636 -64 l  622 -39 605 -21 586 -10 ct 567 1 544 6 517 6 ct p
+530 -36 m  551 -36 569 -41 585 -50 ct 601 -59 613 -72 622 -88 ct 632 -104 636 -121 636 -138 ct 
+636 -166 l  575 -165 l  549 -164 529 -161 515 -157 ct 502 -152 491 -144 484 -134 ct 
+477 -123 473 -110 473 -93 ct 473 -75 478 -61 488 -51 ct 498 -41 512 -36 530 -36 ct 
+p ef
+997 0 m  997 -213 l  997 -235 995 -252 990 -264 ct 986 -277 979 -285 970 -291 ct 
+960 -296 946 -299 928 -299 ct 901 -299 880 -290 864 -271 ct 849 -253 841 -227 841 -195 ct 
+841 0 l  785 0 l  785 -264 l  785 -303 784 -327 783 -336 ct 836 -336 l  836 -335 836 -332 837 -327 ct 
+837 -323 837 -318 837 -312 ct 838 -306 838 -295 838 -278 ct 839 -278 l  852 -302 867 -318 884 -328 ct 
+901 -337 922 -342 947 -342 ct 983 -342 1010 -333 1027 -315 ct 1044 -296 1053 -266 1053 -224 ct 
+1053 0 l  997 0 l  p ef
+1387 -93 m  1387 -61 1375 -37 1351 -20 ct 1327 -3 1293 6 1250 6 ct 1209 6 1177 -1 1154 -15 ct 
+1131 -28 1117 -50 1110 -79 ct 1159 -89 l  1164 -71 1174 -57 1188 -49 ct 1203 -41 1224 -37 1250 -37 ct 
+1279 -37 1299 -41 1313 -50 ct 1326 -58 1332 -71 1332 -89 ct 1332 -102 1328 -113 1319 -121 ct 
+1310 -129 1295 -136 1275 -141 ct 1235 -152 l  1203 -160 1180 -168 1166 -176 ct 
+1153 -184 1142 -194 1134 -205 ct 1127 -217 1123 -231 1123 -247 ct 1123 -278 1134 -301 1156 -317 ct 
+1178 -333 1209 -341 1251 -341 ct 1288 -341 1317 -335 1339 -322 ct 1361 -309 1375 -288 1381 -259 ct 
+1330 -253 l  1327 -268 1319 -279 1305 -287 ct 1292 -295 1274 -299 1251 -299 ct 
+1226 -299 1207 -295 1195 -288 ct 1183 -280 1177 -268 1177 -253 ct 1177 -243 1180 -235 1185 -229 ct 
+1190 -223 1197 -218 1207 -213 ct 1216 -209 1237 -203 1268 -195 ct 1298 -188 1319 -181 1332 -175 ct 
+1345 -168 1355 -161 1363 -154 ct 1370 -146 1376 -137 1380 -127 ct 1385 -117 1387 -106 1387 -93 ct 
+p ef
+1452 -407 m  1452 -460 l  1508 -460 l  1508 -407 l  1452 -407 l  p
+1452 1 m  1452 -335 l  1508 -335 l  1508 1 l  1452 1 l  p ef
+1721 -2 m  1702 3 1684 5 1664 5 ct 1620 5 1597 -20 1597 -71 ct 1597 -295 l  1559 -295 l 
+1559 -336 l  1600 -336 l  1616 -411 l  1653 -411 l  1653 -336 l  1715 -336 l 
+1715 -295 l  1653 -295 l  1653 -83 l  1653 -67 1656 -56 1661 -49 ct 1666 -43 1676 -39 1689 -39 ct 
+1696 -39 1707 -41 1721 -44 ct 1721 -2 l  p ef
+1769 -407 m  1769 -460 l  1825 -460 l  1825 -407 l  1769 -407 l  p
+1769 1 m  1769 -335 l  1825 -335 l  1825 1 l  1769 1 l  p ef
+2193 -168 m  2193 -110 2181 -66 2155 -37 ct 2129 -8 2091 6 2042 6 ct 1993 6 1956 -9 1931 -39 ct 
+1906 -69 1894 -112 1894 -168 ct 1894 -284 1944 -342 2044 -342 ct 2095 -342 2133 -328 2157 -300 ct 
+2181 -272 2193 -228 2193 -168 ct p
+2135 -168 m  2135 -215 2128 -248 2114 -269 ct 2101 -291 2077 -301 2045 -301 ct 
+2012 -301 1989 -290 1974 -269 ct 1960 -247 1952 -214 1952 -168 ct 1952 -124 1959 -91 1974 -69 ct 
+1988 -46 2011 -35 2042 -35 ct 2075 -35 2099 -46 2113 -68 ct 2128 -89 2135 -123 2135 -168 ct 
+p ef
+2474 0 m  2474 -213 l  2474 -235 2472 -252 2467 -264 ct 2463 -277 2456 -285 2447 -291 ct 
+2437 -296 2423 -299 2405 -299 ct 2378 -299 2357 -290 2341 -271 ct 2326 -253 2318 -227 2318 -195 ct 
+2318 0 l  2262 0 l  2262 -264 l  2262 -303 2261 -327 2260 -336 ct 2313 -336 l 
+2313 -335 2313 -332 2314 -327 ct 2314 -323 2314 -318 2314 -312 ct 2315 -306 2315 -295 2315 -278 ct 
+2316 -278 l  2329 -302 2344 -318 2361 -328 ct 2378 -337 2399 -342 2424 -342 ct 
+2460 -342 2487 -333 2504 -315 ct 2521 -296 2530 -266 2530 -224 ct 2530 0 l  2474 0 l 
+p ef
+pom
+pum
+24412 13187 t
+255 -53 m  244 -32 231 -16 213 -7 ct 196 2 175 7 150 7 ct 108 7 77 -7 57 -36 ct 
+37 -64 27 -108 27 -166 ct 27 -283 68 -341 150 -341 ct 175 -341 197 -337 214 -327 ct 
+231 -318 244 -303 255 -283 ct 255 -283 l  255 -321 l  255 -460 l  310 -460 l 
+310 -68 l  310 -33 311 -10 312 1 ct 259 1 l  258 -3 258 -10 257 -22 ct 256 -34 256 -45 256 -53 ct 
+255 -53 l  p
+85 -167 m  85 -120 91 -87 104 -67 ct 116 -46 136 -36 164 -36 ct 196 -36 219 -47 233 -69 ct 
+247 -91 255 -125 255 -171 ct 255 -216 247 -248 233 -269 ct 219 -290 196 -300 165 -300 ct 
+137 -300 117 -290 104 -269 ct 92 -248 85 -214 85 -167 ct p ef
+437 -156 m  437 -118 445 -88 460 -67 ct 476 -46 500 -36 530 -36 ct 554 -36 574 -41 588 -50 ct 
+603 -60 613 -72 618 -87 ct 667 -73 l  647 -20 601 6 530 6 ct 481 6 443 -9 417 -38 ct 
+391 -68 378 -112 378 -170 ct 378 -226 391 -268 417 -298 ct 443 -327 480 -342 528 -342 ct 
+627 -342 676 -283 676 -164 ct 676 -156 l  437 -156 l  p
+618 -199 m  615 -234 606 -260 591 -277 ct 576 -293 555 -301 527 -301 ct 500 -301 479 -292 463 -274 ct 
+447 -256 438 -231 437 -199 ct 618 -199 l  p ef
+746 1 m  746 -460 l  802 -460 l  802 1 l  746 1 l  p ef
+970 6 m  937 6 911 -3 894 -21 ct 877 -38 869 -63 869 -94 ct 869 -129 880 -155 903 -174 ct 
+926 -192 963 -202 1014 -204 ct 1089 -205 l  1089 -223 l  1089 -250 1083 -270 1072 -282 ct 
+1060 -294 1042 -299 1017 -299 ct 992 -299 974 -295 963 -287 ct 951 -278 944 -265 942 -246 ct 
+884 -251 l  893 -312 938 -342 1018 -342 ct 1061 -342 1092 -332 1114 -313 ct 
+1135 -294 1146 -266 1146 -229 ct 1146 -85 l  1146 -68 1148 -55 1152 -47 ct 1156 -39 1165 -35 1177 -35 ct 
+1182 -35 1188 -35 1195 -37 ct 1195 -2 l  1181 1 1167 3 1152 3 ct 1131 3 1116 -2 1107 -13 ct 
+1098 -24 1092 -41 1091 -64 ct 1089 -64 l  1075 -39 1058 -21 1039 -10 ct 1020 1 997 6 970 6 ct 
+p
+983 -36 m  1004 -36 1022 -41 1038 -50 ct 1054 -59 1066 -72 1075 -88 ct 1085 -104 1089 -121 1089 -138 ct 
+1089 -166 l  1028 -165 l  1002 -164 982 -161 968 -157 ct 955 -152 944 -144 937 -134 ct 
+930 -123 926 -110 926 -93 ct 926 -75 931 -61 941 -51 ct 951 -41 965 -36 983 -36 ct 
+p ef
+1253 132 m  1238 132 1225 131 1215 129 ct 1215 87 l  1223 88 1231 89 1241 89 ct 
+1276 89 1303 63 1323 12 ct 1329 -1 l  1196 -336 l  1255 -336 l  1326 -150 l 
+1327 -147 1328 -144 1330 -140 ct 1331 -135 1336 -122 1343 -99 ct 1351 -76 1356 -63 1356 -61 ct 
+1378 -122 l  1451 -336 l  1510 -336 l  1381 0 l  1367 36 1355 63 1343 80 ct 
+1331 98 1317 111 1303 119 ct 1288 128 1272 132 1253 132 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 7720 14128 m  8229 13619 l  ps
+7840 14569 m  8790 13619 l  ps
+8401 14569 m  9351 13619 l  ps
+8962 14569 m  9912 13619 l  ps
+9523 14569 m  10473 13619 l  ps
+10084 14569 m  11034 13619 l  ps
+10645 14569 m  11595 13619 l  ps
+11206 14569 m  12156 13619 l  ps
+11767 14569 m  12717 13619 l  ps
+12328 14569 m  13278 13619 l  ps
+12889 14569 m  13839 13619 l  ps
+13450 14569 m  14400 13619 l  ps
+14011 14569 m  14961 13619 l  ps
+14572 14569 m  15522 13619 l  ps
+15133 14569 m  16083 13619 l  ps
+15694 14569 m  16644 13619 l  ps
+16255 14569 m  17205 13619 l  ps
+16816 14569 m  17766 13619 l  ps
+17377 14569 m  18327 13619 l  ps
+17938 14569 m  18888 13619 l  ps
+18499 14569 m  19449 13619 l  ps
+19060 14569 m  20010 13619 l  ps
+19621 14569 m  20571 13619 l  ps
+20182 14569 m  21132 13619 l  ps
+20743 14569 m  21693 13619 l  ps
+21304 14569 m  22254 13619 l  ps
+21865 14569 m  22815 13619 l  ps
+22426 14569 m  23376 13619 l  ps
+22987 14569 m  23937 13619 l  ps
+23548 14569 m  24498 13619 l  ps
+24109 14569 m  25059 13619 l  ps
+24670 14569 m  25620 13619 l  ps
+25231 14569 m  26181 13619 l  ps
+25792 14569 m  26742 13619 l  ps
+26353 14569 m  27049 13873 l  ps
+26914 14569 m  27049 14434 l  ps
+26794 13619 m  27049 13874 l  ps
+26233 13619 m  27049 14435 l  ps
+25672 13619 m  26622 14569 l  ps
+25111 13619 m  26061 14569 l  ps
+24550 13619 m  25500 14569 l  ps
+23989 13619 m  24939 14569 l  ps
+23428 13619 m  24378 14569 l  ps
+22867 13619 m  23817 14569 l  ps
+22306 13619 m  23256 14569 l  ps
+21745 13619 m  22695 14569 l  ps
+21184 13619 m  22134 14569 l  ps
+20623 13619 m  21573 14569 l  ps
+20062 13619 m  21012 14569 l  ps
+19501 13619 m  20451 14569 l  ps
+18940 13619 m  19890 14569 l  ps
+18379 13619 m  19329 14569 l  ps
+17818 13619 m  18768 14569 l  ps
+17257 13619 m  18207 14569 l  ps
+16696 13619 m  17646 14569 l  ps
+16135 13619 m  17085 14569 l  ps
+15574 13619 m  16524 14569 l  ps
+15013 13619 m  15963 14569 l  ps
+14452 13619 m  15402 14569 l  ps
+13891 13619 m  14841 14569 l  ps
+13330 13619 m  14280 14569 l  ps
+12769 13619 m  13719 14569 l  ps
+12208 13619 m  13158 14569 l  ps
+11647 13619 m  12597 14569 l  ps
+11086 13619 m  12036 14569 l  ps
+10525 13619 m  11475 14569 l  ps
+9964 13619 m  10914 14569 l  ps
+9403 13619 m  10353 14569 l  ps
+8842 13619 m  9792 14569 l  ps
+8281 13619 m  9231 14569 l  ps
+7720 13619 m  8670 14569 l  ps
+7720 14180 m  8109 14569 l  ps
+gr
+gs
+0 lw 1 lj 17384 14568 m  7719 14568 l  7719 13618 l  27048 13618 l  27048 14568 l 
+17384 14568 l  pc
+gr
+pum
+15798 14314 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 12682 15278 m  13191 14769 l  ps
+12803 15718 m  13752 14769 l  ps
+13364 15718 m  14313 14769 l  ps
+13925 15718 m  14874 14769 l  ps
+14486 15718 m  15435 14769 l  ps
+15047 15718 m  15996 14769 l  ps
+15608 15718 m  16557 14769 l  ps
+16169 15718 m  17118 14769 l  ps
+16730 15718 m  17679 14769 l  ps
+17291 15718 m  18240 14769 l  ps
+17852 15718 m  18801 14769 l  ps
+18413 15718 m  19362 14769 l  ps
+18974 15718 m  19923 14769 l  ps
+19535 15718 m  20484 14769 l  ps
+20096 15718 m  21045 14769 l  ps
+20657 15718 m  21606 14769 l  ps
+21218 15718 m  22167 14769 l  ps
+21779 15718 m  22728 14769 l  ps
+22340 15718 m  23252 14806 l  ps
+22901 15718 m  23252 15367 l  ps
+22780 14769 m  23252 15241 l  ps
+22219 14769 m  23168 15718 l  ps
+21658 14769 m  22607 15718 l  ps
+21097 14769 m  22046 15718 l  ps
+20536 14769 m  21485 15718 l  ps
+19975 14769 m  20924 15718 l  ps
+19414 14769 m  20363 15718 l  ps
+18853 14769 m  19802 15718 l  ps
+18292 14769 m  19241 15718 l  ps
+17731 14769 m  18680 15718 l  ps
+17170 14769 m  18119 15718 l  ps
+16609 14769 m  17558 15718 l  ps
+16048 14769 m  16997 15718 l  ps
+15487 14769 m  16436 15718 l  ps
+14926 14769 m  15875 15718 l  ps
+14365 14769 m  15314 15718 l  ps
+13804 14769 m  14753 15718 l  ps
+13243 14769 m  14192 15718 l  ps
+12682 14769 m  13631 15718 l  ps
+12682 15330 m  13070 15718 l  ps
+gr
+gs
+0 lw 1 lj 17967 15718 m  12682 15718 l  12682 14768 l  23251 14768 l  23251 15718 l 
+17967 15718 l  pc
+gr
+pum
+16381 15463 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+gs
+1 lj 23486 5769 m  26722 5769 l  ps
+gr
+gs
+23199 5769 m  23499 5869 l  23499 5669 l  23199 5769 l  p ef
+gr
+gs
+27009 5769 m  26709 5669 l  26709 5869 l  27009 5769 l  p ef
+gr
+gs
+0 lw 1 lj 23198 6469 m  23198 5569 l  ps
+gr
+gs
+0 lw 1 lj 27008 6469 m  27008 5569 l  ps
+gr
+pum
+23832 4877 t
+172 -2 m  153 3 135 5 115 5 ct 71 5 48 -20 48 -71 ct 48 -295 l  10 -295 l  10 -336 l 
+51 -336 l  67 -411 l  104 -411 l  104 -336 l  166 -336 l  166 -295 l  104 -295 l 
+104 -83 l  104 -67 107 -56 112 -49 ct 117 -43 127 -39 140 -39 ct 147 -39 158 -41 172 -44 ct 
+172 -2 l  p ef
+222 0 m  222 -258 l  222 -281 221 -307 220 -336 ct 273 -336 l  275 -298 275 -275 275 -267 ct 
+277 -267 l  285 -296 296 -316 307 -326 ct 319 -337 335 -342 356 -342 ct 364 -342 371 -341 379 -339 ct 
+379 -288 l  371 -290 362 -291 349 -291 ct 326 -291 308 -281 296 -261 ct 284 -241 278 -212 278 -175 ct 
+278 0 l  222 0 l  p ef
+517 6 m  484 6 458 -3 441 -21 ct 424 -38 416 -63 416 -94 ct 416 -129 427 -155 450 -174 ct 
+473 -192 510 -202 561 -204 ct 636 -205 l  636 -223 l  636 -250 630 -270 619 -282 ct 
+607 -294 589 -299 564 -299 ct 539 -299 521 -295 510 -287 ct 498 -278 491 -265 489 -246 ct 
+431 -251 l  440 -312 485 -342 565 -342 ct 608 -342 639 -332 661 -313 ct 682 -294 693 -266 693 -229 ct 
+693 -85 l  693 -68 695 -55 699 -47 ct 703 -39 712 -35 724 -35 ct 729 -35 735 -35 742 -37 ct 
+742 -2 l  728 1 714 3 699 3 ct 678 3 663 -2 654 -13 ct 645 -24 639 -41 638 -64 ct 
+636 -64 l  622 -39 605 -21 586 -10 ct 567 1 544 6 517 6 ct p
+530 -36 m  551 -36 569 -41 585 -50 ct 601 -59 613 -72 622 -88 ct 632 -104 636 -121 636 -138 ct 
+636 -166 l  575 -165 l  549 -164 529 -161 515 -157 ct 502 -152 491 -144 484 -134 ct 
+477 -123 473 -110 473 -93 ct 473 -75 478 -61 488 -51 ct 498 -41 512 -36 530 -36 ct 
+p ef
+997 0 m  997 -213 l  997 -235 995 -252 990 -264 ct 986 -277 979 -285 970 -291 ct 
+960 -296 946 -299 928 -299 ct 901 -299 880 -290 864 -271 ct 849 -253 841 -227 841 -195 ct 
+841 0 l  785 0 l  785 -264 l  785 -303 784 -327 783 -336 ct 836 -336 l  836 -335 836 -332 837 -327 ct 
+837 -323 837 -318 837 -312 ct 838 -306 838 -295 838 -278 ct 839 -278 l  852 -302 867 -318 884 -328 ct 
+901 -337 922 -342 947 -342 ct 983 -342 1010 -333 1027 -315 ct 1044 -296 1053 -266 1053 -224 ct 
+1053 0 l  997 0 l  p ef
+1387 -93 m  1387 -61 1375 -37 1351 -20 ct 1327 -3 1293 6 1250 6 ct 1209 6 1177 -1 1154 -15 ct 
+1131 -28 1117 -50 1110 -79 ct 1159 -89 l  1164 -71 1174 -57 1188 -49 ct 1203 -41 1224 -37 1250 -37 ct 
+1279 -37 1299 -41 1313 -50 ct 1326 -58 1332 -71 1332 -89 ct 1332 -102 1328 -113 1319 -121 ct 
+1310 -129 1295 -136 1275 -141 ct 1235 -152 l  1203 -160 1180 -168 1166 -176 ct 
+1153 -184 1142 -194 1134 -205 ct 1127 -217 1123 -231 1123 -247 ct 1123 -278 1134 -301 1156 -317 ct 
+1178 -333 1209 -341 1251 -341 ct 1288 -341 1317 -335 1339 -322 ct 1361 -309 1375 -288 1381 -259 ct 
+1330 -253 l  1327 -268 1319 -279 1305 -287 ct 1292 -295 1274 -299 1251 -299 ct 
+1226 -299 1207 -295 1195 -288 ct 1183 -280 1177 -268 1177 -253 ct 1177 -243 1180 -235 1185 -229 ct 
+1190 -223 1197 -218 1207 -213 ct 1216 -209 1237 -203 1268 -195 ct 1298 -188 1319 -181 1332 -175 ct 
+1345 -168 1355 -161 1363 -154 ct 1370 -146 1376 -137 1380 -127 ct 1385 -117 1387 -106 1387 -93 ct 
+p ef
+1452 -407 m  1452 -460 l  1508 -460 l  1508 -407 l  1452 -407 l  p
+1452 1 m  1452 -335 l  1508 -335 l  1508 1 l  1452 1 l  p ef
+1721 -2 m  1702 3 1684 5 1664 5 ct 1620 5 1597 -20 1597 -71 ct 1597 -295 l  1559 -295 l 
+1559 -336 l  1600 -336 l  1616 -411 l  1653 -411 l  1653 -336 l  1715 -336 l 
+1715 -295 l  1653 -295 l  1653 -83 l  1653 -67 1656 -56 1661 -49 ct 1666 -43 1676 -39 1689 -39 ct 
+1696 -39 1707 -41 1721 -44 ct 1721 -2 l  p ef
+1769 -407 m  1769 -460 l  1825 -460 l  1825 -407 l  1769 -407 l  p
+1769 1 m  1769 -335 l  1825 -335 l  1825 1 l  1769 1 l  p ef
+2193 -168 m  2193 -110 2181 -66 2155 -37 ct 2129 -8 2091 6 2042 6 ct 1993 6 1956 -9 1931 -39 ct 
+1906 -69 1894 -112 1894 -168 ct 1894 -284 1944 -342 2044 -342 ct 2095 -342 2133 -328 2157 -300 ct 
+2181 -272 2193 -228 2193 -168 ct p
+2135 -168 m  2135 -215 2128 -248 2114 -269 ct 2101 -291 2077 -301 2045 -301 ct 
+2012 -301 1989 -290 1974 -269 ct 1960 -247 1952 -214 1952 -168 ct 1952 -124 1959 -91 1974 -69 ct 
+1988 -46 2011 -35 2042 -35 ct 2075 -35 2099 -46 2113 -68 ct 2128 -89 2135 -123 2135 -168 ct 
+p ef
+2474 0 m  2474 -213 l  2474 -235 2472 -252 2467 -264 ct 2463 -277 2456 -285 2447 -291 ct 
+2437 -296 2423 -299 2405 -299 ct 2378 -299 2357 -290 2341 -271 ct 2326 -253 2318 -227 2318 -195 ct 
+2318 0 l  2262 0 l  2262 -264 l  2262 -303 2261 -327 2260 -336 ct 2313 -336 l 
+2313 -335 2313 -332 2314 -327 ct 2314 -323 2314 -318 2314 -312 ct 2315 -306 2315 -295 2315 -278 ct 
+2316 -278 l  2329 -302 2344 -318 2361 -328 ct 2378 -337 2399 -342 2424 -342 ct 
+2460 -342 2487 -333 2504 -315 ct 2521 -296 2530 -266 2530 -224 ct 2530 0 l  2474 0 l 
+p ef
+pom
+pum
+24273 5587 t
+255 -53 m  244 -32 231 -16 213 -7 ct 196 2 175 7 150 7 ct 108 7 77 -7 57 -36 ct 
+37 -64 27 -108 27 -166 ct 27 -283 68 -341 150 -341 ct 175 -341 197 -337 214 -327 ct 
+231 -318 244 -303 255 -283 ct 255 -283 l  255 -321 l  255 -460 l  310 -460 l 
+310 -68 l  310 -33 311 -10 312 1 ct 259 1 l  258 -3 258 -10 257 -22 ct 256 -34 256 -45 256 -53 ct 
+255 -53 l  p
+85 -167 m  85 -120 91 -87 104 -67 ct 116 -46 136 -36 164 -36 ct 196 -36 219 -47 233 -69 ct 
+247 -91 255 -125 255 -171 ct 255 -216 247 -248 233 -269 ct 219 -290 196 -300 165 -300 ct 
+137 -300 117 -290 104 -269 ct 92 -248 85 -214 85 -167 ct p ef
+437 -156 m  437 -118 445 -88 460 -67 ct 476 -46 500 -36 530 -36 ct 554 -36 574 -41 588 -50 ct 
+603 -60 613 -72 618 -87 ct 667 -73 l  647 -20 601 6 530 6 ct 481 6 443 -9 417 -38 ct 
+391 -68 378 -112 378 -170 ct 378 -226 391 -268 417 -298 ct 443 -327 480 -342 528 -342 ct 
+627 -342 676 -283 676 -164 ct 676 -156 l  437 -156 l  p
+618 -199 m  615 -234 606 -260 591 -277 ct 576 -293 555 -301 527 -301 ct 500 -301 479 -292 463 -274 ct 
+447 -256 438 -231 437 -199 ct 618 -199 l  p ef
+746 1 m  746 -460 l  802 -460 l  802 1 l  746 1 l  p ef
+970 6 m  937 6 911 -3 894 -21 ct 877 -38 869 -63 869 -94 ct 869 -129 880 -155 903 -174 ct 
+926 -192 963 -202 1014 -204 ct 1089 -205 l  1089 -223 l  1089 -250 1083 -270 1072 -282 ct 
+1060 -294 1042 -299 1017 -299 ct 992 -299 974 -295 963 -287 ct 951 -278 944 -265 942 -246 ct 
+884 -251 l  893 -312 938 -342 1018 -342 ct 1061 -342 1092 -332 1114 -313 ct 
+1135 -294 1146 -266 1146 -229 ct 1146 -85 l  1146 -68 1148 -55 1152 -47 ct 1156 -39 1165 -35 1177 -35 ct 
+1182 -35 1188 -35 1195 -37 ct 1195 -2 l  1181 1 1167 3 1152 3 ct 1131 3 1116 -2 1107 -13 ct 
+1098 -24 1092 -41 1091 -64 ct 1089 -64 l  1075 -39 1058 -21 1039 -10 ct 1020 1 997 6 970 6 ct 
+p
+983 -36 m  1004 -36 1022 -41 1038 -50 ct 1054 -59 1066 -72 1075 -88 ct 1085 -104 1089 -121 1089 -138 ct 
+1089 -166 l  1028 -165 l  1002 -164 982 -161 968 -157 ct 955 -152 944 -144 937 -134 ct 
+930 -123 926 -110 926 -93 ct 926 -75 931 -61 941 -51 ct 951 -41 965 -36 983 -36 ct 
+p ef
+1253 132 m  1238 132 1225 131 1215 129 ct 1215 87 l  1223 88 1231 89 1241 89 ct 
+1276 89 1303 63 1323 12 ct 1329 -1 l  1196 -336 l  1255 -336 l  1326 -150 l 
+1327 -147 1328 -144 1330 -140 ct 1331 -135 1336 -122 1343 -99 ct 1351 -76 1356 -63 1356 -61 ct 
+1378 -122 l  1451 -336 l  1510 -336 l  1381 0 l  1367 36 1355 63 1343 80 ct 
+1331 98 1317 111 1303 119 ct 1288 128 1272 132 1253 132 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 23198 6528 m  23707 6019 l  ps
+23318 6969 m  24268 6019 l  ps
+23879 6969 m  24829 6019 l  ps
+24440 6969 m  25390 6019 l  ps
+25001 6969 m  25951 6019 l  ps
+25562 6969 m  26512 6019 l  ps
+26123 6969 m  26985 6107 l  ps
+26684 6969 m  26985 6668 l  ps
+26564 6019 m  26985 6440 l  ps
+26003 6019 m  26953 6969 l  ps
+25442 6019 m  26392 6969 l  ps
+24881 6019 m  25831 6969 l  ps
+24320 6019 m  25270 6969 l  ps
+23759 6019 m  24709 6969 l  ps
+23198 6019 m  24148 6969 l  ps
+23198 6580 m  23587 6969 l  ps
+gr
+gs
+0 lw 1 lj 25091 6969 m  23197 6969 l  23197 6019 l  26984 6019 l  26984 6969 l 
+25091 6969 l  pc
+gr
+pum
+23505 6714 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+0 20289 t 
+pom
+count op_count sub {pop} repeat countdictstack dict_count sub {end} repeat b4_inc_state restore
+%%PageTrailer
+%%Trailer
+%%EOF
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/doc/source/figures/emlsr-ul-txop.eps ns-3.41/src/wifi/doc/source/figures/emlsr-ul-txop.eps
--- ns-3.40/src/wifi/doc/source/figures/emlsr-ul-txop.eps	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/wifi/doc/source/figures/emlsr-ul-txop.eps	2024-02-09 14:44:08.569795000 -0800
@@ -0,0 +1,3351 @@
+%!PS-Adobe-3.0 EPSF-3.0 
+%%BoundingBox: 0 0 1077 567
+%%Pages: 0
+%%Creator: LibreOffice 7.5
+%%Title: none
+%%CreationDate: none
+%%LanguageLevel: 2
+%%EndComments
+%%BeginProlog
+%%BeginResource: procset SDRes-Prolog 1.0 0
+/b4_inc_state save def
+/dict_count countdictstack def
+/op_count count 1 sub def
+userdict begin
+0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit[] 0 setdash newpath
+/languagelevel where {pop languagelevel 1 ne {false setstrokeadjust false setoverprint} if} if
+/bdef {bind def} bind def
+/c {setrgbcolor} bdef
+/l {neg lineto} bdef
+/rl {neg rlineto} bdef
+/lc {setlinecap} bdef
+/lj {setlinejoin} bdef
+/lw {setlinewidth} bdef
+/ml {setmiterlimit} bdef
+/ld {setdash} bdef
+/m {neg moveto} bdef
+/ct {6 2 roll neg 6 2 roll neg 6 2 roll neg curveto} bdef
+/r {rotate} bdef
+/t {neg translate} bdef
+/s {scale} bdef
+/sw {show} bdef
+/gs {gsave} bdef
+/gr {grestore} bdef
+/f {findfont dup length dict begin
+{1 index /FID ne {def} {pop pop} ifelse} forall /Encoding ISOLatin1Encoding def
+currentdict end /NFont exch definefont pop /NFont findfont} bdef
+/p {closepath} bdef
+/sf {scalefont setfont} bdef
+/ef {eofill}bdef
+/pc {closepath stroke}bdef
+/ps {stroke}bdef
+/pum {matrix currentmatrix}bdef
+/pom {setmatrix}bdef
+/bs {/aString exch def /nXOfs exch def /nWidth exch def currentpoint nXOfs 0 rmoveto pum nWidth aString stringwidth pop div 1 scale aString show pom moveto} bdef
+%%EndResource
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+%%EndPageSetup
+pum
+0.02834 0.02832 s 
+0 -20018 t
+/tm matrix currentmatrix def
+tm setmatrix
+-2305 -2322 t 
+1 1 s 
+gs
+0 lw 1 lj 0.003 0.003 0.003 c 2291 7066 m  37533 7066 l  ps
+gr
+pum
+2542 6732 t
+0.003 0.003 0.003 c 362 0 m  312 -128 l  113 -128 l  63 0 l  1 0 l  180 -438 l 
+247 -438 l  422 0 l  362 0 l  p
+212 -393 m  210 -384 l  204 -367 197 -345 187 -318 ct 131 -174 l  294 -174 l 
+238 -319 l  232 -333 227 -349 221 -367 ct 212 -393 l  p ef
+813 -305 m  813 -264 800 -231 773 -207 ct 746 -182 709 -170 663 -170 ct 534 -170 l 
+534 1 l  475 1 l  475 -437 l  659 -437 l  708 -437 746 -426 773 -403 ct 800 -380 813 -347 813 -305 ct 
+p
+754 -305 m  754 -361 720 -390 652 -390 ct 534 -390 l  534 -217 l  654 -217 l 
+720 -217 754 -246 754 -305 ct p ef
+1436 0 m  1436 -292 l  1436 -324 1436 -355 1438 -385 ct 1428 -348 1419 -319 1411 -298 ct 
+1298 0 l  1257 0 l  1142 -298 l  1125 -351 l  1115 -385 l  1116 -351 l 
+1117 -292 l  1117 0 l  1064 0 l  1064 -438 l  1142 -438 l  1258 -134 l 
+1262 -122 1266 -109 1270 -95 ct 1274 -81 1276 -71 1278 -65 ct 1279 -73 1283 -85 1288 -102 ct 
+1293 -119 1297 -130 1299 -134 ct 1413 -438 l  1489 -438 l  1489 0 l  1436 0 l 
+p ef
+1593 0 m  1593 -438 l  1652 -438 l  1652 -48 l  1873 -48 l  1873 0 l  1593 0 l 
+p ef
+2320 -223 m  2320 -178 2311 -139 2294 -105 ct 2276 -71 2251 -45 2219 -27 ct 
+2187 -9 2150 0 2107 0 ct 1944 0 l  1944 -437 l  2089 -437 l  2163 -437 2220 -419 2260 -382 ct 
+2300 -345 2320 -292 2320 -223 ct p
+2261 -223 m  2261 -277 2246 -319 2216 -347 ct 2187 -376 2144 -390 2087 -390 ct 
+2003 -390 l  2003 -47 l  2101 -47 l  2133 -47 2161 -55 2185 -69 ct 2209 -83 2228 -103 2241 -129 ct 
+2254 -156 2261 -187 2261 -223 ct p ef
+pom
+pum
+2542 8032 t
+52 0 m  52 -437 l  384 -437 l  384 -389 l  111 -389 l  111 -249 l  365 -249 l 
+365 -201 l  111 -201 l  111 -48 l  396 -48 l  396 0 l  52 0 l  p ef
+847 0 m  847 -292 l  847 -324 847 -355 849 -385 ct 839 -348 830 -319 822 -298 ct 
+709 0 l  668 0 l  553 -298 l  536 -351 l  526 -385 l  527 -351 l  528 -292 l 
+528 0 l  475 0 l  475 -438 l  553 -438 l  669 -134 l  673 -122 677 -109 681 -95 ct 
+685 -81 687 -71 689 -65 ct 690 -73 694 -85 699 -102 ct 704 -119 708 -130 710 -134 ct 
+824 -438 l  900 -438 l  900 0 l  847 0 l  p ef
+1005 0 m  1005 -438 l  1064 -438 l  1064 -48 l  1285 -48 l  1285 0 l  1005 0 l 
+p ef
+1698 -121 m  1698 -81 1683 -49 1651 -27 ct 1620 -5 1575 6 1518 6 ct 1411 6 1350 -31 1333 -105 ct 
+1390 -117 l  1397 -90 1411 -71 1432 -59 ct 1454 -46 1483 -40 1520 -40 ct 1558 -40 1588 -47 1609 -60 ct 
+1629 -73 1640 -92 1640 -118 ct 1640 -132 1637 -144 1630 -153 ct 1624 -161 1614 -169 1603 -175 ct 
+1591 -180 1577 -185 1560 -189 ct 1544 -193 1526 -197 1506 -202 ct 1472 -210 1445 -217 1428 -225 ct 
+1410 -233 1396 -241 1385 -250 ct 1375 -260 1367 -271 1362 -284 ct 1356 -296 1353 -311 1353 -327 ct 
+1353 -364 1368 -393 1396 -414 ct 1425 -434 1466 -444 1519 -444 ct 1569 -444 1607 -436 1633 -421 ct 
+1659 -406 1678 -380 1688 -343 ct 1630 -333 l  1623 -356 1611 -373 1593 -384 ct 
+1575 -394 1550 -399 1519 -399 ct 1484 -399 1457 -394 1439 -382 ct 1420 -370 1411 -353 1411 -330 ct 
+1411 -317 1415 -306 1422 -297 ct 1429 -288 1439 -281 1453 -274 ct 1466 -268 1493 -261 1533 -252 ct 
+1546 -249 1560 -246 1573 -242 ct 1586 -239 1599 -235 1611 -231 ct 1623 -226 1635 -221 1646 -215 ct 
+1656 -209 1665 -202 1673 -193 ct 1681 -185 1687 -174 1692 -163 ct 1696 -151 1698 -137 1698 -121 ct 
+p ef
+2088 -1 m  1974 -182 l  1838 -182 l  1838 -1 l  1779 -1 l  1779 -437 l 
+1985 -437 l  2034 -437 2072 -426 2099 -404 ct 2125 -382 2139 -352 2139 -313 ct 
+2139 -280 2129 -253 2110 -231 ct 2091 -209 2065 -195 2032 -189 ct 2156 -1 l 
+2088 -1 l  p
+2079 -312 m  2079 -337 2071 -357 2053 -370 ct 2036 -383 2011 -390 1979 -390 ct 
+1838 -390 l  1838 -229 l  1981 -229 l  2012 -229 2037 -236 2054 -251 ct 2071 -265 2079 -286 2079 -312 ct 
+p ef
+pom
+pum
+2896 8743 t
+85 -170 m  85 -125 92 -92 106 -70 ct 120 -49 142 -38 170 -38 ct 190 -38 206 -43 220 -54 ct 
+233 -65 241 -81 244 -104 ct 301 -100 l  296 -68 283 -42 260 -23 ct 236 -4 207 6 171 6 ct 
+125 6 89 -9 64 -39 ct 39 -68 27 -111 27 -168 ct 27 -225 39 -268 64 -297 ct 89 -327 125 -342 171 -342 ct 
+205 -342 234 -333 256 -315 ct 279 -298 293 -273 299 -242 ct 242 -237 l  239 -256 231 -271 220 -282 ct 
+208 -293 191 -298 169 -298 ct 140 -298 118 -288 105 -269 ct 92 -249 85 -216 85 -170 ct 
+p ef
+361 1 m  361 -460 l  417 -460 l  417 1 l  361 1 l  p ef
+499 -407 m  499 -460 l  555 -460 l  555 -407 l  499 -407 l  p
+499 1 m  499 -335 l  555 -335 l  555 1 l  499 1 l  p ef
+683 -156 m  683 -118 691 -88 706 -67 ct 722 -46 746 -36 776 -36 ct 800 -36 820 -41 834 -50 ct 
+849 -60 859 -72 864 -87 ct 913 -73 l  893 -20 847 6 776 6 ct 727 6 689 -9 663 -38 ct 
+637 -68 624 -112 624 -170 ct 624 -226 637 -268 663 -298 ct 689 -327 726 -342 774 -342 ct 
+873 -342 922 -283 922 -164 ct 922 -156 l  683 -156 l  p
+864 -199 m  861 -234 852 -260 837 -277 ct 822 -293 801 -301 773 -301 ct 746 -301 725 -292 709 -274 ct 
+693 -256 684 -231 683 -199 ct 864 -199 l  p ef
+1204 0 m  1204 -213 l  1204 -235 1202 -252 1197 -264 ct 1193 -277 1186 -285 1177 -291 ct 
+1167 -296 1153 -299 1135 -299 ct 1108 -299 1087 -290 1071 -271 ct 1056 -253 1048 -227 1048 -195 ct 
+1048 0 l  992 0 l  992 -264 l  992 -303 991 -327 990 -336 ct 1043 -336 l 
+1043 -335 1043 -332 1044 -327 ct 1044 -323 1044 -318 1044 -312 ct 1045 -306 1045 -295 1045 -278 ct 
+1046 -278 l  1059 -302 1074 -318 1091 -328 ct 1108 -337 1129 -342 1154 -342 ct 
+1190 -342 1217 -333 1234 -315 ct 1251 -296 1260 -266 1260 -224 ct 1260 0 l  1204 0 l 
+p ef
+1472 -2 m  1453 3 1435 5 1415 5 ct 1371 5 1348 -20 1348 -71 ct 1348 -295 l  1310 -295 l 
+1310 -336 l  1351 -336 l  1367 -411 l  1404 -411 l  1404 -336 l  1466 -336 l 
+1466 -295 l  1404 -295 l  1404 -83 l  1404 -67 1407 -56 1412 -49 ct 1417 -43 1427 -39 1440 -39 ct 
+1447 -39 1458 -41 1472 -44 ct 1472 -2 l  p ef
+pom
+pum
+2542 14233 t
+362 0 m  312 -128 l  113 -128 l  63 0 l  1 0 l  180 -438 l  247 -438 l 
+422 0 l  362 0 l  p
+212 -393 m  210 -384 l  204 -367 197 -345 187 -318 ct 131 -174 l  294 -174 l 
+238 -319 l  232 -333 227 -349 221 -367 ct 212 -393 l  p ef
+813 -305 m  813 -264 800 -231 773 -207 ct 746 -182 709 -170 663 -170 ct 534 -170 l 
+534 1 l  475 1 l  475 -437 l  659 -437 l  708 -437 746 -426 773 -403 ct 800 -380 813 -347 813 -305 ct 
+p
+754 -305 m  754 -361 720 -390 652 -390 ct 534 -390 l  534 -217 l  654 -217 l 
+720 -217 754 -246 754 -305 ct p ef
+1436 0 m  1436 -292 l  1436 -324 1436 -355 1438 -385 ct 1428 -348 1419 -319 1411 -298 ct 
+1298 0 l  1257 0 l  1142 -298 l  1125 -351 l  1115 -385 l  1116 -351 l 
+1117 -292 l  1117 0 l  1064 0 l  1064 -438 l  1142 -438 l  1258 -134 l 
+1262 -122 1266 -109 1270 -95 ct 1274 -81 1276 -71 1278 -65 ct 1279 -73 1283 -85 1288 -102 ct 
+1293 -119 1297 -130 1299 -134 ct 1413 -438 l  1489 -438 l  1489 0 l  1436 0 l 
+p ef
+1593 0 m  1593 -438 l  1652 -438 l  1652 -48 l  1873 -48 l  1873 0 l  1593 0 l 
+p ef
+2320 -223 m  2320 -178 2311 -139 2294 -105 ct 2276 -71 2251 -45 2219 -27 ct 
+2187 -9 2150 0 2107 0 ct 1944 0 l  1944 -437 l  2089 -437 l  2163 -437 2220 -419 2260 -382 ct 
+2300 -345 2320 -292 2320 -223 ct p
+2261 -223 m  2261 -277 2246 -319 2216 -347 ct 2187 -376 2144 -390 2087 -390 ct 
+2003 -390 l  2003 -47 l  2101 -47 l  2133 -47 2161 -55 2185 -69 ct 2209 -83 2228 -103 2241 -129 ct 
+2254 -156 2261 -187 2261 -223 ct p ef
+pom
+pum
+2542 15532 t
+52 0 m  52 -437 l  384 -437 l  384 -389 l  111 -389 l  111 -249 l  365 -249 l 
+365 -201 l  111 -201 l  111 -48 l  396 -48 l  396 0 l  52 0 l  p ef
+847 0 m  847 -292 l  847 -324 847 -355 849 -385 ct 839 -348 830 -319 822 -298 ct 
+709 0 l  668 0 l  553 -298 l  536 -351 l  526 -385 l  527 -351 l  528 -292 l 
+528 0 l  475 0 l  475 -438 l  553 -438 l  669 -134 l  673 -122 677 -109 681 -95 ct 
+685 -81 687 -71 689 -65 ct 690 -73 694 -85 699 -102 ct 704 -119 708 -130 710 -134 ct 
+824 -438 l  900 -438 l  900 0 l  847 0 l  p ef
+1005 0 m  1005 -438 l  1064 -438 l  1064 -48 l  1285 -48 l  1285 0 l  1005 0 l 
+p ef
+1698 -121 m  1698 -81 1683 -49 1651 -27 ct 1620 -5 1575 6 1518 6 ct 1411 6 1350 -31 1333 -105 ct 
+1390 -117 l  1397 -90 1411 -71 1432 -59 ct 1454 -46 1483 -40 1520 -40 ct 1558 -40 1588 -47 1609 -60 ct 
+1629 -73 1640 -92 1640 -118 ct 1640 -132 1637 -144 1630 -153 ct 1624 -161 1614 -169 1603 -175 ct 
+1591 -180 1577 -185 1560 -189 ct 1544 -193 1526 -197 1506 -202 ct 1472 -210 1445 -217 1428 -225 ct 
+1410 -233 1396 -241 1385 -250 ct 1375 -260 1367 -271 1362 -284 ct 1356 -296 1353 -311 1353 -327 ct 
+1353 -364 1368 -393 1396 -414 ct 1425 -434 1466 -444 1519 -444 ct 1569 -444 1607 -436 1633 -421 ct 
+1659 -406 1678 -380 1688 -343 ct 1630 -333 l  1623 -356 1611 -373 1593 -384 ct 
+1575 -394 1550 -399 1519 -399 ct 1484 -399 1457 -394 1439 -382 ct 1420 -370 1411 -353 1411 -330 ct 
+1411 -317 1415 -306 1422 -297 ct 1429 -288 1439 -281 1453 -274 ct 1466 -268 1493 -261 1533 -252 ct 
+1546 -249 1560 -246 1573 -242 ct 1586 -239 1599 -235 1611 -231 ct 1623 -226 1635 -221 1646 -215 ct 
+1656 -209 1665 -202 1673 -193 ct 1681 -185 1687 -174 1692 -163 ct 1696 -151 1698 -137 1698 -121 ct 
+p ef
+2088 -1 m  1974 -182 l  1838 -182 l  1838 -1 l  1779 -1 l  1779 -437 l 
+1985 -437 l  2034 -437 2072 -426 2099 -404 ct 2125 -382 2139 -352 2139 -313 ct 
+2139 -280 2129 -253 2110 -231 ct 2091 -209 2065 -195 2032 -189 ct 2156 -1 l 
+2088 -1 l  p
+2079 -312 m  2079 -337 2071 -357 2053 -370 ct 2036 -383 2011 -390 1979 -390 ct 
+1838 -390 l  1838 -229 l  1981 -229 l  2012 -229 2037 -236 2054 -251 ct 2071 -265 2079 -286 2079 -312 ct 
+p ef
+pom
+pum
+2896 16243 t
+85 -170 m  85 -125 92 -92 106 -70 ct 120 -49 142 -38 170 -38 ct 190 -38 206 -43 220 -54 ct 
+233 -65 241 -81 244 -104 ct 301 -100 l  296 -68 283 -42 260 -23 ct 236 -4 207 6 171 6 ct 
+125 6 89 -9 64 -39 ct 39 -68 27 -111 27 -168 ct 27 -225 39 -268 64 -297 ct 89 -327 125 -342 171 -342 ct 
+205 -342 234 -333 256 -315 ct 279 -298 293 -273 299 -242 ct 242 -237 l  239 -256 231 -271 220 -282 ct 
+208 -293 191 -298 169 -298 ct 140 -298 118 -288 105 -269 ct 92 -249 85 -216 85 -170 ct 
+p ef
+361 1 m  361 -460 l  417 -460 l  417 1 l  361 1 l  p ef
+499 -407 m  499 -460 l  555 -460 l  555 -407 l  499 -407 l  p
+499 1 m  499 -335 l  555 -335 l  555 1 l  499 1 l  p ef
+683 -156 m  683 -118 691 -88 706 -67 ct 722 -46 746 -36 776 -36 ct 800 -36 820 -41 834 -50 ct 
+849 -60 859 -72 864 -87 ct 913 -73 l  893 -20 847 6 776 6 ct 727 6 689 -9 663 -38 ct 
+637 -68 624 -112 624 -170 ct 624 -226 637 -268 663 -298 ct 689 -327 726 -342 774 -342 ct 
+873 -342 922 -283 922 -164 ct 922 -156 l  683 -156 l  p
+864 -199 m  861 -234 852 -260 837 -277 ct 822 -293 801 -301 773 -301 ct 746 -301 725 -292 709 -274 ct 
+693 -256 684 -231 683 -199 ct 864 -199 l  p ef
+1204 0 m  1204 -213 l  1204 -235 1202 -252 1197 -264 ct 1193 -277 1186 -285 1177 -291 ct 
+1167 -296 1153 -299 1135 -299 ct 1108 -299 1087 -290 1071 -271 ct 1056 -253 1048 -227 1048 -195 ct 
+1048 0 l  992 0 l  992 -264 l  992 -303 991 -327 990 -336 ct 1043 -336 l 
+1043 -335 1043 -332 1044 -327 ct 1044 -323 1044 -318 1044 -312 ct 1045 -306 1045 -295 1045 -278 ct 
+1046 -278 l  1059 -302 1074 -318 1091 -328 ct 1108 -337 1129 -342 1154 -342 ct 
+1190 -342 1217 -333 1234 -315 ct 1251 -296 1260 -266 1260 -224 ct 1260 0 l  1204 0 l 
+p ef
+1472 -2 m  1453 3 1435 5 1415 5 ct 1371 5 1348 -20 1348 -71 ct 1348 -295 l  1310 -295 l 
+1310 -336 l  1351 -336 l  1367 -411 l  1404 -411 l  1404 -336 l  1466 -336 l 
+1466 -295 l  1404 -295 l  1404 -83 l  1404 -67 1407 -56 1412 -49 ct 1417 -43 1427 -39 1440 -39 ct 
+1447 -39 1458 -41 1472 -44 ct 1472 -2 l  p ef
+pom
+pum
+38348 7285 t
+52 0 m  52 -438 l  111 -438 l  111 -48 l  332 -48 l  332 0 l  52 0 l  p ef
+393 -407 m  393 -460 l  449 -460 l  449 -407 l  393 -407 l  p
+393 1 m  393 -335 l  449 -335 l  449 1 l  393 1 l  p ef
+747 0 m  747 -213 l  747 -235 745 -252 740 -264 ct 736 -277 729 -285 720 -291 ct 
+710 -296 696 -299 678 -299 ct 651 -299 630 -290 614 -271 ct 599 -253 591 -227 591 -195 ct 
+591 0 l  535 0 l  535 -264 l  535 -303 534 -327 533 -336 ct 586 -336 l  586 -335 586 -332 587 -327 ct 
+587 -323 587 -318 587 -312 ct 588 -306 588 -295 588 -278 ct 589 -278 l  602 -302 617 -318 634 -328 ct 
+651 -337 672 -342 697 -342 ct 733 -342 760 -333 777 -315 ct 794 -296 803 -266 803 -224 ct 
+803 0 l  747 0 l  p ef
+1095 1 m  982 -153 l  941 -119 l  941 1 l  885 1 l  885 -460 l  941 -460 l 
+941 -172 l  1088 -335 l  1153 -335 l  1017 -191 l  1160 1 l  1095 1 l 
+p ef
+1666 -219 m  1666 -146 1653 -90 1628 -52 ct 1602 -13 1564 6 1514 6 ct 1464 6 1426 -13 1401 -51 ct 
+1375 -90 1363 -146 1363 -219 ct 1363 -294 1375 -350 1400 -388 ct 1424 -425 1463 -444 1516 -444 ct 
+1567 -444 1605 -425 1630 -387 ct 1654 -349 1666 -293 1666 -219 ct p
+1610 -219 m  1610 -282 1602 -328 1588 -356 ct 1573 -385 1549 -399 1516 -399 ct 
+1481 -399 1457 -385 1442 -357 ct 1427 -329 1419 -283 1419 -219 ct 1419 -157 1427 -112 1442 -83 ct 
+1457 -54 1481 -40 1514 -40 ct 1547 -40 1571 -54 1587 -84 ct 1602 -113 1610 -158 1610 -219 ct 
+p ef
+pom
+pum
+38349 14885 t
+52 0 m  52 -438 l  111 -438 l  111 -48 l  332 -48 l  332 0 l  52 0 l  p ef
+393 -407 m  393 -460 l  449 -460 l  449 -407 l  393 -407 l  p
+393 1 m  393 -335 l  449 -335 l  449 1 l  393 1 l  p ef
+747 0 m  747 -213 l  747 -235 745 -252 740 -264 ct 736 -277 729 -285 720 -291 ct 
+710 -296 696 -299 678 -299 ct 651 -299 630 -290 614 -271 ct 599 -253 591 -227 591 -195 ct 
+591 0 l  535 0 l  535 -264 l  535 -303 534 -327 533 -336 ct 586 -336 l  586 -335 586 -332 587 -327 ct 
+587 -323 587 -318 587 -312 ct 588 -306 588 -295 588 -278 ct 589 -278 l  602 -302 617 -318 634 -328 ct 
+651 -337 672 -342 697 -342 ct 733 -342 760 -333 777 -315 ct 794 -296 803 -266 803 -224 ct 
+803 0 l  747 0 l  p ef
+1095 1 m  982 -153 l  941 -119 l  941 1 l  885 1 l  885 -460 l  941 -460 l 
+941 -172 l  1088 -335 l  1153 -335 l  1017 -191 l  1160 1 l  1095 1 l 
+p ef
+1386 0 m  1386 -47 l  1498 -47 l  1498 -384 l  1399 -314 l  1399 -366 l 
+1502 -438 l  1554 -438 l  1554 -47 l  1660 -47 l  1660 0 l  1386 0 l  p ef
+pom
+gs
+1.000 1.000 0.843 c 3968 22179 m  2856 22179 l  2856 21544 l  5079 21544 l 
+5079 22179 l  3968 22179 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 3967 22178 m  2855 22178 l  2855 21543 l 
+5078 21543 l  5078 22178 l  3967 22178 l  pc
+gr
+pum
+5665 22064 t
+238 0 m  238 -213 l  238 -245 234 -268 225 -280 ct 216 -293 200 -299 177 -299 ct 
+153 -299 134 -290 120 -272 ct 106 -253 100 -228 100 -195 ct 100 0 l  44 0 l 
+44 -264 l  44 -303 43 -327 42 -336 ct 95 -336 l  95 -335 95 -332 96 -327 ct 
+96 -323 96 -318 96 -312 ct 97 -306 97 -295 97 -278 ct 98 -278 l  110 -302 124 -319 140 -328 ct 
+155 -337 174 -342 196 -342 ct 222 -342 242 -337 257 -327 ct 271 -317 282 -301 287 -278 ct 
+288 -278 l  300 -301 314 -317 330 -327 ct 347 -337 367 -342 390 -342 ct 424 -342 449 -333 464 -314 ct 
+479 -296 487 -266 487 -224 ct 487 0 l  432 0 l  432 -213 l  432 -245 427 -268 419 -280 ct 
+410 -293 394 -299 371 -299 ct 346 -299 327 -290 314 -272 ct 300 -254 293 -228 293 -195 ct 
+293 0 l  238 0 l  p ef
+657 6 m  624 6 598 -3 581 -21 ct 564 -38 556 -63 556 -94 ct 556 -129 567 -155 590 -174 ct 
+613 -192 650 -202 701 -204 ct 776 -205 l  776 -223 l  776 -250 770 -270 759 -282 ct 
+747 -294 729 -299 704 -299 ct 679 -299 661 -295 650 -287 ct 638 -278 631 -265 629 -246 ct 
+571 -251 l  580 -312 625 -342 705 -342 ct 748 -342 779 -332 801 -313 ct 822 -294 833 -266 833 -229 ct 
+833 -85 l  833 -68 835 -55 839 -47 ct 843 -39 852 -35 864 -35 ct 869 -35 875 -35 882 -37 ct 
+882 -2 l  868 1 854 3 839 3 ct 818 3 803 -2 794 -13 ct 785 -24 779 -41 778 -64 ct 
+776 -64 l  762 -39 745 -21 726 -10 ct 707 1 684 6 657 6 ct p
+670 -36 m  691 -36 709 -41 725 -50 ct 741 -59 753 -72 762 -88 ct 772 -104 776 -121 776 -138 ct 
+776 -166 l  715 -165 l  689 -164 669 -161 655 -157 ct 642 -152 631 -144 624 -134 ct 
+617 -123 613 -110 613 -93 ct 613 -75 618 -61 628 -51 ct 638 -41 652 -36 670 -36 ct 
+p ef
+923 -407 m  923 -460 l  979 -460 l  979 -407 l  923 -407 l  p
+923 1 m  923 -335 l  979 -335 l  979 1 l  923 1 l  p ef
+1276 0 m  1276 -213 l  1276 -235 1274 -252 1269 -264 ct 1265 -277 1258 -285 1249 -291 ct 
+1239 -296 1225 -299 1207 -299 ct 1180 -299 1159 -290 1143 -271 ct 1128 -253 1120 -227 1120 -195 ct 
+1120 0 l  1064 0 l  1064 -264 l  1064 -303 1063 -327 1062 -336 ct 1115 -336 l 
+1115 -335 1115 -332 1116 -327 ct 1116 -323 1116 -318 1116 -312 ct 1117 -306 1117 -295 1117 -278 ct 
+1118 -278 l  1131 -302 1146 -318 1163 -328 ct 1180 -337 1201 -342 1226 -342 ct 
+1262 -342 1289 -333 1306 -315 ct 1323 -296 1332 -266 1332 -224 ct 1332 0 l  1276 0 l 
+p ef
+1939 -305 m  1939 -264 1926 -231 1899 -207 ct 1872 -182 1835 -170 1789 -170 ct 
+1660 -170 l  1660 1 l  1601 1 l  1601 -437 l  1785 -437 l  1834 -437 1872 -426 1899 -403 ct 
+1926 -380 1939 -347 1939 -305 ct p
+1880 -305 m  1880 -361 1846 -390 1778 -390 ct 1660 -390 l  1660 -217 l  1780 -217 l 
+1846 -217 1880 -246 1880 -305 ct p ef
+2321 -1 m  2321 -203 l  2084 -203 l  2084 -1 l  2025 -1 l  2025 -438 l 
+2084 -438 l  2084 -253 l  2321 -253 l  2321 -438 l  2380 -438 l  2380 -1 l 
+2321 -1 l  p ef
+2671 -181 m  2671 0 l  2612 0 l  2612 -181 l  2444 -438 l  2509 -438 l 
+2642 -229 l  2774 -438 l  2840 -438 l  2671 -181 l  p ef
+pom
+gs
+0.871 0.902 0.937 c 13067 22179 m  11955 22179 l  11955 21544 l  14178 21544 l 
+14178 22179 l  13067 22179 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 13067 22178 m  11955 22178 l  11955 21543 l 
+14178 21543 l  14178 22178 l  13067 22178 l  pc
+gr
+pum
+14764 22064 t
+128 6 m  95 6 69 -3 52 -21 ct 35 -38 27 -63 27 -94 ct 27 -129 38 -155 61 -174 ct 
+84 -192 121 -202 172 -204 ct 247 -205 l  247 -223 l  247 -250 241 -270 230 -282 ct 
+218 -294 200 -299 175 -299 ct 150 -299 132 -295 121 -287 ct 109 -278 102 -265 100 -246 ct 
+42 -251 l  51 -312 96 -342 176 -342 ct 219 -342 250 -332 272 -313 ct 293 -294 304 -266 304 -229 ct 
+304 -85 l  304 -68 306 -55 310 -47 ct 314 -39 323 -35 335 -35 ct 340 -35 346 -35 353 -37 ct 
+353 -2 l  339 1 325 3 310 3 ct 289 3 274 -2 265 -13 ct 256 -24 250 -41 249 -64 ct 
+247 -64 l  233 -39 216 -21 197 -10 ct 178 1 155 6 128 6 ct p
+141 -36 m  162 -36 180 -41 196 -50 ct 212 -59 224 -72 233 -88 ct 243 -104 247 -121 247 -138 ct 
+247 -166 l  186 -165 l  160 -164 140 -161 126 -157 ct 113 -152 102 -144 95 -134 ct 
+88 -123 84 -110 84 -93 ct 84 -75 89 -61 99 -51 ct 109 -41 123 -36 141 -36 ct p ef
+448 -336 m  448 -123 l  448 -101 451 -84 455 -72 ct 459 -59 466 -51 476 -45 ct 
+485 -40 499 -37 518 -37 ct 544 -37 566 -46 581 -65 ct 597 -83 604 -109 604 -141 ct 
+604 -336 l  660 -336 l  660 -72 l  660 -33 661 -9 662 0 ct 609 0 l  609 -1 609 -4 609 -9 ct 
+608 -13 608 -18 608 -24 ct 608 -30 607 -41 607 -58 ct 606 -58 l  593 -34 578 -18 561 -8 ct 
+545 1 524 6 499 6 ct 462 6 435 -3 418 -21 ct 401 -40 392 -70 392 -112 ct 392 -336 l 
+448 -336 l  p ef
+951 0 m  861 -138 l  770 0 l  710 0 l  830 -173 l  716 -336 l  777 -336 l 
+861 -205 l  944 -336 l  1007 -336 l  893 -173 l  1014 0 l  951 0 l  p ef
+1588 -305 m  1588 -264 1575 -231 1548 -207 ct 1521 -182 1484 -170 1438 -170 ct 
+1309 -170 l  1309 1 l  1250 1 l  1250 -437 l  1434 -437 l  1483 -437 1521 -426 1548 -403 ct 
+1575 -380 1588 -347 1588 -305 ct p
+1529 -305 m  1529 -361 1495 -390 1427 -390 ct 1309 -390 l  1309 -217 l  1429 -217 l 
+1495 -217 1529 -246 1529 -305 ct p ef
+1969 -1 m  1969 -203 l  1732 -203 l  1732 -1 l  1673 -1 l  1673 -438 l 
+1732 -438 l  1732 -253 l  1969 -253 l  1969 -438 l  2028 -438 l  2028 -1 l 
+1969 -1 l  p ef
+2320 -181 m  2320 0 l  2261 0 l  2261 -181 l  2093 -438 l  2158 -438 l 
+2291 -229 l  2423 -438 l  2489 -438 l  2320 -181 l  p ef
+pom
+gs
+0.871 0.902 0.937 c 10489 8119 m  5080 8119 l  5080 7169 l  15899 7169 l 
+15899 8119 l  10489 8119 l  p ef
+gr
+gs
+1.000 1.000 0.843 c 10489 15718 m  5080 15718 l  5080 14769 l  15899 14769 l 
+15899 15718 l  10489 15718 l  p ef
+gr
+gs
+0.933 0.933 0.933 c 7306 2309 m  7306 2309 l  7133 2309 6963 2326 6813 2359 ct 
+6663 2391 6538 2438 6451 2494 ct 6365 2550 6319 2614 6319 2679 ct 6319 2955 l 
+6319 3232 l  6319 3609 l  6319 3886 l  6319 4163 l  6319 4163 l  6319 4228 6365 4292 6451 4348 ct 
+6538 4404 6663 4451 6813 4483 ct 6963 4516 7133 4533 7306 4533 ct 8046 4533 l 
+8785 4533 l  9791 4533 l  11499 5392 l  11270 4533 l  11271 4533 l  11444 4533 11614 4516 11764 4483 ct 
+11914 4451 12039 4404 12126 4348 ct 12212 4292 12258 4228 12258 4163 ct 12258 3886 l 
+12258 3609 l  12258 3232 l  12258 2955 l  12258 2678 l  12258 2679 l  12258 2679 l 
+12258 2614 12212 2550 12126 2494 ct 12039 2438 11914 2391 11764 2359 ct 11614 2326 11444 2309 11271 2309 ct 
+10531 2309 l  9791 2309 l  8785 2309 l  8046 2309 l  7306 2309 l  p ef
+gr
+gs
+0 lw 1 lj 7305 2308 m  7305 2308 l  7132 2308 6962 2325 6812 2358 ct 6662 2390 6537 2437 6450 2493 ct 
+6364 2549 6318 2613 6318 2678 ct 6318 2954 l  6318 3231 l  6318 3608 l  6318 3885 l 
+6318 4162 l  6318 4162 l  6318 4227 6364 4291 6450 4347 ct 6537 4403 6662 4450 6812 4482 ct 
+6962 4515 7132 4532 7305 4532 ct 8045 4532 l  8784 4532 l  9791 4532 l  11499 5392 l 
+11270 4532 l  11271 4532 l  11444 4532 11614 4515 11764 4482 ct 11914 4450 12039 4403 12126 4347 ct 
+12212 4291 12258 4227 12258 4162 ct 12258 3885 l  12258 3608 l  12258 3231 l 
+12258 2954 l  12258 2677 l  12258 2678 l  12258 2678 l  12258 2613 12212 2549 12126 2493 ct 
+12039 2437 11914 2390 11764 2358 ct 11614 2325 11444 2308 11271 2308 ct 10531 2308 l 
+9791 2308 l  8784 2308 l  8045 2308 l  7305 2308 l  pc
+gr
+pum
+6816 2988 t
+198 -346 m  198 -1 l  146 -1 l  146 -346 l  13 -346 l  13 -389 l  332 -389 l 
+332 -346 l  198 -346 l  p ef
+430 -247 m  441 -266 454 -281 469 -290 ct 484 -299 503 -303 526 -303 ct 558 -303 582 -295 597 -279 ct 
+612 -263 620 -236 620 -198 ct 620 1 l  570 1 l  570 -189 l  570 -210 568 -225 564 -235 ct 
+561 -246 554 -253 545 -258 ct 537 -263 524 -265 509 -265 ct 485 -265 467 -257 453 -241 ct 
+439 -225 432 -203 432 -175 ct 432 1 l  382 1 l  382 -409 l  432 -409 l  432 -302 l 
+432 -291 431 -279 431 -267 ct 430 -256 430 -249 429 -247 ct 430 -247 l  p ef
+732 -138 m  732 -104 739 -78 753 -59 ct 767 -41 788 -31 815 -31 ct 837 -31 854 -36 867 -44 ct 
+880 -53 889 -64 893 -77 ct 937 -65 l  919 -18 878 6 815 6 ct 771 6 738 -7 715 -33 ct 
+691 -60 680 -99 680 -151 ct 680 -200 691 -238 715 -264 ct 738 -291 770 -304 813 -304 ct 
+901 -304 945 -251 945 -145 ct 945 -138 l  732 -138 l  p
+893 -176 m  891 -208 883 -231 869 -245 ct 856 -260 837 -267 812 -267 ct 788 -267 769 -259 755 -243 ct 
+741 -227 734 -204 733 -176 ct 893 -176 l  p ef
+1417 -1 m  1373 -114 l  1196 -114 l  1152 -1 l  1097 -1 l  1255 -389 l 
+1315 -389 l  1471 -1 l  1417 -1 l  p
+1285 -349 m  1282 -342 l  1278 -326 1271 -307 1262 -283 ct 1212 -155 l  1357 -155 l 
+1308 -283 l  1302 -296 1297 -310 1292 -326 ct 1285 -349 l  p ef
+1819 -271 m  1819 -234 1807 -205 1784 -184 ct 1760 -162 1727 -151 1686 -151 ct 
+1572 -151 l  1572 1 l  1519 1 l  1519 -388 l  1683 -388 l  1726 -388 1760 -378 1784 -358 ct 
+1808 -337 1819 -308 1819 -271 ct p
+1767 -271 m  1767 -321 1736 -346 1676 -346 ct 1572 -346 l  1572 -193 l  1678 -193 l 
+1737 -193 1767 -219 1767 -271 ct p ef
+2370 0 m  2370 -259 l  2370 -288 2371 -315 2373 -342 ct 2364 -309 2356 -283 2348 -265 ct 
+2248 0 l  2211 0 l  2110 -265 l  2094 -312 l  2085 -342 l  2086 -311 l 
+2087 -259 l  2087 0 l  2040 0 l  2040 -389 l  2109 -389 l  2213 -119 l 
+2216 -108 2220 -97 2223 -84 ct 2227 -72 2229 -63 2230 -57 ct 2231 -65 2235 -76 2239 -91 ct 
+2244 -106 2247 -115 2249 -119 ct 2350 -389 l  2418 -389 l  2418 0 l  2370 0 l 
+p ef
+2510 0 m  2510 -389 l  2563 -389 l  2563 -43 l  2759 -43 l  2759 0 l  2510 0 l 
+p ef
+3157 -199 m  3157 -158 3150 -123 3134 -93 ct 3118 -63 3096 -40 3068 -24 ct 3039 -8 3006 0 2968 0 ct 
+2823 0 l  2823 -389 l  2952 -389 l  3017 -389 3068 -372 3104 -339 ct 3139 -306 3157 -259 3157 -199 ct 
+p
+3104 -199 m  3104 -247 3091 -284 3065 -309 ct 3039 -334 3000 -347 2951 -347 ct 
+2876 -347 l  2876 -42 l  2962 -42 l  2991 -42 3016 -48 3037 -61 ct 3059 -73 3076 -92 3087 -115 ct 
+3099 -139 3104 -166 3104 -199 ct p ef
+3630 -150 m  3630 -46 3593 6 3520 6 ct 3498 6 3479 2 3464 -6 ct 3449 -14 3437 -28 3428 -46 ct 
+3427 -46 l  3427 -40 3427 -31 3426 -20 ct 3425 -8 3425 -1 3424 0 ct 3376 0 l 
+3377 -9 3378 -30 3378 -61 ct 3378 -409 l  3428 -409 l  3428 -292 l  3428 -280 3427 -266 3426 -250 ct 
+3428 -250 l  3437 -269 3449 -283 3464 -291 ct 3479 -299 3498 -303 3520 -303 ct 
+3558 -303 3586 -291 3603 -265 ct 3621 -240 3630 -202 3630 -150 ct p
+3578 -148 m  3578 -190 3572 -220 3561 -238 ct 3550 -256 3533 -265 3508 -265 ct 
+3480 -265 3459 -255 3447 -236 ct 3434 -217 3428 -187 3428 -145 ct 3428 -106 3434 -77 3446 -59 ct 
+3459 -40 3479 -31 3507 -31 ct 3532 -31 3550 -40 3561 -58 ct 3572 -77 3578 -107 3578 -148 ct 
+p ef
+3691 1 m  3691 -409 l  3741 -409 l  3741 1 l  3691 1 l  p ef
+4070 -149 m  4070 -97 4059 -58 4036 -32 ct 4013 -7 3979 6 3936 6 ct 3892 6 3859 -7 3837 -34 ct 
+3815 -60 3804 -99 3804 -149 ct 3804 -252 3848 -304 3937 -304 ct 3983 -304 4016 -291 4038 -266 ct 
+4059 -241 4070 -202 4070 -149 ct p
+4018 -149 m  4018 -190 4012 -220 4000 -239 ct 3987 -258 3967 -267 3938 -267 ct 
+3909 -267 3888 -257 3875 -238 ct 3862 -219 3856 -190 3856 -149 ct 3856 -110 3862 -80 3875 -60 ct 
+3888 -41 3908 -31 3935 -31 ct 3965 -31 3986 -40 3999 -59 ct 4012 -79 4018 -108 4018 -149 ct 
+p ef
+4170 -150 m  4170 -111 4176 -81 4188 -62 ct 4201 -43 4220 -33 4245 -33 ct 4263 -33 4277 -38 4289 -48 ct 
+4301 -57 4308 -72 4311 -92 ct 4361 -88 l  4357 -60 4345 -37 4325 -20 ct 4304 -3 4278 6 4246 6 ct 
+4205 6 4173 -7 4151 -34 ct 4129 -60 4118 -99 4118 -149 ct 4118 -200 4129 -238 4151 -264 ct 
+4173 -291 4205 -304 4246 -304 ct 4276 -304 4302 -296 4322 -280 ct 4342 -264 4354 -243 4359 -215 ct 
+4309 -211 l  4306 -227 4299 -241 4289 -250 ct 4279 -260 4264 -265 4244 -265 ct 
+4218 -265 4199 -256 4187 -239 ct 4176 -221 4170 -192 4170 -150 ct p ef
+4602 0 m  4501 -136 l  4465 -106 l  4465 0 l  4415 0 l  4415 -409 l  4465 -409 l 
+4465 -153 l  4595 -298 l  4654 -298 l  4533 -170 l  4660 0 l  4602 0 l 
+p ef
+4923 -82 m  4923 -54 4912 -32 4891 -17 ct 4870 -2 4840 6 4802 6 ct 4765 6 4736 0 4716 -12 ct 
+4696 -25 4683 -44 4677 -70 ct 4720 -78 l  4725 -62 4733 -50 4747 -43 ct 4760 -36 4778 -32 4802 -32 ct 
+4827 -32 4845 -36 4857 -43 ct 4869 -51 4874 -63 4874 -78 ct 4874 -90 4870 -99 4862 -107 ct 
+4854 -114 4841 -120 4823 -125 ct 4788 -134 l  4759 -142 4739 -149 4727 -156 ct 
+4715 -163 4706 -172 4699 -182 ct 4692 -192 4689 -204 4689 -219 ct 4689 -246 4698 -267 4718 -281 ct 
+4737 -295 4765 -303 4802 -303 ct 4835 -303 4861 -297 4881 -285 ct 4900 -274 4912 -255 4917 -229 ct 
+4873 -224 l  4870 -237 4863 -247 4851 -254 ct 4839 -261 4822 -265 4802 -265 ct 
+4780 -265 4763 -262 4753 -255 ct 4742 -248 4737 -238 4737 -224 ct 4737 -215 4739 -208 4743 -203 ct 
+4748 -197 4754 -193 4763 -189 ct 4772 -185 4790 -180 4817 -173 ct 4844 -166 4863 -160 4874 -155 ct 
+4886 -149 4895 -143 4902 -136 ct 4908 -129 4913 -121 4917 -112 ct 4921 -103 4923 -93 4923 -82 ct 
+p ef
+pom
+pum
+6808 3615 t
+153 -3 m  136 2 120 4 102 4 ct 63 4 43 -19 43 -64 ct 43 -263 l  9 -263 l  9 -299 l 
+45 -299 l  59 -366 l  93 -366 l  93 -299 l  148 -299 l  148 -263 l  93 -263 l 
+93 -74 l  93 -60 95 -50 100 -44 ct 104 -38 112 -35 124 -35 ct 131 -35 140 -37 153 -39 ct 
+153 -3 l  p ef
+196 0 m  196 -229 l  196 -250 196 -273 194 -298 ct 241 -298 l  243 -265 243 -244 243 -238 ct 
+245 -238 l  252 -263 262 -281 272 -290 ct 282 -299 297 -304 315 -304 ct 322 -304 329 -303 335 -301 ct 
+335 -256 l  329 -258 320 -258 309 -258 ct 288 -258 273 -250 262 -232 ct 251 -214 246 -189 246 -156 ct 
+246 0 l  196 0 l  p ef
+457 6 m  427 6 405 -2 390 -18 ct 374 -34 367 -55 367 -83 ct 367 -114 377 -138 397 -154 ct 
+418 -171 450 -180 496 -181 ct 562 -182 l  562 -198 l  562 -222 557 -240 547 -250 ct 
+537 -261 521 -266 499 -266 ct 476 -266 460 -262 450 -255 ct 440 -247 434 -235 432 -219 ct 
+380 -223 l  389 -277 428 -304 500 -304 ct 537 -304 565 -295 584 -278 ct 603 -261 613 -236 613 -203 ct 
+613 -74 l  613 -60 615 -49 618 -41 ct 622 -34 630 -30 640 -30 ct 645 -30 651 -31 657 -32 ct 
+657 -1 l  644 2 631 3 618 3 ct 600 3 587 -1 578 -11 ct 570 -21 565 -36 564 -57 ct 
+562 -57 l  550 -34 535 -18 518 -8 ct 501 1 481 6 457 6 ct p
+468 -31 m  486 -31 503 -35 517 -44 ct 531 -52 542 -63 550 -78 ct 558 -92 562 -107 562 -122 ct 
+562 -147 l  508 -146 l  485 -145 467 -143 455 -139 ct 443 -134 434 -127 428 -118 ct 
+421 -109 418 -97 418 -82 ct 418 -66 422 -53 431 -44 ct 440 -36 452 -31 468 -31 ct 
+p ef
+883 0 m  883 -189 l  883 -209 881 -224 877 -235 ct 874 -246 867 -254 859 -258 ct 
+851 -263 838 -266 822 -266 ct 798 -266 779 -257 765 -241 ct 752 -225 745 -202 745 -173 ct 
+745 0 l  695 0 l  695 -235 l  695 -270 695 -291 693 -298 ct 740 -298 l  740 -298 741 -295 741 -291 ct 
+741 -287 741 -282 742 -277 ct 742 -272 742 -262 742 -247 ct 743 -247 l  755 -268 768 -283 783 -291 ct 
+798 -300 816 -304 839 -304 ct 871 -304 895 -296 910 -280 ct 925 -263 933 -236 933 -199 ct 
+933 0 l  883 0 l  p ef
+1231 -82 m  1231 -54 1220 -32 1199 -17 ct 1178 -2 1148 6 1110 6 ct 1073 6 1044 0 1024 -12 ct 
+1004 -25 991 -44 985 -70 ct 1028 -78 l  1033 -62 1041 -50 1055 -43 ct 1068 -36 1086 -32 1110 -32 ct 
+1135 -32 1153 -36 1165 -43 ct 1177 -51 1182 -63 1182 -78 ct 1182 -90 1178 -99 1170 -107 ct 
+1162 -114 1149 -120 1131 -125 ct 1096 -134 l  1067 -142 1047 -149 1035 -156 ct 
+1023 -163 1014 -172 1007 -182 ct 1000 -192 997 -204 997 -219 ct 997 -246 1006 -267 1026 -281 ct 
+1045 -295 1073 -303 1110 -303 ct 1143 -303 1169 -297 1189 -285 ct 1208 -274 1220 -255 1225 -229 ct 
+1181 -224 l  1178 -237 1171 -247 1159 -254 ct 1147 -261 1130 -265 1110 -265 ct 
+1088 -265 1071 -262 1061 -255 ct 1050 -248 1045 -238 1045 -224 ct 1045 -215 1047 -208 1051 -203 ct 
+1056 -197 1062 -193 1071 -189 ct 1080 -185 1098 -180 1125 -173 ct 1152 -166 1171 -160 1182 -155 ct 
+1194 -149 1203 -143 1210 -136 ct 1216 -129 1221 -121 1225 -112 ct 1229 -103 1231 -93 1231 -82 ct 
+p ef
+1465 0 m  1465 -189 l  1465 -218 1461 -238 1453 -249 ct 1445 -260 1431 -266 1410 -266 ct 
+1389 -266 1372 -258 1360 -241 ct 1348 -225 1341 -202 1341 -173 ct 1341 0 l  1292 0 l 
+1292 -235 l  1292 -270 1292 -291 1290 -298 ct 1337 -298 l  1337 -298 1338 -295 1338 -291 ct 
+1338 -287 1338 -282 1339 -277 ct 1339 -272 1339 -262 1339 -247 ct 1340 -247 l 
+1351 -269 1363 -283 1377 -292 ct 1391 -300 1408 -304 1427 -304 ct 1450 -304 1468 -300 1481 -290 ct 
+1494 -281 1503 -267 1508 -247 ct 1509 -247 l  1519 -268 1532 -282 1546 -291 ct 
+1561 -300 1579 -304 1599 -304 ct 1630 -304 1651 -296 1665 -279 ct 1679 -263 1686 -236 1686 -199 ct 
+1686 0 l  1637 0 l  1637 -189 l  1637 -218 1633 -238 1625 -249 ct 1617 -260 1603 -266 1582 -266 ct 
+1560 -266 1544 -258 1532 -242 ct 1520 -225 1514 -203 1514 -173 ct 1514 0 l  1465 0 l 
+p ef
+1761 -362 m  1761 -409 l  1810 -409 l  1810 -362 l  1761 -362 l  p
+1761 0 m  1761 -298 l  1810 -298 l  1810 0 l  1761 0 l  p ef
+2112 -82 m  2112 -54 2101 -32 2080 -17 ct 2059 -2 2029 6 1991 6 ct 1954 6 1925 0 1905 -12 ct 
+1885 -25 1872 -44 1866 -70 ct 1909 -78 l  1914 -62 1922 -50 1936 -43 ct 1949 -36 1967 -32 1991 -32 ct 
+2016 -32 2034 -36 2046 -43 ct 2058 -51 2063 -63 2063 -78 ct 2063 -90 2059 -99 2051 -107 ct 
+2043 -114 2030 -120 2012 -125 ct 1977 -134 l  1948 -142 1928 -149 1916 -156 ct 
+1904 -163 1895 -172 1888 -182 ct 1881 -192 1878 -204 1878 -219 ct 1878 -246 1887 -267 1907 -281 ct 
+1926 -295 1954 -303 1991 -303 ct 2024 -303 2050 -297 2070 -285 ct 2089 -274 2101 -255 2106 -229 ct 
+2062 -224 l  2059 -237 2052 -247 2040 -254 ct 2028 -261 2011 -265 1991 -265 ct 
+1969 -265 1952 -262 1942 -255 ct 1931 -248 1926 -238 1926 -224 ct 1926 -215 1928 -208 1932 -203 ct 
+1937 -197 1943 -193 1952 -189 ct 1961 -185 1979 -180 2006 -173 ct 2033 -166 2052 -160 2063 -155 ct 
+2075 -149 2084 -143 2091 -136 ct 2097 -129 2102 -121 2106 -112 ct 2110 -103 2112 -93 2112 -82 ct 
+p ef
+2396 -82 m  2396 -54 2385 -32 2364 -17 ct 2343 -2 2313 6 2275 6 ct 2238 6 2209 0 2189 -12 ct 
+2169 -25 2156 -44 2150 -70 ct 2193 -78 l  2198 -62 2206 -50 2220 -43 ct 2233 -36 2251 -32 2275 -32 ct 
+2300 -32 2318 -36 2330 -43 ct 2342 -51 2347 -63 2347 -78 ct 2347 -90 2343 -99 2335 -107 ct 
+2327 -114 2314 -120 2296 -125 ct 2261 -134 l  2232 -142 2212 -149 2200 -156 ct 
+2188 -163 2179 -172 2172 -182 ct 2165 -192 2162 -204 2162 -219 ct 2162 -246 2171 -267 2191 -281 ct 
+2210 -295 2238 -303 2275 -303 ct 2308 -303 2334 -297 2354 -285 ct 2373 -274 2385 -255 2390 -229 ct 
+2346 -224 l  2343 -237 2336 -247 2324 -254 ct 2312 -261 2295 -265 2275 -265 ct 
+2253 -265 2236 -262 2226 -255 ct 2215 -248 2210 -238 2210 -224 ct 2210 -215 2212 -208 2216 -203 ct 
+2221 -197 2227 -193 2236 -189 ct 2245 -185 2263 -180 2290 -173 ct 2317 -166 2336 -160 2347 -155 ct 
+2359 -149 2368 -143 2375 -136 ct 2381 -129 2386 -121 2390 -112 ct 2394 -103 2396 -93 2396 -82 ct 
+p ef
+2455 -362 m  2455 -409 l  2504 -409 l  2504 -362 l  2455 -362 l  p
+2455 0 m  2455 -298 l  2504 -298 l  2504 0 l  2455 0 l  p ef
+2834 -149 m  2834 -97 2823 -58 2800 -32 ct 2777 -7 2743 6 2700 6 ct 2656 6 2623 -7 2601 -34 ct 
+2579 -60 2568 -99 2568 -149 ct 2568 -252 2612 -304 2701 -304 ct 2747 -304 2780 -291 2802 -266 ct 
+2823 -241 2834 -202 2834 -149 ct p
+2782 -149 m  2782 -190 2776 -220 2764 -239 ct 2751 -258 2731 -267 2702 -267 ct 
+2673 -267 2652 -257 2639 -238 ct 2626 -219 2620 -190 2620 -149 ct 2620 -110 2626 -80 2639 -60 ct 
+2652 -41 2672 -31 2699 -31 ct 2729 -31 2750 -40 2763 -59 ct 2776 -79 2782 -108 2782 -149 ct 
+p ef
+3085 0 m  3085 -189 l  3085 -209 3083 -224 3079 -235 ct 3076 -246 3069 -254 3061 -258 ct 
+3053 -263 3040 -266 3024 -266 ct 3000 -266 2981 -257 2967 -241 ct 2954 -225 2947 -202 2947 -173 ct 
+2947 0 l  2897 0 l  2897 -235 l  2897 -270 2897 -291 2895 -298 ct 2942 -298 l 
+2942 -298 2943 -295 2943 -291 ct 2943 -287 2943 -282 2944 -277 ct 2944 -272 2944 -262 2944 -247 ct 
+2945 -247 l  2957 -268 2970 -283 2985 -291 ct 3000 -300 3018 -304 3041 -304 ct 
+3073 -304 3097 -296 3112 -280 ct 3127 -263 3135 -236 3135 -199 ct 3135 0 l  3085 0 l 
+p ef
+3433 -82 m  3433 -54 3422 -32 3401 -17 ct 3380 -2 3350 6 3312 6 ct 3275 6 3246 0 3226 -12 ct 
+3206 -25 3193 -44 3187 -70 ct 3230 -78 l  3235 -62 3243 -50 3257 -43 ct 3270 -36 3288 -32 3312 -32 ct 
+3337 -32 3355 -36 3367 -43 ct 3379 -51 3384 -63 3384 -78 ct 3384 -90 3380 -99 3372 -107 ct 
+3364 -114 3351 -120 3333 -125 ct 3298 -134 l  3269 -142 3249 -149 3237 -156 ct 
+3225 -163 3216 -172 3209 -182 ct 3202 -192 3199 -204 3199 -219 ct 3199 -246 3208 -267 3228 -281 ct 
+3247 -295 3275 -303 3312 -303 ct 3345 -303 3371 -297 3391 -285 ct 3410 -274 3422 -255 3427 -229 ct 
+3383 -224 l  3380 -237 3373 -247 3361 -254 ct 3349 -261 3332 -265 3312 -265 ct 
+3290 -265 3273 -262 3263 -255 ct 3252 -248 3247 -238 3247 -224 ct 3247 -215 3249 -208 3253 -203 ct 
+3258 -197 3264 -193 3273 -189 ct 3282 -185 3300 -180 3327 -173 ct 3354 -166 3373 -160 3384 -155 ct 
+3396 -149 3405 -143 3412 -136 ct 3418 -129 3423 -121 3427 -112 ct 3431 -103 3433 -93 3433 -82 ct 
+p ef
+3901 -149 m  3901 -97 3890 -58 3867 -32 ct 3844 -7 3810 6 3767 6 ct 3723 6 3690 -7 3668 -34 ct 
+3646 -60 3635 -99 3635 -149 ct 3635 -252 3679 -304 3768 -304 ct 3814 -304 3847 -291 3869 -266 ct 
+3890 -241 3901 -202 3901 -149 ct p
+3849 -149 m  3849 -190 3843 -220 3831 -239 ct 3818 -258 3798 -267 3769 -267 ct 
+3740 -267 3719 -257 3706 -238 ct 3693 -219 3687 -190 3687 -149 ct 3687 -110 3693 -80 3706 -60 ct 
+3719 -41 3739 -31 3766 -31 ct 3796 -31 3817 -40 3830 -59 ct 3843 -79 3849 -108 3849 -149 ct 
+p ef
+4151 0 m  4151 -189 l  4151 -209 4149 -224 4145 -235 ct 4142 -246 4135 -254 4127 -258 ct 
+4119 -263 4106 -266 4090 -266 ct 4066 -266 4047 -257 4033 -241 ct 4020 -225 4013 -202 4013 -173 ct 
+4013 0 l  3963 0 l  3963 -235 l  3963 -270 3963 -291 3961 -298 ct 4008 -298 l 
+4008 -298 4009 -295 4009 -291 ct 4009 -287 4009 -282 4010 -277 ct 4010 -272 4010 -262 4010 -247 ct 
+4011 -247 l  4023 -268 4036 -283 4051 -291 ct 4066 -300 4084 -304 4107 -304 ct 
+4139 -304 4163 -296 4178 -280 ct 4193 -263 4201 -236 4201 -199 ct 4201 0 l  4151 0 l 
+p ef
+4508 6 m  4478 6 4456 -2 4441 -18 ct 4425 -34 4418 -55 4418 -83 ct 4418 -114 4428 -138 4448 -154 ct 
+4469 -171 4501 -180 4547 -181 ct 4613 -182 l  4613 -198 l  4613 -222 4608 -240 4598 -250 ct 
+4588 -261 4572 -266 4550 -266 ct 4527 -266 4511 -262 4501 -255 ct 4491 -247 4485 -235 4483 -219 ct 
+4431 -223 l  4440 -277 4479 -304 4551 -304 ct 4588 -304 4616 -295 4635 -278 ct 
+4654 -261 4664 -236 4664 -203 ct 4664 -74 l  4664 -60 4666 -49 4669 -41 ct 4673 -34 4681 -30 4691 -30 ct 
+4696 -30 4702 -31 4708 -32 ct 4708 -1 l  4695 2 4682 3 4669 3 ct 4651 3 4638 -1 4629 -11 ct 
+4621 -21 4616 -36 4615 -57 ct 4613 -57 l  4601 -34 4586 -18 4569 -8 ct 4552 1 4532 6 4508 6 ct 
+p
+4519 -31 m  4537 -31 4554 -35 4568 -44 ct 4582 -52 4593 -63 4601 -78 ct 4609 -92 4613 -107 4613 -122 ct 
+4613 -147 l  4559 -146 l  4536 -145 4518 -143 4506 -139 ct 4494 -134 4485 -127 4479 -118 ct 
+4472 -109 4469 -97 4469 -82 ct 4469 -66 4473 -53 4482 -44 ct 4491 -36 4503 -31 4519 -31 ct 
+p ef
+4745 1 m  4745 -409 l  4795 -409 l  4795 1 l  4745 1 l  p ef
+4872 1 m  4872 -409 l  4922 -409 l  4922 1 l  4872 1 l  p ef
+pom
+pum
+7671 4242 t
+46 0 m  46 -389 l  341 -389 l  341 -346 l  99 -346 l  99 -221 l  324 -221 l 
+324 -179 l  99 -179 l  99 -43 l  352 -43 l  352 0 l  46 0 l  p ef
+753 0 m  753 -259 l  753 -288 754 -315 756 -342 ct 747 -309 739 -283 731 -265 ct 
+631 0 l  594 0 l  493 -265 l  477 -312 l  468 -342 l  469 -311 l  470 -259 l 
+470 0 l  423 0 l  423 -389 l  492 -389 l  596 -119 l  599 -108 603 -97 606 -84 ct 
+610 -72 612 -63 613 -57 ct 614 -65 618 -76 622 -91 ct 627 -106 630 -115 632 -119 ct 
+733 -389 l  801 -389 l  801 0 l  753 0 l  p ef
+893 0 m  893 -389 l  946 -389 l  946 -43 l  1142 -43 l  1142 0 l  893 0 l 
+p ef
+1510 -107 m  1510 -71 1496 -43 1468 -24 ct 1440 -4 1401 6 1350 6 ct 1255 6 1201 -27 1186 -93 ct 
+1237 -103 l  1242 -80 1255 -62 1274 -52 ct 1293 -41 1319 -35 1352 -35 ct 1386 -35 1412 -41 1431 -53 ct 
+1449 -64 1458 -81 1458 -104 ct 1458 -117 1455 -127 1450 -135 ct 1444 -143 1436 -149 1425 -155 ct 
+1415 -160 1402 -164 1388 -168 ct 1373 -171 1357 -175 1340 -179 ct 1309 -186 1286 -192 1270 -199 ct 
+1254 -206 1241 -214 1232 -222 ct 1223 -230 1216 -240 1211 -251 ct 1206 -263 1204 -275 1204 -290 ct 
+1204 -323 1217 -349 1242 -367 ct 1267 -385 1304 -394 1351 -394 ct 1395 -394 1429 -387 1452 -374 ct 
+1476 -360 1492 -337 1501 -305 ct 1449 -296 l  1444 -316 1433 -331 1417 -340 ct 
+1401 -350 1379 -354 1351 -354 ct 1320 -354 1296 -349 1280 -339 ct 1263 -329 1255 -313 1255 -293 ct 
+1255 -281 1258 -271 1265 -263 ct 1271 -255 1280 -249 1292 -243 ct 1304 -238 1328 -231 1363 -223 ct 
+1375 -220 1387 -218 1399 -215 ct 1411 -212 1422 -209 1433 -205 ct 1444 -201 1454 -196 1463 -191 ct 
+1473 -185 1481 -179 1488 -171 ct 1495 -163 1500 -154 1504 -144 ct 1508 -133 1510 -121 1510 -107 ct 
+p ef
+1858 1 m  1757 -161 l  1636 -161 l  1636 1 l  1583 1 l  1583 -388 l  1766 -388 l 
+1810 -388 1843 -378 1867 -359 ct 1891 -339 1903 -312 1903 -277 ct 1903 -248 1894 -224 1878 -204 ct 
+1861 -185 1838 -172 1808 -167 ct 1918 1 l  1858 1 l  p
+1850 -277 m  1850 -299 1842 -316 1827 -328 ct 1812 -340 1789 -346 1761 -346 ct 
+1636 -346 l  1636 -203 l  1763 -203 l  1791 -203 1812 -209 1827 -222 ct 1842 -235 1850 -253 1850 -277 ct 
+p ef
+2138 1 m  2138 -409 l  2188 -409 l  2188 1 l  2138 1 l  p ef
+2265 -362 m  2265 -409 l  2314 -409 l  2314 -362 l  2265 -362 l  p
+2265 0 m  2265 -298 l  2314 -298 l  2314 0 l  2265 0 l  p ef
+2581 0 m  2581 -189 l  2581 -209 2579 -224 2575 -235 ct 2572 -246 2565 -254 2557 -258 ct 
+2549 -263 2536 -266 2520 -266 ct 2496 -266 2477 -257 2463 -241 ct 2450 -225 2443 -202 2443 -173 ct 
+2443 0 l  2393 0 l  2393 -235 l  2393 -270 2393 -291 2391 -298 ct 2438 -298 l 
+2438 -298 2439 -295 2439 -291 ct 2439 -287 2439 -282 2440 -277 ct 2440 -272 2440 -262 2440 -247 ct 
+2441 -247 l  2453 -268 2466 -283 2481 -291 ct 2496 -300 2514 -304 2537 -304 ct 
+2569 -304 2593 -296 2608 -280 ct 2623 -263 2631 -236 2631 -199 ct 2631 0 l  2581 0 l 
+p ef
+2892 0 m  2791 -136 l  2755 -106 l  2755 0 l  2705 0 l  2705 -409 l  2755 -409 l 
+2755 -153 l  2885 -298 l  2944 -298 l  2823 -170 l  2950 0 l  2892 0 l 
+p ef
+3213 -82 m  3213 -54 3202 -32 3181 -17 ct 3160 -2 3130 6 3092 6 ct 3055 6 3026 0 3006 -12 ct 
+2986 -25 2973 -44 2967 -70 ct 3010 -78 l  3015 -62 3023 -50 3037 -43 ct 3050 -36 3068 -32 3092 -32 ct 
+3117 -32 3135 -36 3147 -43 ct 3159 -51 3164 -63 3164 -78 ct 3164 -90 3160 -99 3152 -107 ct 
+3144 -114 3131 -120 3113 -125 ct 3078 -134 l  3049 -142 3029 -149 3017 -156 ct 
+3005 -163 2996 -172 2989 -182 ct 2982 -192 2979 -204 2979 -219 ct 2979 -246 2988 -267 3008 -281 ct 
+3027 -295 3055 -303 3092 -303 ct 3125 -303 3151 -297 3171 -285 ct 3190 -274 3202 -255 3207 -229 ct 
+3163 -224 l  3160 -237 3153 -247 3141 -254 ct 3129 -261 3112 -265 3092 -265 ct 
+3070 -265 3053 -262 3043 -255 ct 3032 -248 3027 -238 3027 -224 ct 3027 -215 3029 -208 3033 -203 ct 
+3038 -197 3044 -193 3053 -189 ct 3062 -185 3080 -180 3107 -173 ct 3134 -166 3153 -160 3164 -155 ct 
+3176 -149 3185 -143 3192 -136 ct 3198 -129 3203 -121 3207 -112 ct 3211 -103 3213 -93 3213 -82 ct 
+p ef
+pom
+gs
+1.000 1.000 0.843 c 20399 8119 m  15899 8119 l  15899 7169 l  24899 7169 l 
+24899 8119 l  20399 8119 l  p ef
+gr
+gs
+0.507 0.832 0.105 c 14799 8439 m  13699 8439 l  13699 7169 l  15899 7169 l 
+15899 8439 l  14799 8439 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 14799 8439 m  13699 8439 l  13699 7169 l 
+15899 7169 l  15899 8439 l  14799 8439 l  pc
+gr
+pum
+14167 8024 t
+361 -1 m  247 -182 l  111 -182 l  111 -1 l  52 -1 l  52 -437 l  258 -437 l 
+307 -437 345 -426 372 -404 ct 398 -382 412 -352 412 -313 ct 412 -280 402 -253 383 -231 ct 
+364 -209 338 -195 305 -189 ct 429 -1 l  361 -1 l  p
+352 -312 m  352 -337 344 -357 326 -370 ct 309 -383 284 -390 252 -390 ct 111 -390 l 
+111 -229 l  254 -229 l  285 -229 310 -236 327 -251 ct 344 -265 352 -286 352 -312 ct 
+p ef
+672 -389 m  672 0 l  613 0 l  613 -389 l  463 -389 l  463 -437 l  822 -437 l 
+822 -389 l  672 -389 l  p ef
+1232 -121 m  1232 -81 1217 -49 1185 -27 ct 1154 -5 1109 6 1052 6 ct 945 6 884 -31 867 -105 ct 
+924 -117 l  931 -90 945 -71 966 -59 ct 988 -46 1017 -40 1054 -40 ct 1092 -40 1122 -47 1143 -60 ct 
+1163 -73 1174 -92 1174 -118 ct 1174 -132 1171 -144 1164 -153 ct 1158 -161 1148 -169 1137 -175 ct 
+1125 -180 1111 -185 1094 -189 ct 1078 -193 1060 -197 1040 -202 ct 1006 -210 979 -217 962 -225 ct 
+944 -233 930 -241 919 -250 ct 909 -260 901 -271 896 -284 ct 890 -296 887 -311 887 -327 ct 
+887 -364 902 -393 930 -414 ct 959 -434 1000 -444 1053 -444 ct 1103 -444 1141 -436 1167 -421 ct 
+1193 -406 1212 -380 1222 -343 ct 1164 -333 l  1157 -356 1145 -373 1127 -384 ct 
+1109 -394 1084 -399 1053 -399 ct 1018 -399 991 -394 973 -382 ct 954 -370 945 -353 945 -330 ct 
+945 -317 949 -306 956 -297 ct 963 -288 973 -281 987 -274 ct 1000 -268 1027 -261 1067 -252 ct 
+1080 -249 1094 -246 1107 -242 ct 1120 -239 1133 -235 1145 -231 ct 1157 -226 1169 -221 1180 -215 ct 
+1190 -209 1199 -202 1207 -193 ct 1215 -185 1221 -174 1226 -163 ct 1230 -151 1232 -137 1232 -121 ct 
+p ef
+pom
+gs
+0.871 0.902 0.937 c 32418 8119 m  27439 8119 l  27439 7169 l  37398 7169 l 
+37398 8119 l  32418 8119 l  p ef
+gr
+gs
+1.000 1.000 0.843 c 32410 15720 m  27423 15720 l  27423 14769 l  37397 14769 l 
+37397 15720 l  32410 15720 l  p ef
+gr
+gs
+0.933 0.933 0.933 c 7145 17407 m  7145 17407 l  6950 17407 6758 17422 6589 17451 ct 
+6419 17479 6279 17520 6181 17570 ct 6083 17619 6032 17675 6032 17733 ct 6032 17976 l 
+6032 18220 l  6032 18552 l  6032 18796 l  6032 19040 l  6032 19040 l  6032 19098 6083 19154 6181 19203 ct 
+6279 19253 6419 19294 6589 19322 ct 6758 19351 6950 19366 7145 19366 ct 7979 19365 l 
+8813 19365 l  9948 19365 l  10782 19365 l  11616 19365 l  11617 19366 l 
+11812 19366 12004 19351 12173 19322 ct 12343 19294 12483 19253 12581 19203 ct 
+12679 19154 12730 19098 12730 19040 ct 12730 18796 l  12730 18552 l  12730 18220 l 
+12730 17976 l  12730 17732 l  12730 17733 l  12730 17733 l  12730 17675 12679 17619 12581 17570 ct 
+12483 17520 12343 17479 12173 17451 ct 12004 17422 11812 17407 11617 17407 ct 
+10782 17407 l  9948 17407 l  8813 17407 l  8684 16110 l  7145 17407 l 
+p ef
+gr
+gs
+0 lw 1 lj 7144 17407 m  7144 17407 l  6949 17407 6757 17422 6588 17451 ct 
+6418 17479 6278 17520 6180 17570 ct 6082 17619 6031 17675 6031 17733 ct 6031 17976 l 
+6031 18220 l  6031 18552 l  6031 18796 l  6031 19040 l  6031 19040 l  6031 19098 6082 19154 6180 19203 ct 
+6278 19253 6418 19294 6588 19322 ct 6757 19351 6949 19366 7144 19366 ct 7978 19365 l 
+8812 19365 l  9948 19365 l  10782 19365 l  11616 19365 l  11617 19366 l 
+11812 19366 12004 19351 12173 19322 ct 12343 19294 12483 19253 12581 19203 ct 
+12679 19154 12730 19098 12730 19040 ct 12730 18796 l  12730 18552 l  12730 18220 l 
+12730 17976 l  12730 17732 l  12730 17733 l  12730 17733 l  12730 17675 12679 17619 12581 17570 ct 
+12483 17520 12343 17479 12173 17451 ct 12004 17422 11812 17407 11617 17407 ct 
+10782 17407 l  9948 17407 l  8812 17407 l  8683 16110 l  7144 17407 l 
+pc
+gr
+pum
+6529 18267 t
+376 0 m  376 -259 l  376 -288 377 -315 379 -342 ct 370 -309 362 -283 354 -265 ct 
+254 0 l  217 0 l  116 -265 l  100 -312 l  91 -342 l  92 -311 l  93 -259 l 
+93 0 l  46 0 l  46 -389 l  115 -389 l  219 -119 l  222 -108 226 -97 229 -84 ct 
+233 -72 235 -63 236 -57 ct 237 -65 241 -76 245 -91 ct 250 -106 253 -115 255 -119 ct 
+356 -389 l  424 -389 l  424 0 l  376 0 l  p ef
+584 6 m  554 6 532 -2 517 -18 ct 501 -34 494 -55 494 -83 ct 494 -114 504 -138 524 -154 ct 
+545 -171 577 -180 623 -181 ct 689 -182 l  689 -198 l  689 -222 684 -240 674 -250 ct 
+664 -261 648 -266 626 -266 ct 603 -266 587 -262 577 -255 ct 567 -247 561 -235 559 -219 ct 
+507 -223 l  516 -277 555 -304 627 -304 ct 664 -304 692 -295 711 -278 ct 730 -261 740 -236 740 -203 ct 
+740 -74 l  740 -60 742 -49 745 -41 ct 749 -34 757 -30 767 -30 ct 772 -30 778 -31 784 -32 ct 
+784 -1 l  771 2 758 3 745 3 ct 727 3 714 -1 705 -11 ct 697 -21 692 -36 691 -57 ct 
+689 -57 l  677 -34 662 -18 645 -8 ct 628 1 608 6 584 6 ct p
+595 -31 m  613 -31 630 -35 644 -44 ct 658 -52 669 -63 677 -78 ct 685 -92 689 -107 689 -122 ct 
+689 -147 l  635 -146 l  612 -145 594 -143 582 -139 ct 570 -134 561 -127 555 -118 ct 
+548 -109 545 -97 545 -82 ct 545 -66 549 -53 558 -44 ct 567 -36 579 -31 595 -31 ct 
+p ef
+821 -362 m  821 -409 l  870 -409 l  870 -362 l  821 -362 l  p
+821 0 m  821 -298 l  870 -298 l  870 0 l  821 0 l  p ef
+1137 0 m  1137 -189 l  1137 -209 1135 -224 1131 -235 ct 1128 -246 1121 -254 1113 -258 ct 
+1105 -263 1092 -266 1076 -266 ct 1052 -266 1033 -257 1019 -241 ct 1006 -225 999 -202 999 -173 ct 
+999 0 l  949 0 l  949 -235 l  949 -270 949 -291 947 -298 ct 994 -298 l  994 -298 995 -295 995 -291 ct 
+995 -287 995 -282 996 -277 ct 996 -272 996 -262 996 -247 ct 997 -247 l  1009 -268 1022 -283 1037 -291 ct 
+1052 -300 1070 -304 1093 -304 ct 1125 -304 1149 -296 1164 -280 ct 1179 -263 1187 -236 1187 -199 ct 
+1187 0 l  1137 0 l  p ef
+1726 -271 m  1726 -234 1714 -205 1691 -184 ct 1667 -162 1634 -151 1593 -151 ct 
+1479 -151 l  1479 1 l  1426 1 l  1426 -388 l  1590 -388 l  1633 -388 1667 -378 1691 -358 ct 
+1715 -337 1726 -308 1726 -271 ct p
+1674 -271 m  1674 -321 1643 -346 1583 -346 ct 1479 -346 l  1479 -193 l  1585 -193 l 
+1644 -193 1674 -219 1674 -271 ct p ef
+2066 0 m  2066 -180 l  1856 -180 l  1856 0 l  1803 0 l  1803 -389 l  1856 -389 l 
+1856 -224 l  2066 -224 l  2066 -389 l  2118 -389 l  2118 0 l  2066 0 l 
+p ef
+2377 -161 m  2377 0 l  2325 0 l  2325 -161 l  2175 -389 l  2233 -389 l 
+2351 -203 l  2469 -389 l  2527 -389 l  2377 -161 l  p ef
+2760 -150 m  2760 -111 2766 -81 2778 -62 ct 2791 -43 2810 -33 2835 -33 ct 2853 -33 2867 -38 2879 -48 ct 
+2891 -57 2898 -72 2901 -92 ct 2951 -88 l  2947 -60 2935 -37 2915 -20 ct 2894 -3 2868 6 2836 6 ct 
+2795 6 2763 -7 2741 -34 ct 2719 -60 2708 -99 2708 -149 ct 2708 -200 2719 -238 2741 -264 ct 
+2763 -291 2795 -304 2836 -304 ct 2866 -304 2892 -296 2912 -280 ct 2932 -264 2944 -243 2949 -215 ct 
+2899 -211 l  2896 -227 2889 -241 2879 -250 ct 2869 -260 2854 -265 2834 -265 ct 
+2808 -265 2789 -256 2777 -239 ct 2766 -221 2760 -192 2760 -150 ct p ef
+3082 6 m  3052 6 3030 -2 3015 -18 ct 2999 -34 2992 -55 2992 -83 ct 2992 -114 3002 -138 3022 -154 ct 
+3043 -171 3075 -180 3121 -181 ct 3187 -182 l  3187 -198 l  3187 -222 3182 -240 3172 -250 ct 
+3162 -261 3146 -266 3124 -266 ct 3101 -266 3085 -262 3075 -255 ct 3065 -247 3059 -235 3057 -219 ct 
+3005 -223 l  3014 -277 3053 -304 3125 -304 ct 3162 -304 3190 -295 3209 -278 ct 
+3228 -261 3238 -236 3238 -203 ct 3238 -74 l  3238 -60 3240 -49 3243 -41 ct 3247 -34 3255 -30 3265 -30 ct 
+3270 -30 3276 -31 3282 -32 ct 3282 -1 l  3269 2 3256 3 3243 3 ct 3225 3 3212 -1 3203 -11 ct 
+3195 -21 3190 -36 3189 -57 ct 3187 -57 l  3175 -34 3160 -18 3143 -8 ct 3126 1 3106 6 3082 6 ct 
+p
+3093 -31 m  3111 -31 3128 -35 3142 -44 ct 3156 -52 3167 -63 3175 -78 ct 3183 -92 3187 -107 3187 -122 ct 
+3187 -147 l  3133 -146 l  3110 -145 3092 -143 3080 -139 ct 3068 -134 3059 -127 3053 -118 ct 
+3046 -109 3043 -97 3043 -82 ct 3043 -66 3047 -53 3056 -44 ct 3065 -36 3077 -31 3093 -31 ct 
+p ef
+3508 0 m  3508 -189 l  3508 -209 3506 -224 3502 -235 ct 3499 -246 3492 -254 3484 -258 ct 
+3476 -263 3463 -266 3447 -266 ct 3423 -266 3404 -257 3390 -241 ct 3377 -225 3370 -202 3370 -173 ct 
+3370 0 l  3320 0 l  3320 -235 l  3320 -270 3320 -291 3318 -298 ct 3365 -298 l 
+3365 -298 3366 -295 3366 -291 ct 3366 -287 3366 -282 3367 -277 ct 3367 -272 3367 -262 3367 -247 ct 
+3368 -247 l  3380 -268 3393 -283 3408 -291 ct 3423 -300 3441 -304 3464 -304 ct 
+3496 -304 3520 -296 3535 -280 ct 3550 -263 3558 -236 3558 -199 ct 3558 0 l  3508 0 l 
+p ef
+4013 -82 m  4013 -54 4002 -32 3981 -17 ct 3960 -2 3930 6 3892 6 ct 3855 6 3826 0 3806 -12 ct 
+3786 -25 3773 -44 3767 -70 ct 3810 -78 l  3815 -62 3823 -50 3837 -43 ct 3850 -36 3868 -32 3892 -32 ct 
+3917 -32 3935 -36 3947 -43 ct 3959 -51 3964 -63 3964 -78 ct 3964 -90 3960 -99 3952 -107 ct 
+3944 -114 3931 -120 3913 -125 ct 3878 -134 l  3849 -142 3829 -149 3817 -156 ct 
+3805 -163 3796 -172 3789 -182 ct 3782 -192 3779 -204 3779 -219 ct 3779 -246 3788 -267 3808 -281 ct 
+3827 -295 3855 -303 3892 -303 ct 3925 -303 3951 -297 3971 -285 ct 3990 -274 4002 -255 4007 -229 ct 
+3963 -224 l  3960 -237 3953 -247 3941 -254 ct 3929 -261 3912 -265 3892 -265 ct 
+3870 -265 3853 -262 3843 -255 ct 3832 -248 3827 -238 3827 -224 ct 3827 -215 3829 -208 3833 -203 ct 
+3838 -197 3844 -193 3853 -189 ct 3862 -185 3880 -180 3907 -173 ct 3934 -166 3953 -160 3964 -155 ct 
+3976 -149 3985 -143 3992 -136 ct 3998 -129 4003 -121 4007 -112 ct 4011 -103 4013 -93 4013 -82 ct 
+p ef
+4187 -3 m  4170 2 4154 4 4136 4 ct 4097 4 4077 -19 4077 -64 ct 4077 -263 l  4043 -263 l 
+4043 -299 l  4079 -299 l  4093 -366 l  4127 -366 l  4127 -299 l  4182 -299 l 
+4182 -263 l  4127 -263 l  4127 -74 l  4127 -60 4129 -50 4134 -44 ct 4138 -38 4146 -35 4158 -35 ct 
+4165 -35 4174 -37 4187 -39 ct 4187 -3 l  p ef
+4305 6 m  4275 6 4253 -2 4238 -18 ct 4222 -34 4215 -55 4215 -83 ct 4215 -114 4225 -138 4245 -154 ct 
+4266 -171 4298 -180 4344 -181 ct 4410 -182 l  4410 -198 l  4410 -222 4405 -240 4395 -250 ct 
+4385 -261 4369 -266 4347 -266 ct 4324 -266 4308 -262 4298 -255 ct 4288 -247 4282 -235 4280 -219 ct 
+4228 -223 l  4237 -277 4276 -304 4348 -304 ct 4385 -304 4413 -295 4432 -278 ct 
+4451 -261 4461 -236 4461 -203 ct 4461 -74 l  4461 -60 4463 -49 4466 -41 ct 4470 -34 4478 -30 4488 -30 ct 
+4493 -30 4499 -31 4505 -32 ct 4505 -1 l  4492 2 4479 3 4466 3 ct 4448 3 4435 -1 4426 -11 ct 
+4418 -21 4413 -36 4412 -57 ct 4410 -57 l  4398 -34 4383 -18 4366 -8 ct 4349 1 4329 6 4305 6 ct 
+p
+4316 -31 m  4334 -31 4351 -35 4365 -44 ct 4379 -52 4390 -63 4398 -78 ct 4406 -92 4410 -107 4410 -122 ct 
+4410 -147 l  4356 -146 l  4333 -145 4315 -143 4303 -139 ct 4291 -134 4282 -127 4276 -118 ct 
+4269 -109 4266 -97 4266 -82 ct 4266 -66 4270 -53 4279 -44 ct 4288 -36 4300 -31 4316 -31 ct 
+p ef
+4543 0 m  4543 -229 l  4543 -250 4543 -273 4541 -298 ct 4588 -298 l  4590 -265 4590 -244 4590 -238 ct 
+4592 -238 l  4599 -263 4609 -281 4619 -290 ct 4629 -299 4644 -304 4662 -304 ct 
+4669 -304 4676 -303 4682 -301 ct 4682 -256 l  4676 -258 4667 -258 4656 -258 ct 
+4635 -258 4620 -250 4609 -232 ct 4598 -214 4593 -189 4593 -156 ct 4593 0 l  4543 0 l 
+p ef
+4844 -3 m  4827 2 4811 4 4793 4 ct 4754 4 4734 -19 4734 -64 ct 4734 -263 l  4700 -263 l 
+4700 -299 l  4736 -299 l  4750 -366 l  4784 -366 l  4784 -299 l  4839 -299 l 
+4839 -263 l  4784 -263 l  4784 -74 l  4784 -60 4786 -50 4791 -44 ct 4795 -38 4803 -35 4815 -35 ct 
+4822 -35 4831 -37 4844 -39 ct 4844 -3 l  p ef
+5205 6 m  5174 6 5146 0 5122 -11 ct 5098 -23 5080 -40 5067 -62 ct 5054 -84 5048 -110 5048 -141 ct 
+5048 -388 l  5100 -388 l  5100 -145 l  5100 -110 5109 -83 5127 -64 ct 5145 -46 5171 -37 5205 -37 ct 
+5240 -37 5267 -46 5286 -65 ct 5306 -84 5315 -112 5315 -149 ct 5315 -388 l  5368 -388 l 
+5368 -146 l  5368 -114 5361 -87 5348 -64 ct 5335 -42 5316 -24 5291 -12 ct 5267 0 5238 6 5205 6 ct 
+p ef
+5456 0 m  5456 -389 l  5509 -389 l  5509 -43 l  5705 -43 l  5705 0 l  5456 0 l 
+p ef
+pom
+pum
+7012 18894 t
+198 -346 m  198 -1 l  146 -1 l  146 -346 l  13 -346 l  13 -389 l  332 -389 l 
+332 -346 l  198 -346 l  p ef
+649 0 m  533 -170 l  414 0 l  356 0 l  503 -202 l  367 -389 l  425 -389 l 
+533 -236 l  638 -389 l  696 -389 l  563 -204 l  707 0 l  649 0 l  p ef
+1132 -196 m  1132 -155 1124 -119 1108 -89 ct 1093 -58 1071 -35 1042 -19 ct 1013 -2 978 6 939 6 ct 
+899 6 865 -2 836 -18 ct 807 -34 785 -58 770 -88 ct 754 -119 747 -155 747 -196 ct 
+747 -258 764 -306 798 -341 ct 832 -376 879 -394 939 -394 ct 979 -394 1013 -386 1042 -370 ct 
+1071 -355 1093 -332 1109 -302 ct 1124 -272 1132 -236 1132 -196 ct p
+1078 -196 m  1078 -244 1066 -282 1042 -310 ct 1018 -337 984 -351 939 -351 ct 
+895 -351 861 -337 836 -310 ct 812 -283 800 -245 800 -196 ct 800 -147 812 -108 837 -80 ct 
+861 -51 895 -37 939 -37 ct 984 -37 1018 -51 1042 -78 ct 1066 -106 1078 -145 1078 -196 ct 
+p ef
+1502 -271 m  1502 -234 1490 -205 1467 -184 ct 1443 -162 1410 -151 1369 -151 ct 
+1255 -151 l  1255 1 l  1202 1 l  1202 -388 l  1366 -388 l  1409 -388 1443 -378 1467 -358 ct 
+1491 -337 1502 -308 1502 -271 ct p
+1450 -271 m  1450 -321 1419 -346 1359 -346 ct 1255 -346 l  1255 -193 l  1361 -193 l 
+1420 -193 1450 -219 1450 -271 ct p ef
+1999 0 m  1942 0 l  1890 -211 l  1880 -258 l  1878 -249 1876 -237 1872 -222 ct 
+1869 -206 1850 -132 1816 0 ct 1759 0 l  1675 -299 l  1724 -299 l  1775 -96 l 
+1776 -91 1780 -73 1786 -41 ct 1791 -62 l  1853 -299 l  1907 -299 l  1959 -94 l 
+1971 -41 l  1980 -79 l  2036 -299 l  2085 -299 l  1999 0 l  p ef
+2121 -362 m  2121 -409 l  2170 -409 l  2170 -362 l  2121 -362 l  p
+2121 0 m  2121 -298 l  2170 -298 l  2170 0 l  2121 0 l  p ef
+2363 -3 m  2346 2 2330 4 2312 4 ct 2273 4 2253 -19 2253 -64 ct 2253 -263 l  2219 -263 l 
+2219 -299 l  2255 -299 l  2269 -366 l  2303 -366 l  2303 -299 l  2358 -299 l 
+2358 -263 l  2303 -263 l  2303 -74 l  2303 -60 2305 -50 2310 -44 ct 2314 -38 2322 -35 2334 -35 ct 
+2341 -35 2350 -37 2363 -39 ct 2363 -3 l  p ef
+2453 -247 m  2464 -266 2477 -281 2492 -290 ct 2507 -299 2526 -303 2549 -303 ct 
+2581 -303 2605 -295 2620 -279 ct 2635 -263 2643 -236 2643 -198 ct 2643 1 l  2593 1 l 
+2593 -189 l  2593 -210 2591 -225 2587 -235 ct 2584 -246 2577 -253 2568 -258 ct 
+2560 -263 2547 -265 2532 -265 ct 2508 -265 2490 -257 2476 -241 ct 2462 -225 2455 -203 2455 -175 ct 
+2455 1 l  2405 1 l  2405 -409 l  2455 -409 l  2455 -302 l  2455 -291 2454 -279 2454 -267 ct 
+2453 -256 2453 -249 2452 -247 ct 2453 -247 l  p ef
+3063 0 m  3063 -189 l  3063 -209 3061 -224 3057 -235 ct 3054 -246 3047 -254 3039 -258 ct 
+3031 -263 3018 -266 3002 -266 ct 2978 -266 2959 -257 2945 -241 ct 2932 -225 2925 -202 2925 -173 ct 
+2925 0 l  2875 0 l  2875 -235 l  2875 -270 2875 -291 2873 -298 ct 2920 -298 l 
+2920 -298 2921 -295 2921 -291 ct 2921 -287 2921 -282 2922 -277 ct 2922 -272 2922 -262 2922 -247 ct 
+2923 -247 l  2935 -268 2948 -283 2963 -291 ct 2978 -300 2996 -304 3019 -304 ct 
+3051 -304 3075 -296 3090 -280 ct 3105 -263 3113 -236 3113 -199 ct 3113 0 l  3063 0 l 
+p ef
+3440 -149 m  3440 -97 3429 -58 3406 -32 ct 3383 -7 3349 6 3306 6 ct 3262 6 3229 -7 3207 -34 ct 
+3185 -60 3174 -99 3174 -149 ct 3174 -252 3218 -304 3307 -304 ct 3353 -304 3386 -291 3408 -266 ct 
+3429 -241 3440 -202 3440 -149 ct p
+3388 -149 m  3388 -190 3382 -220 3370 -239 ct 3357 -258 3337 -267 3308 -267 ct 
+3279 -267 3258 -257 3245 -238 ct 3232 -219 3226 -190 3226 -149 ct 3226 -110 3232 -80 3245 -60 ct 
+3258 -41 3278 -31 3305 -31 ct 3335 -31 3356 -40 3369 -59 ct 3382 -79 3388 -108 3388 -149 ct 
+p ef
+3941 1 m  3840 -161 l  3719 -161 l  3719 1 l  3666 1 l  3666 -388 l  3849 -388 l 
+3893 -388 3926 -378 3950 -359 ct 3974 -339 3986 -312 3986 -277 ct 3986 -248 3977 -224 3961 -204 ct 
+3944 -185 3921 -172 3891 -167 ct 4001 1 l  3941 1 l  p
+3933 -277 m  3933 -299 3925 -316 3910 -328 ct 3895 -340 3872 -346 3844 -346 ct 
+3719 -346 l  3719 -203 l  3846 -203 l  3874 -203 3895 -209 3910 -222 ct 3925 -235 3933 -253 3933 -277 ct 
+p ef
+4215 -346 m  4215 -1 l  4163 -1 l  4163 -346 l  4030 -346 l  4030 -389 l 
+4349 -389 l  4349 -346 l  4215 -346 l  p ef
+4710 -107 m  4710 -71 4696 -43 4668 -24 ct 4640 -4 4601 6 4550 6 ct 4455 6 4401 -27 4386 -93 ct 
+4437 -103 l  4442 -80 4455 -62 4474 -52 ct 4493 -41 4519 -35 4552 -35 ct 4586 -35 4612 -41 4631 -53 ct 
+4649 -64 4658 -81 4658 -104 ct 4658 -117 4655 -127 4650 -135 ct 4644 -143 4636 -149 4625 -155 ct 
+4615 -160 4602 -164 4588 -168 ct 4573 -171 4557 -175 4540 -179 ct 4509 -186 4486 -192 4470 -199 ct 
+4454 -206 4441 -214 4432 -222 ct 4423 -230 4416 -240 4411 -251 ct 4406 -263 4404 -275 4404 -290 ct 
+4404 -323 4417 -349 4442 -367 ct 4467 -385 4504 -394 4551 -394 ct 4595 -394 4629 -387 4652 -374 ct 
+4676 -360 4692 -337 4701 -305 ct 4649 -296 l  4644 -316 4633 -331 4617 -340 ct 
+4601 -350 4579 -354 4551 -354 ct 4520 -354 4496 -349 4480 -339 ct 4463 -329 4455 -313 4455 -293 ct 
+4455 -281 4458 -271 4465 -263 ct 4471 -255 4480 -249 4492 -243 ct 4504 -238 4528 -231 4563 -223 ct 
+4575 -220 4587 -218 4599 -215 ct 4611 -212 4622 -209 4633 -205 ct 4644 -201 4654 -196 4663 -191 ct 
+4673 -185 4681 -179 4688 -171 ct 4695 -163 4700 -154 4704 -144 ct 4708 -133 4710 -121 4710 -107 ct 
+p ef
+pom
+gs
+0.507 0.832 0.105 c 7320 16038 m  5720 16038 l  5720 14769 l  8920 14769 l 
+8920 16038 l  7320 16038 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 7319 16038 m  5719 16038 l  5719 14768 l 
+8919 14768 l  8919 16038 l  7319 16038 l  pc
+gr
+pum
+6650 15623 t
+428 -223 m  428 -178 419 -139 402 -105 ct 384 -71 359 -45 327 -27 ct 295 -9 258 0 215 0 ct 
+52 0 l  52 -437 l  197 -437 l  271 -437 328 -419 368 -382 ct 408 -345 428 -292 428 -223 ct 
+p
+369 -223 m  369 -277 354 -319 324 -347 ct 295 -376 252 -390 195 -390 ct 111 -390 l 
+111 -47 l  209 -47 l  241 -47 269 -55 293 -69 ct 317 -83 336 -103 349 -129 ct 
+362 -156 369 -187 369 -223 ct p ef
+585 6 m  552 6 526 -3 509 -21 ct 492 -38 484 -63 484 -94 ct 484 -129 495 -155 518 -174 ct 
+541 -192 578 -202 629 -204 ct 704 -205 l  704 -223 l  704 -250 698 -270 687 -282 ct 
+675 -294 657 -299 632 -299 ct 607 -299 589 -295 578 -287 ct 566 -278 559 -265 557 -246 ct 
+499 -251 l  508 -312 553 -342 633 -342 ct 676 -342 707 -332 729 -313 ct 750 -294 761 -266 761 -229 ct 
+761 -85 l  761 -68 763 -55 767 -47 ct 771 -39 780 -35 792 -35 ct 797 -35 803 -35 810 -37 ct 
+810 -2 l  796 1 782 3 767 3 ct 746 3 731 -2 722 -13 ct 713 -24 707 -41 706 -64 ct 
+704 -64 l  690 -39 673 -21 654 -10 ct 635 1 612 6 585 6 ct p
+598 -36 m  619 -36 637 -41 653 -50 ct 669 -59 681 -72 690 -88 ct 700 -104 704 -121 704 -138 ct 
+704 -166 l  643 -165 l  617 -164 597 -161 583 -157 ct 570 -152 559 -144 552 -134 ct 
+545 -123 541 -110 541 -93 ct 541 -75 546 -61 556 -51 ct 566 -41 580 -36 598 -36 ct 
+p ef
+981 -2 m  962 3 944 5 924 5 ct 880 5 857 -20 857 -71 ct 857 -295 l  819 -295 l 
+819 -336 l  860 -336 l  876 -411 l  913 -411 l  913 -336 l  975 -336 l 
+975 -295 l  913 -295 l  913 -83 l  913 -67 916 -56 921 -49 ct 926 -43 936 -39 949 -39 ct 
+956 -39 967 -41 981 -44 ct 981 -2 l  p ef
+1114 6 m  1081 6 1055 -3 1038 -21 ct 1021 -38 1013 -63 1013 -94 ct 1013 -129 1024 -155 1047 -174 ct 
+1070 -192 1107 -202 1158 -204 ct 1233 -205 l  1233 -223 l  1233 -250 1227 -270 1216 -282 ct 
+1204 -294 1186 -299 1161 -299 ct 1136 -299 1118 -295 1107 -287 ct 1095 -278 1088 -265 1086 -246 ct 
+1028 -251 l  1037 -312 1082 -342 1162 -342 ct 1205 -342 1236 -332 1258 -313 ct 
+1279 -294 1290 -266 1290 -229 ct 1290 -85 l  1290 -68 1292 -55 1296 -47 ct 1300 -39 1309 -35 1321 -35 ct 
+1326 -35 1332 -35 1339 -37 ct 1339 -2 l  1325 1 1311 3 1296 3 ct 1275 3 1260 -2 1251 -13 ct 
+1242 -24 1236 -41 1235 -64 ct 1233 -64 l  1219 -39 1202 -21 1183 -10 ct 1164 1 1141 6 1114 6 ct 
+p
+1127 -36 m  1148 -36 1166 -41 1182 -50 ct 1198 -59 1210 -72 1219 -88 ct 1229 -104 1233 -121 1233 -138 ct 
+1233 -166 l  1172 -165 l  1146 -164 1126 -161 1112 -157 ct 1099 -152 1088 -144 1081 -134 ct 
+1074 -123 1070 -110 1070 -93 ct 1070 -75 1075 -61 1085 -51 ct 1095 -41 1109 -36 1127 -36 ct 
+p ef
+pom
+gs
+1.000 0.714 0.425 c 10469 14549 m  9420 14549 l  9420 13279 l  11519 13279 l 
+11519 14549 l  10469 14549 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 10469 14548 m  9419 14548 l  9419 13278 l 
+11519 13278 l  11519 14548 l  10469 14548 l  pc
+gr
+pum
+9939 14134 t
+362 0 m  312 -128 l  113 -128 l  63 0 l  1 0 l  180 -438 l  247 -438 l 
+422 0 l  362 0 l  p
+212 -393 m  210 -384 l  204 -367 197 -345 187 -318 ct 131 -174 l  294 -174 l 
+238 -319 l  232 -333 227 -349 221 -367 ct 212 -393 l  p ef
+508 -170 m  508 -125 515 -92 529 -70 ct 543 -49 565 -38 593 -38 ct 613 -38 629 -43 643 -54 ct 
+656 -65 664 -81 667 -104 ct 724 -100 l  719 -68 706 -42 683 -23 ct 659 -4 630 6 594 6 ct 
+548 6 512 -9 487 -39 ct 462 -68 450 -111 450 -168 ct 450 -225 462 -268 487 -297 ct 
+512 -327 548 -342 594 -342 ct 628 -342 657 -333 679 -315 ct 702 -298 716 -273 722 -242 ct 
+665 -237 l  662 -256 654 -271 643 -282 ct 631 -293 614 -298 592 -298 ct 563 -298 541 -288 528 -269 ct 
+515 -249 508 -216 508 -170 ct p ef
+994 1 m  881 -153 l  840 -119 l  840 1 l  784 1 l  784 -460 l  840 -460 l 
+840 -172 l  987 -335 l  1052 -335 l  916 -191 l  1059 1 l  994 1 l  p ef
+pom
+gs
+1 lj 11806 13349 m  15042 13349 l  ps
+gr
+gs
+11519 13349 m  11819 13449 l  11819 13249 l  11519 13349 l  p ef
+gr
+gs
+15329 13349 m  15029 13249 l  15029 13449 l  15329 13349 l  p ef
+gr
+gs
+0 lw 1 lj 11519 14048 m  11519 13148 l  ps
+gr
+gs
+0 lw 1 lj 15329 14048 m  15329 13148 l  ps
+gr
+pum
+12152 12456 t
+172 -2 m  153 3 135 5 115 5 ct 71 5 48 -20 48 -71 ct 48 -295 l  10 -295 l  10 -336 l 
+51 -336 l  67 -411 l  104 -411 l  104 -336 l  166 -336 l  166 -295 l  104 -295 l 
+104 -83 l  104 -67 107 -56 112 -49 ct 117 -43 127 -39 140 -39 ct 147 -39 158 -41 172 -44 ct 
+172 -2 l  p ef
+222 0 m  222 -258 l  222 -281 221 -307 220 -336 ct 273 -336 l  275 -298 275 -275 275 -267 ct 
+277 -267 l  285 -296 296 -316 307 -326 ct 319 -337 335 -342 356 -342 ct 364 -342 371 -341 379 -339 ct 
+379 -288 l  371 -290 362 -291 349 -291 ct 326 -291 308 -281 296 -261 ct 284 -241 278 -212 278 -175 ct 
+278 0 l  222 0 l  p ef
+517 6 m  484 6 458 -3 441 -21 ct 424 -38 416 -63 416 -94 ct 416 -129 427 -155 450 -174 ct 
+473 -192 510 -202 561 -204 ct 636 -205 l  636 -223 l  636 -250 630 -270 619 -282 ct 
+607 -294 589 -299 564 -299 ct 539 -299 521 -295 510 -287 ct 498 -278 491 -265 489 -246 ct 
+431 -251 l  440 -312 485 -342 565 -342 ct 608 -342 639 -332 661 -313 ct 682 -294 693 -266 693 -229 ct 
+693 -85 l  693 -68 695 -55 699 -47 ct 703 -39 712 -35 724 -35 ct 729 -35 735 -35 742 -37 ct 
+742 -2 l  728 1 714 3 699 3 ct 678 3 663 -2 654 -13 ct 645 -24 639 -41 638 -64 ct 
+636 -64 l  622 -39 605 -21 586 -10 ct 567 1 544 6 517 6 ct p
+530 -36 m  551 -36 569 -41 585 -50 ct 601 -59 613 -72 622 -88 ct 632 -104 636 -121 636 -138 ct 
+636 -166 l  575 -165 l  549 -164 529 -161 515 -157 ct 502 -152 491 -144 484 -134 ct 
+477 -123 473 -110 473 -93 ct 473 -75 478 -61 488 -51 ct 498 -41 512 -36 530 -36 ct 
+p ef
+997 0 m  997 -213 l  997 -235 995 -252 990 -264 ct 986 -277 979 -285 970 -291 ct 
+960 -296 946 -299 928 -299 ct 901 -299 880 -290 864 -271 ct 849 -253 841 -227 841 -195 ct 
+841 0 l  785 0 l  785 -264 l  785 -303 784 -327 783 -336 ct 836 -336 l  836 -335 836 -332 837 -327 ct 
+837 -323 837 -318 837 -312 ct 838 -306 838 -295 838 -278 ct 839 -278 l  852 -302 867 -318 884 -328 ct 
+901 -337 922 -342 947 -342 ct 983 -342 1010 -333 1027 -315 ct 1044 -296 1053 -266 1053 -224 ct 
+1053 0 l  997 0 l  p ef
+1387 -93 m  1387 -61 1375 -37 1351 -20 ct 1327 -3 1293 6 1250 6 ct 1209 6 1177 -1 1154 -15 ct 
+1131 -28 1117 -50 1110 -79 ct 1159 -89 l  1164 -71 1174 -57 1188 -49 ct 1203 -41 1224 -37 1250 -37 ct 
+1279 -37 1299 -41 1313 -50 ct 1326 -58 1332 -71 1332 -89 ct 1332 -102 1328 -113 1319 -121 ct 
+1310 -129 1295 -136 1275 -141 ct 1235 -152 l  1203 -160 1180 -168 1166 -176 ct 
+1153 -184 1142 -194 1134 -205 ct 1127 -217 1123 -231 1123 -247 ct 1123 -278 1134 -301 1156 -317 ct 
+1178 -333 1209 -341 1251 -341 ct 1288 -341 1317 -335 1339 -322 ct 1361 -309 1375 -288 1381 -259 ct 
+1330 -253 l  1327 -268 1319 -279 1305 -287 ct 1292 -295 1274 -299 1251 -299 ct 
+1226 -299 1207 -295 1195 -288 ct 1183 -280 1177 -268 1177 -253 ct 1177 -243 1180 -235 1185 -229 ct 
+1190 -223 1197 -218 1207 -213 ct 1216 -209 1237 -203 1268 -195 ct 1298 -188 1319 -181 1332 -175 ct 
+1345 -168 1355 -161 1363 -154 ct 1370 -146 1376 -137 1380 -127 ct 1385 -117 1387 -106 1387 -93 ct 
+p ef
+1452 -407 m  1452 -460 l  1508 -460 l  1508 -407 l  1452 -407 l  p
+1452 1 m  1452 -335 l  1508 -335 l  1508 1 l  1452 1 l  p ef
+1721 -2 m  1702 3 1684 5 1664 5 ct 1620 5 1597 -20 1597 -71 ct 1597 -295 l  1559 -295 l 
+1559 -336 l  1600 -336 l  1616 -411 l  1653 -411 l  1653 -336 l  1715 -336 l 
+1715 -295 l  1653 -295 l  1653 -83 l  1653 -67 1656 -56 1661 -49 ct 1666 -43 1676 -39 1689 -39 ct 
+1696 -39 1707 -41 1721 -44 ct 1721 -2 l  p ef
+1769 -407 m  1769 -460 l  1825 -460 l  1825 -407 l  1769 -407 l  p
+1769 1 m  1769 -335 l  1825 -335 l  1825 1 l  1769 1 l  p ef
+2193 -168 m  2193 -110 2181 -66 2155 -37 ct 2129 -8 2091 6 2042 6 ct 1993 6 1956 -9 1931 -39 ct 
+1906 -69 1894 -112 1894 -168 ct 1894 -284 1944 -342 2044 -342 ct 2095 -342 2133 -328 2157 -300 ct 
+2181 -272 2193 -228 2193 -168 ct p
+2135 -168 m  2135 -215 2128 -248 2114 -269 ct 2101 -291 2077 -301 2045 -301 ct 
+2012 -301 1989 -290 1974 -269 ct 1960 -247 1952 -214 1952 -168 ct 1952 -124 1959 -91 1974 -69 ct 
+1988 -46 2011 -35 2042 -35 ct 2075 -35 2099 -46 2113 -68 ct 2128 -89 2135 -123 2135 -168 ct 
+p ef
+2474 0 m  2474 -213 l  2474 -235 2472 -252 2467 -264 ct 2463 -277 2456 -285 2447 -291 ct 
+2437 -296 2423 -299 2405 -299 ct 2378 -299 2357 -290 2341 -271 ct 2326 -253 2318 -227 2318 -195 ct 
+2318 0 l  2262 0 l  2262 -264 l  2262 -303 2261 -327 2260 -336 ct 2313 -336 l 
+2313 -335 2313 -332 2314 -327 ct 2314 -323 2314 -318 2314 -312 ct 2315 -306 2315 -295 2315 -278 ct 
+2316 -278 l  2329 -302 2344 -318 2361 -328 ct 2378 -337 2399 -342 2424 -342 ct 
+2460 -342 2487 -333 2504 -315 ct 2521 -296 2530 -266 2530 -224 ct 2530 0 l  2474 0 l 
+p ef
+pom
+pum
+12593 13167 t
+255 -53 m  244 -32 231 -16 213 -7 ct 196 2 175 7 150 7 ct 108 7 77 -7 57 -36 ct 
+37 -64 27 -108 27 -166 ct 27 -283 68 -341 150 -341 ct 175 -341 197 -337 214 -327 ct 
+231 -318 244 -303 255 -283 ct 255 -283 l  255 -321 l  255 -460 l  310 -460 l 
+310 -68 l  310 -33 311 -10 312 1 ct 259 1 l  258 -3 258 -10 257 -22 ct 256 -34 256 -45 256 -53 ct 
+255 -53 l  p
+85 -167 m  85 -120 91 -87 104 -67 ct 116 -46 136 -36 164 -36 ct 196 -36 219 -47 233 -69 ct 
+247 -91 255 -125 255 -171 ct 255 -216 247 -248 233 -269 ct 219 -290 196 -300 165 -300 ct 
+137 -300 117 -290 104 -269 ct 92 -248 85 -214 85 -167 ct p ef
+437 -156 m  437 -118 445 -88 460 -67 ct 476 -46 500 -36 530 -36 ct 554 -36 574 -41 588 -50 ct 
+603 -60 613 -72 618 -87 ct 667 -73 l  647 -20 601 6 530 6 ct 481 6 443 -9 417 -38 ct 
+391 -68 378 -112 378 -170 ct 378 -226 391 -268 417 -298 ct 443 -327 480 -342 528 -342 ct 
+627 -342 676 -283 676 -164 ct 676 -156 l  437 -156 l  p
+618 -199 m  615 -234 606 -260 591 -277 ct 576 -293 555 -301 527 -301 ct 500 -301 479 -292 463 -274 ct 
+447 -256 438 -231 437 -199 ct 618 -199 l  p ef
+746 1 m  746 -460 l  802 -460 l  802 1 l  746 1 l  p ef
+970 6 m  937 6 911 -3 894 -21 ct 877 -38 869 -63 869 -94 ct 869 -129 880 -155 903 -174 ct 
+926 -192 963 -202 1014 -204 ct 1089 -205 l  1089 -223 l  1089 -250 1083 -270 1072 -282 ct 
+1060 -294 1042 -299 1017 -299 ct 992 -299 974 -295 963 -287 ct 951 -278 944 -265 942 -246 ct 
+884 -251 l  893 -312 938 -342 1018 -342 ct 1061 -342 1092 -332 1114 -313 ct 
+1135 -294 1146 -266 1146 -229 ct 1146 -85 l  1146 -68 1148 -55 1152 -47 ct 1156 -39 1165 -35 1177 -35 ct 
+1182 -35 1188 -35 1195 -37 ct 1195 -2 l  1181 1 1167 3 1152 3 ct 1131 3 1116 -2 1107 -13 ct 
+1098 -24 1092 -41 1091 -64 ct 1089 -64 l  1075 -39 1058 -21 1039 -10 ct 1020 1 997 6 970 6 ct 
+p
+983 -36 m  1004 -36 1022 -41 1038 -50 ct 1054 -59 1066 -72 1075 -88 ct 1085 -104 1089 -121 1089 -138 ct 
+1089 -166 l  1028 -165 l  1002 -164 982 -161 968 -157 ct 955 -152 944 -144 937 -134 ct 
+930 -123 926 -110 926 -93 ct 926 -75 931 -61 941 -51 ct 951 -41 965 -36 983 -36 ct 
+p ef
+1253 132 m  1238 132 1225 131 1215 129 ct 1215 87 l  1223 88 1231 89 1241 89 ct 
+1276 89 1303 63 1323 12 ct 1329 -1 l  1196 -336 l  1255 -336 l  1326 -150 l 
+1327 -147 1328 -144 1330 -140 ct 1331 -135 1336 -122 1343 -99 ct 1351 -76 1356 -63 1356 -61 ct 
+1378 -122 l  1451 -336 l  1510 -336 l  1381 0 l  1367 36 1355 63 1343 80 ct 
+1331 98 1317 111 1303 119 ct 1288 128 1272 132 1253 132 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 11512 14108 m  12021 13599 l  ps
+11632 14549 m  12582 13599 l  ps
+12193 14549 m  13143 13599 l  ps
+12754 14549 m  13704 13599 l  ps
+13315 14549 m  14265 13599 l  ps
+13876 14549 m  14826 13599 l  ps
+14437 14549 m  15387 13599 l  ps
+14998 14549 m  15948 13599 l  ps
+15559 14549 m  16509 13599 l  ps
+16120 14549 m  17070 13599 l  ps
+16681 14549 m  17631 13599 l  ps
+17242 14549 m  18192 13599 l  ps
+17803 14549 m  18753 13599 l  ps
+18364 14549 m  19314 13599 l  ps
+18925 14549 m  19875 13599 l  ps
+19486 14549 m  20436 13599 l  ps
+20047 14549 m  20997 13599 l  ps
+20608 14549 m  21558 13599 l  ps
+21169 14549 m  22119 13599 l  ps
+21730 14549 m  22680 13599 l  ps
+22291 14549 m  23241 13599 l  ps
+22852 14549 m  23802 13599 l  ps
+23413 14549 m  24363 13599 l  ps
+23974 14549 m  24924 13599 l  ps
+24535 14549 m  25485 13599 l  ps
+25096 14549 m  26046 13599 l  ps
+25657 14549 m  26607 13599 l  ps
+26218 14549 m  27168 13599 l  ps
+26779 14549 m  27729 13599 l  ps
+27340 14549 m  28290 13599 l  ps
+27901 14549 m  28681 13769 l  ps
+28462 14549 m  28681 14330 l  ps
+28342 13599 m  28681 13938 l  ps
+27781 13599 m  28681 14499 l  ps
+27220 13599 m  28170 14549 l  ps
+26659 13599 m  27609 14549 l  ps
+26098 13599 m  27048 14549 l  ps
+25537 13599 m  26487 14549 l  ps
+24976 13599 m  25926 14549 l  ps
+24415 13599 m  25365 14549 l  ps
+23854 13599 m  24804 14549 l  ps
+23293 13599 m  24243 14549 l  ps
+22732 13599 m  23682 14549 l  ps
+22171 13599 m  23121 14549 l  ps
+21610 13599 m  22560 14549 l  ps
+21049 13599 m  21999 14549 l  ps
+20488 13599 m  21438 14549 l  ps
+19927 13599 m  20877 14549 l  ps
+19366 13599 m  20316 14549 l  ps
+18805 13599 m  19755 14549 l  ps
+18244 13599 m  19194 14549 l  ps
+17683 13599 m  18633 14549 l  ps
+17122 13599 m  18072 14549 l  ps
+16561 13599 m  17511 14549 l  ps
+16000 13599 m  16950 14549 l  ps
+15439 13599 m  16389 14549 l  ps
+14878 13599 m  15828 14549 l  ps
+14317 13599 m  15267 14549 l  ps
+13756 13599 m  14706 14549 l  ps
+13195 13599 m  14145 14549 l  ps
+12634 13599 m  13584 14549 l  ps
+12073 13599 m  13023 14549 l  ps
+11512 13599 m  12462 14549 l  ps
+11512 14160 m  11901 14549 l  ps
+gr
+gs
+0 lw 1 lj 20096 14548 m  11512 14548 l  11512 13598 l  28681 13598 l  28681 14548 l 
+20096 14548 l  pc
+gr
+pum
+18511 14294 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+gs
+1 lj 11806 5769 m  15042 5769 l  ps
+gr
+gs
+11519 5769 m  11819 5869 l  11819 5669 l  11519 5769 l  p ef
+gr
+gs
+15329 5769 m  15029 5669 l  15029 5869 l  15329 5769 l  p ef
+gr
+gs
+0 lw 1 lj 11519 6469 m  11519 5569 l  ps
+gr
+gs
+0 lw 1 lj 15329 6469 m  15329 5569 l  ps
+gr
+pum
+12152 4877 t
+172 -2 m  153 3 135 5 115 5 ct 71 5 48 -20 48 -71 ct 48 -295 l  10 -295 l  10 -336 l 
+51 -336 l  67 -411 l  104 -411 l  104 -336 l  166 -336 l  166 -295 l  104 -295 l 
+104 -83 l  104 -67 107 -56 112 -49 ct 117 -43 127 -39 140 -39 ct 147 -39 158 -41 172 -44 ct 
+172 -2 l  p ef
+222 0 m  222 -258 l  222 -281 221 -307 220 -336 ct 273 -336 l  275 -298 275 -275 275 -267 ct 
+277 -267 l  285 -296 296 -316 307 -326 ct 319 -337 335 -342 356 -342 ct 364 -342 371 -341 379 -339 ct 
+379 -288 l  371 -290 362 -291 349 -291 ct 326 -291 308 -281 296 -261 ct 284 -241 278 -212 278 -175 ct 
+278 0 l  222 0 l  p ef
+517 6 m  484 6 458 -3 441 -21 ct 424 -38 416 -63 416 -94 ct 416 -129 427 -155 450 -174 ct 
+473 -192 510 -202 561 -204 ct 636 -205 l  636 -223 l  636 -250 630 -270 619 -282 ct 
+607 -294 589 -299 564 -299 ct 539 -299 521 -295 510 -287 ct 498 -278 491 -265 489 -246 ct 
+431 -251 l  440 -312 485 -342 565 -342 ct 608 -342 639 -332 661 -313 ct 682 -294 693 -266 693 -229 ct 
+693 -85 l  693 -68 695 -55 699 -47 ct 703 -39 712 -35 724 -35 ct 729 -35 735 -35 742 -37 ct 
+742 -2 l  728 1 714 3 699 3 ct 678 3 663 -2 654 -13 ct 645 -24 639 -41 638 -64 ct 
+636 -64 l  622 -39 605 -21 586 -10 ct 567 1 544 6 517 6 ct p
+530 -36 m  551 -36 569 -41 585 -50 ct 601 -59 613 -72 622 -88 ct 632 -104 636 -121 636 -138 ct 
+636 -166 l  575 -165 l  549 -164 529 -161 515 -157 ct 502 -152 491 -144 484 -134 ct 
+477 -123 473 -110 473 -93 ct 473 -75 478 -61 488 -51 ct 498 -41 512 -36 530 -36 ct 
+p ef
+997 0 m  997 -213 l  997 -235 995 -252 990 -264 ct 986 -277 979 -285 970 -291 ct 
+960 -296 946 -299 928 -299 ct 901 -299 880 -290 864 -271 ct 849 -253 841 -227 841 -195 ct 
+841 0 l  785 0 l  785 -264 l  785 -303 784 -327 783 -336 ct 836 -336 l  836 -335 836 -332 837 -327 ct 
+837 -323 837 -318 837 -312 ct 838 -306 838 -295 838 -278 ct 839 -278 l  852 -302 867 -318 884 -328 ct 
+901 -337 922 -342 947 -342 ct 983 -342 1010 -333 1027 -315 ct 1044 -296 1053 -266 1053 -224 ct 
+1053 0 l  997 0 l  p ef
+1387 -93 m  1387 -61 1375 -37 1351 -20 ct 1327 -3 1293 6 1250 6 ct 1209 6 1177 -1 1154 -15 ct 
+1131 -28 1117 -50 1110 -79 ct 1159 -89 l  1164 -71 1174 -57 1188 -49 ct 1203 -41 1224 -37 1250 -37 ct 
+1279 -37 1299 -41 1313 -50 ct 1326 -58 1332 -71 1332 -89 ct 1332 -102 1328 -113 1319 -121 ct 
+1310 -129 1295 -136 1275 -141 ct 1235 -152 l  1203 -160 1180 -168 1166 -176 ct 
+1153 -184 1142 -194 1134 -205 ct 1127 -217 1123 -231 1123 -247 ct 1123 -278 1134 -301 1156 -317 ct 
+1178 -333 1209 -341 1251 -341 ct 1288 -341 1317 -335 1339 -322 ct 1361 -309 1375 -288 1381 -259 ct 
+1330 -253 l  1327 -268 1319 -279 1305 -287 ct 1292 -295 1274 -299 1251 -299 ct 
+1226 -299 1207 -295 1195 -288 ct 1183 -280 1177 -268 1177 -253 ct 1177 -243 1180 -235 1185 -229 ct 
+1190 -223 1197 -218 1207 -213 ct 1216 -209 1237 -203 1268 -195 ct 1298 -188 1319 -181 1332 -175 ct 
+1345 -168 1355 -161 1363 -154 ct 1370 -146 1376 -137 1380 -127 ct 1385 -117 1387 -106 1387 -93 ct 
+p ef
+1452 -407 m  1452 -460 l  1508 -460 l  1508 -407 l  1452 -407 l  p
+1452 1 m  1452 -335 l  1508 -335 l  1508 1 l  1452 1 l  p ef
+1721 -2 m  1702 3 1684 5 1664 5 ct 1620 5 1597 -20 1597 -71 ct 1597 -295 l  1559 -295 l 
+1559 -336 l  1600 -336 l  1616 -411 l  1653 -411 l  1653 -336 l  1715 -336 l 
+1715 -295 l  1653 -295 l  1653 -83 l  1653 -67 1656 -56 1661 -49 ct 1666 -43 1676 -39 1689 -39 ct 
+1696 -39 1707 -41 1721 -44 ct 1721 -2 l  p ef
+1769 -407 m  1769 -460 l  1825 -460 l  1825 -407 l  1769 -407 l  p
+1769 1 m  1769 -335 l  1825 -335 l  1825 1 l  1769 1 l  p ef
+2193 -168 m  2193 -110 2181 -66 2155 -37 ct 2129 -8 2091 6 2042 6 ct 1993 6 1956 -9 1931 -39 ct 
+1906 -69 1894 -112 1894 -168 ct 1894 -284 1944 -342 2044 -342 ct 2095 -342 2133 -328 2157 -300 ct 
+2181 -272 2193 -228 2193 -168 ct p
+2135 -168 m  2135 -215 2128 -248 2114 -269 ct 2101 -291 2077 -301 2045 -301 ct 
+2012 -301 1989 -290 1974 -269 ct 1960 -247 1952 -214 1952 -168 ct 1952 -124 1959 -91 1974 -69 ct 
+1988 -46 2011 -35 2042 -35 ct 2075 -35 2099 -46 2113 -68 ct 2128 -89 2135 -123 2135 -168 ct 
+p ef
+2474 0 m  2474 -213 l  2474 -235 2472 -252 2467 -264 ct 2463 -277 2456 -285 2447 -291 ct 
+2437 -296 2423 -299 2405 -299 ct 2378 -299 2357 -290 2341 -271 ct 2326 -253 2318 -227 2318 -195 ct 
+2318 0 l  2262 0 l  2262 -264 l  2262 -303 2261 -327 2260 -336 ct 2313 -336 l 
+2313 -335 2313 -332 2314 -327 ct 2314 -323 2314 -318 2314 -312 ct 2315 -306 2315 -295 2315 -278 ct 
+2316 -278 l  2329 -302 2344 -318 2361 -328 ct 2378 -337 2399 -342 2424 -342 ct 
+2460 -342 2487 -333 2504 -315 ct 2521 -296 2530 -266 2530 -224 ct 2530 0 l  2474 0 l 
+p ef
+pom
+pum
+12593 5587 t
+255 -53 m  244 -32 231 -16 213 -7 ct 196 2 175 7 150 7 ct 108 7 77 -7 57 -36 ct 
+37 -64 27 -108 27 -166 ct 27 -283 68 -341 150 -341 ct 175 -341 197 -337 214 -327 ct 
+231 -318 244 -303 255 -283 ct 255 -283 l  255 -321 l  255 -460 l  310 -460 l 
+310 -68 l  310 -33 311 -10 312 1 ct 259 1 l  258 -3 258 -10 257 -22 ct 256 -34 256 -45 256 -53 ct 
+255 -53 l  p
+85 -167 m  85 -120 91 -87 104 -67 ct 116 -46 136 -36 164 -36 ct 196 -36 219 -47 233 -69 ct 
+247 -91 255 -125 255 -171 ct 255 -216 247 -248 233 -269 ct 219 -290 196 -300 165 -300 ct 
+137 -300 117 -290 104 -269 ct 92 -248 85 -214 85 -167 ct p ef
+437 -156 m  437 -118 445 -88 460 -67 ct 476 -46 500 -36 530 -36 ct 554 -36 574 -41 588 -50 ct 
+603 -60 613 -72 618 -87 ct 667 -73 l  647 -20 601 6 530 6 ct 481 6 443 -9 417 -38 ct 
+391 -68 378 -112 378 -170 ct 378 -226 391 -268 417 -298 ct 443 -327 480 -342 528 -342 ct 
+627 -342 676 -283 676 -164 ct 676 -156 l  437 -156 l  p
+618 -199 m  615 -234 606 -260 591 -277 ct 576 -293 555 -301 527 -301 ct 500 -301 479 -292 463 -274 ct 
+447 -256 438 -231 437 -199 ct 618 -199 l  p ef
+746 1 m  746 -460 l  802 -460 l  802 1 l  746 1 l  p ef
+970 6 m  937 6 911 -3 894 -21 ct 877 -38 869 -63 869 -94 ct 869 -129 880 -155 903 -174 ct 
+926 -192 963 -202 1014 -204 ct 1089 -205 l  1089 -223 l  1089 -250 1083 -270 1072 -282 ct 
+1060 -294 1042 -299 1017 -299 ct 992 -299 974 -295 963 -287 ct 951 -278 944 -265 942 -246 ct 
+884 -251 l  893 -312 938 -342 1018 -342 ct 1061 -342 1092 -332 1114 -313 ct 
+1135 -294 1146 -266 1146 -229 ct 1146 -85 l  1146 -68 1148 -55 1152 -47 ct 1156 -39 1165 -35 1177 -35 ct 
+1182 -35 1188 -35 1195 -37 ct 1195 -2 l  1181 1 1167 3 1152 3 ct 1131 3 1116 -2 1107 -13 ct 
+1098 -24 1092 -41 1091 -64 ct 1089 -64 l  1075 -39 1058 -21 1039 -10 ct 1020 1 997 6 970 6 ct 
+p
+983 -36 m  1004 -36 1022 -41 1038 -50 ct 1054 -59 1066 -72 1075 -88 ct 1085 -104 1089 -121 1089 -138 ct 
+1089 -166 l  1028 -165 l  1002 -164 982 -161 968 -157 ct 955 -152 944 -144 937 -134 ct 
+930 -123 926 -110 926 -93 ct 926 -75 931 -61 941 -51 ct 951 -41 965 -36 983 -36 ct 
+p ef
+1253 132 m  1238 132 1225 131 1215 129 ct 1215 87 l  1223 88 1231 89 1241 89 ct 
+1276 89 1303 63 1323 12 ct 1329 -1 l  1196 -336 l  1255 -336 l  1326 -150 l 
+1327 -147 1328 -144 1330 -140 ct 1331 -135 1336 -122 1343 -99 ct 1351 -76 1356 -63 1356 -61 ct 
+1378 -122 l  1451 -336 l  1510 -336 l  1381 0 l  1367 36 1355 63 1343 80 ct 
+1331 98 1317 111 1303 119 ct 1288 128 1272 132 1253 132 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 8920 6528 m  9429 6019 l  ps
+9040 6969 m  9990 6019 l  ps
+9601 6969 m  10551 6019 l  ps
+10162 6969 m  11112 6019 l  ps
+10723 6969 m  11673 6019 l  ps
+11284 6969 m  12234 6019 l  ps
+11845 6969 m  12795 6019 l  ps
+12406 6969 m  13356 6019 l  ps
+12967 6969 m  13917 6019 l  ps
+13528 6969 m  14478 6019 l  ps
+14089 6969 m  15039 6019 l  ps
+14650 6969 m  15329 6290 l  ps
+15211 6969 m  15329 6851 l  ps
+15091 6019 m  15329 6257 l  ps
+14530 6019 m  15329 6818 l  ps
+13969 6019 m  14919 6969 l  ps
+13408 6019 m  14358 6969 l  ps
+12847 6019 m  13797 6969 l  ps
+12286 6019 m  13236 6969 l  ps
+11725 6019 m  12675 6969 l  ps
+11164 6019 m  12114 6969 l  ps
+10603 6019 m  11553 6969 l  ps
+10042 6019 m  10992 6969 l  ps
+9481 6019 m  10431 6969 l  ps
+8920 6019 m  9870 6969 l  ps
+8920 6580 m  9309 6969 l  ps
+gr
+gs
+0 lw 1 lj 12124 6969 m  8919 6969 l  8919 6019 l  15329 6019 l  15329 6969 l 
+12124 6969 l  pc
+gr
+pum
+10538 6714 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+gs
+1.000 0.714 0.425 c 17499 6939 m  16399 6939 l  16399 5669 l  18599 5669 l 
+18599 6939 l  17499 6939 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 17499 6939 m  16399 6939 l  16399 5669 l 
+18599 5669 l  18599 6939 l  17499 6939 l  pc
+gr
+pum
+16863 6524 t
+246 -396 m  197 -396 160 -380 133 -349 ct 106 -318 92 -275 92 -221 ct 92 -167 106 -124 134 -92 ct 
+162 -59 200 -43 248 -43 ct 309 -43 355 -73 386 -134 ct 434 -109 l  416 -72 391 -43 359 -23 ct 
+326 -4 288 6 245 6 ct 201 6 163 -3 131 -21 ct 99 -40 74 -66 58 -100 ct 41 -134 32 -174 32 -221 ct 
+32 -291 51 -345 89 -385 ct 126 -424 178 -444 245 -444 ct 291 -444 330 -435 362 -417 ct 
+393 -398 416 -371 430 -336 ct 374 -317 l  364 -343 348 -362 325 -375 ct 303 -389 276 -396 246 -396 ct 
+p ef
+680 -389 m  680 0 l  621 0 l  621 -389 l  471 -389 l  471 -437 l  830 -437 l 
+830 -389 l  680 -389 l  p ef
+1241 -121 m  1241 -81 1226 -49 1194 -27 ct 1163 -5 1118 6 1061 6 ct 954 6 893 -31 876 -105 ct 
+933 -117 l  940 -90 954 -71 975 -59 ct 997 -46 1026 -40 1063 -40 ct 1101 -40 1131 -47 1152 -60 ct 
+1172 -73 1183 -92 1183 -118 ct 1183 -132 1180 -144 1173 -153 ct 1167 -161 1157 -169 1146 -175 ct 
+1134 -180 1120 -185 1103 -189 ct 1087 -193 1069 -197 1049 -202 ct 1015 -210 988 -217 971 -225 ct 
+953 -233 939 -241 928 -250 ct 918 -260 910 -271 905 -284 ct 899 -296 896 -311 896 -327 ct 
+896 -364 911 -393 939 -414 ct 968 -434 1009 -444 1062 -444 ct 1112 -444 1150 -436 1176 -421 ct 
+1202 -406 1221 -380 1231 -343 ct 1173 -333 l  1166 -356 1154 -373 1136 -384 ct 
+1118 -394 1093 -399 1062 -399 ct 1027 -399 1000 -394 982 -382 ct 963 -370 954 -353 954 -330 ct 
+954 -317 958 -306 965 -297 ct 972 -288 982 -281 996 -274 ct 1009 -268 1036 -261 1076 -252 ct 
+1089 -249 1103 -246 1116 -242 ct 1129 -239 1142 -235 1154 -231 ct 1166 -226 1178 -221 1189 -215 ct 
+1199 -209 1208 -202 1216 -193 ct 1224 -185 1230 -174 1235 -163 ct 1239 -151 1241 -137 1241 -121 ct 
+p ef
+pom
+gs
+0.507 0.832 0.105 c 20699 8439 m  19099 8439 l  19099 7169 l  22299 7169 l 
+22299 8439 l  20699 8439 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 20698 8439 m  19098 8439 l  19098 7169 l 
+22298 7169 l  22298 8439 l  20698 8439 l  pc
+gr
+pum
+20029 8024 t
+428 -223 m  428 -178 419 -139 402 -105 ct 384 -71 359 -45 327 -27 ct 295 -9 258 0 215 0 ct 
+52 0 l  52 -437 l  197 -437 l  271 -437 328 -419 368 -382 ct 408 -345 428 -292 428 -223 ct 
+p
+369 -223 m  369 -277 354 -319 324 -347 ct 295 -376 252 -390 195 -390 ct 111 -390 l 
+111 -47 l  209 -47 l  241 -47 269 -55 293 -69 ct 317 -83 336 -103 349 -129 ct 
+362 -156 369 -187 369 -223 ct p ef
+585 6 m  552 6 526 -3 509 -21 ct 492 -38 484 -63 484 -94 ct 484 -129 495 -155 518 -174 ct 
+541 -192 578 -202 629 -204 ct 704 -205 l  704 -223 l  704 -250 698 -270 687 -282 ct 
+675 -294 657 -299 632 -299 ct 607 -299 589 -295 578 -287 ct 566 -278 559 -265 557 -246 ct 
+499 -251 l  508 -312 553 -342 633 -342 ct 676 -342 707 -332 729 -313 ct 750 -294 761 -266 761 -229 ct 
+761 -85 l  761 -68 763 -55 767 -47 ct 771 -39 780 -35 792 -35 ct 797 -35 803 -35 810 -37 ct 
+810 -2 l  796 1 782 3 767 3 ct 746 3 731 -2 722 -13 ct 713 -24 707 -41 706 -64 ct 
+704 -64 l  690 -39 673 -21 654 -10 ct 635 1 612 6 585 6 ct p
+598 -36 m  619 -36 637 -41 653 -50 ct 669 -59 681 -72 690 -88 ct 700 -104 704 -121 704 -138 ct 
+704 -166 l  643 -165 l  617 -164 597 -161 583 -157 ct 570 -152 559 -144 552 -134 ct 
+545 -123 541 -110 541 -93 ct 541 -75 546 -61 556 -51 ct 566 -41 580 -36 598 -36 ct 
+p ef
+981 -2 m  962 3 944 5 924 5 ct 880 5 857 -20 857 -71 ct 857 -295 l  819 -295 l 
+819 -336 l  860 -336 l  876 -411 l  913 -411 l  913 -336 l  975 -336 l 
+975 -295 l  913 -295 l  913 -83 l  913 -67 916 -56 921 -49 ct 926 -43 936 -39 949 -39 ct 
+956 -39 967 -41 981 -44 ct 981 -2 l  p ef
+1114 6 m  1081 6 1055 -3 1038 -21 ct 1021 -38 1013 -63 1013 -94 ct 1013 -129 1024 -155 1047 -174 ct 
+1070 -192 1107 -202 1158 -204 ct 1233 -205 l  1233 -223 l  1233 -250 1227 -270 1216 -282 ct 
+1204 -294 1186 -299 1161 -299 ct 1136 -299 1118 -295 1107 -287 ct 1095 -278 1088 -265 1086 -246 ct 
+1028 -251 l  1037 -312 1082 -342 1162 -342 ct 1205 -342 1236 -332 1258 -313 ct 
+1279 -294 1290 -266 1290 -229 ct 1290 -85 l  1290 -68 1292 -55 1296 -47 ct 1300 -39 1309 -35 1321 -35 ct 
+1326 -35 1332 -35 1339 -37 ct 1339 -2 l  1325 1 1311 3 1296 3 ct 1275 3 1260 -2 1251 -13 ct 
+1242 -24 1236 -41 1235 -64 ct 1233 -64 l  1219 -39 1202 -21 1183 -10 ct 1164 1 1141 6 1114 6 ct 
+p
+1127 -36 m  1148 -36 1166 -41 1182 -50 ct 1198 -59 1210 -72 1219 -88 ct 1229 -104 1233 -121 1233 -138 ct 
+1233 -166 l  1172 -165 l  1146 -164 1126 -161 1112 -157 ct 1099 -152 1088 -144 1081 -134 ct 
+1074 -123 1070 -110 1070 -93 ct 1070 -75 1075 -61 1085 -51 ct 1095 -41 1109 -36 1127 -36 ct 
+p ef
+pom
+gs
+1.000 0.714 0.425 c 23849 6969 m  22799 6969 l  22799 5699 l  24899 5699 l 
+24899 6969 l  23849 6969 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 23848 6969 m  22798 6969 l  22798 5699 l 
+24898 5699 l  24898 6969 l  23848 6969 l  pc
+gr
+pum
+23319 6554 t
+362 0 m  312 -128 l  113 -128 l  63 0 l  1 0 l  180 -438 l  247 -438 l 
+422 0 l  362 0 l  p
+212 -393 m  210 -384 l  204 -367 197 -345 187 -318 ct 131 -174 l  294 -174 l 
+238 -319 l  232 -333 227 -349 221 -367 ct 212 -393 l  p ef
+508 -170 m  508 -125 515 -92 529 -70 ct 543 -49 565 -38 593 -38 ct 613 -38 629 -43 643 -54 ct 
+656 -65 664 -81 667 -104 ct 724 -100 l  719 -68 706 -42 683 -23 ct 659 -4 630 6 594 6 ct 
+548 6 512 -9 487 -39 ct 462 -68 450 -111 450 -168 ct 450 -225 462 -268 487 -297 ct 
+512 -327 548 -342 594 -342 ct 628 -342 657 -333 679 -315 ct 702 -298 716 -273 722 -242 ct 
+665 -237 l  662 -256 654 -271 643 -282 ct 631 -293 614 -298 592 -298 ct 563 -298 541 -288 528 -269 ct 
+515 -249 508 -216 508 -170 ct p ef
+994 1 m  881 -153 l  840 -119 l  840 1 l  784 1 l  784 -460 l  840 -460 l 
+840 -172 l  987 -335 l  1052 -335 l  916 -191 l  1059 1 l  994 1 l  p ef
+pom
+gs
+1 lj 11746 8693 m  23672 8693 l  ps
+gr
+gs
+11459 8693 m  11759 8793 l  11759 8593 l  11459 8693 l  p ef
+gr
+gs
+23959 8693 m  23659 8593 l  23659 8793 l  23959 8693 l  p ef
+gr
+gs
+0 lw 1 lj 11459 9393 m  11459 8493 l  ps
+gr
+gs
+0 lw 1 lj 23958 9393 m  23958 8493 l  ps
+gr
+pum
+15069 9340 t
+424 0 m  424 -292 l  424 -324 424 -355 426 -385 ct 416 -348 407 -319 399 -298 ct 
+286 0 l  245 0 l  130 -298 l  113 -351 l  103 -385 l  104 -351 l  105 -292 l 
+105 0 l  52 0 l  52 -438 l  130 -438 l  246 -134 l  250 -122 254 -109 258 -95 ct 
+262 -81 264 -71 266 -65 ct 267 -73 271 -85 276 -102 ct 281 -119 285 -130 287 -134 ct 
+401 -438 l  477 -438 l  477 0 l  424 0 l  p ef
+615 -156 m  615 -118 623 -88 638 -67 ct 654 -46 678 -36 708 -36 ct 732 -36 752 -41 766 -50 ct 
+781 -60 791 -72 796 -87 ct 845 -73 l  825 -20 779 6 708 6 ct 659 6 621 -9 595 -38 ct 
+569 -68 556 -112 556 -170 ct 556 -226 569 -268 595 -298 ct 621 -327 658 -342 706 -342 ct 
+805 -342 854 -283 854 -164 ct 854 -156 l  615 -156 l  p
+796 -199 m  793 -234 784 -260 769 -277 ct 754 -293 733 -301 705 -301 ct 678 -301 657 -292 641 -274 ct 
+625 -256 616 -231 615 -199 ct 796 -199 l  p ef
+1136 -53 m  1125 -32 1112 -16 1094 -7 ct 1077 2 1056 7 1031 7 ct 989 7 958 -7 938 -36 ct 
+918 -64 908 -108 908 -166 ct 908 -283 949 -341 1031 -341 ct 1056 -341 1078 -337 1095 -327 ct 
+1112 -318 1125 -303 1136 -283 ct 1136 -283 l  1136 -321 l  1136 -460 l  1191 -460 l 
+1191 -68 l  1191 -33 1192 -10 1193 1 ct 1140 1 l  1139 -3 1139 -10 1138 -22 ct 
+1137 -34 1137 -45 1137 -53 ct 1136 -53 l  p
+966 -167 m  966 -120 972 -87 985 -67 ct 997 -46 1017 -36 1045 -36 ct 1077 -36 1100 -47 1114 -69 ct 
+1128 -91 1136 -125 1136 -171 ct 1136 -216 1128 -248 1114 -269 ct 1100 -290 1077 -300 1046 -300 ct 
+1018 -300 998 -290 985 -269 ct 973 -248 966 -214 966 -167 ct p ef
+1274 -407 m  1274 -460 l  1330 -460 l  1330 -407 l  1274 -407 l  p
+1274 1 m  1274 -335 l  1330 -335 l  1330 1 l  1274 1 l  p ef
+1469 -336 m  1469 -123 l  1469 -101 1472 -84 1476 -72 ct 1480 -59 1487 -51 1497 -45 ct 
+1506 -40 1520 -37 1539 -37 ct 1565 -37 1587 -46 1602 -65 ct 1618 -83 1625 -109 1625 -141 ct 
+1625 -336 l  1681 -336 l  1681 -72 l  1681 -33 1682 -9 1683 0 ct 1630 0 l 
+1630 -1 1630 -4 1630 -9 ct 1629 -13 1629 -18 1629 -24 ct 1629 -30 1628 -41 1628 -58 ct 
+1627 -58 l  1614 -34 1599 -18 1582 -8 ct 1566 1 1545 6 1520 6 ct 1483 6 1456 -3 1439 -21 ct 
+1422 -40 1413 -70 1413 -112 ct 1413 -336 l  1469 -336 l  p ef
+1961 0 m  1961 -213 l  1961 -245 1957 -268 1948 -280 ct 1939 -293 1923 -299 1900 -299 ct 
+1876 -299 1857 -290 1843 -272 ct 1829 -253 1823 -228 1823 -195 ct 1823 0 l  1767 0 l 
+1767 -264 l  1767 -303 1766 -327 1765 -336 ct 1818 -336 l  1818 -335 1818 -332 1819 -327 ct 
+1819 -323 1819 -318 1819 -312 ct 1820 -306 1820 -295 1820 -278 ct 1821 -278 l 
+1833 -302 1847 -319 1863 -328 ct 1878 -337 1897 -342 1919 -342 ct 1945 -342 1965 -337 1980 -327 ct 
+1994 -317 2005 -301 2010 -278 ct 2011 -278 l  2023 -301 2037 -317 2053 -327 ct 
+2070 -337 2090 -342 2113 -342 ct 2147 -342 2172 -333 2187 -314 ct 2202 -296 2210 -266 2210 -224 ct 
+2210 0 l  2155 0 l  2155 -213 l  2155 -245 2150 -268 2142 -280 ct 2133 -293 2117 -299 2094 -299 ct 
+2069 -299 2050 -290 2037 -272 ct 2023 -254 2016 -228 2016 -195 ct 2016 0 l  1961 0 l 
+p ef
+2646 -121 m  2646 -81 2631 -49 2599 -27 ct 2568 -5 2523 6 2466 6 ct 2359 6 2298 -31 2281 -105 ct 
+2338 -117 l  2345 -90 2359 -71 2380 -59 ct 2402 -46 2431 -40 2468 -40 ct 2506 -40 2536 -47 2557 -60 ct 
+2577 -73 2588 -92 2588 -118 ct 2588 -132 2585 -144 2578 -153 ct 2572 -161 2562 -169 2551 -175 ct 
+2539 -180 2525 -185 2508 -189 ct 2492 -193 2474 -197 2454 -202 ct 2420 -210 2393 -217 2376 -225 ct 
+2358 -233 2344 -241 2333 -250 ct 2323 -260 2315 -271 2310 -284 ct 2304 -296 2301 -311 2301 -327 ct 
+2301 -364 2316 -393 2344 -414 ct 2373 -434 2414 -444 2467 -444 ct 2517 -444 2555 -436 2581 -421 ct 
+2607 -406 2626 -380 2636 -343 ct 2578 -333 l  2571 -356 2559 -373 2541 -384 ct 
+2523 -394 2498 -399 2467 -399 ct 2432 -399 2405 -394 2387 -382 ct 2368 -370 2359 -353 2359 -330 ct 
+2359 -317 2363 -306 2370 -297 ct 2377 -288 2387 -281 2401 -274 ct 2414 -268 2441 -261 2481 -252 ct 
+2494 -249 2508 -246 2521 -242 ct 2534 -239 2547 -235 2559 -231 ct 2571 -226 2583 -221 2594 -215 ct 
+2604 -209 2613 -202 2621 -193 ct 2629 -185 2635 -174 2640 -163 ct 2644 -151 2646 -137 2646 -121 ct 
+p ef
+2734 132 m  2719 132 2706 131 2696 129 ct 2696 87 l  2704 88 2712 89 2722 89 ct 
+2757 89 2784 63 2804 12 ct 2810 -1 l  2677 -336 l  2736 -336 l  2807 -150 l 
+2808 -147 2809 -144 2811 -140 ct 2812 -135 2817 -122 2824 -99 ct 2832 -76 2837 -63 2837 -61 ct 
+2859 -122 l  2932 -336 l  2991 -336 l  2862 0 l  2848 36 2836 63 2824 80 ct 
+2812 98 2798 111 2784 119 ct 2769 128 2753 132 2734 132 ct p ef
+3249 0 m  3249 -213 l  3249 -235 3247 -252 3242 -264 ct 3238 -277 3231 -285 3222 -291 ct 
+3212 -296 3198 -299 3180 -299 ct 3153 -299 3132 -290 3116 -271 ct 3101 -253 3093 -227 3093 -195 ct 
+3093 0 l  3037 0 l  3037 -264 l  3037 -303 3036 -327 3035 -336 ct 3088 -336 l 
+3088 -335 3088 -332 3089 -327 ct 3089 -323 3089 -318 3089 -312 ct 3090 -306 3090 -295 3090 -278 ct 
+3091 -278 l  3104 -302 3119 -318 3136 -328 ct 3153 -337 3174 -342 3199 -342 ct 
+3235 -342 3262 -333 3279 -315 ct 3296 -296 3305 -266 3305 -224 ct 3305 0 l  3249 0 l 
+p ef
+3429 -170 m  3429 -125 3436 -92 3450 -70 ct 3464 -49 3486 -38 3514 -38 ct 3534 -38 3550 -43 3564 -54 ct 
+3577 -65 3585 -81 3588 -104 ct 3645 -100 l  3640 -68 3627 -42 3604 -23 ct 3580 -4 3551 6 3515 6 ct 
+3469 6 3433 -9 3408 -39 ct 3383 -68 3371 -111 3371 -168 ct 3371 -225 3383 -268 3408 -297 ct 
+3433 -327 3469 -342 3515 -342 ct 3549 -342 3578 -333 3600 -315 ct 3623 -298 3637 -273 3643 -242 ct 
+3586 -237 l  3583 -256 3575 -271 3564 -282 ct 3552 -293 3535 -298 3513 -298 ct 
+3484 -298 3462 -288 3449 -269 ct 3436 -249 3429 -216 3429 -170 ct p ef
+4090 -223 m  4090 -178 4081 -139 4064 -105 ct 4046 -71 4021 -45 3989 -27 ct 
+3957 -9 3920 0 3877 0 ct 3714 0 l  3714 -437 l  3859 -437 l  3933 -437 3990 -419 4030 -382 ct 
+4070 -345 4090 -292 4090 -223 ct p
+4031 -223 m  4031 -277 4016 -319 3986 -347 ct 3957 -376 3914 -390 3857 -390 ct 
+3773 -390 l  3773 -47 l  3871 -47 l  3903 -47 3931 -55 3955 -69 ct 3979 -83 3998 -103 4011 -129 ct 
+4024 -156 4031 -187 4031 -223 ct p ef
+4205 -156 m  4205 -118 4213 -88 4228 -67 ct 4244 -46 4268 -36 4298 -36 ct 4322 -36 4342 -41 4356 -50 ct 
+4371 -60 4381 -72 4386 -87 ct 4435 -73 l  4415 -20 4369 6 4298 6 ct 4249 6 4211 -9 4185 -38 ct 
+4159 -68 4146 -112 4146 -170 ct 4146 -226 4159 -268 4185 -298 ct 4211 -327 4248 -342 4296 -342 ct 
+4395 -342 4444 -283 4444 -164 ct 4444 -156 l  4205 -156 l  p
+4386 -199 m  4383 -234 4374 -260 4359 -277 ct 4344 -293 4323 -301 4295 -301 ct 
+4268 -301 4247 -292 4231 -274 ct 4215 -256 4206 -231 4205 -199 ct 4386 -199 l 
+p ef
+4513 1 m  4513 -460 l  4569 -460 l  4569 1 l  4513 1 l  p ef
+4738 6 m  4705 6 4679 -3 4662 -21 ct 4645 -38 4637 -63 4637 -94 ct 4637 -129 4648 -155 4671 -174 ct 
+4694 -192 4731 -202 4782 -204 ct 4857 -205 l  4857 -223 l  4857 -250 4851 -270 4840 -282 ct 
+4828 -294 4810 -299 4785 -299 ct 4760 -299 4742 -295 4731 -287 ct 4719 -278 4712 -265 4710 -246 ct 
+4652 -251 l  4661 -312 4706 -342 4786 -342 ct 4829 -342 4860 -332 4882 -313 ct 
+4903 -294 4914 -266 4914 -229 ct 4914 -85 l  4914 -68 4916 -55 4920 -47 ct 4924 -39 4933 -35 4945 -35 ct 
+4950 -35 4956 -35 4963 -37 ct 4963 -2 l  4949 1 4935 3 4920 3 ct 4899 3 4884 -2 4875 -13 ct 
+4866 -24 4860 -41 4859 -64 ct 4857 -64 l  4843 -39 4826 -21 4807 -10 ct 4788 1 4765 6 4738 6 ct 
+p
+4751 -36 m  4772 -36 4790 -41 4806 -50 ct 4822 -59 4834 -72 4843 -88 ct 4853 -104 4857 -121 4857 -138 ct 
+4857 -166 l  4796 -165 l  4770 -164 4750 -161 4736 -157 ct 4723 -152 4712 -144 4705 -134 ct 
+4698 -123 4694 -110 4694 -93 ct 4694 -75 4699 -61 4709 -51 ct 4719 -41 4733 -36 4751 -36 ct 
+p ef
+5020 132 m  5005 132 4992 131 4982 129 ct 4982 87 l  4990 88 4998 89 5008 89 ct 
+5043 89 5070 63 5090 12 ct 5096 -1 l  4963 -336 l  5022 -336 l  5093 -150 l 
+5094 -147 5095 -144 5097 -140 ct 5098 -135 5103 -122 5110 -99 ct 5118 -76 5123 -63 5123 -61 ct 
+5145 -122 l  5218 -336 l  5277 -336 l  5148 0 l  5134 36 5122 63 5110 80 ct 
+5098 98 5084 111 5070 119 ct 5055 128 5039 132 5020 132 ct p ef
+pom
+gs
+1.000 0.843 0.843 c 26180 8121 m  24921 8121 l  24921 7169 l  27438 7169 l 
+27438 8121 l  26180 8121 l  p ef
+gr
+gs
+1 lj 0.167 0.378 0.601 c 24921 7417 m  25169 7169 l  ps
+24921 7717 m  25469 7169 l  ps
+24921 8017 m  25769 7169 l  ps
+25117 8121 m  26069 7169 l  ps
+25417 8121 m  26369 7169 l  ps
+25717 8121 m  26669 7169 l  ps
+26017 8121 m  26969 7169 l  ps
+26317 8121 m  27269 7169 l  ps
+26617 8121 m  27438 7300 l  ps
+26917 8121 m  27438 7600 l  ps
+27217 8121 m  27438 7900 l  ps
+gr
+gs
+1 lj 25254 8438 m  27200 8438 l  ps
+gr
+gs
+24967 8438 m  25267 8538 l  25267 8338 l  24967 8438 l  p ef
+gr
+gs
+27487 8438 m  27187 8338 l  27187 8538 l  27487 8438 l  p ef
+gr
+gs
+0 lw 1 lj 24966 9138 m  24966 8238 l  ps
+gr
+gs
+0 lw 1 lj 27486 9138 m  27486 8238 l  ps
+gr
+pum
+25149 9325 t
+85 -170 m  85 -125 92 -92 106 -70 ct 120 -49 142 -38 170 -38 ct 190 -38 206 -43 220 -54 ct 
+233 -65 241 -81 244 -104 ct 301 -100 l  296 -68 283 -42 260 -23 ct 236 -4 207 6 171 6 ct 
+125 6 89 -9 64 -39 ct 39 -68 27 -111 27 -168 ct 27 -225 39 -268 64 -297 ct 89 -327 125 -342 171 -342 ct 
+205 -342 234 -333 256 -315 ct 279 -298 293 -273 299 -242 ct 242 -237 l  239 -256 231 -271 220 -282 ct 
+208 -293 191 -298 169 -298 ct 140 -298 118 -288 105 -269 ct 92 -249 85 -216 85 -170 ct 
+p ef
+416 -278 m  428 -299 443 -316 460 -326 ct 476 -336 498 -341 524 -341 ct 560 -341 587 -332 604 -314 ct 
+621 -296 630 -266 630 -223 ct 630 1 l  574 1 l  574 -212 l  574 -236 572 -253 567 -265 ct 
+563 -276 556 -285 546 -290 ct 536 -295 522 -298 505 -298 ct 478 -298 457 -289 442 -271 ct 
+426 -253 418 -228 418 -197 ct 418 1 l  362 1 l  362 -460 l  418 -460 l  418 -340 l 
+418 -327 417 -314 417 -301 ct 416 -287 416 -280 415 -278 ct 416 -278 l  p ef
+797 6 m  764 6 738 -3 721 -21 ct 704 -38 696 -63 696 -94 ct 696 -129 707 -155 730 -174 ct 
+753 -192 790 -202 841 -204 ct 916 -205 l  916 -223 l  916 -250 910 -270 899 -282 ct 
+887 -294 869 -299 844 -299 ct 819 -299 801 -295 790 -287 ct 778 -278 771 -265 769 -246 ct 
+711 -251 l  720 -312 765 -342 845 -342 ct 888 -342 919 -332 941 -313 ct 962 -294 973 -266 973 -229 ct 
+973 -85 l  973 -68 975 -55 979 -47 ct 983 -39 992 -35 1004 -35 ct 1009 -35 1015 -35 1022 -37 ct 
+1022 -2 l  1008 1 994 3 979 3 ct 958 3 943 -2 934 -13 ct 925 -24 919 -41 918 -64 ct 
+916 -64 l  902 -39 885 -21 866 -10 ct 847 1 824 6 797 6 ct p
+810 -36 m  831 -36 849 -41 865 -50 ct 881 -59 893 -72 902 -88 ct 912 -104 916 -121 916 -138 ct 
+916 -166 l  855 -165 l  829 -164 809 -161 795 -157 ct 782 -152 771 -144 764 -134 ct 
+757 -123 753 -110 753 -93 ct 753 -75 758 -61 768 -51 ct 778 -41 792 -36 810 -36 ct 
+p ef
+1276 0 m  1276 -213 l  1276 -235 1274 -252 1269 -264 ct 1265 -277 1258 -285 1249 -291 ct 
+1239 -296 1225 -299 1207 -299 ct 1180 -299 1159 -290 1143 -271 ct 1128 -253 1120 -227 1120 -195 ct 
+1120 0 l  1064 0 l  1064 -264 l  1064 -303 1063 -327 1062 -336 ct 1115 -336 l 
+1115 -335 1115 -332 1116 -327 ct 1116 -323 1116 -318 1116 -312 ct 1117 -306 1117 -295 1117 -278 ct 
+1118 -278 l  1131 -302 1146 -318 1163 -328 ct 1180 -337 1201 -342 1226 -342 ct 
+1262 -342 1289 -333 1306 -315 ct 1323 -296 1332 -266 1332 -224 ct 1332 0 l  1276 0 l 
+p ef
+1628 0 m  1628 -213 l  1628 -235 1626 -252 1621 -264 ct 1617 -277 1610 -285 1601 -291 ct 
+1591 -296 1577 -299 1559 -299 ct 1532 -299 1511 -290 1495 -271 ct 1480 -253 1472 -227 1472 -195 ct 
+1472 0 l  1416 0 l  1416 -264 l  1416 -303 1415 -327 1414 -336 ct 1467 -336 l 
+1467 -335 1467 -332 1468 -327 ct 1468 -323 1468 -318 1468 -312 ct 1469 -306 1469 -295 1469 -278 ct 
+1470 -278 l  1483 -302 1498 -318 1515 -328 ct 1532 -337 1553 -342 1578 -342 ct 
+1614 -342 1641 -333 1658 -315 ct 1675 -296 1684 -266 1684 -224 ct 1684 0 l  1628 0 l 
+p ef
+1809 -156 m  1809 -118 1817 -88 1832 -67 ct 1848 -46 1872 -36 1902 -36 ct 1926 -36 1946 -41 1960 -50 ct 
+1975 -60 1985 -72 1990 -87 ct 2039 -73 l  2019 -20 1973 6 1902 6 ct 1853 6 1815 -9 1789 -38 ct 
+1763 -68 1750 -112 1750 -170 ct 1750 -226 1763 -268 1789 -298 ct 1815 -327 1852 -342 1900 -342 ct 
+1999 -342 2048 -283 2048 -164 ct 2048 -156 l  1809 -156 l  p
+1990 -199 m  1987 -234 1978 -260 1963 -277 ct 1948 -293 1927 -301 1899 -301 ct 
+1872 -301 1851 -292 1835 -274 ct 1819 -256 1810 -231 1809 -199 ct 1990 -199 l 
+p ef
+2117 1 m  2117 -460 l  2173 -460 l  2173 1 l  2117 1 l  p ef
+pom
+pum
+25376 10036 t
+295 -93 m  295 -61 283 -37 259 -20 ct 235 -3 201 6 158 6 ct 117 6 85 -1 62 -15 ct 
+39 -28 25 -50 18 -79 ct 67 -89 l  72 -71 82 -57 96 -49 ct 111 -41 132 -37 158 -37 ct 
+187 -37 207 -41 221 -50 ct 234 -58 240 -71 240 -89 ct 240 -102 236 -113 227 -121 ct 
+218 -129 203 -136 183 -141 ct 143 -152 l  111 -160 88 -168 74 -176 ct 61 -184 50 -194 42 -205 ct 
+35 -217 31 -231 31 -247 ct 31 -278 42 -301 64 -317 ct 86 -333 117 -341 159 -341 ct 
+196 -341 225 -335 247 -322 ct 269 -309 283 -288 289 -259 ct 238 -253 l  235 -268 227 -279 213 -287 ct 
+200 -295 182 -299 159 -299 ct 134 -299 115 -295 103 -288 ct 91 -280 85 -268 85 -253 ct 
+85 -243 88 -235 93 -229 ct 98 -223 105 -218 115 -213 ct 124 -209 145 -203 176 -195 ct 
+206 -188 227 -181 240 -175 ct 253 -168 263 -161 271 -154 ct 278 -146 284 -137 288 -127 ct 
+293 -117 295 -106 295 -93 ct p ef
+682 0 m  617 0 l  559 -238 l  547 -290 l  546 -281 543 -267 539 -250 ct 535 -232 514 -149 476 0 ct 
+411 0 l  317 -336 l  372 -336 l  429 -108 l  430 -103 435 -82 442 -46 ct 
+448 -69 l  518 -336 l  578 -336 l  636 -105 l  650 -46 l  660 -89 l  724 -336 l 
+778 -336 l  682 0 l  p ef
+817 -407 m  817 -460 l  873 -460 l  873 -407 l  817 -407 l  p
+817 1 m  817 -335 l  873 -335 l  873 1 l  817 1 l  p ef
+1086 -2 m  1067 3 1049 5 1029 5 ct 985 5 962 -20 962 -71 ct 962 -295 l  924 -295 l 
+924 -336 l  965 -336 l  981 -411 l  1018 -411 l  1018 -336 l  1080 -336 l 
+1080 -295 l  1018 -295 l  1018 -83 l  1018 -67 1021 -56 1026 -49 ct 1031 -43 1041 -39 1054 -39 ct 
+1061 -39 1072 -41 1086 -44 ct 1086 -2 l  p ef
+1177 -170 m  1177 -125 1184 -92 1198 -70 ct 1212 -49 1234 -38 1262 -38 ct 1282 -38 1298 -43 1312 -54 ct 
+1325 -65 1333 -81 1336 -104 ct 1393 -100 l  1388 -68 1375 -42 1352 -23 ct 1328 -4 1299 6 1263 6 ct 
+1217 6 1181 -9 1156 -39 ct 1131 -68 1119 -111 1119 -168 ct 1119 -225 1131 -268 1156 -297 ct 
+1181 -327 1217 -342 1263 -342 ct 1297 -342 1326 -333 1348 -315 ct 1371 -298 1385 -273 1391 -242 ct 
+1334 -237 l  1331 -256 1323 -271 1312 -282 ct 1300 -293 1283 -298 1261 -298 ct 
+1232 -298 1210 -288 1197 -269 ct 1184 -249 1177 -216 1177 -170 ct p ef
+1508 -278 m  1520 -299 1535 -316 1552 -326 ct 1568 -336 1590 -341 1616 -341 ct 
+1652 -341 1679 -332 1696 -314 ct 1713 -296 1722 -266 1722 -223 ct 1722 1 l  1666 1 l 
+1666 -212 l  1666 -236 1664 -253 1659 -265 ct 1655 -276 1648 -285 1638 -290 ct 
+1628 -295 1614 -298 1597 -298 ct 1570 -298 1549 -289 1534 -271 ct 1518 -253 1510 -228 1510 -197 ct 
+1510 1 l  1454 1 l  1454 -460 l  1510 -460 l  1510 -340 l  1510 -327 1509 -314 1509 -301 ct 
+1508 -287 1508 -280 1507 -278 ct 1508 -278 l  p ef
+pom
+gs
+1 lj 25186 5769 m  28422 5769 l  ps
+gr
+gs
+24899 5769 m  25199 5869 l  25199 5669 l  24899 5769 l  p ef
+gr
+gs
+28708 5769 m  28409 5669 l  28409 5869 l  28708 5769 l  p ef
+gr
+gs
+0 lw 1 lj 24898 6469 m  24898 5569 l  ps
+gr
+gs
+0 lw 1 lj 28708 6469 m  28708 5569 l  ps
+gr
+pum
+25532 4877 t
+172 -2 m  153 3 135 5 115 5 ct 71 5 48 -20 48 -71 ct 48 -295 l  10 -295 l  10 -336 l 
+51 -336 l  67 -411 l  104 -411 l  104 -336 l  166 -336 l  166 -295 l  104 -295 l 
+104 -83 l  104 -67 107 -56 112 -49 ct 117 -43 127 -39 140 -39 ct 147 -39 158 -41 172 -44 ct 
+172 -2 l  p ef
+222 0 m  222 -258 l  222 -281 221 -307 220 -336 ct 273 -336 l  275 -298 275 -275 275 -267 ct 
+277 -267 l  285 -296 296 -316 307 -326 ct 319 -337 335 -342 356 -342 ct 364 -342 371 -341 379 -339 ct 
+379 -288 l  371 -290 362 -291 349 -291 ct 326 -291 308 -281 296 -261 ct 284 -241 278 -212 278 -175 ct 
+278 0 l  222 0 l  p ef
+517 6 m  484 6 458 -3 441 -21 ct 424 -38 416 -63 416 -94 ct 416 -129 427 -155 450 -174 ct 
+473 -192 510 -202 561 -204 ct 636 -205 l  636 -223 l  636 -250 630 -270 619 -282 ct 
+607 -294 589 -299 564 -299 ct 539 -299 521 -295 510 -287 ct 498 -278 491 -265 489 -246 ct 
+431 -251 l  440 -312 485 -342 565 -342 ct 608 -342 639 -332 661 -313 ct 682 -294 693 -266 693 -229 ct 
+693 -85 l  693 -68 695 -55 699 -47 ct 703 -39 712 -35 724 -35 ct 729 -35 735 -35 742 -37 ct 
+742 -2 l  728 1 714 3 699 3 ct 678 3 663 -2 654 -13 ct 645 -24 639 -41 638 -64 ct 
+636 -64 l  622 -39 605 -21 586 -10 ct 567 1 544 6 517 6 ct p
+530 -36 m  551 -36 569 -41 585 -50 ct 601 -59 613 -72 622 -88 ct 632 -104 636 -121 636 -138 ct 
+636 -166 l  575 -165 l  549 -164 529 -161 515 -157 ct 502 -152 491 -144 484 -134 ct 
+477 -123 473 -110 473 -93 ct 473 -75 478 -61 488 -51 ct 498 -41 512 -36 530 -36 ct 
+p ef
+997 0 m  997 -213 l  997 -235 995 -252 990 -264 ct 986 -277 979 -285 970 -291 ct 
+960 -296 946 -299 928 -299 ct 901 -299 880 -290 864 -271 ct 849 -253 841 -227 841 -195 ct 
+841 0 l  785 0 l  785 -264 l  785 -303 784 -327 783 -336 ct 836 -336 l  836 -335 836 -332 837 -327 ct 
+837 -323 837 -318 837 -312 ct 838 -306 838 -295 838 -278 ct 839 -278 l  852 -302 867 -318 884 -328 ct 
+901 -337 922 -342 947 -342 ct 983 -342 1010 -333 1027 -315 ct 1044 -296 1053 -266 1053 -224 ct 
+1053 0 l  997 0 l  p ef
+1387 -93 m  1387 -61 1375 -37 1351 -20 ct 1327 -3 1293 6 1250 6 ct 1209 6 1177 -1 1154 -15 ct 
+1131 -28 1117 -50 1110 -79 ct 1159 -89 l  1164 -71 1174 -57 1188 -49 ct 1203 -41 1224 -37 1250 -37 ct 
+1279 -37 1299 -41 1313 -50 ct 1326 -58 1332 -71 1332 -89 ct 1332 -102 1328 -113 1319 -121 ct 
+1310 -129 1295 -136 1275 -141 ct 1235 -152 l  1203 -160 1180 -168 1166 -176 ct 
+1153 -184 1142 -194 1134 -205 ct 1127 -217 1123 -231 1123 -247 ct 1123 -278 1134 -301 1156 -317 ct 
+1178 -333 1209 -341 1251 -341 ct 1288 -341 1317 -335 1339 -322 ct 1361 -309 1375 -288 1381 -259 ct 
+1330 -253 l  1327 -268 1319 -279 1305 -287 ct 1292 -295 1274 -299 1251 -299 ct 
+1226 -299 1207 -295 1195 -288 ct 1183 -280 1177 -268 1177 -253 ct 1177 -243 1180 -235 1185 -229 ct 
+1190 -223 1197 -218 1207 -213 ct 1216 -209 1237 -203 1268 -195 ct 1298 -188 1319 -181 1332 -175 ct 
+1345 -168 1355 -161 1363 -154 ct 1370 -146 1376 -137 1380 -127 ct 1385 -117 1387 -106 1387 -93 ct 
+p ef
+1452 -407 m  1452 -460 l  1508 -460 l  1508 -407 l  1452 -407 l  p
+1452 1 m  1452 -335 l  1508 -335 l  1508 1 l  1452 1 l  p ef
+1721 -2 m  1702 3 1684 5 1664 5 ct 1620 5 1597 -20 1597 -71 ct 1597 -295 l  1559 -295 l 
+1559 -336 l  1600 -336 l  1616 -411 l  1653 -411 l  1653 -336 l  1715 -336 l 
+1715 -295 l  1653 -295 l  1653 -83 l  1653 -67 1656 -56 1661 -49 ct 1666 -43 1676 -39 1689 -39 ct 
+1696 -39 1707 -41 1721 -44 ct 1721 -2 l  p ef
+1769 -407 m  1769 -460 l  1825 -460 l  1825 -407 l  1769 -407 l  p
+1769 1 m  1769 -335 l  1825 -335 l  1825 1 l  1769 1 l  p ef
+2193 -168 m  2193 -110 2181 -66 2155 -37 ct 2129 -8 2091 6 2042 6 ct 1993 6 1956 -9 1931 -39 ct 
+1906 -69 1894 -112 1894 -168 ct 1894 -284 1944 -342 2044 -342 ct 2095 -342 2133 -328 2157 -300 ct 
+2181 -272 2193 -228 2193 -168 ct p
+2135 -168 m  2135 -215 2128 -248 2114 -269 ct 2101 -291 2077 -301 2045 -301 ct 
+2012 -301 1989 -290 1974 -269 ct 1960 -247 1952 -214 1952 -168 ct 1952 -124 1959 -91 1974 -69 ct 
+1988 -46 2011 -35 2042 -35 ct 2075 -35 2099 -46 2113 -68 ct 2128 -89 2135 -123 2135 -168 ct 
+p ef
+2474 0 m  2474 -213 l  2474 -235 2472 -252 2467 -264 ct 2463 -277 2456 -285 2447 -291 ct 
+2437 -296 2423 -299 2405 -299 ct 2378 -299 2357 -290 2341 -271 ct 2326 -253 2318 -227 2318 -195 ct 
+2318 0 l  2262 0 l  2262 -264 l  2262 -303 2261 -327 2260 -336 ct 2313 -336 l 
+2313 -335 2313 -332 2314 -327 ct 2314 -323 2314 -318 2314 -312 ct 2315 -306 2315 -295 2315 -278 ct 
+2316 -278 l  2329 -302 2344 -318 2361 -328 ct 2378 -337 2399 -342 2424 -342 ct 
+2460 -342 2487 -333 2504 -315 ct 2521 -296 2530 -266 2530 -224 ct 2530 0 l  2474 0 l 
+p ef
+pom
+pum
+25973 5587 t
+255 -53 m  244 -32 231 -16 213 -7 ct 196 2 175 7 150 7 ct 108 7 77 -7 57 -36 ct 
+37 -64 27 -108 27 -166 ct 27 -283 68 -341 150 -341 ct 175 -341 197 -337 214 -327 ct 
+231 -318 244 -303 255 -283 ct 255 -283 l  255 -321 l  255 -460 l  310 -460 l 
+310 -68 l  310 -33 311 -10 312 1 ct 259 1 l  258 -3 258 -10 257 -22 ct 256 -34 256 -45 256 -53 ct 
+255 -53 l  p
+85 -167 m  85 -120 91 -87 104 -67 ct 116 -46 136 -36 164 -36 ct 196 -36 219 -47 233 -69 ct 
+247 -91 255 -125 255 -171 ct 255 -216 247 -248 233 -269 ct 219 -290 196 -300 165 -300 ct 
+137 -300 117 -290 104 -269 ct 92 -248 85 -214 85 -167 ct p ef
+437 -156 m  437 -118 445 -88 460 -67 ct 476 -46 500 -36 530 -36 ct 554 -36 574 -41 588 -50 ct 
+603 -60 613 -72 618 -87 ct 667 -73 l  647 -20 601 6 530 6 ct 481 6 443 -9 417 -38 ct 
+391 -68 378 -112 378 -170 ct 378 -226 391 -268 417 -298 ct 443 -327 480 -342 528 -342 ct 
+627 -342 676 -283 676 -164 ct 676 -156 l  437 -156 l  p
+618 -199 m  615 -234 606 -260 591 -277 ct 576 -293 555 -301 527 -301 ct 500 -301 479 -292 463 -274 ct 
+447 -256 438 -231 437 -199 ct 618 -199 l  p ef
+746 1 m  746 -460 l  802 -460 l  802 1 l  746 1 l  p ef
+970 6 m  937 6 911 -3 894 -21 ct 877 -38 869 -63 869 -94 ct 869 -129 880 -155 903 -174 ct 
+926 -192 963 -202 1014 -204 ct 1089 -205 l  1089 -223 l  1089 -250 1083 -270 1072 -282 ct 
+1060 -294 1042 -299 1017 -299 ct 992 -299 974 -295 963 -287 ct 951 -278 944 -265 942 -246 ct 
+884 -251 l  893 -312 938 -342 1018 -342 ct 1061 -342 1092 -332 1114 -313 ct 
+1135 -294 1146 -266 1146 -229 ct 1146 -85 l  1146 -68 1148 -55 1152 -47 ct 1156 -39 1165 -35 1177 -35 ct 
+1182 -35 1188 -35 1195 -37 ct 1195 -2 l  1181 1 1167 3 1152 3 ct 1131 3 1116 -2 1107 -13 ct 
+1098 -24 1092 -41 1091 -64 ct 1089 -64 l  1075 -39 1058 -21 1039 -10 ct 1020 1 997 6 970 6 ct 
+p
+983 -36 m  1004 -36 1022 -41 1038 -50 ct 1054 -59 1066 -72 1075 -88 ct 1085 -104 1089 -121 1089 -138 ct 
+1089 -166 l  1028 -165 l  1002 -164 982 -161 968 -157 ct 955 -152 944 -144 937 -134 ct 
+930 -123 926 -110 926 -93 ct 926 -75 931 -61 941 -51 ct 951 -41 965 -36 983 -36 ct 
+p ef
+1253 132 m  1238 132 1225 131 1215 129 ct 1215 87 l  1223 88 1231 89 1241 89 ct 
+1276 89 1303 63 1323 12 ct 1329 -1 l  1196 -336 l  1255 -336 l  1326 -150 l 
+1327 -147 1328 -144 1330 -140 ct 1331 -135 1336 -122 1343 -99 ct 1351 -76 1356 -63 1356 -61 ct 
+1378 -122 l  1451 -336 l  1510 -336 l  1381 0 l  1367 36 1355 63 1343 80 ct 
+1331 98 1317 111 1303 119 ct 1288 128 1272 132 1253 132 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 24898 6528 m  25407 6019 l  ps
+25018 6969 m  25968 6019 l  ps
+25579 6969 m  26529 6019 l  ps
+26140 6969 m  27090 6019 l  ps
+26701 6969 m  27651 6019 l  ps
+27262 6969 m  28212 6019 l  ps
+27823 6969 m  28684 6108 l  ps
+28384 6969 m  28684 6669 l  ps
+28264 6019 m  28684 6439 l  ps
+27703 6019 m  28653 6969 l  ps
+27142 6019 m  28092 6969 l  ps
+26581 6019 m  27531 6969 l  ps
+26020 6019 m  26970 6969 l  ps
+25459 6019 m  26409 6969 l  ps
+24898 6019 m  25848 6969 l  ps
+24898 6580 m  25287 6969 l  ps
+gr
+gs
+0 lw 1 lj 26791 6969 m  24897 6969 l  24897 6019 l  28684 6019 l  28684 6969 l 
+26791 6969 l  pc
+gr
+pum
+25205 6714 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+gs
+1 lj 25186 13349 m  28422 13349 l  ps
+gr
+gs
+24899 13349 m  25199 13449 l  25199 13249 l  24899 13349 l  p ef
+gr
+gs
+28708 13349 m  28409 13249 l  28409 13449 l  28708 13349 l  p ef
+gr
+gs
+0 lw 1 lj 24898 14048 m  24898 13148 l  ps
+gr
+gs
+0 lw 1 lj 28708 14048 m  28708 13148 l  ps
+gr
+pum
+25532 12456 t
+172 -2 m  153 3 135 5 115 5 ct 71 5 48 -20 48 -71 ct 48 -295 l  10 -295 l  10 -336 l 
+51 -336 l  67 -411 l  104 -411 l  104 -336 l  166 -336 l  166 -295 l  104 -295 l 
+104 -83 l  104 -67 107 -56 112 -49 ct 117 -43 127 -39 140 -39 ct 147 -39 158 -41 172 -44 ct 
+172 -2 l  p ef
+222 0 m  222 -258 l  222 -281 221 -307 220 -336 ct 273 -336 l  275 -298 275 -275 275 -267 ct 
+277 -267 l  285 -296 296 -316 307 -326 ct 319 -337 335 -342 356 -342 ct 364 -342 371 -341 379 -339 ct 
+379 -288 l  371 -290 362 -291 349 -291 ct 326 -291 308 -281 296 -261 ct 284 -241 278 -212 278 -175 ct 
+278 0 l  222 0 l  p ef
+517 6 m  484 6 458 -3 441 -21 ct 424 -38 416 -63 416 -94 ct 416 -129 427 -155 450 -174 ct 
+473 -192 510 -202 561 -204 ct 636 -205 l  636 -223 l  636 -250 630 -270 619 -282 ct 
+607 -294 589 -299 564 -299 ct 539 -299 521 -295 510 -287 ct 498 -278 491 -265 489 -246 ct 
+431 -251 l  440 -312 485 -342 565 -342 ct 608 -342 639 -332 661 -313 ct 682 -294 693 -266 693 -229 ct 
+693 -85 l  693 -68 695 -55 699 -47 ct 703 -39 712 -35 724 -35 ct 729 -35 735 -35 742 -37 ct 
+742 -2 l  728 1 714 3 699 3 ct 678 3 663 -2 654 -13 ct 645 -24 639 -41 638 -64 ct 
+636 -64 l  622 -39 605 -21 586 -10 ct 567 1 544 6 517 6 ct p
+530 -36 m  551 -36 569 -41 585 -50 ct 601 -59 613 -72 622 -88 ct 632 -104 636 -121 636 -138 ct 
+636 -166 l  575 -165 l  549 -164 529 -161 515 -157 ct 502 -152 491 -144 484 -134 ct 
+477 -123 473 -110 473 -93 ct 473 -75 478 -61 488 -51 ct 498 -41 512 -36 530 -36 ct 
+p ef
+997 0 m  997 -213 l  997 -235 995 -252 990 -264 ct 986 -277 979 -285 970 -291 ct 
+960 -296 946 -299 928 -299 ct 901 -299 880 -290 864 -271 ct 849 -253 841 -227 841 -195 ct 
+841 0 l  785 0 l  785 -264 l  785 -303 784 -327 783 -336 ct 836 -336 l  836 -335 836 -332 837 -327 ct 
+837 -323 837 -318 837 -312 ct 838 -306 838 -295 838 -278 ct 839 -278 l  852 -302 867 -318 884 -328 ct 
+901 -337 922 -342 947 -342 ct 983 -342 1010 -333 1027 -315 ct 1044 -296 1053 -266 1053 -224 ct 
+1053 0 l  997 0 l  p ef
+1387 -93 m  1387 -61 1375 -37 1351 -20 ct 1327 -3 1293 6 1250 6 ct 1209 6 1177 -1 1154 -15 ct 
+1131 -28 1117 -50 1110 -79 ct 1159 -89 l  1164 -71 1174 -57 1188 -49 ct 1203 -41 1224 -37 1250 -37 ct 
+1279 -37 1299 -41 1313 -50 ct 1326 -58 1332 -71 1332 -89 ct 1332 -102 1328 -113 1319 -121 ct 
+1310 -129 1295 -136 1275 -141 ct 1235 -152 l  1203 -160 1180 -168 1166 -176 ct 
+1153 -184 1142 -194 1134 -205 ct 1127 -217 1123 -231 1123 -247 ct 1123 -278 1134 -301 1156 -317 ct 
+1178 -333 1209 -341 1251 -341 ct 1288 -341 1317 -335 1339 -322 ct 1361 -309 1375 -288 1381 -259 ct 
+1330 -253 l  1327 -268 1319 -279 1305 -287 ct 1292 -295 1274 -299 1251 -299 ct 
+1226 -299 1207 -295 1195 -288 ct 1183 -280 1177 -268 1177 -253 ct 1177 -243 1180 -235 1185 -229 ct 
+1190 -223 1197 -218 1207 -213 ct 1216 -209 1237 -203 1268 -195 ct 1298 -188 1319 -181 1332 -175 ct 
+1345 -168 1355 -161 1363 -154 ct 1370 -146 1376 -137 1380 -127 ct 1385 -117 1387 -106 1387 -93 ct 
+p ef
+1452 -407 m  1452 -460 l  1508 -460 l  1508 -407 l  1452 -407 l  p
+1452 1 m  1452 -335 l  1508 -335 l  1508 1 l  1452 1 l  p ef
+1721 -2 m  1702 3 1684 5 1664 5 ct 1620 5 1597 -20 1597 -71 ct 1597 -295 l  1559 -295 l 
+1559 -336 l  1600 -336 l  1616 -411 l  1653 -411 l  1653 -336 l  1715 -336 l 
+1715 -295 l  1653 -295 l  1653 -83 l  1653 -67 1656 -56 1661 -49 ct 1666 -43 1676 -39 1689 -39 ct 
+1696 -39 1707 -41 1721 -44 ct 1721 -2 l  p ef
+1769 -407 m  1769 -460 l  1825 -460 l  1825 -407 l  1769 -407 l  p
+1769 1 m  1769 -335 l  1825 -335 l  1825 1 l  1769 1 l  p ef
+2193 -168 m  2193 -110 2181 -66 2155 -37 ct 2129 -8 2091 6 2042 6 ct 1993 6 1956 -9 1931 -39 ct 
+1906 -69 1894 -112 1894 -168 ct 1894 -284 1944 -342 2044 -342 ct 2095 -342 2133 -328 2157 -300 ct 
+2181 -272 2193 -228 2193 -168 ct p
+2135 -168 m  2135 -215 2128 -248 2114 -269 ct 2101 -291 2077 -301 2045 -301 ct 
+2012 -301 1989 -290 1974 -269 ct 1960 -247 1952 -214 1952 -168 ct 1952 -124 1959 -91 1974 -69 ct 
+1988 -46 2011 -35 2042 -35 ct 2075 -35 2099 -46 2113 -68 ct 2128 -89 2135 -123 2135 -168 ct 
+p ef
+2474 0 m  2474 -213 l  2474 -235 2472 -252 2467 -264 ct 2463 -277 2456 -285 2447 -291 ct 
+2437 -296 2423 -299 2405 -299 ct 2378 -299 2357 -290 2341 -271 ct 2326 -253 2318 -227 2318 -195 ct 
+2318 0 l  2262 0 l  2262 -264 l  2262 -303 2261 -327 2260 -336 ct 2313 -336 l 
+2313 -335 2313 -332 2314 -327 ct 2314 -323 2314 -318 2314 -312 ct 2315 -306 2315 -295 2315 -278 ct 
+2316 -278 l  2329 -302 2344 -318 2361 -328 ct 2378 -337 2399 -342 2424 -342 ct 
+2460 -342 2487 -333 2504 -315 ct 2521 -296 2530 -266 2530 -224 ct 2530 0 l  2474 0 l 
+p ef
+pom
+pum
+25973 13167 t
+255 -53 m  244 -32 231 -16 213 -7 ct 196 2 175 7 150 7 ct 108 7 77 -7 57 -36 ct 
+37 -64 27 -108 27 -166 ct 27 -283 68 -341 150 -341 ct 175 -341 197 -337 214 -327 ct 
+231 -318 244 -303 255 -283 ct 255 -283 l  255 -321 l  255 -460 l  310 -460 l 
+310 -68 l  310 -33 311 -10 312 1 ct 259 1 l  258 -3 258 -10 257 -22 ct 256 -34 256 -45 256 -53 ct 
+255 -53 l  p
+85 -167 m  85 -120 91 -87 104 -67 ct 116 -46 136 -36 164 -36 ct 196 -36 219 -47 233 -69 ct 
+247 -91 255 -125 255 -171 ct 255 -216 247 -248 233 -269 ct 219 -290 196 -300 165 -300 ct 
+137 -300 117 -290 104 -269 ct 92 -248 85 -214 85 -167 ct p ef
+437 -156 m  437 -118 445 -88 460 -67 ct 476 -46 500 -36 530 -36 ct 554 -36 574 -41 588 -50 ct 
+603 -60 613 -72 618 -87 ct 667 -73 l  647 -20 601 6 530 6 ct 481 6 443 -9 417 -38 ct 
+391 -68 378 -112 378 -170 ct 378 -226 391 -268 417 -298 ct 443 -327 480 -342 528 -342 ct 
+627 -342 676 -283 676 -164 ct 676 -156 l  437 -156 l  p
+618 -199 m  615 -234 606 -260 591 -277 ct 576 -293 555 -301 527 -301 ct 500 -301 479 -292 463 -274 ct 
+447 -256 438 -231 437 -199 ct 618 -199 l  p ef
+746 1 m  746 -460 l  802 -460 l  802 1 l  746 1 l  p ef
+970 6 m  937 6 911 -3 894 -21 ct 877 -38 869 -63 869 -94 ct 869 -129 880 -155 903 -174 ct 
+926 -192 963 -202 1014 -204 ct 1089 -205 l  1089 -223 l  1089 -250 1083 -270 1072 -282 ct 
+1060 -294 1042 -299 1017 -299 ct 992 -299 974 -295 963 -287 ct 951 -278 944 -265 942 -246 ct 
+884 -251 l  893 -312 938 -342 1018 -342 ct 1061 -342 1092 -332 1114 -313 ct 
+1135 -294 1146 -266 1146 -229 ct 1146 -85 l  1146 -68 1148 -55 1152 -47 ct 1156 -39 1165 -35 1177 -35 ct 
+1182 -35 1188 -35 1195 -37 ct 1195 -2 l  1181 1 1167 3 1152 3 ct 1131 3 1116 -2 1107 -13 ct 
+1098 -24 1092 -41 1091 -64 ct 1089 -64 l  1075 -39 1058 -21 1039 -10 ct 1020 1 997 6 970 6 ct 
+p
+983 -36 m  1004 -36 1022 -41 1038 -50 ct 1054 -59 1066 -72 1075 -88 ct 1085 -104 1089 -121 1089 -138 ct 
+1089 -166 l  1028 -165 l  1002 -164 982 -161 968 -157 ct 955 -152 944 -144 937 -134 ct 
+930 -123 926 -110 926 -93 ct 926 -75 931 -61 941 -51 ct 951 -41 965 -36 983 -36 ct 
+p ef
+1253 132 m  1238 132 1225 131 1215 129 ct 1215 87 l  1223 88 1231 89 1241 89 ct 
+1276 89 1303 63 1323 12 ct 1329 -1 l  1196 -336 l  1255 -336 l  1326 -150 l 
+1327 -147 1328 -144 1330 -140 ct 1331 -135 1336 -122 1343 -99 ct 1351 -76 1356 -63 1356 -61 ct 
+1378 -122 l  1451 -336 l  1510 -336 l  1381 0 l  1367 36 1355 63 1343 80 ct 
+1331 98 1317 111 1303 119 ct 1288 128 1272 132 1253 132 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 13699 15278 m  14208 14769 l  ps
+13820 15718 m  14769 14769 l  ps
+14381 15718 m  15330 14769 l  ps
+14942 15718 m  15891 14769 l  ps
+15503 15718 m  16452 14769 l  ps
+16064 15718 m  17013 14769 l  ps
+16625 15718 m  17574 14769 l  ps
+17186 15718 m  18135 14769 l  ps
+17747 15718 m  18696 14769 l  ps
+18308 15718 m  19257 14769 l  ps
+18869 15718 m  19818 14769 l  ps
+19430 15718 m  20379 14769 l  ps
+19991 15718 m  20940 14769 l  ps
+20552 15718 m  21501 14769 l  ps
+21113 15718 m  22062 14769 l  ps
+21674 15718 m  22623 14769 l  ps
+22235 15718 m  23184 14769 l  ps
+22796 15718 m  23745 14769 l  ps
+23357 15718 m  24306 14769 l  ps
+23918 15718 m  24867 14769 l  ps
+24479 15718 m  24899 15298 l  ps
+24358 14769 m  24899 15310 l  ps
+23797 14769 m  24746 15718 l  ps
+23236 14769 m  24185 15718 l  ps
+22675 14769 m  23624 15718 l  ps
+22114 14769 m  23063 15718 l  ps
+21553 14769 m  22502 15718 l  ps
+20992 14769 m  21941 15718 l  ps
+20431 14769 m  21380 15718 l  ps
+19870 14769 m  20819 15718 l  ps
+19309 14769 m  20258 15718 l  ps
+18748 14769 m  19697 15718 l  ps
+18187 14769 m  19136 15718 l  ps
+17626 14769 m  18575 15718 l  ps
+17065 14769 m  18014 15718 l  ps
+16504 14769 m  17453 15718 l  ps
+15943 14769 m  16892 15718 l  ps
+15382 14769 m  16331 15718 l  ps
+14821 14769 m  15770 15718 l  ps
+14260 14769 m  15209 15718 l  ps
+13699 14769 m  14648 15718 l  ps
+13699 15330 m  14087 15718 l  ps
+gr
+gs
+0 lw 1 lj 19298 15718 m  13699 15718 l  13699 14768 l  24898 14768 l  24898 15718 l 
+19298 15718 l  pc
+gr
+pum
+17713 15463 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+gs
+1 lj 0.699 0.699 0.699 c 5720 7678 m  6229 7169 l  ps
+5840 8119 m  6790 7169 l  ps
+6401 8119 m  7351 7169 l  ps
+6962 8119 m  7912 7169 l  ps
+7523 8119 m  8473 7169 l  ps
+8084 8119 m  9034 7169 l  ps
+8645 8119 m  9595 7169 l  ps
+9206 8119 m  10156 7169 l  ps
+9767 8119 m  10717 7169 l  ps
+10328 8119 m  11278 7169 l  ps
+10889 8119 m  11519 7489 l  ps
+11450 8119 m  11519 8050 l  ps
+11330 7169 m  11519 7358 l  ps
+10769 7169 m  11519 7919 l  ps
+10208 7169 m  11158 8119 l  ps
+9647 7169 m  10597 8119 l  ps
+9086 7169 m  10036 8119 l  ps
+8525 7169 m  9475 8119 l  ps
+7964 7169 m  8914 8119 l  ps
+7403 7169 m  8353 8119 l  ps
+6842 7169 m  7792 8119 l  ps
+6281 7169 m  7231 8119 l  ps
+5720 7169 m  6670 8119 l  ps
+5720 7730 m  6109 8119 l  ps
+gr
+gs
+0 lw 1 lj 8619 8119 m  5719 8119 l  5719 7169 l  11519 7169 l  11519 8119 l 
+8619 8119 l  pc
+gr
+pum
+7034 7864 t
+223 -389 m  223 0 l  164 0 l  164 -389 l  14 -389 l  14 -437 l  373 -437 l 
+373 -389 l  223 -389 l  p ef
+734 0 m  603 -191 l  469 0 l  403 0 l  569 -227 l  416 -438 l  481 -438 l 
+603 -266 l  721 -438 l  787 -438 l  637 -229 l  799 0 l  734 0 l  p ef
+1317 -169 m  1317 -52 1276 7 1194 7 ct 1169 7 1148 2 1131 -7 ct 1114 -16 1100 -31 1090 -51 ct 
+1089 -51 l  1089 -45 1089 -35 1088 -22 ct 1087 -9 1086 -1 1086 1 ct 1032 1 l 
+1033 -10 1034 -33 1034 -68 ct 1034 -460 l  1090 -460 l  1090 -328 l  1090 -315 1089 -299 1088 -281 ct 
+1090 -281 l  1100 -302 1114 -318 1131 -327 ct 1148 -336 1169 -341 1194 -341 ct 
+1236 -341 1268 -327 1288 -298 ct 1308 -270 1317 -227 1317 -169 ct p
+1259 -167 m  1259 -214 1253 -247 1240 -268 ct 1228 -288 1208 -298 1180 -298 ct 
+1148 -298 1126 -287 1111 -266 ct 1097 -244 1090 -210 1090 -163 ct 1090 -119 1097 -87 1111 -66 ct 
+1125 -45 1148 -34 1179 -34 ct 1207 -34 1228 -45 1240 -65 ct 1253 -86 1259 -120 1259 -167 ct 
+p ef
+1385 1 m  1385 -460 l  1441 -460 l  1441 1 l  1385 1 l  p ef
+1808 -168 m  1808 -110 1796 -66 1770 -37 ct 1744 -8 1706 6 1657 6 ct 1608 6 1571 -9 1546 -39 ct 
+1521 -69 1509 -112 1509 -168 ct 1509 -284 1559 -342 1659 -342 ct 1710 -342 1748 -328 1772 -300 ct 
+1796 -272 1808 -228 1808 -168 ct p
+1750 -168 m  1750 -215 1743 -248 1729 -269 ct 1716 -291 1692 -301 1660 -301 ct 
+1627 -301 1604 -290 1589 -269 ct 1575 -247 1567 -214 1567 -168 ct 1567 -124 1574 -91 1589 -69 ct 
+1603 -46 1626 -35 1657 -35 ct 1690 -35 1714 -46 1728 -68 ct 1743 -89 1750 -123 1750 -168 ct 
+p ef
+1918 -170 m  1918 -125 1925 -92 1939 -70 ct 1953 -49 1975 -38 2003 -38 ct 2023 -38 2039 -43 2053 -54 ct 
+2066 -65 2074 -81 2077 -104 ct 2134 -100 l  2129 -68 2116 -42 2093 -23 ct 2069 -4 2040 6 2004 6 ct 
+1958 6 1922 -9 1897 -39 ct 1872 -68 1860 -111 1860 -168 ct 1860 -225 1872 -268 1897 -297 ct 
+1922 -327 1958 -342 2004 -342 ct 2038 -342 2067 -333 2089 -315 ct 2112 -298 2126 -273 2132 -242 ct 
+2075 -237 l  2072 -256 2064 -271 2053 -282 ct 2041 -293 2024 -298 2002 -298 ct 
+1973 -298 1951 -288 1938 -269 ct 1925 -249 1918 -216 1918 -170 ct p ef
+2404 1 m  2291 -153 l  2250 -119 l  2250 1 l  2194 1 l  2194 -460 l  2250 -460 l 
+2250 -172 l  2397 -335 l  2462 -335 l  2326 -191 l  2469 1 l  2404 1 l 
+p ef
+2554 -156 m  2554 -118 2562 -88 2577 -67 ct 2593 -46 2617 -36 2647 -36 ct 2671 -36 2691 -41 2705 -50 ct 
+2720 -60 2730 -72 2735 -87 ct 2784 -73 l  2764 -20 2718 6 2647 6 ct 2598 6 2560 -9 2534 -38 ct 
+2508 -68 2495 -112 2495 -170 ct 2495 -226 2508 -268 2534 -298 ct 2560 -327 2597 -342 2645 -342 ct 
+2744 -342 2793 -283 2793 -164 ct 2793 -156 l  2554 -156 l  p
+2735 -199 m  2732 -234 2723 -260 2708 -277 ct 2693 -293 2672 -301 2644 -301 ct 
+2617 -301 2596 -292 2580 -274 ct 2564 -256 2555 -231 2554 -199 ct 2735 -199 l 
+p ef
+3074 -53 m  3063 -32 3050 -16 3032 -7 ct 3015 2 2994 7 2969 7 ct 2927 7 2896 -7 2876 -36 ct 
+2856 -64 2846 -108 2846 -166 ct 2846 -283 2887 -341 2969 -341 ct 2994 -341 3016 -337 3033 -327 ct 
+3050 -318 3063 -303 3074 -283 ct 3074 -283 l  3074 -321 l  3074 -460 l  3129 -460 l 
+3129 -68 l  3129 -33 3130 -10 3131 1 ct 3078 1 l  3077 -3 3077 -10 3076 -22 ct 
+3075 -34 3075 -45 3075 -53 ct 3074 -53 l  p
+2904 -167 m  2904 -120 2910 -87 2923 -67 ct 2935 -46 2955 -36 2983 -36 ct 3015 -36 3038 -47 3052 -69 ct 
+3066 -91 3074 -125 3074 -171 ct 3074 -216 3066 -248 3052 -269 ct 3038 -290 3015 -300 2984 -300 ct 
+2956 -300 2936 -290 2923 -269 ct 2911 -248 2904 -214 2904 -167 ct p ef
+pom
+gs
+0.507 0.832 0.105 c 30399 16038 m  29299 16038 l  29299 14769 l  31499 14769 l 
+31499 16038 l  30399 16038 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 30399 16038 m  29299 16038 l  29299 14768 l 
+31499 14768 l  31499 16038 l  30399 16038 l  pc
+gr
+pum
+29767 15623 t
+361 -1 m  247 -182 l  111 -182 l  111 -1 l  52 -1 l  52 -437 l  258 -437 l 
+307 -437 345 -426 372 -404 ct 398 -382 412 -352 412 -313 ct 412 -280 402 -253 383 -231 ct 
+364 -209 338 -195 305 -189 ct 429 -1 l  361 -1 l  p
+352 -312 m  352 -337 344 -357 326 -370 ct 309 -383 284 -390 252 -390 ct 111 -390 l 
+111 -229 l  254 -229 l  285 -229 310 -236 327 -251 ct 344 -265 352 -286 352 -312 ct 
+p ef
+672 -389 m  672 0 l  613 0 l  613 -389 l  463 -389 l  463 -437 l  822 -437 l 
+822 -389 l  672 -389 l  p ef
+1232 -121 m  1232 -81 1217 -49 1185 -27 ct 1154 -5 1109 6 1052 6 ct 945 6 884 -31 867 -105 ct 
+924 -117 l  931 -90 945 -71 966 -59 ct 988 -46 1017 -40 1054 -40 ct 1092 -40 1122 -47 1143 -60 ct 
+1163 -73 1174 -92 1174 -118 ct 1174 -132 1171 -144 1164 -153 ct 1158 -161 1148 -169 1137 -175 ct 
+1125 -180 1111 -185 1094 -189 ct 1078 -193 1060 -197 1040 -202 ct 1006 -210 979 -217 962 -225 ct 
+944 -233 930 -241 919 -250 ct 909 -260 901 -271 896 -284 ct 890 -296 887 -311 887 -327 ct 
+887 -364 902 -393 930 -414 ct 959 -434 1000 -444 1053 -444 ct 1103 -444 1141 -436 1167 -421 ct 
+1193 -406 1212 -380 1222 -343 ct 1164 -333 l  1157 -356 1145 -373 1127 -384 ct 
+1109 -394 1084 -399 1053 -399 ct 1018 -399 991 -394 973 -382 ct 954 -370 945 -353 945 -330 ct 
+945 -317 949 -306 956 -297 ct 963 -288 973 -281 987 -274 ct 1000 -268 1027 -261 1067 -252 ct 
+1080 -249 1094 -246 1107 -242 ct 1120 -239 1133 -235 1145 -231 ct 1157 -226 1169 -221 1180 -215 ct 
+1190 -209 1199 -202 1207 -193 ct 1215 -185 1221 -174 1226 -163 ct 1230 -151 1232 -137 1232 -121 ct 
+p ef
+pom
+gs
+1 lj 25126 16288 m  37051 16288 l  ps
+gr
+gs
+24839 16288 m  25139 16388 l  25139 16188 l  24839 16288 l  p ef
+gr
+gs
+37338 16288 m  37038 16188 l  37038 16388 l  37338 16288 l  p ef
+gr
+gs
+0 lw 1 lj 24838 16988 m  24838 16088 l  ps
+gr
+gs
+0 lw 1 lj 37338 16988 m  37338 16088 l  ps
+gr
+pum
+28449 16935 t
+424 0 m  424 -292 l  424 -324 424 -355 426 -385 ct 416 -348 407 -319 399 -298 ct 
+286 0 l  245 0 l  130 -298 l  113 -351 l  103 -385 l  104 -351 l  105 -292 l 
+105 0 l  52 0 l  52 -438 l  130 -438 l  246 -134 l  250 -122 254 -109 258 -95 ct 
+262 -81 264 -71 266 -65 ct 267 -73 271 -85 276 -102 ct 281 -119 285 -130 287 -134 ct 
+401 -438 l  477 -438 l  477 0 l  424 0 l  p ef
+615 -156 m  615 -118 623 -88 638 -67 ct 654 -46 678 -36 708 -36 ct 732 -36 752 -41 766 -50 ct 
+781 -60 791 -72 796 -87 ct 845 -73 l  825 -20 779 6 708 6 ct 659 6 621 -9 595 -38 ct 
+569 -68 556 -112 556 -170 ct 556 -226 569 -268 595 -298 ct 621 -327 658 -342 706 -342 ct 
+805 -342 854 -283 854 -164 ct 854 -156 l  615 -156 l  p
+796 -199 m  793 -234 784 -260 769 -277 ct 754 -293 733 -301 705 -301 ct 678 -301 657 -292 641 -274 ct 
+625 -256 616 -231 615 -199 ct 796 -199 l  p ef
+1136 -53 m  1125 -32 1112 -16 1094 -7 ct 1077 2 1056 7 1031 7 ct 989 7 958 -7 938 -36 ct 
+918 -64 908 -108 908 -166 ct 908 -283 949 -341 1031 -341 ct 1056 -341 1078 -337 1095 -327 ct 
+1112 -318 1125 -303 1136 -283 ct 1136 -283 l  1136 -321 l  1136 -460 l  1191 -460 l 
+1191 -68 l  1191 -33 1192 -10 1193 1 ct 1140 1 l  1139 -3 1139 -10 1138 -22 ct 
+1137 -34 1137 -45 1137 -53 ct 1136 -53 l  p
+966 -167 m  966 -120 972 -87 985 -67 ct 997 -46 1017 -36 1045 -36 ct 1077 -36 1100 -47 1114 -69 ct 
+1128 -91 1136 -125 1136 -171 ct 1136 -216 1128 -248 1114 -269 ct 1100 -290 1077 -300 1046 -300 ct 
+1018 -300 998 -290 985 -269 ct 973 -248 966 -214 966 -167 ct p ef
+1274 -407 m  1274 -460 l  1330 -460 l  1330 -407 l  1274 -407 l  p
+1274 1 m  1274 -335 l  1330 -335 l  1330 1 l  1274 1 l  p ef
+1469 -336 m  1469 -123 l  1469 -101 1472 -84 1476 -72 ct 1480 -59 1487 -51 1497 -45 ct 
+1506 -40 1520 -37 1539 -37 ct 1565 -37 1587 -46 1602 -65 ct 1618 -83 1625 -109 1625 -141 ct 
+1625 -336 l  1681 -336 l  1681 -72 l  1681 -33 1682 -9 1683 0 ct 1630 0 l 
+1630 -1 1630 -4 1630 -9 ct 1629 -13 1629 -18 1629 -24 ct 1629 -30 1628 -41 1628 -58 ct 
+1627 -58 l  1614 -34 1599 -18 1582 -8 ct 1566 1 1545 6 1520 6 ct 1483 6 1456 -3 1439 -21 ct 
+1422 -40 1413 -70 1413 -112 ct 1413 -336 l  1469 -336 l  p ef
+1961 0 m  1961 -213 l  1961 -245 1957 -268 1948 -280 ct 1939 -293 1923 -299 1900 -299 ct 
+1876 -299 1857 -290 1843 -272 ct 1829 -253 1823 -228 1823 -195 ct 1823 0 l  1767 0 l 
+1767 -264 l  1767 -303 1766 -327 1765 -336 ct 1818 -336 l  1818 -335 1818 -332 1819 -327 ct 
+1819 -323 1819 -318 1819 -312 ct 1820 -306 1820 -295 1820 -278 ct 1821 -278 l 
+1833 -302 1847 -319 1863 -328 ct 1878 -337 1897 -342 1919 -342 ct 1945 -342 1965 -337 1980 -327 ct 
+1994 -317 2005 -301 2010 -278 ct 2011 -278 l  2023 -301 2037 -317 2053 -327 ct 
+2070 -337 2090 -342 2113 -342 ct 2147 -342 2172 -333 2187 -314 ct 2202 -296 2210 -266 2210 -224 ct 
+2210 0 l  2155 0 l  2155 -213 l  2155 -245 2150 -268 2142 -280 ct 2133 -293 2117 -299 2094 -299 ct 
+2069 -299 2050 -290 2037 -272 ct 2023 -254 2016 -228 2016 -195 ct 2016 0 l  1961 0 l 
+p ef
+2646 -121 m  2646 -81 2631 -49 2599 -27 ct 2568 -5 2523 6 2466 6 ct 2359 6 2298 -31 2281 -105 ct 
+2338 -117 l  2345 -90 2359 -71 2380 -59 ct 2402 -46 2431 -40 2468 -40 ct 2506 -40 2536 -47 2557 -60 ct 
+2577 -73 2588 -92 2588 -118 ct 2588 -132 2585 -144 2578 -153 ct 2572 -161 2562 -169 2551 -175 ct 
+2539 -180 2525 -185 2508 -189 ct 2492 -193 2474 -197 2454 -202 ct 2420 -210 2393 -217 2376 -225 ct 
+2358 -233 2344 -241 2333 -250 ct 2323 -260 2315 -271 2310 -284 ct 2304 -296 2301 -311 2301 -327 ct 
+2301 -364 2316 -393 2344 -414 ct 2373 -434 2414 -444 2467 -444 ct 2517 -444 2555 -436 2581 -421 ct 
+2607 -406 2626 -380 2636 -343 ct 2578 -333 l  2571 -356 2559 -373 2541 -384 ct 
+2523 -394 2498 -399 2467 -399 ct 2432 -399 2405 -394 2387 -382 ct 2368 -370 2359 -353 2359 -330 ct 
+2359 -317 2363 -306 2370 -297 ct 2377 -288 2387 -281 2401 -274 ct 2414 -268 2441 -261 2481 -252 ct 
+2494 -249 2508 -246 2521 -242 ct 2534 -239 2547 -235 2559 -231 ct 2571 -226 2583 -221 2594 -215 ct 
+2604 -209 2613 -202 2621 -193 ct 2629 -185 2635 -174 2640 -163 ct 2644 -151 2646 -137 2646 -121 ct 
+p ef
+2734 132 m  2719 132 2706 131 2696 129 ct 2696 87 l  2704 88 2712 89 2722 89 ct 
+2757 89 2784 63 2804 12 ct 2810 -1 l  2677 -336 l  2736 -336 l  2807 -150 l 
+2808 -147 2809 -144 2811 -140 ct 2812 -135 2817 -122 2824 -99 ct 2832 -76 2837 -63 2837 -61 ct 
+2859 -122 l  2932 -336 l  2991 -336 l  2862 0 l  2848 36 2836 63 2824 80 ct 
+2812 98 2798 111 2784 119 ct 2769 128 2753 132 2734 132 ct p ef
+3249 0 m  3249 -213 l  3249 -235 3247 -252 3242 -264 ct 3238 -277 3231 -285 3222 -291 ct 
+3212 -296 3198 -299 3180 -299 ct 3153 -299 3132 -290 3116 -271 ct 3101 -253 3093 -227 3093 -195 ct 
+3093 0 l  3037 0 l  3037 -264 l  3037 -303 3036 -327 3035 -336 ct 3088 -336 l 
+3088 -335 3088 -332 3089 -327 ct 3089 -323 3089 -318 3089 -312 ct 3090 -306 3090 -295 3090 -278 ct 
+3091 -278 l  3104 -302 3119 -318 3136 -328 ct 3153 -337 3174 -342 3199 -342 ct 
+3235 -342 3262 -333 3279 -315 ct 3296 -296 3305 -266 3305 -224 ct 3305 0 l  3249 0 l 
+p ef
+3429 -170 m  3429 -125 3436 -92 3450 -70 ct 3464 -49 3486 -38 3514 -38 ct 3534 -38 3550 -43 3564 -54 ct 
+3577 -65 3585 -81 3588 -104 ct 3645 -100 l  3640 -68 3627 -42 3604 -23 ct 3580 -4 3551 6 3515 6 ct 
+3469 6 3433 -9 3408 -39 ct 3383 -68 3371 -111 3371 -168 ct 3371 -225 3383 -268 3408 -297 ct 
+3433 -327 3469 -342 3515 -342 ct 3549 -342 3578 -333 3600 -315 ct 3623 -298 3637 -273 3643 -242 ct 
+3586 -237 l  3583 -256 3575 -271 3564 -282 ct 3552 -293 3535 -298 3513 -298 ct 
+3484 -298 3462 -288 3449 -269 ct 3436 -249 3429 -216 3429 -170 ct p ef
+4090 -223 m  4090 -178 4081 -139 4064 -105 ct 4046 -71 4021 -45 3989 -27 ct 
+3957 -9 3920 0 3877 0 ct 3714 0 l  3714 -437 l  3859 -437 l  3933 -437 3990 -419 4030 -382 ct 
+4070 -345 4090 -292 4090 -223 ct p
+4031 -223 m  4031 -277 4016 -319 3986 -347 ct 3957 -376 3914 -390 3857 -390 ct 
+3773 -390 l  3773 -47 l  3871 -47 l  3903 -47 3931 -55 3955 -69 ct 3979 -83 3998 -103 4011 -129 ct 
+4024 -156 4031 -187 4031 -223 ct p ef
+4205 -156 m  4205 -118 4213 -88 4228 -67 ct 4244 -46 4268 -36 4298 -36 ct 4322 -36 4342 -41 4356 -50 ct 
+4371 -60 4381 -72 4386 -87 ct 4435 -73 l  4415 -20 4369 6 4298 6 ct 4249 6 4211 -9 4185 -38 ct 
+4159 -68 4146 -112 4146 -170 ct 4146 -226 4159 -268 4185 -298 ct 4211 -327 4248 -342 4296 -342 ct 
+4395 -342 4444 -283 4444 -164 ct 4444 -156 l  4205 -156 l  p
+4386 -199 m  4383 -234 4374 -260 4359 -277 ct 4344 -293 4323 -301 4295 -301 ct 
+4268 -301 4247 -292 4231 -274 ct 4215 -256 4206 -231 4205 -199 ct 4386 -199 l 
+p ef
+4513 1 m  4513 -460 l  4569 -460 l  4569 1 l  4513 1 l  p ef
+4738 6 m  4705 6 4679 -3 4662 -21 ct 4645 -38 4637 -63 4637 -94 ct 4637 -129 4648 -155 4671 -174 ct 
+4694 -192 4731 -202 4782 -204 ct 4857 -205 l  4857 -223 l  4857 -250 4851 -270 4840 -282 ct 
+4828 -294 4810 -299 4785 -299 ct 4760 -299 4742 -295 4731 -287 ct 4719 -278 4712 -265 4710 -246 ct 
+4652 -251 l  4661 -312 4706 -342 4786 -342 ct 4829 -342 4860 -332 4882 -313 ct 
+4903 -294 4914 -266 4914 -229 ct 4914 -85 l  4914 -68 4916 -55 4920 -47 ct 4924 -39 4933 -35 4945 -35 ct 
+4950 -35 4956 -35 4963 -37 ct 4963 -2 l  4949 1 4935 3 4920 3 ct 4899 3 4884 -2 4875 -13 ct 
+4866 -24 4860 -41 4859 -64 ct 4857 -64 l  4843 -39 4826 -21 4807 -10 ct 4788 1 4765 6 4738 6 ct 
+p
+4751 -36 m  4772 -36 4790 -41 4806 -50 ct 4822 -59 4834 -72 4843 -88 ct 4853 -104 4857 -121 4857 -138 ct 
+4857 -166 l  4796 -165 l  4770 -164 4750 -161 4736 -157 ct 4723 -152 4712 -144 4705 -134 ct 
+4698 -123 4694 -110 4694 -93 ct 4694 -75 4699 -61 4709 -51 ct 4719 -41 4733 -36 4751 -36 ct 
+p ef
+5020 132 m  5005 132 4992 131 4982 129 ct 4982 87 l  4990 88 4998 89 5008 89 ct 
+5043 89 5070 63 5090 12 ct 5096 -1 l  4963 -336 l  5022 -336 l  5093 -150 l 
+5094 -147 5095 -144 5097 -140 ct 5098 -135 5103 -122 5110 -99 ct 5118 -76 5123 -63 5123 -61 ct 
+5145 -122 l  5218 -336 l  5277 -336 l  5148 0 l  5134 36 5122 63 5110 80 ct 
+5098 98 5084 111 5070 119 ct 5055 128 5039 132 5020 132 ct p ef
+pom
+gs
+0.507 0.832 0.105 c 33250 16038 m  32150 16038 l  32150 14769 l  34350 14769 l 
+34350 16038 l  33250 16038 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 33250 16038 m  32150 16038 l  32150 14768 l 
+34350 14768 l  34350 16038 l  33250 16038 l  pc
+gr
+pum
+32618 15623 t
+361 -1 m  247 -182 l  111 -182 l  111 -1 l  52 -1 l  52 -437 l  258 -437 l 
+307 -437 345 -426 372 -404 ct 398 -382 412 -352 412 -313 ct 412 -280 402 -253 383 -231 ct 
+364 -209 338 -195 305 -189 ct 429 -1 l  361 -1 l  p
+352 -312 m  352 -337 344 -357 326 -370 ct 309 -383 284 -390 252 -390 ct 111 -390 l 
+111 -229 l  254 -229 l  285 -229 310 -236 327 -251 ct 344 -265 352 -286 352 -312 ct 
+p ef
+672 -389 m  672 0 l  613 0 l  613 -389 l  463 -389 l  463 -437 l  822 -437 l 
+822 -389 l  672 -389 l  p ef
+1232 -121 m  1232 -81 1217 -49 1185 -27 ct 1154 -5 1109 6 1052 6 ct 945 6 884 -31 867 -105 ct 
+924 -117 l  931 -90 945 -71 966 -59 ct 988 -46 1017 -40 1054 -40 ct 1092 -40 1122 -47 1143 -60 ct 
+1163 -73 1174 -92 1174 -118 ct 1174 -132 1171 -144 1164 -153 ct 1158 -161 1148 -169 1137 -175 ct 
+1125 -180 1111 -185 1094 -189 ct 1078 -193 1060 -197 1040 -202 ct 1006 -210 979 -217 962 -225 ct 
+944 -233 930 -241 919 -250 ct 909 -260 901 -271 896 -284 ct 890 -296 887 -311 887 -327 ct 
+887 -364 902 -393 930 -414 ct 959 -434 1000 -444 1053 -444 ct 1103 -444 1141 -436 1167 -421 ct 
+1193 -406 1212 -380 1222 -343 ct 1164 -333 l  1157 -356 1145 -373 1127 -384 ct 
+1109 -394 1084 -399 1053 -399 ct 1018 -399 991 -394 973 -382 ct 954 -370 945 -353 945 -330 ct 
+945 -317 949 -306 956 -297 ct 963 -288 973 -281 987 -274 ct 1000 -268 1027 -261 1067 -252 ct 
+1080 -249 1094 -246 1107 -242 ct 1120 -239 1133 -235 1145 -231 ct 1157 -226 1169 -221 1180 -215 ct 
+1190 -209 1199 -202 1207 -193 ct 1215 -185 1221 -174 1226 -163 ct 1230 -151 1232 -137 1232 -121 ct 
+p ef
+pom
+gs
+0.507 0.832 0.105 c 36099 16038 m  34999 16038 l  34999 14769 l  37199 14769 l 
+37199 16038 l  36099 16038 l  p ef
+gr
+gs
+0 lw 1 lj 0.207 0.398 0.644 c 36099 16038 m  34999 16038 l  34999 14768 l 
+37199 14768 l  37199 16038 l  36099 16038 l  pc
+gr
+pum
+35467 15623 t
+361 -1 m  247 -182 l  111 -182 l  111 -1 l  52 -1 l  52 -437 l  258 -437 l 
+307 -437 345 -426 372 -404 ct 398 -382 412 -352 412 -313 ct 412 -280 402 -253 383 -231 ct 
+364 -209 338 -195 305 -189 ct 429 -1 l  361 -1 l  p
+352 -312 m  352 -337 344 -357 326 -370 ct 309 -383 284 -390 252 -390 ct 111 -390 l 
+111 -229 l  254 -229 l  285 -229 310 -236 327 -251 ct 344 -265 352 -286 352 -312 ct 
+p ef
+672 -389 m  672 0 l  613 0 l  613 -389 l  463 -389 l  463 -437 l  822 -437 l 
+822 -389 l  672 -389 l  p ef
+1232 -121 m  1232 -81 1217 -49 1185 -27 ct 1154 -5 1109 6 1052 6 ct 945 6 884 -31 867 -105 ct 
+924 -117 l  931 -90 945 -71 966 -59 ct 988 -46 1017 -40 1054 -40 ct 1092 -40 1122 -47 1143 -60 ct 
+1163 -73 1174 -92 1174 -118 ct 1174 -132 1171 -144 1164 -153 ct 1158 -161 1148 -169 1137 -175 ct 
+1125 -180 1111 -185 1094 -189 ct 1078 -193 1060 -197 1040 -202 ct 1006 -210 979 -217 962 -225 ct 
+944 -233 930 -241 919 -250 ct 909 -260 901 -271 896 -284 ct 890 -296 887 -311 887 -327 ct 
+887 -364 902 -393 930 -414 ct 959 -434 1000 -444 1053 -444 ct 1103 -444 1141 -436 1167 -421 ct 
+1193 -406 1212 -380 1222 -343 ct 1164 -333 l  1157 -356 1145 -373 1127 -384 ct 
+1109 -394 1084 -399 1053 -399 ct 1018 -399 991 -394 973 -382 ct 954 -370 945 -353 945 -330 ct 
+945 -317 949 -306 956 -297 ct 963 -288 973 -281 987 -274 ct 1000 -268 1027 -261 1067 -252 ct 
+1080 -249 1094 -246 1107 -242 ct 1120 -239 1133 -235 1145 -231 ct 1157 -226 1169 -221 1180 -215 ct 
+1190 -209 1199 -202 1207 -193 ct 1215 -185 1221 -174 1226 -163 ct 1230 -151 1232 -137 1232 -121 ct 
+p ef
+pom
+gs
+0 lw 1 lj 2292 14665 m  37534 14665 l  ps
+gr
+gs
+0.933 0.933 0.933 c 9859 9844 m  9860 9844 l  9679 9844 9501 9857 9345 9882 ct 
+9189 9907 9059 9944 8968 9987 ct 8878 10031 8830 10080 8830 10130 ct 8830 10344 l 
+8830 10559 l  8830 10850 l  8830 11065 l  8830 11279 l  8830 11280 l  8830 11330 8878 11379 8968 11423 ct 
+9059 11466 9189 11503 9345 11528 ct 9501 11553 9679 11566 9860 11566 ct 10631 11566 l 
+11404 11566 l  12454 11566 l  13227 11566 l  13999 11566 l  13999 11566 l 
+14180 11566 14358 11553 14515 11528 ct 14671 11503 14801 11466 14892 11423 ct 
+14982 11379 15030 11330 15030 11280 ct 15030 11065 l  15030 10850 l  15030 10559 l 
+15030 10344 l  15030 10130 l  15030 10130 l  15030 10130 l  15030 10080 14982 10031 14892 9987 ct 
+14801 9944 14671 9907 14515 9882 ct 14358 9857 14180 9844 13999 9844 ct 13703 8954 l 
+12454 9844 l  11404 9844 l  10631 9844 l  9859 9844 l  p ef
+gr
+gs
+0 lw 1 lj 9859 9844 m  9860 9844 l  9679 9844 9501 9857 9344 9882 ct 9188 9907 9058 9944 8967 9987 ct 
+8877 10030 8829 10079 8829 10129 ct 8829 10343 l  8829 10558 l  8829 10849 l 
+8829 11064 l  8829 11278 l  8829 11279 l  8829 11329 8877 11378 8967 11422 ct 
+9058 11465 9188 11502 9344 11527 ct 9501 11552 9679 11565 9860 11565 ct 10631 11565 l 
+11404 11565 l  12454 11565 l  13227 11565 l  13999 11565 l  13999 11565 l 
+14180 11565 14358 11552 14515 11527 ct 14671 11502 14801 11465 14892 11422 ct 
+14982 11378 15030 11329 15030 11279 ct 15030 11064 l  15030 10849 l  15030 10558 l 
+15030 10343 l  15030 10129 l  15030 10129 l  15030 10129 l  15030 10079 14982 10030 14892 9987 ct 
+14801 9944 14671 9907 14515 9882 ct 14358 9857 14180 9844 13999 9844 ct 13703 8954 l 
+12454 9844 l  11404 9844 l  10631 9844 l  9859 9844 l  pc
+gr
+pum
+9322 10585 t
+321 -1 m  277 -114 l  100 -114 l  56 -1 l  1 -1 l  159 -389 l  219 -389 l 
+375 -1 l  321 -1 l  p
+189 -349 m  186 -342 l  182 -326 175 -307 166 -283 ct 116 -155 l  261 -155 l 
+212 -283 l  206 -296 201 -310 196 -326 ct 189 -349 l  p ef
+463 -298 m  463 -109 l  463 -89 465 -74 469 -63 ct 473 -52 479 -44 488 -40 ct 
+496 -35 509 -32 525 -32 ct 549 -32 568 -41 581 -57 ct 595 -73 602 -96 602 -125 ct 
+602 -298 l  652 -298 l  652 -63 l  652 -28 652 -7 653 0 ct 606 0 l  606 0 606 -3 606 -7 ct 
+606 -11 605 -16 605 -21 ct 605 -26 605 -36 604 -51 ct 603 -51 l  592 -30 579 -15 564 -7 ct 
+549 2 530 6 508 6 ct 475 6 451 -2 436 -18 ct 421 -35 414 -62 414 -99 ct 414 -298 l 
+463 -298 l  p ef
+911 0 m  830 -122 l  750 0 l  696 0 l  802 -153 l  701 -298 l  756 -298 l 
+830 -182 l  904 -298 l  960 -298 l  859 -154 l  966 0 l  911 0 l  p ef
+1476 -271 m  1476 -234 1464 -205 1441 -184 ct 1417 -162 1384 -151 1343 -151 ct 
+1229 -151 l  1229 1 l  1176 1 l  1176 -388 l  1340 -388 l  1383 -388 1417 -378 1441 -358 ct 
+1465 -337 1476 -308 1476 -271 ct p
+1424 -271 m  1424 -321 1393 -346 1333 -346 ct 1229 -346 l  1229 -193 l  1335 -193 l 
+1394 -193 1424 -219 1424 -271 ct p ef
+1816 0 m  1816 -180 l  1606 -180 l  1606 0 l  1553 0 l  1553 -389 l  1606 -389 l 
+1606 -224 l  1816 -224 l  1816 -389 l  1868 -389 l  1868 0 l  1816 0 l 
+p ef
+2127 -161 m  2127 0 l  2075 0 l  2075 -161 l  1925 -389 l  1983 -389 l 
+2101 -203 l  2219 -389 l  2277 -389 l  2127 -161 l  p ef
+2696 -82 m  2696 -54 2685 -32 2664 -17 ct 2643 -2 2613 6 2575 6 ct 2538 6 2509 0 2489 -12 ct 
+2469 -25 2456 -44 2450 -70 ct 2493 -78 l  2498 -62 2506 -50 2520 -43 ct 2533 -36 2551 -32 2575 -32 ct 
+2600 -32 2618 -36 2630 -43 ct 2642 -51 2647 -63 2647 -78 ct 2647 -90 2643 -99 2635 -107 ct 
+2627 -114 2614 -120 2596 -125 ct 2561 -134 l  2532 -142 2512 -149 2500 -156 ct 
+2488 -163 2479 -172 2472 -182 ct 2465 -192 2462 -204 2462 -219 ct 2462 -246 2471 -267 2491 -281 ct 
+2510 -295 2538 -303 2575 -303 ct 2608 -303 2634 -297 2654 -285 ct 2673 -274 2685 -255 2690 -229 ct 
+2646 -224 l  2643 -237 2636 -247 2624 -254 ct 2612 -261 2595 -265 2575 -265 ct 
+2553 -265 2536 -262 2526 -255 ct 2515 -248 2510 -238 2510 -224 ct 2510 -215 2512 -208 2516 -203 ct 
+2521 -197 2527 -193 2536 -189 ct 2545 -185 2563 -180 2590 -173 ct 2617 -166 2636 -160 2647 -155 ct 
+2659 -149 2668 -143 2675 -136 ct 2681 -129 2686 -121 2690 -112 ct 2694 -103 2696 -93 2696 -82 ct 
+p ef
+2794 -138 m  2794 -104 2801 -78 2815 -59 ct 2829 -41 2850 -31 2877 -31 ct 2899 -31 2916 -36 2929 -44 ct 
+2942 -53 2951 -64 2955 -77 ct 2999 -65 l  2981 -18 2940 6 2877 6 ct 2833 6 2800 -7 2777 -33 ct 
+2753 -60 2742 -99 2742 -151 ct 2742 -200 2753 -238 2777 -264 ct 2800 -291 2832 -304 2875 -304 ct 
+2963 -304 3007 -251 3007 -145 ct 3007 -138 l  2794 -138 l  p
+2955 -176 m  2953 -208 2945 -231 2931 -245 ct 2918 -260 2899 -267 2874 -267 ct 
+2850 -267 2831 -259 2817 -243 ct 2803 -227 2796 -204 2795 -176 ct 2955 -176 l 
+p ef
+3258 0 m  3258 -189 l  3258 -209 3256 -224 3252 -235 ct 3249 -246 3242 -254 3234 -258 ct 
+3226 -263 3213 -266 3197 -266 ct 3173 -266 3154 -257 3140 -241 ct 3127 -225 3120 -202 3120 -173 ct 
+3120 0 l  3070 0 l  3070 -235 l  3070 -270 3070 -291 3068 -298 ct 3115 -298 l 
+3115 -298 3116 -295 3116 -291 ct 3116 -287 3116 -282 3117 -277 ct 3117 -272 3117 -262 3117 -247 ct 
+3118 -247 l  3130 -268 3143 -283 3158 -291 ct 3173 -300 3191 -304 3214 -304 ct 
+3246 -304 3270 -296 3285 -280 ct 3300 -263 3308 -236 3308 -199 ct 3308 0 l  3258 0 l 
+p ef
+3570 -48 m  3561 -28 3549 -15 3534 -6 ct 3518 2 3500 6 3477 6 ct 3440 6 3412 -7 3394 -32 ct 
+3377 -57 3368 -96 3368 -147 ct 3368 -252 3404 -304 3477 -304 ct 3500 -304 3519 -300 3534 -291 ct 
+3549 -283 3561 -270 3570 -252 ct 3571 -252 l  3570 -285 l  3570 -409 l  3620 -409 l 
+3620 -61 l  3620 -30 3620 -9 3621 0 ct 3574 0 l  3573 -2 3573 -9 3572 -20 ct 
+3572 -31 3571 -40 3571 -48 ct 3570 -48 l  p
+3420 -149 m  3420 -107 3425 -77 3436 -59 ct 3447 -41 3465 -32 3490 -32 ct 3518 -32 3538 -42 3551 -62 ct 
+3564 -81 3570 -111 3570 -152 ct 3570 -192 3564 -221 3551 -239 ct 3538 -258 3518 -267 3491 -267 ct 
+3466 -267 3448 -258 3436 -239 ct 3425 -221 3420 -191 3420 -149 ct p ef
+3920 -82 m  3920 -54 3909 -32 3888 -17 ct 3867 -2 3837 6 3799 6 ct 3762 6 3733 0 3713 -12 ct 
+3693 -25 3680 -44 3674 -70 ct 3717 -78 l  3722 -62 3730 -50 3744 -43 ct 3757 -36 3775 -32 3799 -32 ct 
+3824 -32 3842 -36 3854 -43 ct 3866 -51 3871 -63 3871 -78 ct 3871 -90 3867 -99 3859 -107 ct 
+3851 -114 3838 -120 3820 -125 ct 3785 -134 l  3756 -142 3736 -149 3724 -156 ct 
+3712 -163 3703 -172 3696 -182 ct 3689 -192 3686 -204 3686 -219 ct 3686 -246 3695 -267 3715 -281 ct 
+3734 -295 3762 -303 3799 -303 ct 3832 -303 3858 -297 3878 -285 ct 3897 -274 3909 -255 3914 -229 ct 
+3870 -224 l  3867 -237 3860 -247 3848 -254 ct 3836 -261 3819 -265 3799 -265 ct 
+3777 -265 3760 -262 3750 -255 ct 3739 -248 3734 -238 3734 -224 ct 3734 -215 3736 -208 3740 -203 ct 
+3745 -197 3751 -193 3760 -189 ct 3769 -185 3787 -180 3814 -173 ct 3841 -166 3860 -160 3871 -155 ct 
+3883 -149 3892 -143 3899 -136 ct 3905 -129 3910 -121 3914 -112 ct 3918 -103 3920 -93 3920 -82 ct 
+p ef
+4419 1 m  4318 -161 l  4197 -161 l  4197 1 l  4144 1 l  4144 -388 l  4327 -388 l 
+4371 -388 4404 -378 4428 -359 ct 4452 -339 4464 -312 4464 -277 ct 4464 -248 4455 -224 4439 -204 ct 
+4422 -185 4399 -172 4369 -167 ct 4479 1 l  4419 1 l  p
+4411 -277 m  4411 -299 4403 -316 4388 -328 ct 4373 -340 4350 -346 4322 -346 ct 
+4197 -346 l  4197 -203 l  4324 -203 l  4352 -203 4373 -209 4388 -222 ct 4403 -235 4411 -253 4411 -277 ct 
+p ef
+4694 -346 m  4694 -1 l  4642 -1 l  4642 -346 l  4509 -346 l  4509 -389 l 
+4828 -389 l  4828 -346 l  4694 -346 l  p ef
+5189 -107 m  5189 -71 5175 -43 5147 -24 ct 5119 -4 5080 6 5029 6 ct 4934 6 4880 -27 4865 -93 ct 
+4916 -103 l  4921 -80 4934 -62 4953 -52 ct 4972 -41 4998 -35 5031 -35 ct 5065 -35 5091 -41 5110 -53 ct 
+5128 -64 5137 -81 5137 -104 ct 5137 -117 5134 -127 5129 -135 ct 5123 -143 5115 -149 5104 -155 ct 
+5094 -160 5081 -164 5067 -168 ct 5052 -171 5036 -175 5019 -179 ct 4988 -186 4965 -192 4949 -199 ct 
+4933 -206 4920 -214 4911 -222 ct 4902 -230 4895 -240 4890 -251 ct 4885 -263 4883 -275 4883 -290 ct 
+4883 -323 4896 -349 4921 -367 ct 4946 -385 4983 -394 5030 -394 ct 5074 -394 5108 -387 5131 -374 ct 
+5155 -360 5171 -337 5180 -305 ct 5128 -296 l  5123 -316 5112 -331 5096 -340 ct 
+5080 -350 5058 -354 5030 -354 ct 4999 -354 4975 -349 4959 -339 ct 4942 -329 4934 -313 4934 -293 ct 
+4934 -281 4937 -271 4944 -263 ct 4950 -255 4959 -249 4971 -243 ct 4983 -238 5007 -231 5042 -223 ct 
+5054 -220 5066 -218 5078 -215 ct 5090 -212 5101 -209 5112 -205 ct 5123 -201 5133 -196 5142 -191 ct 
+5152 -185 5160 -179 5167 -171 ct 5174 -163 5179 -154 5183 -144 ct 5187 -133 5189 -121 5189 -107 ct 
+p ef
+pom
+pum
+9799 11212 t
+153 -3 m  136 2 120 4 102 4 ct 63 4 43 -19 43 -64 ct 43 -263 l  9 -263 l  9 -299 l 
+45 -299 l  59 -366 l  93 -366 l  93 -299 l  148 -299 l  148 -263 l  93 -263 l 
+93 -74 l  93 -60 95 -50 100 -44 ct 104 -38 112 -35 124 -35 ct 131 -35 140 -37 153 -39 ct 
+153 -3 l  p ef
+447 -149 m  447 -97 436 -58 413 -32 ct 390 -7 356 6 313 6 ct 269 6 236 -7 214 -34 ct 
+192 -60 181 -99 181 -149 ct 181 -252 225 -304 314 -304 ct 360 -304 393 -291 415 -266 ct 
+436 -241 447 -202 447 -149 ct p
+395 -149 m  395 -190 389 -220 377 -239 ct 364 -258 344 -267 315 -267 ct 286 -267 265 -257 252 -238 ct 
+239 -219 233 -190 233 -149 ct 233 -110 239 -80 252 -60 ct 265 -41 285 -31 312 -31 ct 
+342 -31 363 -40 376 -59 ct 389 -79 395 -108 395 -149 ct p ef
+889 -82 m  889 -54 878 -32 857 -17 ct 836 -2 806 6 768 6 ct 731 6 702 0 682 -12 ct 
+662 -25 649 -44 643 -70 ct 686 -78 l  691 -62 699 -50 713 -43 ct 726 -36 744 -32 768 -32 ct 
+793 -32 811 -36 823 -43 ct 835 -51 840 -63 840 -78 ct 840 -90 836 -99 828 -107 ct 
+820 -114 807 -120 789 -125 ct 754 -134 l  725 -142 705 -149 693 -156 ct 681 -163 672 -172 665 -182 ct 
+658 -192 655 -204 655 -219 ct 655 -246 664 -267 684 -281 ct 703 -295 731 -303 768 -303 ct 
+801 -303 827 -297 847 -285 ct 866 -274 878 -255 883 -229 ct 839 -224 l  836 -237 829 -247 817 -254 ct 
+805 -261 788 -265 768 -265 ct 746 -265 729 -262 719 -255 ct 708 -248 703 -238 703 -224 ct 
+703 -215 705 -208 709 -203 ct 714 -197 720 -193 729 -189 ct 738 -185 756 -180 783 -173 ct 
+810 -166 829 -160 840 -155 ct 852 -149 861 -143 868 -136 ct 874 -129 879 -121 883 -112 ct 
+887 -103 889 -93 889 -82 ct p ef
+1063 -3 m  1046 2 1030 4 1012 4 ct 973 4 953 -19 953 -64 ct 953 -263 l  919 -263 l 
+919 -299 l  955 -299 l  969 -366 l  1003 -366 l  1003 -299 l  1058 -299 l 
+1058 -263 l  1003 -263 l  1003 -74 l  1003 -60 1005 -50 1010 -44 ct 1014 -38 1022 -35 1034 -35 ct 
+1041 -35 1050 -37 1063 -39 ct 1063 -3 l  p ef
+1181 6 m  1151 6 1129 -2 1114 -18 ct 1098 -34 1091 -55 1091 -83 ct 1091 -114 1101 -138 1121 -154 ct 
+1142 -171 1174 -180 1220 -181 ct 1286 -182 l  1286 -198 l  1286 -222 1281 -240 1271 -250 ct 
+1261 -261 1245 -266 1223 -266 ct 1200 -266 1184 -262 1174 -255 ct 1164 -247 1158 -235 1156 -219 ct 
+1104 -223 l  1113 -277 1152 -304 1224 -304 ct 1261 -304 1289 -295 1308 -278 ct 
+1327 -261 1337 -236 1337 -203 ct 1337 -74 l  1337 -60 1339 -49 1342 -41 ct 1346 -34 1354 -30 1364 -30 ct 
+1369 -30 1375 -31 1381 -32 ct 1381 -1 l  1368 2 1355 3 1342 3 ct 1324 3 1311 -1 1302 -11 ct 
+1294 -21 1289 -36 1288 -57 ct 1286 -57 l  1274 -34 1259 -18 1242 -8 ct 1225 1 1205 6 1181 6 ct 
+p
+1192 -31 m  1210 -31 1227 -35 1241 -44 ct 1255 -52 1266 -63 1274 -78 ct 1282 -92 1286 -107 1286 -122 ct 
+1286 -147 l  1232 -146 l  1209 -145 1191 -143 1179 -139 ct 1167 -134 1158 -127 1152 -118 ct 
+1145 -109 1142 -97 1142 -82 ct 1142 -66 1146 -53 1155 -44 ct 1164 -36 1176 -31 1192 -31 ct 
+p ef
+1419 0 m  1419 -229 l  1419 -250 1419 -273 1417 -298 ct 1464 -298 l  1466 -265 1466 -244 1466 -238 ct 
+1468 -238 l  1475 -263 1485 -281 1495 -290 ct 1505 -299 1520 -304 1538 -304 ct 
+1545 -304 1552 -303 1558 -301 ct 1558 -256 l  1552 -258 1543 -258 1532 -258 ct 
+1511 -258 1496 -250 1485 -232 ct 1474 -214 1469 -189 1469 -156 ct 1469 0 l  1419 0 l 
+p ef
+1719 -3 m  1702 2 1686 4 1668 4 ct 1629 4 1609 -19 1609 -64 ct 1609 -263 l  1575 -263 l 
+1575 -299 l  1611 -299 l  1625 -366 l  1659 -366 l  1659 -299 l  1714 -299 l 
+1714 -263 l  1659 -263 l  1659 -74 l  1659 -60 1661 -50 1666 -44 ct 1670 -38 1678 -35 1690 -35 ct 
+1697 -35 1706 -37 1719 -39 ct 1719 -3 l  p ef
+2081 6 m  2050 6 2022 0 1998 -11 ct 1974 -23 1956 -40 1943 -62 ct 1930 -84 1924 -110 1924 -141 ct 
+1924 -388 l  1976 -388 l  1976 -145 l  1976 -110 1985 -83 2003 -64 ct 2021 -46 2047 -37 2081 -37 ct 
+2116 -37 2143 -46 2162 -65 ct 2182 -84 2191 -112 2191 -149 ct 2191 -388 l  2244 -388 l 
+2244 -146 l  2244 -114 2237 -87 2224 -64 ct 2211 -42 2192 -24 2167 -12 ct 2143 0 2114 6 2081 6 ct 
+p ef
+2332 0 m  2332 -389 l  2385 -389 l  2385 -43 l  2581 -43 l  2581 0 l  2332 0 l 
+p ef
+2924 -346 m  2924 -1 l  2872 -1 l  2872 -346 l  2739 -346 l  2739 -389 l 
+3058 -389 l  3058 -346 l  2924 -346 l  p ef
+3375 0 m  3259 -170 l  3140 0 l  3082 0 l  3229 -202 l  3093 -389 l  3151 -389 l 
+3259 -236 l  3364 -389 l  3422 -389 l  3289 -204 l  3433 0 l  3375 0 l 
+p ef
+3858 -196 m  3858 -155 3850 -119 3834 -89 ct 3819 -58 3797 -35 3768 -19 ct 3739 -2 3704 6 3665 6 ct 
+3625 6 3591 -2 3562 -18 ct 3533 -34 3511 -58 3496 -88 ct 3480 -119 3473 -155 3473 -196 ct 
+3473 -258 3490 -306 3524 -341 ct 3558 -376 3605 -394 3665 -394 ct 3705 -394 3739 -386 3768 -370 ct 
+3797 -355 3819 -332 3835 -302 ct 3850 -272 3858 -236 3858 -196 ct p
+3804 -196 m  3804 -244 3792 -282 3768 -310 ct 3744 -337 3710 -351 3665 -351 ct 
+3621 -351 3587 -337 3562 -310 ct 3538 -283 3526 -245 3526 -196 ct 3526 -147 3538 -108 3563 -80 ct 
+3587 -51 3621 -37 3665 -37 ct 3710 -37 3744 -51 3768 -78 ct 3792 -106 3804 -145 3804 -196 ct 
+p ef
+4228 -271 m  4228 -234 4216 -205 4193 -184 ct 4169 -162 4136 -151 4095 -151 ct 
+3981 -151 l  3981 1 l  3928 1 l  3928 -388 l  4092 -388 l  4135 -388 4169 -378 4193 -358 ct 
+4217 -337 4228 -308 4228 -271 ct p
+4176 -271 m  4176 -321 4145 -346 4085 -346 ct 3981 -346 l  3981 -193 l  4087 -193 l 
+4146 -193 4176 -219 4176 -271 ct p ef
+pom
+gs
+0.933 0.933 0.933 c 20512 10249 m  20512 10249 l  20298 10249 20087 10262 19902 10287 ct 
+19716 10312 19562 10349 19455 10392 ct 19347 10436 19291 10485 19291 10535 ct 
+19291 10749 l  19291 10963 l  19291 11255 l  19291 11469 l  19291 11683 l 
+19291 11684 l  19291 11734 19347 11783 19455 11827 ct 19562 11870 19716 11907 19902 11932 ct 
+20087 11957 20298 11970 20512 11970 ct 21427 11970 l  22342 11970 l  23588 11970 l 
+24503 11970 l  25418 11970 l  25419 11970 l  25633 11970 25844 11957 26029 11932 ct 
+26215 11907 26369 11870 26476 11827 ct 26584 11783 26640 11734 26640 11684 ct 
+26640 11469 l  26640 11255 l  26640 10963 l  26640 10749 l  26640 10535 l 
+26640 10535 l  26640 10535 l  26640 10485 26584 10436 26476 10392 ct 26369 10349 26215 10312 26029 10287 ct 
+25844 10262 25633 10249 25419 10249 ct 24863 9327 l  23588 10249 l  22342 10249 l 
+21427 10249 l  20512 10249 l  p ef
+gr
+gs
+0 lw 1 lj 20511 10248 m  20511 10248 l  20297 10248 20086 10261 19901 10286 ct 
+19715 10311 19561 10348 19454 10391 ct 19346 10435 19290 10484 19290 10534 ct 
+19290 10748 l  19290 10962 l  19290 11254 l  19290 11468 l  19290 11682 l 
+19290 11683 l  19290 11733 19346 11782 19454 11826 ct 19561 11869 19715 11906 19901 11931 ct 
+20086 11956 20297 11969 20511 11969 ct 21426 11969 l  22341 11969 l  23587 11969 l 
+24502 11969 l  25417 11969 l  25418 11969 l  25632 11969 25843 11956 26028 11931 ct 
+26214 11906 26368 11869 26475 11826 ct 26583 11782 26639 11733 26639 11683 ct 
+26639 11468 l  26639 11254 l  26639 10962 l  26639 10748 l  26639 10534 l 
+26639 10534 l  26639 10534 l  26639 10484 26583 10435 26475 10391 ct 26368 10348 26214 10311 26028 10286 ct 
+25843 10261 25632 10248 25418 10248 ct 24862 9327 l  23587 10248 l  22341 10248 l 
+21426 10248 l  20511 10248 l  pc
+gr
+pum
+19863 10990 t
+376 0 m  376 -259 l  376 -288 377 -315 379 -342 ct 370 -309 362 -283 354 -265 ct 
+254 0 l  217 0 l  116 -265 l  100 -312 l  91 -342 l  92 -311 l  93 -259 l 
+93 0 l  46 0 l  46 -389 l  115 -389 l  219 -119 l  222 -108 226 -97 229 -84 ct 
+233 -72 235 -63 236 -57 ct 237 -65 241 -76 245 -91 ct 250 -106 253 -115 255 -119 ct 
+356 -389 l  424 -389 l  424 0 l  376 0 l  p ef
+584 6 m  554 6 532 -2 517 -18 ct 501 -34 494 -55 494 -83 ct 494 -114 504 -138 524 -154 ct 
+545 -171 577 -180 623 -181 ct 689 -182 l  689 -198 l  689 -222 684 -240 674 -250 ct 
+664 -261 648 -266 626 -266 ct 603 -266 587 -262 577 -255 ct 567 -247 561 -235 559 -219 ct 
+507 -223 l  516 -277 555 -304 627 -304 ct 664 -304 692 -295 711 -278 ct 730 -261 740 -236 740 -203 ct 
+740 -74 l  740 -60 742 -49 745 -41 ct 749 -34 757 -30 767 -30 ct 772 -30 778 -31 784 -32 ct 
+784 -1 l  771 2 758 3 745 3 ct 727 3 714 -1 705 -11 ct 697 -21 692 -36 691 -57 ct 
+689 -57 l  677 -34 662 -18 645 -8 ct 628 1 608 6 584 6 ct p
+595 -31 m  613 -31 630 -35 644 -44 ct 658 -52 669 -63 677 -78 ct 685 -92 689 -107 689 -122 ct 
+689 -147 l  635 -146 l  612 -145 594 -143 582 -139 ct 570 -134 561 -127 555 -118 ct 
+548 -109 545 -97 545 -82 ct 545 -66 549 -53 558 -44 ct 567 -36 579 -31 595 -31 ct 
+p ef
+821 -362 m  821 -409 l  870 -409 l  870 -362 l  821 -362 l  p
+821 0 m  821 -298 l  870 -298 l  870 0 l  821 0 l  p ef
+1137 0 m  1137 -189 l  1137 -209 1135 -224 1131 -235 ct 1128 -246 1121 -254 1113 -258 ct 
+1105 -263 1092 -266 1076 -266 ct 1052 -266 1033 -257 1019 -241 ct 1006 -225 999 -202 999 -173 ct 
+999 0 l  949 0 l  949 -235 l  949 -270 949 -291 947 -298 ct 994 -298 l  994 -298 995 -295 995 -291 ct 
+995 -287 995 -282 996 -277 ct 996 -272 996 -262 996 -247 ct 997 -247 l  1009 -268 1022 -283 1037 -291 ct 
+1052 -300 1070 -304 1093 -304 ct 1125 -304 1149 -296 1164 -280 ct 1179 -263 1187 -236 1187 -199 ct 
+1187 0 l  1137 0 l  p ef
+1726 -271 m  1726 -234 1714 -205 1691 -184 ct 1667 -162 1634 -151 1593 -151 ct 
+1479 -151 l  1479 1 l  1426 1 l  1426 -388 l  1590 -388 l  1633 -388 1667 -378 1691 -358 ct 
+1715 -337 1726 -308 1726 -271 ct p
+1674 -271 m  1674 -321 1643 -346 1583 -346 ct 1479 -346 l  1479 -193 l  1585 -193 l 
+1644 -193 1674 -219 1674 -271 ct p ef
+2066 0 m  2066 -180 l  1856 -180 l  1856 0 l  1803 0 l  1803 -389 l  1856 -389 l 
+1856 -224 l  2066 -224 l  2066 -389 l  2118 -389 l  2118 0 l  2066 0 l 
+p ef
+2377 -161 m  2377 0 l  2325 0 l  2325 -161 l  2175 -389 l  2233 -389 l 
+2351 -203 l  2469 -389 l  2527 -389 l  2377 -161 l  p ef
+2946 -82 m  2946 -54 2935 -32 2914 -17 ct 2893 -2 2863 6 2825 6 ct 2788 6 2759 0 2739 -12 ct 
+2719 -25 2706 -44 2700 -70 ct 2743 -78 l  2748 -62 2756 -50 2770 -43 ct 2783 -36 2801 -32 2825 -32 ct 
+2850 -32 2868 -36 2880 -43 ct 2892 -51 2897 -63 2897 -78 ct 2897 -90 2893 -99 2885 -107 ct 
+2877 -114 2864 -120 2846 -125 ct 2811 -134 l  2782 -142 2762 -149 2750 -156 ct 
+2738 -163 2729 -172 2722 -182 ct 2715 -192 2712 -204 2712 -219 ct 2712 -246 2721 -267 2741 -281 ct 
+2760 -295 2788 -303 2825 -303 ct 2858 -303 2884 -297 2904 -285 ct 2923 -274 2935 -255 2940 -229 ct 
+2896 -224 l  2893 -237 2886 -247 2874 -254 ct 2862 -261 2845 -265 2825 -265 ct 
+2803 -265 2786 -262 2776 -255 ct 2765 -248 2760 -238 2760 -224 ct 2760 -215 2762 -208 2766 -203 ct 
+2771 -197 2777 -193 2786 -189 ct 2795 -185 2813 -180 2840 -173 ct 2867 -166 2886 -160 2897 -155 ct 
+2909 -149 2918 -143 2925 -136 ct 2931 -129 2936 -121 2940 -112 ct 2944 -103 2946 -93 2946 -82 ct 
+p ef
+3291 0 m  3234 0 l  3182 -211 l  3172 -258 l  3170 -249 3168 -237 3164 -222 ct 
+3161 -206 3142 -132 3108 0 ct 3051 0 l  2967 -299 l  3016 -299 l  3067 -96 l 
+3068 -91 3072 -73 3078 -41 ct 3083 -62 l  3145 -299 l  3199 -299 l  3251 -94 l 
+3263 -41 l  3272 -79 l  3328 -299 l  3377 -299 l  3291 0 l  p ef
+3412 -362 m  3412 -409 l  3461 -409 l  3461 -362 l  3412 -362 l  p
+3412 0 m  3412 -298 l  3461 -298 l  3461 0 l  3412 0 l  p ef
+3654 -3 m  3637 2 3621 4 3603 4 ct 3564 4 3544 -19 3544 -64 ct 3544 -263 l  3510 -263 l 
+3510 -299 l  3546 -299 l  3560 -366 l  3594 -366 l  3594 -299 l  3649 -299 l 
+3649 -263 l  3594 -263 l  3594 -74 l  3594 -60 3596 -50 3601 -44 ct 3605 -38 3613 -35 3625 -35 ct 
+3632 -35 3641 -37 3654 -39 ct 3654 -3 l  p ef
+3734 -150 m  3734 -111 3740 -81 3752 -62 ct 3765 -43 3784 -33 3809 -33 ct 3827 -33 3841 -38 3853 -48 ct 
+3865 -57 3872 -72 3875 -92 ct 3925 -88 l  3921 -60 3909 -37 3889 -20 ct 3868 -3 3842 6 3810 6 ct 
+3769 6 3737 -7 3715 -34 ct 3693 -60 3682 -99 3682 -149 ct 3682 -200 3693 -238 3715 -264 ct 
+3737 -291 3769 -304 3810 -304 ct 3840 -304 3866 -296 3886 -280 ct 3906 -264 3918 -243 3923 -215 ct 
+3873 -211 l  3870 -227 3863 -241 3853 -250 ct 3843 -260 3828 -265 3808 -265 ct 
+3782 -265 3763 -256 3751 -239 ct 3740 -221 3734 -192 3734 -150 ct p ef
+4028 -247 m  4039 -266 4052 -281 4067 -290 ct 4082 -299 4101 -303 4124 -303 ct 
+4156 -303 4180 -295 4195 -279 ct 4210 -263 4218 -236 4218 -198 ct 4218 1 l  4168 1 l 
+4168 -189 l  4168 -210 4166 -225 4162 -235 ct 4159 -246 4152 -253 4143 -258 ct 
+4135 -263 4122 -265 4107 -265 ct 4083 -265 4065 -257 4051 -241 ct 4037 -225 4030 -203 4030 -175 ct 
+4030 1 l  3980 1 l  3980 -409 l  4030 -409 l  4030 -302 l  4030 -291 4029 -279 4029 -267 ct 
+4028 -256 4028 -249 4027 -247 ct 4028 -247 l  p ef
+4331 -138 m  4331 -104 4338 -78 4352 -59 ct 4366 -41 4387 -31 4414 -31 ct 4436 -31 4453 -36 4466 -44 ct 
+4479 -53 4488 -64 4492 -77 ct 4536 -65 l  4518 -18 4477 6 4414 6 ct 4370 6 4337 -7 4314 -33 ct 
+4290 -60 4279 -99 4279 -151 ct 4279 -200 4290 -238 4314 -264 ct 4337 -291 4369 -304 4412 -304 ct 
+4500 -304 4544 -251 4544 -145 ct 4544 -138 l  4331 -138 l  p
+4492 -176 m  4490 -208 4482 -231 4468 -245 ct 4455 -260 4436 -267 4411 -267 ct 
+4387 -267 4368 -259 4354 -243 ct 4340 -227 4333 -204 4332 -176 ct 4492 -176 l 
+p ef
+4830 -82 m  4830 -54 4819 -32 4798 -17 ct 4777 -2 4747 6 4709 6 ct 4672 6 4643 0 4623 -12 ct 
+4603 -25 4590 -44 4584 -70 ct 4627 -78 l  4632 -62 4640 -50 4654 -43 ct 4667 -36 4685 -32 4709 -32 ct 
+4734 -32 4752 -36 4764 -43 ct 4776 -51 4781 -63 4781 -78 ct 4781 -90 4777 -99 4769 -107 ct 
+4761 -114 4748 -120 4730 -125 ct 4695 -134 l  4666 -142 4646 -149 4634 -156 ct 
+4622 -163 4613 -172 4606 -182 ct 4599 -192 4596 -204 4596 -219 ct 4596 -246 4605 -267 4625 -281 ct 
+4644 -295 4672 -303 4709 -303 ct 4742 -303 4768 -297 4788 -285 ct 4807 -274 4819 -255 4824 -229 ct 
+4780 -224 l  4777 -237 4770 -247 4758 -254 ct 4746 -261 4729 -265 4709 -265 ct 
+4687 -265 4670 -262 4660 -255 ct 4649 -248 4644 -238 4644 -224 ct 4644 -215 4646 -208 4650 -203 ct 
+4655 -197 4661 -193 4670 -189 ct 4679 -185 4697 -180 4724 -173 ct 4751 -166 4770 -160 4781 -155 ct 
+4793 -149 4802 -143 4809 -136 ct 4815 -129 4820 -121 4824 -112 ct 4828 -103 4830 -93 4830 -82 ct 
+p ef
+5298 -150 m  5298 -46 5261 6 5188 6 ct 5166 6 5147 2 5132 -6 ct 5117 -14 5105 -28 5096 -46 ct 
+5095 -46 l  5095 -40 5095 -31 5094 -20 ct 5093 -8 5093 -1 5092 0 ct 5044 0 l 
+5045 -9 5046 -30 5046 -61 ct 5046 -409 l  5096 -409 l  5096 -292 l  5096 -280 5095 -266 5094 -250 ct 
+5096 -250 l  5105 -269 5117 -283 5132 -291 ct 5147 -299 5166 -303 5188 -303 ct 
+5226 -303 5254 -291 5271 -265 ct 5289 -240 5298 -202 5298 -150 ct p
+5246 -148 m  5246 -190 5240 -220 5229 -238 ct 5218 -256 5201 -265 5176 -265 ct 
+5148 -265 5127 -255 5115 -236 ct 5102 -217 5096 -187 5096 -145 ct 5096 -106 5102 -77 5114 -59 ct 
+5127 -40 5147 -31 5175 -31 ct 5200 -31 5218 -40 5229 -58 ct 5240 -77 5246 -107 5246 -148 ct 
+p ef
+5435 6 m  5405 6 5383 -2 5368 -18 ct 5352 -34 5345 -55 5345 -83 ct 5345 -114 5355 -138 5375 -154 ct 
+5396 -171 5428 -180 5474 -181 ct 5540 -182 l  5540 -198 l  5540 -222 5535 -240 5525 -250 ct 
+5515 -261 5499 -266 5477 -266 ct 5454 -266 5438 -262 5428 -255 ct 5418 -247 5412 -235 5410 -219 ct 
+5358 -223 l  5367 -277 5406 -304 5478 -304 ct 5515 -304 5543 -295 5562 -278 ct 
+5581 -261 5591 -236 5591 -203 ct 5591 -74 l  5591 -60 5593 -49 5596 -41 ct 5600 -34 5608 -30 5618 -30 ct 
+5623 -30 5629 -31 5635 -32 ct 5635 -1 l  5622 2 5609 3 5596 3 ct 5578 3 5565 -1 5556 -11 ct 
+5548 -21 5543 -36 5542 -57 ct 5540 -57 l  5528 -34 5513 -18 5496 -8 ct 5479 1 5459 6 5435 6 ct 
+p
+5446 -31 m  5464 -31 5481 -35 5495 -44 ct 5509 -52 5520 -63 5528 -78 ct 5536 -92 5540 -107 5540 -122 ct 
+5540 -147 l  5486 -146 l  5463 -145 5445 -143 5433 -139 ct 5421 -134 5412 -127 5406 -118 ct 
+5399 -109 5396 -97 5396 -82 ct 5396 -66 5400 -53 5409 -44 ct 5418 -36 5430 -31 5446 -31 ct 
+p ef
+5711 -150 m  5711 -111 5717 -81 5729 -62 ct 5742 -43 5761 -33 5786 -33 ct 5804 -33 5818 -38 5830 -48 ct 
+5842 -57 5849 -72 5852 -92 ct 5902 -88 l  5898 -60 5886 -37 5866 -20 ct 5845 -3 5819 6 5787 6 ct 
+5746 6 5714 -7 5692 -34 ct 5670 -60 5659 -99 5659 -149 ct 5659 -200 5670 -238 5692 -264 ct 
+5714 -291 5746 -304 5787 -304 ct 5817 -304 5843 -296 5863 -280 ct 5883 -264 5895 -243 5900 -215 ct 
+5850 -211 l  5847 -227 5840 -241 5830 -250 ct 5820 -260 5805 -265 5785 -265 ct 
+5759 -265 5740 -256 5728 -239 ct 5717 -221 5711 -192 5711 -150 ct p ef
+6143 0 m  6042 -136 l  6006 -106 l  6006 0 l  5956 0 l  5956 -409 l  6006 -409 l 
+6006 -153 l  6136 -298 l  6195 -298 l  6074 -170 l  6201 0 l  6143 0 l 
+p ef
+pom
+pum
+20268 11617 t
+153 -3 m  136 2 120 4 102 4 ct 63 4 43 -19 43 -64 ct 43 -263 l  9 -263 l  9 -299 l 
+45 -299 l  59 -366 l  93 -366 l  93 -299 l  148 -299 l  148 -263 l  93 -263 l 
+93 -74 l  93 -60 95 -50 100 -44 ct 104 -38 112 -35 124 -35 ct 131 -35 140 -37 153 -39 ct 
+153 -3 l  p ef
+447 -149 m  447 -97 436 -58 413 -32 ct 390 -7 356 6 313 6 ct 269 6 236 -7 214 -34 ct 
+192 -60 181 -99 181 -149 ct 181 -252 225 -304 314 -304 ct 360 -304 393 -291 415 -266 ct 
+436 -241 447 -202 447 -149 ct p
+395 -149 m  395 -190 389 -220 377 -239 ct 364 -258 344 -267 315 -267 ct 286 -267 265 -257 252 -238 ct 
+239 -219 233 -190 233 -149 ct 233 -110 239 -80 252 -60 ct 265 -41 285 -31 312 -31 ct 
+342 -31 363 -40 376 -59 ct 389 -79 395 -108 395 -149 ct p ef
+917 -149 m  917 -97 906 -58 883 -32 ct 860 -7 826 6 783 6 ct 739 6 706 -7 684 -34 ct 
+662 -60 651 -99 651 -149 ct 651 -252 695 -304 784 -304 ct 830 -304 863 -291 885 -266 ct 
+906 -241 917 -202 917 -149 ct p
+865 -149 m  865 -190 859 -220 847 -239 ct 834 -258 814 -267 785 -267 ct 756 -267 735 -257 722 -238 ct 
+709 -219 703 -190 703 -149 ct 703 -110 709 -80 722 -60 ct 735 -41 755 -31 782 -31 ct 
+812 -31 833 -40 846 -59 ct 859 -79 865 -108 865 -149 ct p ef
+1230 -151 m  1230 -47 1193 5 1120 5 ct 1074 5 1044 -12 1028 -47 ct 1026 -47 l 
+1027 -45 1028 -30 1028 0 ct 1028 117 l  978 117 l  978 -238 l  978 -269 977 -289 976 -299 ct 
+1024 -299 l  1024 -298 1025 -295 1025 -291 ct 1025 -286 1026 -279 1026 -270 ct 
+1027 -261 1027 -254 1027 -251 ct 1028 -251 l  1037 -269 1049 -283 1063 -291 ct 
+1078 -300 1097 -304 1120 -304 ct 1157 -304 1185 -292 1203 -267 ct 1221 -242 1230 -204 1230 -151 ct 
+p
+1178 -150 m  1178 -191 1172 -221 1161 -239 ct 1150 -257 1132 -266 1108 -266 ct 
+1088 -266 1073 -262 1062 -253 ct 1050 -245 1042 -232 1036 -215 ct 1030 -197 1028 -174 1028 -146 ct 
+1028 -107 1034 -78 1046 -60 ct 1059 -41 1079 -32 1107 -32 ct 1132 -32 1150 -41 1161 -59 ct 
+1172 -77 1178 -107 1178 -150 ct p ef
+1329 -138 m  1329 -104 1336 -78 1350 -59 ct 1364 -41 1385 -31 1412 -31 ct 1434 -31 1451 -36 1464 -44 ct 
+1477 -53 1486 -64 1490 -77 ct 1534 -65 l  1516 -18 1475 6 1412 6 ct 1368 6 1335 -7 1312 -33 ct 
+1288 -60 1277 -99 1277 -151 ct 1277 -200 1288 -238 1312 -264 ct 1335 -291 1367 -304 1410 -304 ct 
+1498 -304 1542 -251 1542 -145 ct 1542 -138 l  1329 -138 l  p
+1490 -176 m  1488 -208 1480 -231 1466 -245 ct 1453 -260 1434 -267 1409 -267 ct 
+1385 -267 1366 -259 1352 -243 ct 1338 -227 1331 -204 1330 -176 ct 1490 -176 l 
+p ef
+1605 0 m  1605 -229 l  1605 -250 1605 -273 1603 -298 ct 1650 -298 l  1652 -265 1652 -244 1652 -238 ct 
+1654 -238 l  1661 -263 1671 -281 1681 -290 ct 1691 -299 1706 -304 1724 -304 ct 
+1731 -304 1738 -303 1744 -301 ct 1744 -256 l  1738 -258 1729 -258 1718 -258 ct 
+1697 -258 1682 -250 1671 -232 ct 1660 -214 1655 -189 1655 -156 ct 1655 0 l  1605 0 l 
+p ef
+1867 6 m  1837 6 1815 -2 1800 -18 ct 1784 -34 1777 -55 1777 -83 ct 1777 -114 1787 -138 1807 -154 ct 
+1828 -171 1860 -180 1906 -181 ct 1972 -182 l  1972 -198 l  1972 -222 1967 -240 1957 -250 ct 
+1947 -261 1931 -266 1909 -266 ct 1886 -266 1870 -262 1860 -255 ct 1850 -247 1844 -235 1842 -219 ct 
+1790 -223 l  1799 -277 1838 -304 1910 -304 ct 1947 -304 1975 -295 1994 -278 ct 
+2013 -261 2023 -236 2023 -203 ct 2023 -74 l  2023 -60 2025 -49 2028 -41 ct 2032 -34 2040 -30 2050 -30 ct 
+2055 -30 2061 -31 2067 -32 ct 2067 -1 l  2054 2 2041 3 2028 3 ct 2010 3 1997 -1 1988 -11 ct 
+1980 -21 1975 -36 1974 -57 ct 1972 -57 l  1960 -34 1945 -18 1928 -8 ct 1911 1 1891 6 1867 6 ct 
+p
+1878 -31 m  1896 -31 1913 -35 1927 -44 ct 1941 -52 1952 -63 1960 -78 ct 1968 -92 1972 -107 1972 -122 ct 
+1972 -147 l  1918 -146 l  1895 -145 1877 -143 1865 -139 ct 1853 -134 1844 -127 1838 -118 ct 
+1831 -109 1828 -97 1828 -82 ct 1828 -66 1832 -53 1841 -44 ct 1850 -36 1862 -31 1878 -31 ct 
+p ef
+2219 -3 m  2202 2 2186 4 2168 4 ct 2129 4 2109 -19 2109 -64 ct 2109 -263 l  2075 -263 l 
+2075 -299 l  2111 -299 l  2125 -366 l  2159 -366 l  2159 -299 l  2214 -299 l 
+2214 -263 l  2159 -263 l  2159 -74 l  2159 -60 2161 -50 2166 -44 ct 2170 -38 2178 -35 2190 -35 ct 
+2197 -35 2206 -37 2219 -39 ct 2219 -3 l  p ef
+2261 -362 m  2261 -409 l  2310 -409 l  2310 -362 l  2261 -362 l  p
+2261 0 m  2261 -298 l  2310 -298 l  2310 0 l  2261 0 l  p ef
+2577 0 m  2577 -189 l  2577 -209 2575 -224 2571 -235 ct 2568 -246 2561 -254 2553 -258 ct 
+2545 -263 2532 -266 2516 -266 ct 2492 -266 2473 -257 2459 -241 ct 2446 -225 2439 -202 2439 -173 ct 
+2439 0 l  2389 0 l  2389 -235 l  2389 -270 2389 -291 2387 -298 ct 2434 -298 l 
+2434 -298 2435 -295 2435 -291 ct 2435 -287 2435 -282 2436 -277 ct 2436 -272 2436 -262 2436 -247 ct 
+2437 -247 l  2449 -268 2462 -283 2477 -291 ct 2492 -300 2510 -304 2533 -304 ct 
+2565 -304 2589 -296 2604 -280 ct 2619 -263 2627 -236 2627 -199 ct 2627 0 l  2577 0 l 
+p ef
+2814 117 m  2781 117 2756 111 2736 98 ct 2717 85 2705 67 2699 43 ct 2749 36 l 
+2752 50 2760 61 2771 68 ct 2782 75 2797 79 2815 79 ct 2865 79 2889 50 2889 -8 ct 
+2889 -56 l  2889 -56 l  2879 -37 2867 -22 2850 -12 ct 2834 -3 2815 2 2793 2 ct 
+2756 2 2730 -10 2712 -34 ct 2695 -59 2687 -97 2687 -149 ct 2687 -201 2696 -240 2714 -265 ct 
+2733 -290 2761 -303 2799 -303 ct 2820 -303 2838 -298 2853 -289 ct 2869 -279 2881 -265 2889 -247 ct 
+2890 -247 l  2890 -253 2890 -262 2891 -276 ct 2892 -290 2893 -297 2893 -298 ct 
+2940 -298 l  2939 -288 2939 -268 2939 -237 ct 2939 -9 l  2939 75 2897 117 2814 117 ct 
+p
+2889 -149 m  2889 -173 2886 -194 2879 -212 ct 2873 -229 2864 -243 2852 -252 ct 
+2839 -261 2826 -266 2811 -266 ct 2785 -266 2767 -257 2755 -238 ct 2744 -220 2738 -190 2738 -149 ct 
+2738 -108 2743 -79 2754 -61 ct 2765 -44 2784 -35 2810 -35 ct 2825 -35 2839 -39 2851 -48 ct 
+2863 -58 2873 -71 2879 -88 ct 2886 -105 2889 -126 2889 -149 ct p ef
+3423 -149 m  3423 -97 3412 -58 3389 -32 ct 3366 -7 3332 6 3289 6 ct 3245 6 3212 -7 3190 -34 ct 
+3168 -60 3157 -99 3157 -149 ct 3157 -252 3201 -304 3290 -304 ct 3336 -304 3369 -291 3391 -266 ct 
+3412 -241 3423 -202 3423 -149 ct p
+3371 -149 m  3371 -190 3365 -220 3353 -239 ct 3340 -258 3320 -267 3291 -267 ct 
+3262 -267 3241 -257 3228 -238 ct 3215 -219 3209 -190 3209 -149 ct 3209 -110 3215 -80 3228 -60 ct 
+3241 -41 3261 -31 3288 -31 ct 3318 -31 3339 -40 3352 -59 ct 3365 -79 3371 -108 3371 -149 ct 
+p ef
+3673 0 m  3673 -189 l  3673 -209 3671 -224 3667 -235 ct 3664 -246 3657 -254 3649 -258 ct 
+3641 -263 3628 -266 3612 -266 ct 3588 -266 3569 -257 3555 -241 ct 3542 -225 3535 -202 3535 -173 ct 
+3535 0 l  3485 0 l  3485 -235 l  3485 -270 3485 -291 3483 -298 ct 3530 -298 l 
+3530 -298 3531 -295 3531 -291 ct 3531 -287 3531 -282 3532 -277 ct 3532 -272 3532 -262 3532 -247 ct 
+3533 -247 l  3545 -268 3558 -283 3573 -291 ct 3588 -300 3606 -304 3629 -304 ct 
+3661 -304 3685 -296 3700 -280 ct 3715 -263 3723 -236 3723 -199 ct 3723 0 l  3673 0 l 
+p ef
+3954 1 m  3954 -409 l  4004 -409 l  4004 1 l  3954 1 l  p ef
+4081 -362 m  4081 -409 l  4130 -409 l  4130 -362 l  4081 -362 l  p
+4081 0 m  4081 -298 l  4130 -298 l  4130 0 l  4081 0 l  p ef
+4397 0 m  4397 -189 l  4397 -209 4395 -224 4391 -235 ct 4388 -246 4381 -254 4373 -258 ct 
+4365 -263 4352 -266 4336 -266 ct 4312 -266 4293 -257 4279 -241 ct 4266 -225 4259 -202 4259 -173 ct 
+4259 0 l  4209 0 l  4209 -235 l  4209 -270 4209 -291 4207 -298 ct 4254 -298 l 
+4254 -298 4255 -295 4255 -291 ct 4255 -287 4255 -282 4256 -277 ct 4256 -272 4256 -262 4256 -247 ct 
+4257 -247 l  4269 -268 4282 -283 4297 -291 ct 4312 -300 4330 -304 4353 -304 ct 
+4385 -304 4409 -296 4424 -280 ct 4439 -263 4447 -236 4447 -199 ct 4447 0 l  4397 0 l 
+p ef
+4708 0 m  4607 -136 l  4571 -106 l  4571 0 l  4521 0 l  4521 -409 l  4571 -409 l 
+4571 -153 l  4701 -298 l  4760 -298 l  4639 -170 l  4766 0 l  4708 0 l 
+p ef
+4966 0 m  4966 -42 l  5065 -42 l  5065 -341 l  4977 -278 l  4977 -325 l 
+5069 -389 l  5115 -389 l  5115 -42 l  5209 -42 l  5209 0 l  4966 0 l  p ef
+pom
+gs
+0.933 0.933 0.933 c 25096 17408 m  25097 17408 l  24806 17408 24520 17423 24268 17452 ct 
+24016 17480 23807 17521 23661 17571 ct 23516 17620 23439 17676 23439 17734 ct 
+23439 17977 l  23439 18221 l  23439 18553 l  23439 18797 l  23439 19041 l 
+23439 19041 l  23439 19099 23516 19155 23661 19204 ct 23807 19254 24016 19295 24268 19323 ct 
+24520 19352 24806 19367 25097 19367 ct 26338 19366 l  27580 19366 l  29269 19366 l 
+30511 19366 l  31753 19366 l  31753 19367 l  32044 19367 32330 19352 32582 19323 ct 
+32834 19295 33043 19254 33189 19204 ct 33334 19155 33411 19099 33411 19041 ct 
+33411 18797 l  33411 18553 l  33411 18221 l  33411 17977 l  33411 17733 l 
+33411 17734 l  33411 17734 l  33411 17676 33334 17620 33189 17571 ct 33043 17521 32834 17480 32582 17452 ct 
+32330 17423 32044 17408 31753 17408 ct 30511 17408 l  29269 17408 l  27580 17408 l 
+27577 16551 l  25096 17408 l  p ef
+gr
+gs
+0 lw 1 lj 25095 17408 m  25096 17408 l  24805 17408 24519 17423 24267 17452 ct 
+24015 17480 23806 17521 23660 17571 ct 23515 17620 23438 17676 23438 17734 ct 
+23438 17977 l  23438 18221 l  23438 18553 l  23438 18797 l  23438 19041 l 
+23438 19041 l  23438 19099 23515 19155 23660 19204 ct 23806 19254 24015 19295 24267 19323 ct 
+24519 19352 24805 19367 25096 19367 ct 26337 19366 l  27579 19366 l  29269 19366 l 
+30511 19366 l  31753 19366 l  31753 19367 l  32044 19367 32330 19352 32582 19323 ct 
+32834 19295 33043 19254 33189 19204 ct 33334 19155 33411 19099 33411 19041 ct 
+33411 18797 l  33411 18553 l  33411 18221 l  33411 17977 l  33411 17733 l 
+33411 17734 l  33411 17734 l  33411 17676 33334 17620 33189 17571 ct 33043 17521 32834 17480 32582 17452 ct 
+32330 17423 32044 17408 31753 17408 ct 30511 17408 l  29269 17408 l  27579 17408 l 
+27576 16551 l  25095 17408 l  pc
+gr
+pum
+24695 17954 t
+46 0 m  46 -389 l  99 -389 l  99 -43 l  295 -43 l  295 0 l  46 0 l  p ef
+351 -362 m  351 -409 l  400 -409 l  400 -362 l  351 -362 l  p
+351 0 m  351 -298 l  400 -298 l  400 0 l  351 0 l  p ef
+652 0 m  652 -189 l  652 -218 648 -238 640 -249 ct 632 -260 618 -266 597 -266 ct 
+576 -266 559 -258 547 -241 ct 535 -225 528 -202 528 -173 ct 528 0 l  479 0 l 
+479 -235 l  479 -270 479 -291 477 -298 ct 524 -298 l  524 -298 525 -295 525 -291 ct 
+525 -287 525 -282 526 -277 ct 526 -272 526 -262 526 -247 ct 527 -247 l  538 -269 550 -283 564 -292 ct 
+578 -300 595 -304 614 -304 ct 637 -304 655 -300 668 -290 ct 681 -281 690 -267 695 -247 ct 
+696 -247 l  706 -268 719 -282 733 -291 ct 748 -300 766 -304 786 -304 ct 817 -304 838 -296 852 -279 ct 
+866 -263 873 -236 873 -199 ct 873 0 l  824 0 l  824 -189 l  824 -218 820 -238 812 -249 ct 
+804 -260 790 -266 769 -266 ct 747 -266 731 -258 719 -242 ct 707 -225 701 -203 701 -173 ct 
+701 0 l  652 0 l  p ef
+948 -362 m  948 -409 l  997 -409 l  997 -362 l  948 -362 l  p
+948 0 m  948 -298 l  997 -298 l  997 0 l  948 0 l  p ef
+1190 -3 m  1173 2 1157 4 1139 4 ct 1100 4 1080 -19 1080 -64 ct 1080 -263 l  1046 -263 l 
+1046 -299 l  1082 -299 l  1096 -366 l  1130 -366 l  1130 -299 l  1185 -299 l 
+1185 -263 l  1130 -263 l  1130 -74 l  1130 -60 1132 -50 1137 -44 ct 1141 -38 1149 -35 1161 -35 ct 
+1168 -35 1177 -37 1190 -39 ct 1190 -3 l  p ef
+1270 -138 m  1270 -104 1277 -78 1291 -59 ct 1305 -41 1326 -31 1353 -31 ct 1375 -31 1392 -36 1405 -44 ct 
+1418 -53 1427 -64 1431 -77 ct 1475 -65 l  1457 -18 1416 6 1353 6 ct 1309 6 1276 -7 1253 -33 ct 
+1229 -60 1218 -99 1218 -151 ct 1218 -200 1229 -238 1253 -264 ct 1276 -291 1308 -304 1351 -304 ct 
+1439 -304 1483 -251 1483 -145 ct 1483 -138 l  1270 -138 l  p
+1431 -176 m  1429 -208 1421 -231 1407 -245 ct 1394 -260 1375 -267 1350 -267 ct 
+1326 -267 1307 -259 1293 -243 ct 1279 -227 1272 -204 1271 -176 ct 1431 -176 l 
+p ef
+1733 -48 m  1724 -28 1712 -15 1697 -6 ct 1681 2 1663 6 1640 6 ct 1603 6 1575 -7 1557 -32 ct 
+1540 -57 1531 -96 1531 -147 ct 1531 -252 1567 -304 1640 -304 ct 1663 -304 1682 -300 1697 -291 ct 
+1712 -283 1724 -270 1733 -252 ct 1734 -252 l  1733 -285 l  1733 -409 l  1783 -409 l 
+1783 -61 l  1783 -30 1783 -9 1784 0 ct 1737 0 l  1736 -2 1736 -9 1735 -20 ct 
+1735 -31 1734 -40 1734 -48 ct 1733 -48 l  p
+1583 -149 m  1583 -107 1588 -77 1599 -59 ct 1610 -41 1628 -32 1653 -32 ct 1681 -32 1701 -42 1714 -62 ct 
+1727 -81 1733 -111 1733 -152 ct 1733 -192 1727 -221 1714 -239 ct 1701 -258 1681 -267 1654 -267 ct 
+1629 -267 1611 -258 1599 -239 ct 1588 -221 1583 -191 1583 -149 ct p ef
+2204 0 m  2204 -189 l  2204 -209 2202 -224 2198 -235 ct 2195 -246 2188 -254 2180 -258 ct 
+2172 -263 2159 -266 2143 -266 ct 2119 -266 2100 -257 2086 -241 ct 2073 -225 2066 -202 2066 -173 ct 
+2066 0 l  2016 0 l  2016 -235 l  2016 -270 2016 -291 2014 -298 ct 2061 -298 l 
+2061 -298 2062 -295 2062 -291 ct 2062 -287 2062 -282 2063 -277 ct 2063 -272 2063 -262 2063 -247 ct 
+2064 -247 l  2076 -268 2089 -283 2104 -291 ct 2119 -300 2137 -304 2160 -304 ct 
+2192 -304 2216 -296 2231 -280 ct 2246 -263 2254 -236 2254 -199 ct 2254 0 l  2204 0 l 
+p ef
+2376 -298 m  2376 -109 l  2376 -89 2378 -74 2382 -63 ct 2386 -52 2392 -44 2401 -40 ct 
+2409 -35 2422 -32 2438 -32 ct 2462 -32 2481 -41 2494 -57 ct 2508 -73 2515 -96 2515 -125 ct 
+2515 -298 l  2565 -298 l  2565 -63 l  2565 -28 2565 -7 2566 0 ct 2519 0 l 
+2519 0 2519 -3 2519 -7 ct 2519 -11 2518 -16 2518 -21 ct 2518 -26 2518 -36 2517 -51 ct 
+2516 -51 l  2505 -30 2492 -15 2477 -7 ct 2462 2 2443 6 2421 6 ct 2388 6 2364 -2 2349 -18 ct 
+2334 -35 2327 -62 2327 -99 ct 2327 -298 l  2376 -298 l  p ef
+2816 0 m  2816 -189 l  2816 -218 2812 -238 2804 -249 ct 2796 -260 2782 -266 2761 -266 ct 
+2740 -266 2723 -258 2711 -241 ct 2699 -225 2692 -202 2692 -173 ct 2692 0 l  2643 0 l 
+2643 -235 l  2643 -270 2643 -291 2641 -298 ct 2688 -298 l  2688 -298 2689 -295 2689 -291 ct 
+2689 -287 2689 -282 2690 -277 ct 2690 -272 2690 -262 2690 -247 ct 2691 -247 l 
+2702 -269 2714 -283 2728 -292 ct 2742 -300 2759 -304 2778 -304 ct 2801 -304 2819 -300 2832 -290 ct 
+2845 -281 2854 -267 2859 -247 ct 2860 -247 l  2870 -268 2883 -282 2897 -291 ct 
+2912 -300 2930 -304 2950 -304 ct 2981 -304 3002 -296 3016 -279 ct 3030 -263 3037 -236 3037 -199 ct 
+3037 0 l  2988 0 l  2988 -189 l  2988 -218 2984 -238 2976 -249 ct 2968 -260 2954 -266 2933 -266 ct 
+2911 -266 2895 -258 2883 -242 ct 2871 -225 2865 -203 2865 -173 ct 2865 0 l  2816 0 l 
+p ef
+3363 -150 m  3363 -46 3326 6 3253 6 ct 3231 6 3212 2 3197 -6 ct 3182 -14 3170 -28 3161 -46 ct 
+3160 -46 l  3160 -40 3160 -31 3159 -20 ct 3158 -8 3158 -1 3157 0 ct 3109 0 l 
+3110 -9 3111 -30 3111 -61 ct 3111 -409 l  3161 -409 l  3161 -292 l  3161 -280 3160 -266 3159 -250 ct 
+3161 -250 l  3170 -269 3182 -283 3197 -291 ct 3212 -299 3231 -303 3253 -303 ct 
+3291 -303 3319 -291 3336 -265 ct 3354 -240 3363 -202 3363 -150 ct p
+3311 -148 m  3311 -190 3305 -220 3294 -238 ct 3283 -256 3266 -265 3241 -265 ct 
+3213 -265 3192 -255 3180 -236 ct 3167 -217 3161 -187 3161 -145 ct 3161 -106 3167 -77 3179 -59 ct 
+3192 -40 3212 -31 3240 -31 ct 3265 -31 3283 -40 3294 -58 ct 3305 -77 3311 -107 3311 -148 ct 
+p ef
+3463 -138 m  3463 -104 3470 -78 3484 -59 ct 3498 -41 3519 -31 3546 -31 ct 3568 -31 3585 -36 3598 -44 ct 
+3611 -53 3620 -64 3624 -77 ct 3668 -65 l  3650 -18 3609 6 3546 6 ct 3502 6 3469 -7 3446 -33 ct 
+3422 -60 3411 -99 3411 -151 ct 3411 -200 3422 -238 3446 -264 ct 3469 -291 3501 -304 3544 -304 ct 
+3632 -304 3676 -251 3676 -145 ct 3676 -138 l  3463 -138 l  p
+3624 -176 m  3622 -208 3614 -231 3600 -245 ct 3587 -260 3568 -267 3543 -267 ct 
+3519 -267 3500 -259 3486 -243 ct 3472 -227 3465 -204 3464 -176 ct 3624 -176 l 
+p ef
+3739 0 m  3739 -229 l  3739 -250 3739 -273 3737 -298 ct 3784 -298 l  3786 -265 3786 -244 3786 -238 ct 
+3788 -238 l  3795 -263 3805 -281 3815 -290 ct 3825 -299 3840 -304 3858 -304 ct 
+3865 -304 3872 -303 3878 -301 ct 3878 -256 l  3872 -258 3863 -258 3852 -258 ct 
+3831 -258 3816 -250 3805 -232 ct 3794 -214 3789 -189 3789 -156 ct 3789 0 l  3739 0 l 
+p ef
+4333 -149 m  4333 -97 4322 -58 4299 -32 ct 4276 -7 4242 6 4199 6 ct 4155 6 4122 -7 4100 -34 ct 
+4078 -60 4067 -99 4067 -149 ct 4067 -252 4111 -304 4200 -304 ct 4246 -304 4279 -291 4301 -266 ct 
+4322 -241 4333 -202 4333 -149 ct p
+4281 -149 m  4281 -190 4275 -220 4263 -239 ct 4250 -258 4230 -267 4201 -267 ct 
+4172 -267 4151 -257 4138 -238 ct 4125 -219 4119 -190 4119 -149 ct 4119 -110 4125 -80 4138 -60 ct 
+4151 -41 4171 -31 4198 -31 ct 4228 -31 4249 -40 4262 -59 ct 4275 -79 4281 -108 4281 -149 ct 
+p ef
+4455 -263 m  4455 0 l  4406 0 l  4406 -263 l  4364 -263 l  4364 -299 l 
+4406 -299 l  4406 -333 l  4406 -360 4412 -379 4424 -391 ct 4436 -403 4454 -409 4479 -409 ct 
+4492 -409 4504 -408 4514 -406 ct 4514 -368 l  4505 -369 4498 -370 4492 -370 ct 
+4479 -370 4470 -367 4464 -360 ct 4458 -354 4455 -342 4455 -326 ct 4455 -299 l 
+4514 -299 l  4514 -263 l  4455 -263 l  p ef
+4783 6 m  4753 6 4731 -2 4716 -18 ct 4700 -34 4693 -55 4693 -83 ct 4693 -114 4703 -138 4723 -154 ct 
+4744 -171 4776 -180 4822 -181 ct 4888 -182 l  4888 -198 l  4888 -222 4883 -240 4873 -250 ct 
+4863 -261 4847 -266 4825 -266 ct 4802 -266 4786 -262 4776 -255 ct 4766 -247 4760 -235 4758 -219 ct 
+4706 -223 l  4715 -277 4754 -304 4826 -304 ct 4863 -304 4891 -295 4910 -278 ct 
+4929 -261 4939 -236 4939 -203 ct 4939 -74 l  4939 -60 4941 -49 4944 -41 ct 4948 -34 4956 -30 4966 -30 ct 
+4971 -30 4977 -31 4983 -32 ct 4983 -1 l  4970 2 4957 3 4944 3 ct 4926 3 4913 -1 4904 -11 ct 
+4896 -21 4891 -36 4890 -57 ct 4888 -57 l  4876 -34 4861 -18 4844 -8 ct 4827 1 4807 6 4783 6 ct 
+p
+4794 -31 m  4812 -31 4829 -35 4843 -44 ct 4857 -52 4868 -63 4876 -78 ct 4884 -92 4888 -107 4888 -122 ct 
+4888 -147 l  4834 -146 l  4811 -145 4793 -143 4781 -139 ct 4769 -134 4760 -127 4754 -118 ct 
+4747 -109 4744 -97 4744 -82 ct 4744 -66 4748 -53 4757 -44 ct 4766 -36 4778 -31 4794 -31 ct 
+p ef
+5136 -3 m  5119 2 5103 4 5085 4 ct 5046 4 5026 -19 5026 -64 ct 5026 -263 l  4992 -263 l 
+4992 -299 l  5028 -299 l  5042 -366 l  5076 -366 l  5076 -299 l  5131 -299 l 
+5131 -263 l  5076 -263 l  5076 -74 l  5076 -60 5078 -50 5083 -44 ct 5087 -38 5095 -35 5107 -35 ct 
+5114 -35 5123 -37 5136 -39 ct 5136 -3 l  p ef
+5292 -3 m  5275 2 5259 4 5241 4 ct 5202 4 5182 -19 5182 -64 ct 5182 -263 l  5148 -263 l 
+5148 -299 l  5184 -299 l  5198 -366 l  5232 -366 l  5232 -299 l  5287 -299 l 
+5287 -263 l  5232 -263 l  5232 -74 l  5232 -60 5234 -50 5239 -44 ct 5243 -38 5251 -35 5263 -35 ct 
+5270 -35 5279 -37 5292 -39 ct 5292 -3 l  p ef
+5372 -138 m  5372 -104 5379 -78 5393 -59 ct 5407 -41 5428 -31 5455 -31 ct 5477 -31 5494 -36 5507 -44 ct 
+5520 -53 5529 -64 5533 -77 ct 5577 -65 l  5559 -18 5518 6 5455 6 ct 5411 6 5378 -7 5355 -33 ct 
+5331 -60 5320 -99 5320 -151 ct 5320 -200 5331 -238 5355 -264 ct 5378 -291 5410 -304 5453 -304 ct 
+5541 -304 5585 -251 5585 -145 ct 5585 -138 l  5372 -138 l  p
+5533 -176 m  5531 -208 5523 -231 5509 -245 ct 5496 -260 5477 -267 5452 -267 ct 
+5428 -267 5409 -259 5395 -243 ct 5381 -227 5374 -204 5373 -176 ct 5533 -176 l 
+p ef
+5821 0 m  5821 -189 l  5821 -218 5817 -238 5809 -249 ct 5801 -260 5787 -266 5766 -266 ct 
+5745 -266 5728 -258 5716 -241 ct 5704 -225 5697 -202 5697 -173 ct 5697 0 l  5648 0 l 
+5648 -235 l  5648 -270 5648 -291 5646 -298 ct 5693 -298 l  5693 -298 5694 -295 5694 -291 ct 
+5694 -287 5694 -282 5695 -277 ct 5695 -272 5695 -262 5695 -247 ct 5696 -247 l 
+5707 -269 5719 -283 5733 -292 ct 5747 -300 5764 -304 5783 -304 ct 5806 -304 5824 -300 5837 -290 ct 
+5850 -281 5859 -267 5864 -247 ct 5865 -247 l  5875 -268 5888 -282 5902 -291 ct 
+5917 -300 5935 -304 5955 -304 ct 5986 -304 6007 -296 6021 -279 ct 6035 -263 6042 -236 6042 -199 ct 
+6042 0 l  5993 0 l  5993 -189 l  5993 -218 5989 -238 5981 -249 ct 5973 -260 5959 -266 5938 -266 ct 
+5916 -266 5900 -258 5888 -242 ct 5876 -225 5870 -203 5870 -173 ct 5870 0 l  5821 0 l 
+p ef
+6369 -151 m  6369 -47 6332 5 6259 5 ct 6213 5 6183 -12 6167 -47 ct 6165 -47 l 
+6166 -45 6167 -30 6167 0 ct 6167 117 l  6117 117 l  6117 -238 l  6117 -269 6116 -289 6115 -299 ct 
+6163 -299 l  6163 -298 6164 -295 6164 -291 ct 6164 -286 6165 -279 6165 -270 ct 
+6166 -261 6166 -254 6166 -251 ct 6167 -251 l  6176 -269 6188 -283 6202 -291 ct 
+6217 -300 6236 -304 6259 -304 ct 6296 -304 6324 -292 6342 -267 ct 6360 -242 6369 -204 6369 -151 ct 
+p
+6317 -150 m  6317 -191 6311 -221 6300 -239 ct 6289 -257 6271 -266 6247 -266 ct 
+6227 -266 6212 -262 6201 -253 ct 6189 -245 6181 -232 6175 -215 ct 6169 -197 6167 -174 6167 -146 ct 
+6167 -107 6173 -78 6185 -60 ct 6198 -41 6218 -32 6246 -32 ct 6271 -32 6289 -41 6300 -59 ct 
+6311 -77 6317 -107 6317 -150 ct p ef
+6545 -3 m  6528 2 6512 4 6494 4 ct 6455 4 6435 -19 6435 -64 ct 6435 -263 l  6401 -263 l 
+6401 -299 l  6437 -299 l  6451 -366 l  6485 -366 l  6485 -299 l  6540 -299 l 
+6540 -263 l  6485 -263 l  6485 -74 l  6485 -60 6487 -50 6492 -44 ct 6496 -38 6504 -35 6516 -35 ct 
+6523 -35 6532 -37 6545 -39 ct 6545 -3 l  p ef
+6811 -82 m  6811 -54 6800 -32 6779 -17 ct 6758 -2 6728 6 6690 6 ct 6653 6 6624 0 6604 -12 ct 
+6584 -25 6571 -44 6565 -70 ct 6608 -78 l  6613 -62 6621 -50 6635 -43 ct 6648 -36 6666 -32 6690 -32 ct 
+6715 -32 6733 -36 6745 -43 ct 6757 -51 6762 -63 6762 -78 ct 6762 -90 6758 -99 6750 -107 ct 
+6742 -114 6729 -120 6711 -125 ct 6676 -134 l  6647 -142 6627 -149 6615 -156 ct 
+6603 -163 6594 -172 6587 -182 ct 6580 -192 6577 -204 6577 -219 ct 6577 -246 6586 -267 6606 -281 ct 
+6625 -295 6653 -303 6690 -303 ct 6723 -303 6749 -297 6769 -285 ct 6788 -274 6800 -255 6805 -229 ct 
+6761 -224 l  6758 -237 6751 -247 6739 -254 ct 6727 -261 6710 -265 6690 -265 ct 
+6668 -265 6651 -262 6641 -255 ct 6630 -248 6625 -238 6625 -224 ct 6625 -215 6627 -208 6631 -203 ct 
+6636 -197 6642 -193 6651 -189 ct 6660 -185 6678 -180 6705 -173 ct 6732 -166 6751 -160 6762 -155 ct 
+6774 -149 6783 -143 6790 -136 ct 6796 -129 6801 -121 6805 -112 ct 6809 -103 6811 -93 6811 -82 ct 
+p ef
+7103 6 m  7073 6 7051 -2 7036 -18 ct 7020 -34 7013 -55 7013 -83 ct 7013 -114 7023 -138 7043 -154 ct 
+7064 -171 7096 -180 7142 -181 ct 7208 -182 l  7208 -198 l  7208 -222 7203 -240 7193 -250 ct 
+7183 -261 7167 -266 7145 -266 ct 7122 -266 7106 -262 7096 -255 ct 7086 -247 7080 -235 7078 -219 ct 
+7026 -223 l  7035 -277 7074 -304 7146 -304 ct 7183 -304 7211 -295 7230 -278 ct 
+7249 -261 7259 -236 7259 -203 ct 7259 -74 l  7259 -60 7261 -49 7264 -41 ct 7268 -34 7276 -30 7286 -30 ct 
+7291 -30 7297 -31 7303 -32 ct 7303 -1 l  7290 2 7277 3 7264 3 ct 7246 3 7233 -1 7224 -11 ct 
+7216 -21 7211 -36 7210 -57 ct 7208 -57 l  7196 -34 7181 -18 7164 -8 ct 7147 1 7127 6 7103 6 ct 
+p
+7114 -31 m  7132 -31 7149 -35 7163 -44 ct 7177 -52 7188 -63 7196 -78 ct 7204 -92 7208 -107 7208 -122 ct 
+7208 -147 l  7154 -146 l  7131 -145 7113 -143 7101 -139 ct 7089 -134 7080 -127 7074 -118 ct 
+7067 -109 7064 -97 7064 -82 ct 7064 -66 7068 -53 7077 -44 ct 7086 -36 7098 -31 7114 -31 ct 
+p ef
+7456 -3 m  7439 2 7423 4 7405 4 ct 7366 4 7346 -19 7346 -64 ct 7346 -263 l  7312 -263 l 
+7312 -299 l  7348 -299 l  7362 -366 l  7396 -366 l  7396 -299 l  7451 -299 l 
+7451 -263 l  7396 -263 l  7396 -74 l  7396 -60 7398 -50 7403 -44 ct 7407 -38 7415 -35 7427 -35 ct 
+7434 -35 7443 -37 7456 -39 ct 7456 -3 l  p ef
+pom
+pum
+24654 18581 t
+262 -82 m  262 -54 251 -32 230 -17 ct 209 -2 179 6 141 6 ct 104 6 75 0 55 -12 ct 
+35 -25 22 -44 16 -70 ct 59 -78 l  64 -62 72 -50 86 -43 ct 99 -36 117 -32 141 -32 ct 
+166 -32 184 -36 196 -43 ct 208 -51 213 -63 213 -78 ct 213 -90 209 -99 201 -107 ct 
+193 -114 180 -120 162 -125 ct 127 -134 l  98 -142 78 -149 66 -156 ct 54 -163 45 -172 38 -182 ct 
+31 -192 28 -204 28 -219 ct 28 -246 37 -267 57 -281 ct 76 -295 104 -303 141 -303 ct 
+174 -303 200 -297 220 -285 ct 239 -274 251 -255 256 -229 ct 212 -224 l  209 -237 202 -247 190 -254 ct 
+178 -261 161 -265 141 -265 ct 119 -265 102 -262 92 -255 ct 81 -248 76 -238 76 -224 ct 
+76 -215 78 -208 82 -203 ct 87 -197 93 -193 102 -189 ct 111 -185 129 -180 156 -173 ct 
+183 -166 202 -160 213 -155 ct 225 -149 234 -143 241 -136 ct 247 -129 252 -121 256 -112 ct 
+260 -103 262 -93 262 -82 ct p ef
+437 -3 m  420 2 404 4 386 4 ct 347 4 327 -19 327 -64 ct 327 -263 l  293 -263 l 
+293 -299 l  329 -299 l  343 -366 l  377 -366 l  377 -299 l  432 -299 l 
+432 -263 l  377 -263 l  377 -74 l  377 -60 379 -50 384 -44 ct 388 -38 396 -35 408 -35 ct 
+415 -35 424 -37 437 -39 ct 437 -3 l  p ef
+554 6 m  524 6 502 -2 487 -18 ct 471 -34 464 -55 464 -83 ct 464 -114 474 -138 494 -154 ct 
+515 -171 547 -180 593 -181 ct 659 -182 l  659 -198 l  659 -222 654 -240 644 -250 ct 
+634 -261 618 -266 596 -266 ct 573 -266 557 -262 547 -255 ct 537 -247 531 -235 529 -219 ct 
+477 -223 l  486 -277 525 -304 597 -304 ct 634 -304 662 -295 681 -278 ct 700 -261 710 -236 710 -203 ct 
+710 -74 l  710 -60 712 -49 715 -41 ct 719 -34 727 -30 737 -30 ct 742 -30 748 -31 754 -32 ct 
+754 -1 l  741 2 728 3 715 3 ct 697 3 684 -1 675 -11 ct 667 -21 662 -36 661 -57 ct 
+659 -57 l  647 -34 632 -18 615 -8 ct 598 1 578 6 554 6 ct p
+565 -31 m  583 -31 600 -35 614 -44 ct 628 -52 639 -63 647 -78 ct 655 -92 659 -107 659 -122 ct 
+659 -147 l  605 -146 l  582 -145 564 -143 552 -139 ct 540 -134 531 -127 525 -118 ct 
+518 -109 515 -97 515 -82 ct 515 -66 519 -53 528 -44 ct 537 -36 549 -31 565 -31 ct 
+p ef
+793 0 m  793 -229 l  793 -250 793 -273 791 -298 ct 838 -298 l  840 -265 840 -244 840 -238 ct 
+842 -238 l  849 -263 859 -281 869 -290 ct 879 -299 894 -304 912 -304 ct 919 -304 926 -303 932 -301 ct 
+932 -256 l  926 -258 917 -258 906 -258 ct 885 -258 870 -250 859 -232 ct 848 -214 843 -189 843 -156 ct 
+843 0 l  793 0 l  p ef
+1093 -3 m  1076 2 1060 4 1042 4 ct 1003 4 983 -19 983 -64 ct 983 -263 l  949 -263 l 
+949 -299 l  985 -299 l  999 -366 l  1033 -366 l  1033 -299 l  1088 -299 l 
+1088 -263 l  1033 -263 l  1033 -74 l  1033 -60 1035 -50 1040 -44 ct 1044 -38 1052 -35 1064 -35 ct 
+1071 -35 1080 -37 1093 -39 ct 1093 -3 l  p ef
+1134 -362 m  1134 -409 l  1183 -409 l  1183 -362 l  1134 -362 l  p
+1134 0 m  1134 -298 l  1183 -298 l  1183 0 l  1134 0 l  p ef
+1450 0 m  1450 -189 l  1450 -209 1448 -224 1444 -235 ct 1441 -246 1434 -254 1426 -258 ct 
+1418 -263 1405 -266 1389 -266 ct 1365 -266 1346 -257 1332 -241 ct 1319 -225 1312 -202 1312 -173 ct 
+1312 0 l  1262 0 l  1262 -235 l  1262 -270 1262 -291 1260 -298 ct 1307 -298 l 
+1307 -298 1308 -295 1308 -291 ct 1308 -287 1308 -282 1309 -277 ct 1309 -272 1309 -262 1309 -247 ct 
+1310 -247 l  1322 -268 1335 -283 1350 -291 ct 1365 -300 1383 -304 1406 -304 ct 
+1438 -304 1462 -296 1477 -280 ct 1492 -263 1500 -236 1500 -199 ct 1500 0 l  1450 0 l 
+p ef
+1688 117 m  1655 117 1630 111 1610 98 ct 1591 85 1579 67 1573 43 ct 1623 36 l 
+1626 50 1634 61 1645 68 ct 1656 75 1671 79 1689 79 ct 1739 79 1763 50 1763 -8 ct 
+1763 -56 l  1763 -56 l  1753 -37 1741 -22 1724 -12 ct 1708 -3 1689 2 1667 2 ct 
+1630 2 1604 -10 1586 -34 ct 1569 -59 1561 -97 1561 -149 ct 1561 -201 1570 -240 1588 -265 ct 
+1607 -290 1635 -303 1673 -303 ct 1694 -303 1712 -298 1727 -289 ct 1743 -279 1755 -265 1763 -247 ct 
+1764 -247 l  1764 -253 1764 -262 1765 -276 ct 1766 -290 1767 -297 1767 -298 ct 
+1814 -298 l  1813 -288 1813 -268 1813 -237 ct 1813 -9 l  1813 75 1771 117 1688 117 ct 
+p
+1763 -149 m  1763 -173 1760 -194 1753 -212 ct 1747 -229 1738 -243 1726 -252 ct 
+1713 -261 1700 -266 1685 -266 ct 1659 -266 1641 -257 1629 -238 ct 1618 -220 1612 -190 1612 -149 ct 
+1612 -108 1617 -79 1628 -61 ct 1639 -44 1658 -35 1684 -35 ct 1699 -35 1713 -39 1725 -48 ct 
+1737 -58 1747 -71 1753 -88 ct 1760 -105 1763 -126 1763 -149 ct p ef
+2121 6 m  2091 6 2069 -2 2054 -18 ct 2038 -34 2031 -55 2031 -83 ct 2031 -114 2041 -138 2061 -154 ct 
+2082 -171 2114 -180 2160 -181 ct 2226 -182 l  2226 -198 l  2226 -222 2221 -240 2211 -250 ct 
+2201 -261 2185 -266 2163 -266 ct 2140 -266 2124 -262 2114 -255 ct 2104 -247 2098 -235 2096 -219 ct 
+2044 -223 l  2053 -277 2092 -304 2164 -304 ct 2201 -304 2229 -295 2248 -278 ct 
+2267 -261 2277 -236 2277 -203 ct 2277 -74 l  2277 -60 2279 -49 2282 -41 ct 2286 -34 2294 -30 2304 -30 ct 
+2309 -30 2315 -31 2321 -32 ct 2321 -1 l  2308 2 2295 3 2282 3 ct 2264 3 2251 -1 2242 -11 ct 
+2234 -21 2229 -36 2228 -57 ct 2226 -57 l  2214 -34 2199 -18 2182 -8 ct 2165 1 2145 6 2121 6 ct 
+p
+2132 -31 m  2150 -31 2167 -35 2181 -44 ct 2195 -52 2206 -63 2214 -78 ct 2222 -92 2226 -107 2226 -122 ct 
+2226 -147 l  2172 -146 l  2149 -145 2131 -143 2119 -139 ct 2107 -134 2098 -127 2092 -118 ct 
+2085 -109 2082 -97 2082 -82 ct 2082 -66 2086 -53 2095 -44 ct 2104 -36 2116 -31 2132 -31 ct 
+p ef
+2547 0 m  2547 -189 l  2547 -209 2545 -224 2541 -235 ct 2538 -246 2531 -254 2523 -258 ct 
+2515 -263 2502 -266 2486 -266 ct 2462 -266 2443 -257 2429 -241 ct 2416 -225 2409 -202 2409 -173 ct 
+2409 0 l  2359 0 l  2359 -235 l  2359 -270 2359 -291 2357 -298 ct 2404 -298 l 
+2404 -298 2405 -295 2405 -291 ct 2405 -287 2405 -282 2406 -277 ct 2406 -272 2406 -262 2406 -247 ct 
+2407 -247 l  2419 -268 2432 -283 2447 -291 ct 2462 -300 2480 -304 2503 -304 ct 
+2535 -304 2559 -296 2574 -280 ct 2589 -263 2597 -236 2597 -199 ct 2597 0 l  2547 0 l 
+p ef
+2991 6 m  2960 6 2932 0 2908 -11 ct 2884 -23 2866 -40 2853 -62 ct 2840 -84 2834 -110 2834 -141 ct 
+2834 -388 l  2886 -388 l  2886 -145 l  2886 -110 2895 -83 2913 -64 ct 2931 -46 2957 -37 2991 -37 ct 
+3026 -37 3053 -46 3072 -65 ct 3092 -84 3101 -112 3101 -149 ct 3101 -388 l  3154 -388 l 
+3154 -146 l  3154 -114 3147 -87 3134 -64 ct 3121 -42 3102 -24 3077 -12 ct 3053 0 3024 6 2991 6 ct 
+p ef
+3242 0 m  3242 -389 l  3295 -389 l  3295 -43 l  3491 -43 l  3491 0 l  3242 0 l 
+p ef
+3834 -346 m  3834 -1 l  3782 -1 l  3782 -346 l  3649 -346 l  3649 -389 l 
+3968 -389 l  3968 -346 l  3834 -346 l  p ef
+4285 0 m  4169 -170 l  4050 0 l  3992 0 l  4139 -202 l  4003 -389 l  4061 -389 l 
+4169 -236 l  4274 -389 l  4332 -389 l  4199 -204 l  4343 0 l  4285 0 l 
+p ef
+4768 -196 m  4768 -155 4760 -119 4744 -89 ct 4729 -58 4707 -35 4678 -19 ct 4649 -2 4614 6 4575 6 ct 
+4535 6 4501 -2 4472 -18 ct 4443 -34 4421 -58 4406 -88 ct 4390 -119 4383 -155 4383 -196 ct 
+4383 -258 4400 -306 4434 -341 ct 4468 -376 4515 -394 4575 -394 ct 4615 -394 4649 -386 4678 -370 ct 
+4707 -355 4729 -332 4745 -302 ct 4760 -272 4768 -236 4768 -196 ct p
+4714 -196 m  4714 -244 4702 -282 4678 -310 ct 4654 -337 4620 -351 4575 -351 ct 
+4531 -351 4497 -337 4472 -310 ct 4448 -283 4436 -245 4436 -196 ct 4436 -147 4448 -108 4473 -80 ct 
+4497 -51 4531 -37 4575 -37 ct 4620 -37 4654 -51 4678 -78 ct 4702 -106 4714 -145 4714 -196 ct 
+p ef
+5138 -271 m  5138 -234 5126 -205 5103 -184 ct 5079 -162 5046 -151 5005 -151 ct 
+4891 -151 l  4891 1 l  4838 1 l  4838 -388 l  5002 -388 l  5045 -388 5079 -378 5103 -358 ct 
+5127 -337 5138 -308 5138 -271 ct p
+5086 -271 m  5086 -321 5055 -346 4995 -346 ct 4891 -346 l  4891 -193 l  4997 -193 l 
+5056 -193 5086 -219 5086 -271 ct p ef
+5636 0 m  5579 0 l  5527 -211 l  5517 -258 l  5515 -249 5513 -237 5509 -222 ct 
+5506 -206 5487 -132 5453 0 ct 5396 0 l  5312 -299 l  5361 -299 l  5412 -96 l 
+5413 -91 5417 -73 5423 -41 ct 5428 -62 l  5490 -299 l  5544 -299 l  5596 -94 l 
+5608 -41 l  5617 -79 l  5673 -299 l  5722 -299 l  5636 0 l  p ef
+5806 -247 m  5817 -266 5830 -281 5845 -290 ct 5860 -299 5879 -303 5902 -303 ct 
+5934 -303 5958 -295 5973 -279 ct 5988 -263 5996 -236 5996 -198 ct 5996 1 l  5946 1 l 
+5946 -189 l  5946 -210 5944 -225 5940 -235 ct 5937 -246 5930 -253 5921 -258 ct 
+5913 -263 5900 -265 5885 -265 ct 5861 -265 5843 -257 5829 -241 ct 5815 -225 5808 -203 5808 -175 ct 
+5808 1 l  5758 1 l  5758 -409 l  5808 -409 l  5808 -302 l  5808 -291 5807 -279 5807 -267 ct 
+5806 -256 5806 -249 5805 -247 ct 5806 -247 l  p ef
+6071 -362 m  6071 -409 l  6120 -409 l  6120 -362 l  6071 -362 l  p
+6071 0 m  6071 -298 l  6120 -298 l  6120 0 l  6071 0 l  p ef
+6198 1 m  6198 -409 l  6248 -409 l  6248 1 l  6198 1 l  p ef
+6363 -138 m  6363 -104 6370 -78 6384 -59 ct 6398 -41 6419 -31 6446 -31 ct 6468 -31 6485 -36 6498 -44 ct 
+6511 -53 6520 -64 6524 -77 ct 6568 -65 l  6550 -18 6509 6 6446 6 ct 6402 6 6369 -7 6346 -33 ct 
+6322 -60 6311 -99 6311 -151 ct 6311 -200 6322 -238 6346 -264 ct 6369 -291 6401 -304 6444 -304 ct 
+6532 -304 6576 -251 6576 -145 ct 6576 -138 l  6363 -138 l  p
+6524 -176 m  6522 -208 6514 -231 6500 -245 ct 6487 -260 6468 -267 6443 -267 ct 
+6419 -267 6400 -259 6386 -243 ct 6372 -227 6365 -204 6364 -176 ct 6524 -176 l 
+p ef
+6909 -3 m  6892 2 6876 4 6858 4 ct 6819 4 6799 -19 6799 -64 ct 6799 -263 l  6765 -263 l 
+6765 -299 l  6801 -299 l  6815 -366 l  6849 -366 l  6849 -299 l  6904 -299 l 
+6904 -263 l  6849 -263 l  6849 -74 l  6849 -60 6851 -50 6856 -44 ct 6860 -38 6868 -35 6880 -35 ct 
+6887 -35 6896 -37 6909 -39 ct 6909 -3 l  p ef
+7000 -247 m  7011 -266 7024 -281 7039 -290 ct 7054 -299 7073 -303 7096 -303 ct 
+7128 -303 7152 -295 7167 -279 ct 7182 -263 7190 -236 7190 -198 ct 7190 1 l  7140 1 l 
+7140 -189 l  7140 -210 7138 -225 7134 -235 ct 7131 -246 7124 -253 7115 -258 ct 
+7107 -263 7094 -265 7079 -265 ct 7055 -265 7037 -257 7023 -241 ct 7009 -225 7002 -203 7002 -175 ct 
+7002 1 l  6952 1 l  6952 -409 l  7002 -409 l  7002 -302 l  7002 -291 7001 -279 7001 -267 ct 
+7000 -256 7000 -249 6999 -247 ct 7000 -247 l  p ef
+7302 -138 m  7302 -104 7309 -78 7323 -59 ct 7337 -41 7358 -31 7385 -31 ct 7407 -31 7424 -36 7437 -44 ct 
+7450 -53 7459 -64 7463 -77 ct 7507 -65 l  7489 -18 7448 6 7385 6 ct 7341 6 7308 -7 7285 -33 ct 
+7261 -60 7250 -99 7250 -151 ct 7250 -200 7261 -238 7285 -264 ct 7308 -291 7340 -304 7383 -304 ct 
+7471 -304 7515 -251 7515 -145 ct 7515 -138 l  7302 -138 l  p
+7463 -176 m  7461 -208 7453 -231 7439 -245 ct 7426 -260 7407 -267 7382 -267 ct 
+7358 -267 7339 -259 7325 -243 ct 7311 -227 7304 -204 7303 -176 ct 7463 -176 l 
+p ef
+pom
+pum
+24064 19208 t
+376 0 m  376 -259 l  376 -288 377 -315 379 -342 ct 370 -309 362 -283 354 -265 ct 
+254 0 l  217 0 l  116 -265 l  100 -312 l  91 -342 l  92 -311 l  93 -259 l 
+93 0 l  46 0 l  46 -389 l  115 -389 l  219 -119 l  222 -108 226 -97 229 -84 ct 
+233 -72 235 -63 236 -57 ct 237 -65 241 -76 245 -91 ct 250 -106 253 -115 255 -119 ct 
+356 -389 l  424 -389 l  424 0 l  376 0 l  p ef
+546 -138 m  546 -104 553 -78 567 -59 ct 581 -41 602 -31 629 -31 ct 651 -31 668 -36 681 -44 ct 
+694 -53 703 -64 707 -77 ct 751 -65 l  733 -18 692 6 629 6 ct 585 6 552 -7 529 -33 ct 
+505 -60 494 -99 494 -151 ct 494 -200 505 -238 529 -264 ct 552 -291 584 -304 627 -304 ct 
+715 -304 759 -251 759 -145 ct 759 -138 l  546 -138 l  p
+707 -176 m  705 -208 697 -231 683 -245 ct 670 -260 651 -267 626 -267 ct 602 -267 583 -259 569 -243 ct 
+555 -227 548 -204 547 -176 ct 707 -176 l  p ef
+1009 -48 m  1000 -28 988 -15 973 -6 ct 957 2 939 6 916 6 ct 879 6 851 -7 833 -32 ct 
+816 -57 807 -96 807 -147 ct 807 -252 843 -304 916 -304 ct 939 -304 958 -300 973 -291 ct 
+988 -283 1000 -270 1009 -252 ct 1010 -252 l  1009 -285 l  1009 -409 l  1059 -409 l 
+1059 -61 l  1059 -30 1059 -9 1060 0 ct 1013 0 l  1012 -2 1012 -9 1011 -20 ct 
+1011 -31 1010 -40 1010 -48 ct 1009 -48 l  p
+859 -149 m  859 -107 864 -77 875 -59 ct 886 -41 904 -32 929 -32 ct 957 -32 977 -42 990 -62 ct 
+1003 -81 1009 -111 1009 -152 ct 1009 -192 1003 -221 990 -239 ct 977 -258 957 -267 930 -267 ct 
+905 -267 887 -258 875 -239 ct 864 -221 859 -191 859 -149 ct p ef
+1134 -362 m  1134 -409 l  1183 -409 l  1183 -362 l  1134 -362 l  p
+1134 0 m  1134 -298 l  1183 -298 l  1183 0 l  1134 0 l  p ef
+1309 -298 m  1309 -109 l  1309 -89 1311 -74 1315 -63 ct 1319 -52 1325 -44 1334 -40 ct 
+1342 -35 1355 -32 1371 -32 ct 1395 -32 1414 -41 1427 -57 ct 1441 -73 1448 -96 1448 -125 ct 
+1448 -298 l  1498 -298 l  1498 -63 l  1498 -28 1498 -7 1499 0 ct 1452 0 l 
+1452 0 1452 -3 1452 -7 ct 1452 -11 1451 -16 1451 -21 ct 1451 -26 1451 -36 1450 -51 ct 
+1449 -51 l  1438 -30 1425 -15 1410 -7 ct 1395 2 1376 6 1354 6 ct 1321 6 1297 -2 1282 -18 ct 
+1267 -35 1260 -62 1260 -99 ct 1260 -298 l  1309 -298 l  p ef
+1749 0 m  1749 -189 l  1749 -218 1745 -238 1737 -249 ct 1729 -260 1715 -266 1694 -266 ct 
+1673 -266 1656 -258 1644 -241 ct 1632 -225 1625 -202 1625 -173 ct 1625 0 l  1576 0 l 
+1576 -235 l  1576 -270 1576 -291 1574 -298 ct 1621 -298 l  1621 -298 1622 -295 1622 -291 ct 
+1622 -287 1622 -282 1623 -277 ct 1623 -272 1623 -262 1623 -247 ct 1624 -247 l 
+1635 -269 1647 -283 1661 -292 ct 1675 -300 1692 -304 1711 -304 ct 1734 -304 1752 -300 1765 -290 ct 
+1778 -281 1787 -267 1792 -247 ct 1793 -247 l  1803 -268 1816 -282 1830 -291 ct 
+1845 -300 1863 -304 1883 -304 ct 1914 -304 1935 -296 1949 -279 ct 1963 -263 1970 -236 1970 -199 ct 
+1970 0 l  1921 0 l  1921 -189 l  1921 -218 1917 -238 1909 -249 ct 1901 -260 1887 -266 1866 -266 ct 
+1844 -266 1828 -258 1816 -242 ct 1804 -225 1798 -203 1798 -173 ct 1798 0 l  1749 0 l 
+p ef
+2357 -107 m  2357 -71 2343 -43 2315 -24 ct 2287 -4 2248 6 2197 6 ct 2102 6 2048 -27 2033 -93 ct 
+2084 -103 l  2089 -80 2102 -62 2121 -52 ct 2140 -41 2166 -35 2199 -35 ct 2233 -35 2259 -41 2278 -53 ct 
+2296 -64 2305 -81 2305 -104 ct 2305 -117 2302 -127 2297 -135 ct 2291 -143 2283 -149 2272 -155 ct 
+2262 -160 2249 -164 2235 -168 ct 2220 -171 2204 -175 2187 -179 ct 2156 -186 2133 -192 2117 -199 ct 
+2101 -206 2088 -214 2079 -222 ct 2070 -230 2063 -240 2058 -251 ct 2053 -263 2051 -275 2051 -290 ct 
+2051 -323 2064 -349 2089 -367 ct 2114 -385 2151 -394 2198 -394 ct 2242 -394 2276 -387 2299 -374 ct 
+2323 -360 2339 -337 2348 -305 ct 2296 -296 l  2291 -316 2280 -331 2264 -340 ct 
+2248 -350 2226 -354 2198 -354 ct 2167 -354 2143 -349 2127 -339 ct 2110 -329 2102 -313 2102 -293 ct 
+2102 -281 2105 -271 2112 -263 ct 2118 -255 2127 -249 2139 -243 ct 2151 -238 2175 -231 2210 -223 ct 
+2222 -220 2234 -218 2246 -215 ct 2258 -212 2269 -209 2280 -205 ct 2291 -201 2301 -196 2310 -191 ct 
+2320 -185 2328 -179 2335 -171 ct 2342 -163 2347 -154 2351 -144 ct 2355 -133 2357 -121 2357 -107 ct 
+p ef
+2436 117 m  2422 117 2411 116 2401 114 ct 2401 77 l  2408 78 2416 78 2425 78 ct 
+2455 78 2480 56 2498 10 ct 2503 -2 l  2384 -299 l  2437 -299 l  2500 -134 l 
+2501 -131 2502 -128 2503 -124 ct 2505 -121 2509 -109 2516 -88 ct 2523 -68 2526 -56 2527 -54 ct 
+2546 -108 l  2612 -299 l  2664 -299 l  2549 0 l  2537 32 2526 55 2515 71 ct 
+2504 86 2492 98 2480 106 ct 2467 113 2452 117 2436 117 ct p ef
+2894 0 m  2894 -189 l  2894 -209 2892 -224 2888 -235 ct 2885 -246 2878 -254 2870 -258 ct 
+2862 -263 2849 -266 2833 -266 ct 2809 -266 2790 -257 2776 -241 ct 2763 -225 2756 -202 2756 -173 ct 
+2756 0 l  2706 0 l  2706 -235 l  2706 -270 2706 -291 2704 -298 ct 2751 -298 l 
+2751 -298 2752 -295 2752 -291 ct 2752 -287 2752 -282 2753 -277 ct 2753 -272 2753 -262 2753 -247 ct 
+2754 -247 l  2766 -268 2779 -283 2794 -291 ct 2809 -300 2827 -304 2850 -304 ct 
+2882 -304 2906 -296 2921 -280 ct 2936 -263 2944 -236 2944 -199 ct 2944 0 l  2894 0 l 
+p ef
+3056 -150 m  3056 -111 3062 -81 3074 -62 ct 3087 -43 3106 -33 3131 -33 ct 3149 -33 3163 -38 3175 -48 ct 
+3187 -57 3194 -72 3197 -92 ct 3247 -88 l  3243 -60 3231 -37 3211 -20 ct 3190 -3 3164 6 3132 6 ct 
+3091 6 3059 -7 3037 -34 ct 3015 -60 3004 -99 3004 -149 ct 3004 -200 3015 -238 3037 -264 ct 
+3059 -291 3091 -304 3132 -304 ct 3162 -304 3188 -296 3208 -280 ct 3228 -264 3240 -243 3245 -215 ct 
+3195 -211 l  3192 -227 3185 -241 3175 -250 ct 3165 -260 3150 -265 3130 -265 ct 
+3104 -265 3085 -256 3073 -239 ct 3062 -221 3056 -192 3056 -150 ct p ef
+3644 -199 m  3644 -158 3637 -123 3621 -93 ct 3605 -63 3583 -40 3555 -24 ct 3526 -8 3493 0 3455 0 ct 
+3310 0 l  3310 -389 l  3439 -389 l  3504 -389 3555 -372 3591 -339 ct 3626 -306 3644 -259 3644 -199 ct 
+p
+3591 -199 m  3591 -247 3578 -284 3552 -309 ct 3526 -334 3487 -347 3438 -347 ct 
+3363 -347 l  3363 -42 l  3449 -42 l  3478 -42 3503 -48 3524 -61 ct 3546 -73 3563 -92 3574 -115 ct 
+3586 -139 3591 -166 3591 -199 ct p ef
+3746 -138 m  3746 -104 3753 -78 3767 -59 ct 3781 -41 3802 -31 3829 -31 ct 3851 -31 3868 -36 3881 -44 ct 
+3894 -53 3903 -64 3907 -77 ct 3951 -65 l  3933 -18 3892 6 3829 6 ct 3785 6 3752 -7 3729 -33 ct 
+3705 -60 3694 -99 3694 -151 ct 3694 -200 3705 -238 3729 -264 ct 3752 -291 3784 -304 3827 -304 ct 
+3915 -304 3959 -251 3959 -145 ct 3959 -138 l  3746 -138 l  p
+3907 -176 m  3905 -208 3897 -231 3883 -245 ct 3870 -260 3851 -267 3826 -267 ct 
+3802 -267 3783 -259 3769 -243 ct 3755 -227 3748 -204 3747 -176 ct 3907 -176 l 
+p ef
+4022 1 m  4022 -409 l  4072 -409 l  4072 1 l  4022 1 l  p ef
+4225 6 m  4195 6 4173 -2 4158 -18 ct 4142 -34 4135 -55 4135 -83 ct 4135 -114 4145 -138 4165 -154 ct 
+4186 -171 4218 -180 4264 -181 ct 4330 -182 l  4330 -198 l  4330 -222 4325 -240 4315 -250 ct 
+4305 -261 4289 -266 4267 -266 ct 4244 -266 4228 -262 4218 -255 ct 4208 -247 4202 -235 4200 -219 ct 
+4148 -223 l  4157 -277 4196 -304 4268 -304 ct 4305 -304 4333 -295 4352 -278 ct 
+4371 -261 4381 -236 4381 -203 ct 4381 -74 l  4381 -60 4383 -49 4386 -41 ct 4390 -34 4398 -30 4408 -30 ct 
+4413 -30 4419 -31 4425 -32 ct 4425 -1 l  4412 2 4399 3 4386 3 ct 4368 3 4355 -1 4346 -11 ct 
+4338 -21 4333 -36 4332 -57 ct 4330 -57 l  4318 -34 4303 -18 4286 -8 ct 4269 1 4249 6 4225 6 ct 
+p
+4236 -31 m  4254 -31 4271 -35 4285 -44 ct 4299 -52 4310 -63 4318 -78 ct 4326 -92 4330 -107 4330 -122 ct 
+4330 -147 l  4276 -146 l  4253 -145 4235 -143 4223 -139 ct 4211 -134 4202 -127 4196 -118 ct 
+4189 -109 4186 -97 4186 -82 ct 4186 -66 4190 -53 4199 -44 ct 4208 -36 4220 -31 4236 -31 ct 
+p ef
+4477 117 m  4463 117 4452 116 4442 114 ct 4442 77 l  4449 78 4457 78 4466 78 ct 
+4496 78 4521 56 4539 10 ct 4544 -2 l  4425 -299 l  4478 -299 l  4541 -134 l 
+4542 -131 4543 -128 4544 -124 ct 4546 -121 4550 -109 4557 -88 ct 4564 -68 4567 -56 4568 -54 ct 
+4587 -108 l  4653 -299 l  4705 -299 l  4590 0 l  4578 32 4567 55 4556 71 ct 
+4545 86 4533 98 4521 106 ct 4508 113 4493 117 4477 117 ct p ef
+5017 -3 m  5000 2 4984 4 4966 4 ct 4927 4 4907 -19 4907 -64 ct 4907 -263 l  4873 -263 l 
+4873 -299 l  4909 -299 l  4923 -366 l  4957 -366 l  4957 -299 l  5012 -299 l 
+5012 -263 l  4957 -263 l  4957 -74 l  4957 -60 4959 -50 4964 -44 ct 4968 -38 4976 -35 4988 -35 ct 
+4995 -35 5004 -37 5017 -39 ct 5017 -3 l  p ef
+5059 -362 m  5059 -409 l  5108 -409 l  5108 -362 l  5059 -362 l  p
+5059 0 m  5059 -298 l  5108 -298 l  5108 0 l  5059 0 l  p ef
+5360 0 m  5360 -189 l  5360 -218 5356 -238 5348 -249 ct 5340 -260 5326 -266 5305 -266 ct 
+5284 -266 5267 -258 5255 -241 ct 5243 -225 5236 -202 5236 -173 ct 5236 0 l  5187 0 l 
+5187 -235 l  5187 -270 5187 -291 5185 -298 ct 5232 -298 l  5232 -298 5233 -295 5233 -291 ct 
+5233 -287 5233 -282 5234 -277 ct 5234 -272 5234 -262 5234 -247 ct 5235 -247 l 
+5246 -269 5258 -283 5272 -292 ct 5286 -300 5303 -304 5322 -304 ct 5345 -304 5363 -300 5376 -290 ct 
+5389 -281 5398 -267 5403 -247 ct 5404 -247 l  5414 -268 5427 -282 5441 -291 ct 
+5456 -300 5474 -304 5494 -304 ct 5525 -304 5546 -296 5560 -279 ct 5574 -263 5581 -236 5581 -199 ct 
+5581 0 l  5532 0 l  5532 -189 l  5532 -218 5528 -238 5520 -249 ct 5512 -260 5498 -266 5477 -266 ct 
+5455 -266 5439 -258 5427 -242 ct 5415 -225 5409 -203 5409 -173 ct 5409 0 l  5360 0 l 
+p ef
+5694 -138 m  5694 -104 5701 -78 5715 -59 ct 5729 -41 5750 -31 5777 -31 ct 5799 -31 5816 -36 5829 -44 ct 
+5842 -53 5851 -64 5855 -77 ct 5899 -65 l  5881 -18 5840 6 5777 6 ct 5733 6 5700 -7 5677 -33 ct 
+5653 -60 5642 -99 5642 -151 ct 5642 -200 5653 -238 5677 -264 ct 5700 -291 5732 -304 5775 -304 ct 
+5863 -304 5907 -251 5907 -145 ct 5907 -138 l  5694 -138 l  p
+5855 -176 m  5853 -208 5845 -231 5831 -245 ct 5818 -260 5799 -267 5774 -267 ct 
+5750 -267 5731 -259 5717 -243 ct 5703 -227 5696 -204 5695 -176 ct 5855 -176 l 
+p ef
+5970 0 m  5970 -229 l  5970 -250 5970 -273 5968 -298 ct 6015 -298 l  6017 -265 6017 -244 6017 -238 ct 
+6019 -238 l  6026 -263 6036 -281 6046 -290 ct 6056 -299 6071 -304 6089 -304 ct 
+6096 -304 6103 -303 6109 -301 ct 6109 -256 l  6103 -258 6094 -258 6083 -258 ct 
+6062 -258 6047 -250 6036 -232 ct 6025 -214 6020 -189 6020 -156 ct 6020 0 l  5970 0 l 
+p ef
+6312 -362 m  6312 -409 l  6361 -409 l  6361 -362 l  6312 -362 l  p
+6312 0 m  6312 -298 l  6361 -298 l  6361 0 l  6312 0 l  p ef
+6663 -82 m  6663 -54 6652 -32 6631 -17 ct 6610 -2 6580 6 6542 6 ct 6505 6 6476 0 6456 -12 ct 
+6436 -25 6423 -44 6417 -70 ct 6460 -78 l  6465 -62 6473 -50 6487 -43 ct 6500 -36 6518 -32 6542 -32 ct 
+6567 -32 6585 -36 6597 -43 ct 6609 -51 6614 -63 6614 -78 ct 6614 -90 6610 -99 6602 -107 ct 
+6594 -114 6581 -120 6563 -125 ct 6528 -134 l  6499 -142 6479 -149 6467 -156 ct 
+6455 -163 6446 -172 6439 -182 ct 6432 -192 6429 -204 6429 -219 ct 6429 -246 6438 -267 6458 -281 ct 
+6477 -295 6505 -303 6542 -303 ct 6575 -303 6601 -297 6621 -285 ct 6640 -274 6652 -255 6657 -229 ct 
+6613 -224 l  6610 -237 6603 -247 6591 -254 ct 6579 -261 6562 -265 6542 -265 ct 
+6520 -265 6503 -262 6493 -255 ct 6482 -248 6477 -238 6477 -224 ct 6477 -215 6479 -208 6483 -203 ct 
+6488 -197 6494 -193 6503 -189 ct 6512 -185 6530 -180 6557 -173 ct 6584 -166 6603 -160 6614 -155 ct 
+6626 -149 6635 -143 6642 -136 ct 6648 -129 6653 -121 6657 -112 ct 6661 -103 6663 -93 6663 -82 ct 
+p ef
+6880 0 m  6880 -229 l  6880 -250 6880 -273 6878 -298 ct 6925 -298 l  6927 -265 6927 -244 6927 -238 ct 
+6929 -238 l  6936 -263 6946 -281 6956 -290 ct 6966 -299 6981 -304 6999 -304 ct 
+7006 -304 7013 -303 7019 -301 ct 7019 -256 l  7013 -258 7004 -258 6993 -258 ct 
+6972 -258 6957 -250 6946 -232 ct 6935 -214 6930 -189 6930 -156 ct 6930 0 l  6880 0 l 
+p ef
+7113 -298 m  7113 -109 l  7113 -89 7115 -74 7119 -63 ct 7123 -52 7129 -44 7138 -40 ct 
+7146 -35 7159 -32 7175 -32 ct 7199 -32 7218 -41 7231 -57 ct 7245 -73 7252 -96 7252 -125 ct 
+7252 -298 l  7302 -298 l  7302 -63 l  7302 -28 7302 -7 7303 0 ct 7256 0 l 
+7256 0 7256 -3 7256 -7 ct 7256 -11 7255 -16 7255 -21 ct 7255 -26 7255 -36 7254 -51 ct 
+7253 -51 l  7242 -30 7229 -15 7214 -7 ct 7199 2 7180 6 7158 6 ct 7125 6 7101 -2 7086 -18 ct 
+7071 -35 7064 -62 7064 -99 ct 7064 -298 l  7113 -298 l  p ef
+7568 0 m  7568 -189 l  7568 -209 7566 -224 7562 -235 ct 7559 -246 7552 -254 7544 -258 ct 
+7536 -263 7523 -266 7507 -266 ct 7483 -266 7464 -257 7450 -241 ct 7437 -225 7430 -202 7430 -173 ct 
+7430 0 l  7380 0 l  7380 -235 l  7380 -270 7380 -291 7378 -298 ct 7425 -298 l 
+7425 -298 7426 -295 7426 -291 ct 7426 -287 7426 -282 7427 -277 ct 7427 -272 7427 -262 7427 -247 ct 
+7428 -247 l  7440 -268 7453 -283 7468 -291 ct 7483 -300 7501 -304 7524 -304 ct 
+7556 -304 7580 -296 7595 -280 ct 7610 -263 7618 -236 7618 -199 ct 7618 0 l  7568 0 l 
+p ef
+7881 0 m  7881 -189 l  7881 -209 7879 -224 7875 -235 ct 7872 -246 7865 -254 7857 -258 ct 
+7849 -263 7836 -266 7820 -266 ct 7796 -266 7777 -257 7763 -241 ct 7750 -225 7743 -202 7743 -173 ct 
+7743 0 l  7693 0 l  7693 -235 l  7693 -270 7693 -291 7691 -298 ct 7738 -298 l 
+7738 -298 7739 -295 7739 -291 ct 7739 -287 7739 -282 7740 -277 ct 7740 -272 7740 -262 7740 -247 ct 
+7741 -247 l  7753 -268 7766 -283 7781 -291 ct 7796 -300 7814 -304 7837 -304 ct 
+7869 -304 7893 -296 7908 -280 ct 7923 -263 7931 -236 7931 -199 ct 7931 0 l  7881 0 l 
+p ef
+8005 -362 m  8005 -409 l  8054 -409 l  8054 -362 l  8005 -362 l  p
+8005 0 m  8005 -298 l  8054 -298 l  8054 0 l  8005 0 l  p ef
+8321 0 m  8321 -189 l  8321 -209 8319 -224 8315 -235 ct 8312 -246 8305 -254 8297 -258 ct 
+8289 -263 8276 -266 8260 -266 ct 8236 -266 8217 -257 8203 -241 ct 8190 -225 8183 -202 8183 -173 ct 
+8183 0 l  8133 0 l  8133 -235 l  8133 -270 8133 -291 8131 -298 ct 8178 -298 l 
+8178 -298 8179 -295 8179 -291 ct 8179 -287 8179 -282 8180 -277 ct 8180 -272 8180 -262 8180 -247 ct 
+8181 -247 l  8193 -268 8206 -283 8221 -291 ct 8236 -300 8254 -304 8277 -304 ct 
+8309 -304 8333 -296 8348 -280 ct 8363 -263 8371 -236 8371 -199 ct 8371 0 l  8321 0 l 
+p ef
+8558 117 m  8525 117 8500 111 8480 98 ct 8461 85 8449 67 8443 43 ct 8493 36 l 
+8496 50 8504 61 8515 68 ct 8526 75 8541 79 8559 79 ct 8609 79 8633 50 8633 -8 ct 
+8633 -56 l  8633 -56 l  8623 -37 8611 -22 8594 -12 ct 8578 -3 8559 2 8537 2 ct 
+8500 2 8474 -10 8456 -34 ct 8439 -59 8431 -97 8431 -149 ct 8431 -201 8440 -240 8458 -265 ct 
+8477 -290 8505 -303 8543 -303 ct 8564 -303 8582 -298 8597 -289 ct 8613 -279 8625 -265 8633 -247 ct 
+8634 -247 l  8634 -253 8634 -262 8635 -276 ct 8636 -290 8637 -297 8637 -298 ct 
+8684 -298 l  8683 -288 8683 -268 8683 -237 ct 8683 -9 l  8683 75 8641 117 8558 117 ct 
+p
+8633 -149 m  8633 -173 8630 -194 8623 -212 ct 8617 -229 8608 -243 8596 -252 ct 
+8583 -261 8570 -266 8555 -266 ct 8529 -266 8511 -257 8499 -238 ct 8488 -220 8482 -190 8482 -149 ct 
+8482 -108 8487 -79 8498 -61 ct 8509 -44 8528 -35 8554 -35 ct 8569 -35 8583 -39 8595 -48 ct 
+8607 -58 8617 -71 8623 -88 ct 8630 -105 8633 -126 8633 -149 ct p ef
+pom
+0 20018 t 
+pom
+count op_count sub {pop} repeat countdictstack dict_count sub {end} repeat b4_inc_state restore
+%%PageTrailer
+%%Trailer
+%%EOF
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/doc/source/wifi-design.rst ns-3.41/src/wifi/doc/source/wifi-design.rst
--- ns-3.40/src/wifi/doc/source/wifi-design.rst	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/doc/source/wifi-design.rst	2024-02-09 14:44:08.573794800 -0800
@@ -189,6 +189,11 @@
 * OperationalRateSet is assumed to contain all mandatory rates (see
   `issue 183 <https://gitlab.com/nsnam/ns-3-dev/-/issues/183>`_)
 * The wifi manager always selects the lowest basic rate for management frames.
+* If a STA (AP or non-AP) supports VHT, a Block Ack agreement is always setup once
+  a first packet is enqueued regardless of whether it will be transmitted in an A-MPDU.
+* Once an A-MSDU is created, it is no longer modified, even before it is actually transmitted
+  for the first time. This means that this A-MSDU cannot be aggregated to other MSDUs using A-MSDU
+  aggregation.
 
 Design Details
 **************
@@ -768,6 +773,25 @@
 
    Illustration of signals tracking upon channel switching
 
+MU-MIMO PHY support
+###################
+
+There is a basic support for MU-MIMO at the PHY layer that has been introduced in release
+ns-3.40. The current model can be used for both downlink and uplink MU-MIMO transmissions.
+
+The use of OFDMA and MU-MIMO for Multi-User transmissions depends on how the TXVECTOR
+is filled in by the MAC layer (not implemented yet). Since mixed OFDMA and MU-MIMO
+configurations are not supported, the TXVECTOR determines it carries information for
+MU-MIMO if all users are assigned the same RU, otherwise it corresponds to an OFDMA
+transmission.
+
+At the PHY layer, OFDMA and MU-MIMO transmissions are handled in a similar way, the main difference
+lies in MU-MIMO having the same spectrum shared simultaneously with multiple transmitters (for
+the uplink direction). The current PHY abstraction model assumes perfect conditions where the
+interference helper is able to detect signals belonging to the same MU-MIMO transmission and make
+sure they do not interfere with each others. Interference with other signals, including other
+MU-MIMO transmissions, is still supported by the model.
+
 The MAC model
 =============
 
@@ -818,7 +842,10 @@
 the rest of the device that the link is down (association is lost) and
 restart the scanning process. Note that this can also happen when an
 association request fails without explicit refusal (i.e., the AP fails to
-respond to association request).
+respond to association request). In case of non-AP MLDs, in order for losing
+association, it is necessary that no beacon is received on any link for an
+interval of duration equal to the maximum number of missed beacons times the
+interval between two consecutive Beacon frames.
 
 Roaming
 #######
@@ -899,6 +926,8 @@
 
 *  Decrement the backoff timer.
 *  Initiate the transmission of a frame exchange sequence.
+*  Invoke the backoff procedure due to choosing not to transmit (to be included
+   in the version of the standard following IEEE 802.11-2020)
 *  Invoke the backoff procedure due to an internal collision.
 *  Do nothing.
 
@@ -1104,47 +1133,142 @@
 ##################################################
 
 The IEEE 802.11be amendment introduced EMLSR operating mode to allow a non-AP MLD to alternate
-frame exchanges over a subset of setup links identified as EMLSR links. |ns3| supports EMLSR
-operations as described in the following.
+frame exchanges over a subset of setup links identified as EMLSR links (see section 35.3.17 of
+IEEE 802.11be D4.1). |ns3| supports EMLSR operations as described in the following.
 
-Non-AP MLD side
----------------
+Architecture of a non-AP MLD supporting EMLSR operating mode
+------------------------------------------------------------
 
-A non-AP MLD supports EMLSR operating mode if the ``EmlsrActivated`` attribute of the EHT
-configuration is set to true. In such a case, the WifiMacHelper will install an EMLSR Manager
-by using the type and attribute values configured through the ``SetEmlsrManager`` method. The
-EMLSR Manager is a base class providing the ``EmlsrLinkSet`` attribute, which can be used to
-enable or disable EMLSR mode (after multi-link setup, EMLSR mode is disabled by default). Setting
-the ``EmlsrLinkSet`` attribute triggers the transmission of an EML Operating Mode Notification
-frame to the AP to communicate the new set of EMLSR links, if ML setup has been completed.
-Otherwise, the set of EMLSR links is stored and the EML Operating Mode Notification frame is
-sent as soon as the ML setup is completed. The selection of the link used to transmit
-the EML Operating Mode Notification frame is done by the EMLSR Manager subclass. The default
-EMLSR Manager subclass, ``DefaultEmlsrManager``, selects the link that was used to perform
-ML setup. When the non-AP MLD receives the acknowledgment for the EML Operating Mode Notification
-frame, it starts a timer whose duration is the transition timeout advertised by the AP MLD.
-When the timer expires, or the non-AP MLD receives an EML Operating Mode Notification frame
-from the AP MLD, the EMLSR mode is assumed to be enabled (or disabled).
+The architecture of a non-AP MLD supporting EMLSR operating mode is based on the assumption that
+only one PHY instance (referred to as the *main PHY*) has full TX/RX capabilities, while the other
+PHY instances (referred to as *auxiliary PHYs* or *aux PHYs* for brevity) have limited TX/RX
+capabilities. As a consequence, only the main PHY is able to carry out frame exchanges with the AP
+MLD. Given that frame exchanges can occur on any of the EMLSR links, the link on which the main PHY operates is dynamically switched during a simulation, as detailed below.
+
+Enabling/disabling EMLSR mode
+-----------------------------
+
+EMLSR mode can be enabled on the links (at least two) of a non-AP MLD that supports the EMLSR
+operating mode and performs ML setup with an AP MLD that supports the EMLSR operating mode. The
+``EmlsrActivated`` attribute of the EHT configuration of an MLD determines whether the EMLSR
+operating mode is supported by the MLD. When the ``EmlsrActivated`` attribute is set to true for
+a non-AP MLD, the WifiMacHelper will install an EMLSR Manager by using the type and attribute
+values configured through the ``SetEmlsrManager`` method.
+
+EMLSR mode on the links of a non-AP MLD can be enabled or disabled by using the ``EmlsrLinkSet``
+attribute of the EMLSR Manager base class (after multi-link setup, EMLSR mode is disabled by
+default). Setting the ``EmlsrLinkSet`` attribute triggers the transmission of an EML Operating
+Mode Notification frame to the AP to communicate the new set of EMLSR links, if ML setup has been
+completed. Otherwise, the set of EMLSR links is stored and the EML Operating Mode Notification
+frame is sent as soon as the ML setup is completed. Therefore, users can choose to enable EMLSR
+mode on some links upon the completion of ML setup by setting the ``EmlsrLinkSet`` attribute at
+(or before) initialization time; alternatively, they can leave the ``EmlsrLinkSet`` attribute
+empty at initialization time and set it at runtime to enable EMLSR mode on some links at a
+specific simulation time (after ML setup). The selection of the link used by the non-AP
+MLD to transmit the EML Operating Mode Notification frame is done by the EMLSR Manager subclass.
+The default EMLSR Manager subclass, ``DefaultEmlsrManager``, selects the link on which the main
+PHY is operating. When the non-AP MLD receives the acknowledgment for the EML Operating Mode
+Notification frame, it starts a timer whose duration is the transition timeout advertised by the AP MLD. When the timer expires, or the non-AP MLD receives an EML Operating Mode Notification
+frame from the AP MLD, the EMLSR mode is assumed to be enabled on the requested set of links
+(referred to as EMLSR links), if this set is not empty, or disabled, otherwise. The set of links
+on which it is requested to enable EMLSR mode must include the link on which the main PHY is
+operating; the PHY instances operating on the other links on which EMLSR mode is enabled are
+considered aux PHYs.
+
+The PHY instance acting as main PHY is configured through the ``MainPhyId`` attribute of the EMLSR
+Manager base class. Such a class also enables to define the TX/RX capabilities of the aux PHYs:
+
+* the ``AuxPhyMaxModClass`` attribute indicates the maximum modulation class supported by aux PHYs
+* the ``AuxPhyChannelWidth`` attribute indicates the maximum channel width (MHz) supported by aux
+  PHYs. The value of this attribute may be automatically capped based on the maximum supported
+  modulation class.
+* the ``AuxPhyTxCapable`` attribute indicates whether aux PHYs are capable of transmitting frames
+
+Downlink TXOP
+-------------
+
+.. _fig-emlsr-dl-txop:
+
+.. figure:: figures/emlsr-dl-txop.*
+   :align: center
 
-AP MLD side
+   EMLSR operations: Downlink TXOP
+
+When an AP MLD that supports EMLSR operating mode has to initiate a frame exchange on a link with
+a non-AP MLD that is operating in EMLSR mode on that link, it sends an MU-RTS Trigger Frame
+soliciting a response from the non-AP MLD (and possibly others) as the initial Control frame (ICF)
+for that exchange (see Figure :ref:`fig-emlsr-dl-txop`). The MU-RTS Trigger Frame is carried in a
+non-HT duplicate PPDU transmitted at a rate of 6 Mbps, 12 Mbps or 24 Mbps. When the
+transmission of an initial Control frame starts, the AP MLD blocks transmissions to the solicited
+EMLSR clients on the EMLSR links other than the link used to transmit the initial Control frame,
+so that the AP MLD does not initiate another frame exchange on such links. The MU-RTS Trigger
+Frame includes a Padding field whose transmission duration is the maximum among the padding
+delays advertised by all the EMLSR clients solicited by the MU-RTS Trigger Frame. The padding
+delay should be long enough to allow the EMLSR client to get ready to transmit the CTS response
+(e.g., make the main PHY switch to operate on the link where the DL TXOP is starting). In |ns3|,
+if the ICF is received by the main PHY, no link switch occurs. If the ICF is received by an aux
+PHY (as shown in Fig. :ref:`fig-emlsr-dl-txop`), the main PHY switches to operate on the link
+where the TXOP is starting at the end of the reception of the ICF (including the Padding field)
+and the main PHY sends the CTS response. In |ns3|, the behavior of the aux PHY that received the
+ICF is determined by the EMLSR Manager subclass. The ``DefaultEmlsrManager`` class provides the
+``SwitchAuxPhy`` attribute for this purpose. If this attribute is true, the aux PHY switches to
+operate on the link on which the main PHY was operating and stays on such a link even after
+the completion of the TXOP (as shown in Fig. :ref:`fig-emlsr-dl-txop`); if this attribute is
+false, the aux PHY does not perform any switch, but the main PHY switches back to operate on
+its original link once the TXOP is completed (as shown in Fig. :ref:`fig-emlsr-ul-txop`).
+
+
+The frame exchange with an EMLSR client is assumed to terminate when the AP MLD does not start a
+frame transmission a SIFS after the response to the last frame transmitted by the AP MLD or the
+AP MLD transmits a frame that is not addressed to the EMLSR client. When a frame exchange with an
+EMLSR client terminates, the AP MLD blocks transmissions on all the EMLSR links and starts a
+timer whose duration is the transition delay advertised by the EMLSR client. When the timer
+expires, the EMLSR client is assumed to be back to the listening operations and transmissions on
+all the EMLSR links are unblocked.
+
+The padding delay and the transition delay for an EMLSR client can be set through the
+``EmlsrPaddingDelay`` attribute and the ``EmlsrTransitionDelay`` attribute of the
+``EmlsrManager`` base class, respectively.
+
+Uplink TXOP
 -----------
-An AP MLD supports EMLSR operating mode if the ``EmlsrActivated`` attribute of the EHT
-configuration is set to true. When an AP MLD that supports EMLSR operating mode has to initiate a
-frame exchange with a non-AP MLD that is operating in EMLSR mode, it sends an MU-RTS Trigger Frame
-soliciting a response from the non-AP MLD (and possibly others) as the initial Control frame for
-that exchange. The MU-RTS Trigger Frame includes a Padding field whose transmission duration is the
-maximum among the padding delays advertised by all the EMLSR clients solicited by the MU-RTS
-Trigger Frame. Also, the MU-RTS Trigger Frame is carried in a non-HT (duplicate) PPDU transmitted
-at a rate of 6 Mbps, 12 Mbps or 24 Mbps. When the transmission of an initial Control frame starts,
-the AP MLD blocks transmissions to the solicited EMLSR clients on the EMLSR links other than the
-link used to transmit the initial Control frame, so that the AP MLD does not initiate another
-frame exchange on such links. The frame exchange with an EMLSR client is assumed to terminate
-when the AP MLD does not start a frame transmission a SIFS after the response to the last frame
-transmitted by the AP MLD or the AP MLD transmits a frame that is not addressed to the EMLSR
-client. When a frame exchange with an EMLSR client terminates, the AP MLD blocks transmissions on
-all the EMLSR links and starts a timer whose duration is the transition delay advertised by the
-EMLSR client. When the timer expires, the EMLSR client is assumed to be back to the listening
-operations and transmissions on all the EMLSR links are unblocked.
+
+.. _fig-emlsr-ul-txop:
+
+.. figure:: figures/emlsr-ul-txop.*
+   :align: center
+
+   EMLSR operations: Uplink TXOP
+
+An EMLSR client can start an UL TXOP on any of the EMLSR links. When channel access is obtained
+on a link where an aux PHY is operating, the aux PHY transmits an RTS frame and then the main PHY
+takes over the TXOP. Figure :ref:`fig-emlsr-ul-txop` shows that the EMLSR client obtains a TXOP on
+link 1, which the main PHY is operating on. Therefore, a data frame can be sent without
+protection. Clearly, the EMLSR client blocks transmissions on the other EMLSR links as soon as it
+starts transmitting the data frame, while the AP MLD blocks transmissions to the EMLSR client on
+the other EMLSR links as soon as it receives an MPDU from the EMLSR client. When the UL TXOP
+terminates, the AP MLD starts a transition delay timer, during which it does not attempt to start
+a frame exchange with the EMLSR client on any EMLSR link. The EMLSR client, instead, starts a
+MediumSyncDelay timer on all the EMLSR links other than the one on which the UL TXOP was carried
+out. While the MediumSyncDelay timer is running on a link, the EMLSR client can start an UL TXOP
+on that link, but it can perform at most a predefined number of attempts. The duration of the
+MediumSyncDelay timer, the maximum number of TXOP attempts and the threshold to be used instead
+of the normal CCA sensitivity for the primary 20 MHz channel are all advertised by the AP MLD and
+can be configured through the attributes of the ``EmlsrManager`` class: ``MediumSyncDuration``, ``MsdMaxNTxops`` and ``MsdOfdmEdThreshold``, respectively.
+
+Figure :ref:`fig-emlsr-ul-txop` also shows that, afterwards, the EMLSR client obtains a TXOP on
+link 0, which an aux PHY is operating on. The aux PHY transmits an RTS frame and, in the
+meantime, the main PHY is switched to operate on that link, receives the CTS response and
+transmits the data frame. In the example shown in Fig. :ref:`fig-emlsr-ul-txop`, the
+``SwitchAuxPhy`` attribute of the ``DefaultEmlsrManager`` class is set to false. This means that
+the aux PHY stays on link 0 and, therefore, no PHY is operating on link 1 while the main PHY is
+carrying out the UL TXOP on link 0. Once such UL TXOP terminates, the main PHY is switched back
+to operate on link 1. While the MediumSyncDelay timer is running on link 1, the EMLSR client
+obtains a TXOP on link 1 and, as mandated by the 802.11be specs, sends an RTS frame. If there is
+no response to the RTS frame, the EMLSR client can attempt to obtain the TXOP again and transmit
+another RTS frame. However, the number of attempts performed while the MediumSyncDelay timer is
+running is limited by the value advertised by the AP MLD and configured through the
+``MsdMaxNTxops`` attribute of the ``EmlsrManager`` class.
 
 Ack manager
 ###########
@@ -1396,7 +1520,7 @@
 * Wi-Fi queue management.  The files ``txop.*`` and ``qos-txop.*`` are of interest for this task.
 * Channel access management.  Users should modify the files ``channel-access-manager.*``, which grant access to
   ``Txop`` and ``QosTxop``.
-* Fragmentation and RTS threholds are handled by Wi-Fi remote station manager.  Note that Wi-Fi remote
+* Fragmentation and RTS thresholds are handled by Wi-Fi remote station manager.  Note that Wi-Fi remote
   station manager simply indicates if fragmentation and RTS are needed.  Fragmentation is handled by
   ``Txop`` or ``QosTxop`` while RTS/CTS transaction is handled by ``FrameExchangeManager``.
 * Modifying or creating new rate control algorithms can be done by creating a new child class of Wi-Fi remote
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/doc/source/wifi-user.rst ns-3.41/src/wifi/doc/source/wifi-user.rst
--- ns-3.40/src/wifi/doc/source/wifi-user.rst	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/doc/source/wifi-user.rst	2024-02-09 14:44:08.573794800 -0800
@@ -856,14 +856,15 @@
  Ptr<HeConfiguration> heConfiguration = wnd->GetHeConfiguration();
  heConfiguration->SetGuardInterval(NanoSeconds(1600));
 
-802.11ax allows extended compressed Block ACKs containing a 256-bits bitmap, making
+802.11ax allows compressed Block ACKs containing a 256-bits bitmap, making
 possible transmissions of A-MPDUs containing up to 256 MPDUs, depending on the
 negotiated buffer size. In order to configure the buffer size of an 802.11ax device,
 the following line of code could be used:
 
 .. sourcecode:: cpp
 
- heConfiguration->SetMpduBufferSize(256);
+ WifiMacHelper mac;
+ mac.SetType("ns3::StaWifiMac", "MpduBufferSize", UintegerValue(256));
 
 For transmitting large MPDUs, it might also be needed to increase the maximum
 aggregation size (see above).
@@ -1081,3 +1082,73 @@
   mobility.Install(c);
 
   // other set up (e.g. InternetStack, Application)
+
+EMLSR configuration
++++++++++++++++++++
+
+**DISCLAIMER** EMLSR support is still experimental. Simulations with EMLSR activated may crash unexpectedly.
+
+Proper EMLSR operations require the configuration of multiple RF interfaces, as illustrated above.
+Therefore, in order to configure EMLSR operations, we can start from the configuration shown above
+and apply the changes described in the following. Firstly, we need to activate EMLSR on both the
+AP MLD and the EMLSR client. This can be done by configuring the appropriate ``EhtConfiguration``
+attribute through the wifi helper:
+
+.. sourcecode:: cpp
+
+  WifiHelper wifi;
+  wifi.SetStandard(WIFI_STANDARD_80211be);
+  wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(true));
+
+The ``EhtConfiguration`` class also provides attributes for the various EMLSR parameters that an
+AP MLD that supports EMLSR has to advertise. Given that these attributes are simply ignored by
+non-AP MLDs, we can use the same wifi helper to configure such parameters:
+
+.. sourcecode:: cpp
+
+  WifiHelper wifi;
+  wifi.SetStandard(WIFI_STANDARD_80211be);
+  wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(true));
+  wifi.ConfigEhtOptions("TransitionTimeout", TimeValue(MicroSeconds(1024)));
+  wifi.ConfigEhtOptions("MediumSyncDuration", TimeValue(MicroSeconds(3200)));
+  wifi.ConfigEhtOptions("MsdOfdmEdThreshold", IntegerValue(-72);
+  wifi.ConfigEhtOptions("MsdMaxNTxops", UintegerValue(0)); // unlimited attempts
+
+EMLSR parameters that are client specific can be configured through the attributes of the EMLSR
+Manager class and subclass. The ``WifiMacHelper`` class provides the ``SetEmlsrManager``
+method to conveniently set the type of EMLSR Manager and its attributes:
+
+.. sourcecode:: cpp
+
+  // setup STA.
+  wifiMac.SetType("ns3::StaWifiMac",
+                  "Ssid", SsidValue(ssid),
+                  "ActiveProbing", BooleanValue(false));
+  wifiMac.SetEmlsrManager("ns3::DefaultEmlsrManager",
+                          "EmlsrLinkSet",
+                          StringValue("0,1,2"),  // enable EMLSR on all EMLSR links
+                          "MainPhyId",
+                          UintegerValue(1),
+                          "EmlsrPaddingDelay",
+                          TimeValue(MicroSeconds(32)),
+                          "EmlsrTransitionDelay",
+                          TimeValue(MicroSeconds(128)),
+                          "SwitchAuxPhy",
+                          BooleanValue(false),
+                          "AuxPhyChannelWidth",
+                          UintegerValue(40));
+  NetDeviceContainer staDevice = wifi.Install(wifiPhy, wifiMac, sta);
+  devices.Add(staDevice);
+
+Note that the channel switch delay should be less than the transition delay:
+
+.. sourcecode:: cpp
+
+  // SpectrumWifiPhyHelper (3 links)
+  SpectrumWifiPhyHelper phy(3);
+  phy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
+  phy.AddChannel(spectrumChannel2_4Ghz, WIFI_SPECTRUM_2_4_GHZ);
+  phy.AddChannel(spectrumChannel5Ghz, WIFI_SPECTRUM_5_GHZ);
+  phy.AddChannel(spectrumChannel6Ghz, WIFI_SPECTRUM_6_GHZ);
+  phy.Set("ChannelSwitchDelay", TimeValue(MicroSeconds(100)));
+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/examples/reference/bianchi11ax.py ns-3.41/src/wifi/examples/reference/bianchi11ax.py
--- ns-3.40/src/wifi/examples/reference/bianchi11ax.py	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/examples/reference/bianchi11ax.py	2024-02-09 14:44:08.573794800 -0800
@@ -16,152 +16,224 @@
 #
 # Authors:  Hao Yin and Sebastien Deronne
 #
-import numpy as np
 import math
 
+import numpy as np
+
 
 def bianchi_ax(data_rate, ack_rate, k, difs):
     # Parameters for 11ax
     nA = np.linspace(5, 50, 10)
     CWmin = 15
     CWmax = 1023
-    L_DATA = 1500 * 8 # data size in bits
-    L_ACK = 14 * 8    # ACK size in bits
-    #B = 1/(CWmin+1)
-    B=0
-    EP = L_DATA/(1-B)
-    T_GI = 800e-9                  # guard interval in seconds
-    T_SYMBOL_ACK = 4e-6            # symbol duration in seconds (for ACK)
-    T_SYMBOL_DATA = 12.8e-6 + T_GI # symbol duration in seconds (for DATA)
-    T_PHY_ACK = 20e-6              # PHY preamble & header duration in seconds (for ACK)
-    T_PHY_DATA = 44e-6             # PHY preamble & header duration in seconds (for DATA)
-    L_SERVICE = 16                 # service field length in bits
-    L_TAIL = 6                     # tail length in bits
-    L_MAC = (30) * 8               # MAC header size in bits
-    L_APP_HDR = 8 * 8              # bits added by the upper layer(s)
+    L_DATA = 1500 * 8  # data size in bits
+    L_ACK = 14 * 8  # ACK size in bits
+    # B = 1/(CWmin+1)
+    B = 0
+    EP = L_DATA / (1 - B)
+    T_GI = 800e-9  # guard interval in seconds
+    T_SYMBOL_ACK = 4e-6  # symbol duration in seconds (for ACK)
+    T_SYMBOL_DATA = 12.8e-6 + T_GI  # symbol duration in seconds (for DATA)
+    T_PHY_ACK = 20e-6  # PHY preamble & header duration in seconds (for ACK)
+    T_PHY_DATA = 44e-6  # PHY preamble & header duration in seconds (for DATA)
+    L_SERVICE = 16  # service field length in bits
+    L_TAIL = 6  # tail length in bits
+    L_MAC = (30) * 8  # MAC header size in bits
+    L_APP_HDR = 8 * 8  # bits added by the upper layer(s)
     T_SIFS = 16e-6
     T_DIFS = 34e-6
     T_SLOT = 9e-6
     delta = 1e-7
 
-    Aggregation_Type = 'A_MPDU'   #A_MPDU or A_MSDU (HYBRID not fully supported)
+    Aggregation_Type = "A_MPDU"  # A_MPDU or A_MSDU (HYBRID not fully supported)
     K_MSDU = 1
     K_MPDU = k
     L_MPDU_HEADER = 4
     L_MSDU_HEADER = 14 * 8
-    if (k <= 1):
-        Aggregation_Type = 'NONE'
+    if k <= 1:
+        Aggregation_Type = "NONE"
 
-    N_DBPS = data_rate * T_SYMBOL_DATA   # number of data bits per OFDM symbol
+    N_DBPS = data_rate * T_SYMBOL_DATA  # number of data bits per OFDM symbol
 
-    if (Aggregation_Type == 'NONE'):
-        N_SYMBOLS = math.ceil((L_SERVICE + (L_MAC + L_DATA + L_APP_HDR) + L_TAIL)/N_DBPS)
+    if Aggregation_Type == "NONE":
+        N_SYMBOLS = math.ceil((L_SERVICE + (L_MAC + L_DATA + L_APP_HDR) + L_TAIL) / N_DBPS)
         T_DATA = T_PHY_DATA + (T_SYMBOL_DATA * N_SYMBOLS)
         K_MPDU = 1
         K_MSDU = 1
 
-    if (Aggregation_Type == 'A_MSDU'):
-        N_SYMBOLS = math.ceil((L_SERVICE + K_MPDU*(L_MAC + L_MPDU_HEADER + K_MSDU*(L_MSDU_HEADER + L_DATA + L_APP_HDR)) + L_TAIL)/N_DBPS)
+    if Aggregation_Type == "A_MSDU":
+        N_SYMBOLS = math.ceil(
+            (
+                L_SERVICE
+                + K_MPDU * (L_MAC + L_MPDU_HEADER + K_MSDU * (L_MSDU_HEADER + L_DATA + L_APP_HDR))
+                + L_TAIL
+            )
+            / N_DBPS
+        )
         T_DATA = T_PHY_DATA + (T_SYMBOL_DATA * N_SYMBOLS)
 
-    if (Aggregation_Type == 'A_MPDU'):
-        N_SYMBOLS = math.ceil((L_SERVICE + K_MPDU*(L_MAC + L_MPDU_HEADER + L_DATA + L_APP_HDR) + L_TAIL)/N_DBPS)
+    if Aggregation_Type == "A_MPDU":
+        N_SYMBOLS = math.ceil(
+            (L_SERVICE + K_MPDU * (L_MAC + L_MPDU_HEADER + L_DATA + L_APP_HDR) + L_TAIL) / N_DBPS
+        )
         T_DATA = T_PHY_DATA + (T_SYMBOL_DATA * N_SYMBOLS)
 
-    #Calculate ACK Duration
-    N_DBPS = ack_rate * T_SYMBOL_ACK   # number of data bits per OFDM symbol
-    N_SYMBOLS = math.ceil((L_SERVICE + L_ACK + L_TAIL)/N_DBPS)
+    # Calculate ACK Duration
+    N_DBPS = ack_rate * T_SYMBOL_ACK  # number of data bits per OFDM symbol
+    N_SYMBOLS = math.ceil((L_SERVICE + L_ACK + L_TAIL) / N_DBPS)
     T_ACK = T_PHY_ACK + (T_SYMBOL_ACK * N_SYMBOLS)
 
     T_s = T_DATA + T_SIFS + T_ACK + T_DIFS
-    if difs == 1: #DIFS
+    if difs == 1:  # DIFS
         T_C = T_DATA + T_DIFS
     else:
         T_s = T_DATA + T_SIFS + T_ACK + T_DIFS + delta
         T_C = T_DATA + T_DIFS + T_SIFS + T_ACK + delta
 
-    T_S = T_s/(1-B) + T_SLOT
+    T_S = T_s / (1 - B) + T_SLOT
 
     S_bianchi = np.zeros(len(nA))
     for j in range(len(nA)):
-        n = nA[j]*1
+        n = nA[j] * 1
         W = CWmin + 1
-        m = math.log2((CWmax + 1)/(CWmin + 1))
+        m = math.log2((CWmax + 1) / (CWmin + 1))
         tau1 = np.linspace(0, 0.1, 100000)
-        p = 1 - np.power((1 - tau1),(n - 1))
-        ps = p*0
+        p = 1 - np.power((1 - tau1), (n - 1))
+        ps = p * 0
 
         for i in range(int(m)):
-            ps = ps + np.power(2*p, i)
+            ps = ps + np.power(2 * p, i)
 
-        taup = 2./(1 + W + p*W*ps)
+        taup = 2.0 / (1 + W + p * W * ps)
         b = np.argmin(np.abs(tau1 - taup))
         tau = taup[b]
 
         Ptr = 1 - math.pow((1 - tau), int(n))
-        Ps = n*tau*math.pow((1 - tau), int(n-1))/Ptr
+        Ps = n * tau * math.pow((1 - tau), int(n - 1)) / Ptr
 
-        S_bianchi[j] = K_MSDU*K_MPDU*Ps*Ptr*EP/((1-Ptr)*T_SLOT+Ptr*Ps*T_S+Ptr*(1-Ps)*T_C)/1e6
+        S_bianchi[j] = (
+            K_MSDU
+            * K_MPDU
+            * Ps
+            * Ptr
+            * EP
+            / ((1 - Ptr) * T_SLOT + Ptr * Ps * T_S + Ptr * (1 - Ps) * T_C)
+            / 1e6
+        )
 
     bianchi_result = S_bianchi
     return bianchi_result
 
+
 def str_result(bianchi_result, mcs, bw):
-    str_bianchi = '    {' + '\"HeMcs{:d}'.format(mcs) + '_{:d}MHz\"'.format(bw) + ', {\n'
-    for  i in range (len(bianchi_result)):
-        str_tmp = '        {' + '{:d}, {:.4f}'.format(5*(i+1), bianchi_result[i]) +'},\n'
-        str_bianchi  = str_bianchi + str_tmp
+    str_bianchi = "    {" + '"HeMcs{:d}'.format(mcs) + '_{:d}MHz"'.format(bw) + ", {\n"
+    for i in range(len(bianchi_result)):
+        str_tmp = "        {" + "{:d}, {:.4f}".format(5 * (i + 1), bianchi_result[i]) + "},\n"
+        str_bianchi = str_bianchi + str_tmp
     str_bianchi = str_bianchi + "    }},\n"
     print(str_bianchi)
     return str_bianchi
 
+
 # Settings for different MCS and mode
-data_rates_20MHz = [8.603e6, 17.206e6, 25.8e6, 34.4e6, 51.5e6, 68.8e6, 77.4e6, 86e6, 103.2e6, 114.7e6, 129e6, 143.4e6]
+data_rates_20MHz = [
+    8.603e6,
+    17.206e6,
+    25.8e6,
+    34.4e6,
+    51.5e6,
+    68.8e6,
+    77.4e6,
+    86e6,
+    103.2e6,
+    114.7e6,
+    129e6,
+    143.4e6,
+]
 ack_rates_20MHz = [6e6, 12e6, 12e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6]
-data_rates_40MHz = [17.2e6, 34.4e6, 51.5e6, 68.8e6, 103.2e6, 137.6e6, 154.9e6, 172.1e6, 206.5e6, 229.4e6, 258.1e6, 286.8e6]
+data_rates_40MHz = [
+    17.2e6,
+    34.4e6,
+    51.5e6,
+    68.8e6,
+    103.2e6,
+    137.6e6,
+    154.9e6,
+    172.1e6,
+    206.5e6,
+    229.4e6,
+    258.1e6,
+    286.8e6,
+]
 ack_rates_40MHz = [6e6, 12e6, 12e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6]
-data_rates_80MHz = [36e6, 72.1e6, 108.1e6, 144.1e6, 216.2e6, 288.2e6, 324.3e6, 360.3e6, 432.4e6, 480.4e6, 540.4e6, 600.5e6]
+data_rates_80MHz = [
+    36e6,
+    72.1e6,
+    108.1e6,
+    144.1e6,
+    216.2e6,
+    288.2e6,
+    324.3e6,
+    360.3e6,
+    432.4e6,
+    480.4e6,
+    540.4e6,
+    600.5e6,
+]
 ack_rates_80MHz = [6e6, 12e6, 12e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6]
-data_rates_160MHz = [72.1e6, 144.1e6, 216.2e6, 288.2e6, 432.4e6, 576.5e6, 648.5e6, 720.6e6, 864.7e6, 960.8e6, 1080.9e6, 1201e6]
+data_rates_160MHz = [
+    72.1e6,
+    144.1e6,
+    216.2e6,
+    288.2e6,
+    432.4e6,
+    576.5e6,
+    648.5e6,
+    720.6e6,
+    864.7e6,
+    960.8e6,
+    1080.9e6,
+    1201e6,
+]
 ack_rates_160MHz = [6e6, 12e6, 12e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6, 24e6]
 
 # Generate results with frame aggregation disabled
 k = 1
 
 difs = 1
-with open("bianchi_11ax_difs.txt", "w", encoding="utf-8") as fo:
+with open("bianchi_11ax_difs.txt", "w", encoding="utf-8") as f:
     for i in range(len(data_rates_20MHz)):
         bianchi_result = bianchi_ax(data_rates_20MHz[i], ack_rates_20MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 20)
-        fo.write(str_s)
+        f.write(str_s)
     for i in range(len(data_rates_40MHz)):
         bianchi_result = bianchi_ax(data_rates_40MHz[i], ack_rates_40MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 40)
-        fo.write(str_s)
+        f.write(str_s)
     for i in range(len(data_rates_80MHz)):
         bianchi_result = bianchi_ax(data_rates_80MHz[i], ack_rates_80MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 80)
-        fo.write(str_s)
+        f.write(str_s)
     for i in range(len(data_rates_160MHz)):
         bianchi_result = bianchi_ax(data_rates_160MHz[i], ack_rates_160MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 160)
-        fo.write(str_s)
+        f.write(str_s)
 
 difs = 0
-with open("bianchi_11ax_eifs.txt", "w", encoding="utf-8") as fo:
+with open("bianchi_11ax_eifs.txt", "w", encoding="utf-8") as f:
     for i in range(len(data_rates_20MHz)):
         bianchi_result = bianchi_ax(data_rates_20MHz[i], ack_rates_20MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 20)
-        fo.write(str_s)
+        f.write(str_s)
     for i in range(len(data_rates_40MHz)):
         bianchi_result = bianchi_ax(data_rates_40MHz[i], ack_rates_40MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 40)
-        fo.write(str_s)
+        f.write(str_s)
     for i in range(len(data_rates_80MHz)):
         bianchi_result = bianchi_ax(data_rates_80MHz[i], ack_rates_80MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 80)
-        fo.write(str_s)
+        f.write(str_s)
     for i in range(len(data_rates_160MHz)):
         bianchi_result = bianchi_ax(data_rates_160MHz[i], ack_rates_160MHz[i], k, difs)
         str_s = str_result(bianchi_result, i, 160)
-        fo.write(str_s)
+        f.write(str_s)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/examples/wifi-manager-example.cc ns-3.41/src/wifi/examples/wifi-manager-example.cc
--- ns-3.40/src/wifi/examples/wifi-manager-example.cc	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/examples/wifi-manager-example.cc	2024-02-09 14:44:08.573794800 -0800
@@ -289,14 +289,14 @@
         {
             serverChannelWidth = GetDefaultChannelWidth(WIFI_STANDARD_80211b, WIFI_PHY_BAND_2_4GHZ);
         }
-        NS_ABORT_MSG_IF(serverChannelWidth != 22 && serverChannelWidth != 22,
+        NS_ABORT_MSG_IF(serverChannelWidth != 22,
                         "Invalid channel width for standard " << standard);
         NS_ABORT_MSG_IF(serverNss != 1, "Invalid nss for standard " << standard);
         if (clientChannelWidth == 0)
         {
             clientChannelWidth = GetDefaultChannelWidth(WIFI_STANDARD_80211b, WIFI_PHY_BAND_2_4GHZ);
         }
-        NS_ABORT_MSG_IF(clientChannelWidth != 22 && clientChannelWidth != 22,
+        NS_ABORT_MSG_IF(clientChannelWidth != 22,
                         "Invalid channel width for standard " << standard);
         NS_ABORT_MSG_IF(clientNss != 1, "Invalid nss for standard " << standard);
     }
@@ -635,7 +635,7 @@
     NetDeviceContainer serverDevice;
     NetDeviceContainer clientDevice;
 
-    TupleValue<UintegerValue, UintegerValue, EnumValue, UintegerValue> channelValue;
+    TupleValue<UintegerValue, UintegerValue, EnumValue<WifiPhyBand>, UintegerValue> channelValue;
 
     WifiMacHelper wifiMac;
     if (infrastructure)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/examples/wifi-test-interference-helper.cc ns-3.41/src/wifi/examples/wifi-test-interference-helper.cc
--- ns-3.40/src/wifi/examples/wifi-test-interference-helper.cc	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/examples/wifi-test-interference-helper.cc	2024-02-09 14:44:08.573794800 -0800
@@ -287,10 +287,10 @@
     devRx->SetPhy(rx);
     nodeRx->AddDevice(devRx);
 
-    m_txA->SetOperatingChannel(WifiPhy::ChannelTuple{input.channelA, 0, (int)(input.band), 0});
-    m_txB->SetOperatingChannel(WifiPhy::ChannelTuple{input.channelB, 0, (int)(input.band), 0});
+    m_txA->SetOperatingChannel(WifiPhy::ChannelTuple{input.channelA, 0, input.band, 0});
+    m_txB->SetOperatingChannel(WifiPhy::ChannelTuple{input.channelB, 0, input.band, 0});
     rx->SetOperatingChannel(
-        WifiPhy::ChannelTuple{std::max(input.channelA, input.channelB), 0, (int)(input.band), 0});
+        WifiPhy::ChannelTuple{std::max(input.channelA, input.channelB), 0, input.band, 0});
 
     rx->TraceConnectWithoutContext("PhyRxDrop",
                                    MakeCallback(&InterferenceExperiment::PacketDropped, this));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/examples/wifi-trans-example.cc ns-3.41/src/wifi/examples/wifi-trans-example.cc
--- ns-3.40/src/wifi/examples/wifi-trans-example.cc	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/examples/wifi-trans-example.cc	2024-02-09 14:44:08.573794800 -0800
@@ -269,7 +269,7 @@
     std::vector<double> freqs;
     int margin = 2; // 1MHz margin on each side
     int band = (bw + margin);
-    freqs.reserve((4 * 10 * band));
+    freqs.reserve(4 * 10 * band);
     for (int i = 0; i < (4 * 10 * band); ++i) // conversion to 100kHz scale
     {
         freqs.push_back(i * 1e5 + (freq - 2 * band) * 1e6);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/helper/spectrum-wifi-helper.cc ns-3.41/src/wifi/helper/spectrum-wifi-helper.cc
--- ns-3.40/src/wifi/helper/spectrum-wifi-helper.cc	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/helper/spectrum-wifi-helper.cc	2024-02-09 14:44:08.573794800 -0800
@@ -148,7 +148,7 @@
         }
         InstallPhyInterfaces(i, phy);
         phy->SetChannelSwitchedCallback(
-            MakeCallback(&SpectrumWifiPhyHelper::SpectrumChannelSwitched, this).Bind(phy));
+            MakeCallback(&SpectrumWifiPhyHelper::SpectrumChannelSwitched).Bind(phy));
         phy->SetDevice(device);
         phy->SetMobility(node->GetObject<MobilityModel>());
         ret.emplace_back(phy);
@@ -178,9 +178,8 @@
 }
 
 void
-SpectrumWifiPhyHelper::SpectrumChannelSwitched(Ptr<SpectrumWifiPhy> phy) const
+SpectrumWifiPhyHelper::SpectrumChannelSwitched(Ptr<SpectrumWifiPhy> phy)
 {
-    NS_LOG_FUNCTION(this << phy);
     for (const auto& otherPhy : phy->GetDevice()->GetPhys())
     {
         auto spectrumPhy = DynamicCast<SpectrumWifiPhy>(otherPhy);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/helper/spectrum-wifi-helper.h ns-3.41/src/wifi/helper/spectrum-wifi-helper.h
--- ns-3.40/src/wifi/helper/spectrum-wifi-helper.h	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/helper/spectrum-wifi-helper.h	2024-02-09 14:44:08.573794800 -0800
@@ -120,12 +120,6 @@
     void InstallPhyInterfaces(uint8_t linkId, Ptr<SpectrumWifiPhy> phy) const;
 
     /**
-     * Function that is notified when a spectrum channel switched
-     * \param phy spectrum PHY instance that has switched its channel
-     */
-    void SpectrumChannelSwitched(Ptr<SpectrumWifiPhy> phy) const;
-
-    /**
      * \param channel The channel to inspect to possibly add a WifiBandwidthFilter
      *
      * This method will add a WifiBandwidthFilter object to the SpectrumChannel, only if
@@ -133,6 +127,12 @@
      */
     void AddWifiBandwidthFilter(Ptr<SpectrumChannel> channel);
 
+    /**
+     * Function that is notified when a spectrum channel switched
+     * \param phy spectrum PHY instance that has switched its channel
+     */
+    static void SpectrumChannelSwitched(Ptr<SpectrumWifiPhy> phy);
+
     std::map<FrequencyRange, Ptr<SpectrumChannel>> m_channels; ///< the spectrum channels
     std::map<uint8_t /* linkId */, std::set<FrequencyRange>>
         m_interfacesMap; ///< map of the spectrum PHY interfaces to be added to the PHY instance
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/helper/wifi-helper.cc ns-3.41/src/wifi/helper/wifi-helper.cc
--- ns-3.40/src/wifi/helper/wifi-helper.cc	2023-09-27 13:30:37.898839700 -0700
+++ ns-3.41/src/wifi/helper/wifi-helper.cc	2024-02-09 14:44:08.573794800 -0800
@@ -992,40 +992,45 @@
     for (auto i = c.Begin(); i != c.End(); ++i)
     {
         netDevice = (*i);
-        Ptr<WifiNetDevice> wifi = DynamicCast<WifiNetDevice>(netDevice);
-        if (wifi)
+        if (auto wifi = DynamicCast<WifiNetDevice>(netDevice))
         {
             // Handle any random numbers in the PHY objects.
-            currentStream += wifi->GetPhy()->AssignStreams(currentStream);
+            for (auto& phy : wifi->GetPhys())
+            {
+                currentStream += phy->AssignStreams(currentStream);
+            }
 
             // Handle any random numbers in the station managers.
-            currentStream += wifi->GetRemoteStationManager()->AssignStreams(currentStream);
+            for (auto& manager : wifi->GetRemoteStationManagers())
+            {
+                currentStream += manager->AssignStreams(currentStream);
+            }
 
             // Handle any random numbers in the MAC objects.
-            Ptr<WifiMac> mac = wifi->GetMac();
+            auto mac = wifi->GetMac();
             PointerValue ptr;
             if (!mac->GetQosSupported())
             {
                 mac->GetAttribute("Txop", ptr);
-                Ptr<Txop> txop = ptr.Get<Txop>();
+                auto txop = ptr.Get<Txop>();
                 currentStream += txop->AssignStreams(currentStream);
             }
             else
             {
                 mac->GetAttribute("VO_Txop", ptr);
-                Ptr<QosTxop> vo_txop = ptr.Get<QosTxop>();
+                auto vo_txop = ptr.Get<QosTxop>();
                 currentStream += vo_txop->AssignStreams(currentStream);
 
                 mac->GetAttribute("VI_Txop", ptr);
-                Ptr<QosTxop> vi_txop = ptr.Get<QosTxop>();
+                auto vi_txop = ptr.Get<QosTxop>();
                 currentStream += vi_txop->AssignStreams(currentStream);
 
                 mac->GetAttribute("BE_Txop", ptr);
-                Ptr<QosTxop> be_txop = ptr.Get<QosTxop>();
+                auto be_txop = ptr.Get<QosTxop>();
                 currentStream += be_txop->AssignStreams(currentStream);
 
                 mac->GetAttribute("BK_Txop", ptr);
-                Ptr<QosTxop> bk_txop = ptr.Get<QosTxop>();
+                auto bk_txop = ptr.Get<QosTxop>();
                 currentStream += bk_txop->AssignStreams(currentStream);
             }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/addba-extension.cc ns-3.41/src/wifi/model/addba-extension.cc
--- ns-3.40/src/wifi/model/addba-extension.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/wifi/model/addba-extension.cc	2024-02-09 14:44:08.577794800 -0800
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Stefano Avallone <stavallo@unina.it>
+ */
+
+#include "addba-extension.h"
+
+namespace ns3
+{
+
+void
+AddbaExtension::Print(std::ostream& os) const
+{
+    os << "extBufferSize=" << +m_extParamSet.extBufferSize;
+}
+
+WifiInformationElementId
+AddbaExtension::ElementId() const
+{
+    return IE_ADDBA_EXTENSION;
+}
+
+uint16_t
+AddbaExtension::GetInformationFieldSize() const
+{
+    return 1U; // ADDBA Extended Parameter Set field
+}
+
+void
+AddbaExtension::SerializeInformationField(Buffer::Iterator start) const
+{
+    uint8_t extParamSet = m_extParamSet.noFragment | (m_extParamSet.heFragmentOp << 1) |
+                          (m_extParamSet.extBufferSize << 5);
+    start.WriteU8(extParamSet);
+}
+
+uint16_t
+AddbaExtension::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
+{
+    auto extParamSet = start.ReadU8();
+    m_extParamSet.noFragment = extParamSet & 0x01;
+    m_extParamSet.heFragmentOp = (extParamSet >> 1) & 0x03;
+    m_extParamSet.extBufferSize = (extParamSet >> 5) & 0x07;
+    return 1;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/addba-extension.h ns-3.41/src/wifi/model/addba-extension.h
--- ns-3.40/src/wifi/model/addba-extension.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/wifi/model/addba-extension.h	2024-02-09 14:44:08.577794800 -0800
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Stefano Avallone <stavallo@unina.it>
+ */
+
+#ifndef ADDBA_EXTENSION_H
+#define ADDBA_EXTENSION_H
+
+#include "wifi-information-element.h"
+
+namespace ns3
+{
+
+/**
+ * \ingroup wifi
+ *
+ * The IEEE 802.11 ADDBA Extension Element (Sec. 9.4.2.139 of 802.11-2020)
+ */
+class AddbaExtension : public WifiInformationElement
+{
+  public:
+    AddbaExtension() = default;
+
+    /**
+     * ADDBA Extended Parameter Set
+     */
+    struct ExtParamSet
+    {
+        uint8_t noFragment : 1;    //!< reserved when transmitted by HE STA to HE STA
+        uint8_t heFragmentOp : 2;  //!< indicates level of HE dynamic fragmentation (unsupported)
+        uint8_t : 2;               //!< reserved
+        uint8_t extBufferSize : 3; //!< extended buffer size
+    };
+
+    // Implementations of pure virtual methods of WifiInformationElement
+    WifiInformationElementId ElementId() const override;
+    void Print(std::ostream& os) const override;
+
+    ExtParamSet m_extParamSet{}; //!< ADDBA Extended Parameter Set field
+
+  private:
+    uint16_t GetInformationFieldSize() const override;
+    void SerializeInformationField(Buffer::Iterator start) const override;
+    uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
+};
+
+} // namespace ns3
+
+#endif /* ADDBA_EXTENSION_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/ap-wifi-mac.cc ns-3.41/src/wifi/model/ap-wifi-mac.cc
--- ns-3.40/src/wifi/model/ap-wifi-mac.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/ap-wifi-mac.cc	2024-02-09 14:44:08.577794800 -0800
@@ -25,6 +25,7 @@
 #include "channel-access-manager.h"
 #include "mac-rx-middle.h"
 #include "mac-tx-middle.h"
+#include "mgt-action-headers.h"
 #include "mgt-headers.h"
 #include "msdu-aggregator.h"
 #include "qos-txop.h"
@@ -684,6 +685,26 @@
         TimeValue time;
         ehtConfiguration->GetAttribute("TransitionTimeout", time);
         mle.SetTransitionTimeout(time.Get());
+
+        // An AP affiliated with an AP MLD may include the Medium Synchronization Delay Information
+        // subfield in the Common Info field of the Basic Multi-Link element carried in transmitted
+        // (Re)Association Response or Multi-Link Probe Response frames to provide medium
+        // synchronization information used by the AP MLD. (Section 35.3.16.8.2 of 802.11be D3.1)
+        if (frameType == WIFI_MAC_MGT_ASSOCIATION_RESPONSE)
+        {
+            auto& commonInfo = mle.GetCommonInfoBasic();
+
+            ehtConfiguration->GetAttribute("MediumSyncDuration", time);
+            commonInfo.SetMediumSyncDelayTimer(time.Get());
+
+            IntegerValue ofdmEdThres;
+            ehtConfiguration->GetAttribute("MsdOfdmEdThreshold", ofdmEdThres);
+            commonInfo.SetMediumSyncOfdmEdThreshold(ofdmEdThres.Get());
+
+            UintegerValue maxNTxops;
+            ehtConfiguration->GetAttribute("MsdMaxNTxops", maxNTxops);
+            commonInfo.SetMediumSyncMaxNTxops(maxNTxops.Get());
+        }
     }
 
     // The MLD Capabilities And Operations subfield is present in the Common Info field of the
@@ -698,7 +719,7 @@
         mldCapabilities.emplace();
         mldCapabilities->maxNSimultaneousLinks = GetNLinks() - 1; // assuming STR for now
         mldCapabilities->srsSupport = 0;
-        EnumValue negSupport;
+        EnumValue<WifiTidToLinkMappingNegSupport> negSupport;
         ehtConfiguration->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport);
         mldCapabilities->tidToLinkMappingSupport = negSupport.Get();
         mldCapabilities->freqSepForStrApMld = 0; // not supported yet
@@ -1957,7 +1978,7 @@
                     return failure("Incorrect directions in TID-to-Link Mapping IEs");
                 }
 
-                EnumValue negSupport;
+                EnumValue<WifiTidToLinkMappingNegSupport> negSupport;
                 ehtConfig->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport);
 
                 if (negSupport.Get() == 0)
@@ -2191,7 +2212,7 @@
     // completed. For this purpose, we connect a callback to the PHY TX begin trace to catch
     // the Ack transmitted after the EML Notification frame.
     CallbackBase cb = Callback<void, WifiConstPsduMap, WifiTxVector, double>(
-        [=](WifiConstPsduMap psduMap, WifiTxVector txVector, double /* txPowerW */) {
+        [=, this](WifiConstPsduMap psduMap, WifiTxVector txVector, double /* txPowerW */) {
             NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 &&
                               psduMap.begin()->second->GetHeader(0).IsAck(),
                           "Expected a Normal Ack after EML Notification frame");
@@ -2203,7 +2224,7 @@
             ehtConfiguration->GetAttribute("TransitionTimeout", transitionTimeout);
 
             m_transitionTimeoutEvents[sender] =
-                Simulator::Schedule(ackDuration + transitionTimeout.Get(), [=]() {
+                Simulator::Schedule(ackDuration + transitionTimeout.Get(), [=, this]() {
                     for (uint8_t id = 0; id < GetNLinks(); id++)
                     {
                         auto linkAddress =
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/block-ack-agreement.cc ns-3.41/src/wifi/model/block-ack-agreement.cc
--- ns-3.40/src/wifi/model/block-ack-agreement.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/block-ack-agreement.cc	2024-02-09 14:44:08.577794800 -0800
@@ -23,6 +23,8 @@
 
 #include "ns3/log.h"
 
+#include <set>
+
 namespace ns3
 {
 
@@ -49,8 +51,6 @@
 BlockAckAgreement::SetBufferSize(uint16_t bufferSize)
 {
     NS_LOG_FUNCTION(this << bufferSize);
-    NS_ASSERT(bufferSize <= 256);
-    NS_ASSERT(bufferSize % 16 == 0);
     m_bufferSize = bufferSize;
 }
 
@@ -174,12 +174,13 @@
     {
         return BlockAckType::BASIC;
     }
+
+    std::set<uint16_t> lengths{64, 256, 512, 1024}; // bitmap lengths in bits
+    // first bitmap length that is greater than or equal to the buffer size
+    auto it = lengths.lower_bound(m_bufferSize);
+    NS_ASSERT_MSG(it != lengths.cend(), "Buffer size too large: " << m_bufferSize);
     // Multi-TID Block Ack is not currently supported
-    if (m_bufferSize > 64)
-    {
-        return {BlockAckType::COMPRESSED, {32}};
-    }
-    return {BlockAckType::COMPRESSED, {8}};
+    return {BlockAckType::COMPRESSED, {static_cast<uint8_t>(*it / 8)}};
 }
 
 BlockAckReqType
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/block-ack-manager.cc ns-3.41/src/wifi/model/block-ack-manager.cc
--- ns-3.40/src/wifi/model/block-ack-manager.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/block-ack-manager.cc	2024-02-09 14:44:08.577794800 -0800
@@ -21,7 +21,7 @@
 
 #include "ctrl-headers.h"
 #include "mac-rx-middle.h"
-#include "mgt-headers.h"
+#include "mgt-action-headers.h"
 #include "qos-utils.h"
 #include "wifi-mac-queue.h"
 #include "wifi-tx-vector.h"
@@ -756,6 +756,7 @@
                                    OriginatorBlockAckAgreement::REJECTED);
     }
     it->second.first.SetState(OriginatorBlockAckAgreement::REJECTED);
+    m_unblockPackets(recipient, tid);
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/channel-access-manager.cc ns-3.41/src/wifi/model/channel-access-manager.cc
--- ns-3.40/src/wifi/model/channel-access-manager.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/channel-access-manager.cc	2024-02-09 14:44:08.577794800 -0800
@@ -20,6 +20,7 @@
 #include "channel-access-manager.h"
 
 #include "txop.h"
+#include "wifi-mac-queue.h"
 #include "wifi-phy-listener.h"
 #include "wifi-phy.h"
 
@@ -37,6 +38,8 @@
 
 NS_LOG_COMPONENT_DEFINE("ChannelAccessManager");
 
+NS_OBJECT_ENSURE_REGISTERED(ChannelAccessManager);
+
 /**
  * Listener for PHY events. Forwards to ChannelAccessManager.
  * The ChannelAccessManager may handle multiple PHY listeners connected to distinct PHYs,
@@ -168,6 +171,26 @@
  *      Implement the channel access manager of all Txop holders
  ****************************************************************/
 
+TypeId
+ChannelAccessManager::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::ChannelAccessManager")
+            .SetParent<ns3::Object>()
+            .SetGroupName("Wifi")
+            .AddConstructor<ChannelAccessManager>()
+            .AddAttribute("GenerateBackoffIfTxopWithoutTx",
+                          "Specify whether the backoff should be invoked when the AC gains the "
+                          "right to start a TXOP but it does not transmit any frame "
+                          "(e.g., due to constraints associated with EMLSR operations), "
+                          "provided that the queue is not actually empty.",
+                          BooleanValue(false),
+                          MakeBooleanAccessor(&ChannelAccessManager::SetGenerateBackoffOnNoTx,
+                                              &ChannelAccessManager::GetGenerateBackoffOnNoTx),
+                          MakeBooleanChecker());
+    return tid;
+}
+
 ChannelAccessManager::ChannelAccessManager()
     : m_lastAckTimeoutEnd(0),
       m_lastCtsTimeoutEnd(0),
@@ -177,7 +200,6 @@
       m_lastTxEnd(0),
       m_lastSwitchingEnd(0),
       m_usingOtherEmlsrLink(false),
-      m_lastUsingOtherEmlsrLinkEnd(0),
       m_sleeping(false),
       m_off(false),
       m_linkId(0)
@@ -212,12 +234,12 @@
     m_phyListeners.clear();
 }
 
-PhyListener*
+std::shared_ptr<PhyListener>
 ChannelAccessManager::GetPhyListener(Ptr<WifiPhy> phy) const
 {
     if (auto listenerIt = m_phyListeners.find(phy); listenerIt != m_phyListeners.end())
     {
-        return listenerIt->second.get();
+        return listenerIt->second;
     }
     return nullptr;
 }
@@ -227,9 +249,7 @@
 {
     NS_LOG_FUNCTION(this << phy);
 
-    auto phyListener = GetPhyListener(phy);
-
-    if (phyListener)
+    if (auto phyListener = GetPhyListener(phy))
     {
         // a PHY listener for the given PHY already exists, it must be inactive
         NS_ASSERT_MSG(!phyListener->IsActive(),
@@ -239,7 +259,7 @@
     }
     else
     {
-        phyListener = new PhyListener(this);
+        phyListener = std::make_shared<PhyListener>(this);
         m_phyListeners.emplace(phy, phyListener);
         phy->RegisterListener(phyListener);
     }
@@ -393,12 +413,15 @@
 }
 
 bool
-ChannelAccessManager::NeedBackoffUponAccess(Ptr<Txop> txop)
-{
-    NS_LOG_FUNCTION(this << txop);
-
-    // No backoff needed if in sleep mode, off or when using another EMLSR link
-    if (m_sleeping || m_off || m_usingOtherEmlsrLink)
+ChannelAccessManager::NeedBackoffUponAccess(Ptr<Txop> txop,
+                                            bool hadFramesToTransmit,
+                                            bool checkMediumBusy)
+{
+    NS_LOG_FUNCTION(this << txop << hadFramesToTransmit << checkMediumBusy);
+
+    // No backoff needed if in sleep mode or off. Checking if m_phy is nullptr is a workaround
+    // needed for EMLSR and may be removed in the future
+    if (m_sleeping || m_off || !m_phy)
     {
         return false;
     }
@@ -426,10 +449,10 @@
      *    with that AC has now become non-empty and any other transmit queues
      *    associated with that AC are empty; the medium is busy on the primary channel
      */
-    if (!txop->HasFramesToTransmit(m_linkId) && txop->GetAccessStatus(m_linkId) != Txop::GRANTED &&
-        txop->GetBackoffSlots(m_linkId) == 0)
+    if (!hadFramesToTransmit && txop->HasFramesToTransmit(m_linkId) &&
+        txop->GetAccessStatus(m_linkId) != Txop::GRANTED && txop->GetBackoffSlots(m_linkId) == 0)
     {
-        if (!IsBusy())
+        if (checkMediumBusy && !IsBusy())
         {
             // medium idle. If this is a DCF, use immediate access (we can transmit
             // in a DIFS if the medium remains idle). If this is an EDCAF, update
@@ -453,17 +476,13 @@
 ChannelAccessManager::RequestAccess(Ptr<Txop> txop)
 {
     NS_LOG_FUNCTION(this << txop);
-    if (m_phy)
+    if (m_phy && txop->HasFramesToTransmit(m_linkId))
     {
         m_phy->NotifyChannelAccessRequested();
     }
-    if (m_usingOtherEmlsrLink)
-    {
-        NS_LOG_DEBUG("Channel access cannot be requested while using another EMLSR link");
-        return;
-    }
-    // Deny access if in sleep mode or off
-    if (m_sleeping || m_off)
+    // Deny access if in sleep mode or off. Checking if m_phy is nullptr is a workaround
+    // needed for EMLSR and may be removed in the future
+    if (m_sleeping || m_off || !m_phy)
     {
         return;
     }
@@ -558,6 +577,9 @@
             }
             else
             {
+                // this TXOP did not transmit anything, make sure that backoff counter starts
+                // decreasing in a slot again
+                txop->UpdateBackoffSlotsNow(0, now, m_linkId);
                 // reset the current state to the EDCAF that won the contention
                 // but did not transmit anything
                 i--;
@@ -595,8 +617,6 @@
     Time ackTimeoutAccessStart = m_lastAckTimeoutEnd + sifs;
     Time ctsTimeoutAccessStart = m_lastCtsTimeoutEnd + sifs;
     Time switchingAccessStart = m_lastSwitchingEnd + sifs;
-    Time usingOtherEmlsrLinkAccessStart =
-        (m_usingOtherEmlsrLink ? Simulator::Now() : m_lastUsingOtherEmlsrLinkEnd) + sifs;
     Time accessGrantedStart;
     if (ignoreNav)
     {
@@ -605,7 +625,6 @@
                                        txAccessStart,
                                        ackTimeoutAccessStart,
                                        ctsTimeoutAccessStart,
-                                       usingOtherEmlsrLinkAccessStart,
                                        switchingAccessStart});
     }
     else
@@ -616,18 +635,15 @@
                                        navAccessStart,
                                        ackTimeoutAccessStart,
                                        ctsTimeoutAccessStart,
-                                       usingOtherEmlsrLinkAccessStart,
                                        switchingAccessStart});
     }
-    std::stringstream ss;
-    if (m_usingOtherEmlsrLink)
-    {
-        ss << ", using other EMLSR link access start=" << usingOtherEmlsrLinkAccessStart;
-    }
-    NS_LOG_INFO("access grant start=" << accessGrantedStart << ", rx access start=" << rxAccessStart
-                                      << ", busy access start=" << busyAccessStart
-                                      << ", tx access start=" << txAccessStart
-                                      << ", nav access start=" << navAccessStart << ss.str());
+    NS_LOG_INFO("access grant start=" << accessGrantedStart.As(Time::US)
+                                      << ", rx access start=" << rxAccessStart.As(Time::US)
+                                      << ", busy access start=" << busyAccessStart.As(Time::US)
+                                      << ", tx access start=" << txAccessStart.As(Time::US)
+                                      << ", nav access start=" << navAccessStart.As(Time::US)
+                                      << ", switching access start="
+                                      << switchingAccessStart.As(Time::US));
     return accessGrantedStart;
 }
 
@@ -638,7 +654,8 @@
     Time mostRecentEvent =
         std::max({txop->GetBackoffStart(m_linkId),
                   GetAccessGrantStart() + (txop->GetAifsn(m_linkId) * GetSlot())});
-    NS_LOG_DEBUG("Backoff start: " << mostRecentEvent.As(Time::US));
+    NS_LOG_DEBUG("Backoff start for " << txop->GetWifiMacQueue()->GetAc() << ": "
+                                      << mostRecentEvent.As(Time::US));
 
     return mostRecentEvent;
 }
@@ -648,7 +665,8 @@
 {
     NS_LOG_FUNCTION(this << txop);
     Time backoffEnd = GetBackoffStartFor(txop) + (txop->GetBackoffSlots(m_linkId) * GetSlot());
-    NS_LOG_DEBUG("Backoff end: " << backoffEnd.As(Time::US));
+    NS_LOG_DEBUG("Backoff end for " << txop->GetWifiMacQueue()->GetAc() << ": "
+                                    << backoffEnd.As(Time::US));
 
     return backoffEnd;
 }
@@ -802,6 +820,19 @@
 }
 
 void
+ChannelAccessManager::SetGenerateBackoffOnNoTx(bool enable)
+{
+    NS_LOG_FUNCTION(this << enable);
+    m_generateBackoffOnNoTx = enable;
+}
+
+bool
+ChannelAccessManager::GetGenerateBackoffOnNoTx() const
+{
+    return m_generateBackoffOnNoTx;
+}
+
+void
 ChannelAccessManager::NotifyRxStartNow(Time duration)
 {
     NS_LOG_FUNCTION(this << duration);
@@ -967,6 +998,18 @@
 }
 
 void
+ChannelAccessManager::ResetAllBackoffs()
+{
+    NS_LOG_FUNCTION(this);
+
+    for (const auto& txop : m_txops)
+    {
+        ResetBackoff(txop);
+    }
+    m_accessTimeout.Cancel();
+}
+
+void
 ChannelAccessManager::NotifySleepNow()
 {
     NS_LOG_FUNCTION(this);
@@ -1030,6 +1073,14 @@
 ChannelAccessManager::NotifyNavResetNow(Time duration)
 {
     NS_LOG_FUNCTION(this << duration);
+
+    if (!m_phy)
+    {
+        NS_LOG_DEBUG("Do not reset NAV, CTS may have been missed due to the main PHY switching "
+                     "to another link to take over a TXOP while receiving the CTS");
+        return;
+    }
+
     NS_LOG_DEBUG("nav reset for=" << duration);
     UpdateBackoff();
     m_lastNavEnd = Simulator::Now() + duration;
@@ -1086,18 +1137,7 @@
 ChannelAccessManager::NotifyStartUsingOtherEmlsrLink()
 {
     NS_LOG_FUNCTION(this);
-    // update backoff if a PHY is operating on this link
-    if (m_phy)
-    {
-        UpdateBackoff();
-    }
-    // Cancel timeout
-    if (m_accessTimeout.IsRunning())
-    {
-        m_accessTimeout.Cancel();
-    }
     m_usingOtherEmlsrLink = true;
-    UpdateLastIdlePeriod();
 }
 
 void
@@ -1105,16 +1145,13 @@
 {
     NS_LOG_FUNCTION(this);
     m_usingOtherEmlsrLink = false;
-    m_lastUsingOtherEmlsrLinkEnd = Simulator::Now();
-    DoRestartAccessTimeoutIfNeeded();
 }
 
 void
 ChannelAccessManager::UpdateLastIdlePeriod()
 {
     NS_LOG_FUNCTION(this);
-    Time idleStart =
-        std::max({m_lastTxEnd, m_lastRx.end, m_lastSwitchingEnd, m_lastUsingOtherEmlsrLinkEnd});
+    Time idleStart = std::max({m_lastTxEnd, m_lastRx.end, m_lastSwitchingEnd});
     Time now = Simulator::Now();
 
     if (idleStart >= now)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/channel-access-manager.h ns-3.41/src/wifi/model/channel-access-manager.h
--- ns-3.40/src/wifi/model/channel-access-manager.h	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/channel-access-manager.h	2024-02-09 14:44:08.577794800 -0800
@@ -33,6 +33,8 @@
 #include <unordered_map>
 #include <vector>
 
+class EmlsrUlTxopTest;
+
 namespace ns3
 {
 
@@ -58,11 +60,20 @@
  */
 class ChannelAccessManager : public Object
 {
+    /// Allow test cases to access private members
+    friend class ::EmlsrUlTxopTest;
+
   public:
     ChannelAccessManager();
     ~ChannelAccessManager() override;
 
     /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    /**
      * Set up (or reactivate) listener for PHY events on the given PHY. The new (or reactivated)
      * listener becomes the active listener and the previous active listener attached to another
      * PHY, if any, is deactivated.
@@ -109,13 +120,22 @@
     void Add(Ptr<Txop> txop);
 
     /**
-     * Determine if a new backoff needs to be generated when a packet is queued
-     * for transmission.
+     * Determine if a new backoff needs to be generated as per letter a) of Section 10.23.2.2
+     * of IEEE 802.11-2020 ("EDCA backoff procedure"). This method is called upon the occurrence
+     * of events such as the enqueuing of a packet or the unblocking of some links after they
+     * have been blocked for some reason (e.g., wait for ADDBA Response, wait for TX on another
+     * EMLSR link to finish, etc.). The <i>checkMediumBusy</i> argument allows to generate a new
+     * backoff regardless of the busy/idle state of the medium, as per Section 35.3.16.4 of
+     * 802.11be D4.0.
      *
      * \param txop the Txop requesting to generate a backoff
+     * \param hadFramesToTransmit whether packets available for transmission were queued just
+     *                            before the occurrence of the event triggering this call
+     * \param checkMediumBusy whether generation of backoff (also) depends on the busy/idle state
+     *                        of the medium
      * \return true if backoff needs to be generated, false otherwise
      */
-    bool NeedBackoffUponAccess(Ptr<Txop> txop);
+    bool NeedBackoffUponAccess(Ptr<Txop> txop, bool hadFramesToTransmit, bool checkMediumBusy);
 
     /**
      * \param txop a Txop
@@ -149,6 +169,22 @@
     void DisableEdcaFor(Ptr<Txop> qosTxop, Time duration);
 
     /**
+     * Set the member variable indicating whether the backoff should be invoked when an AC gains
+     * the right to start a TXOP but it does not transmit any frame (e.g., due to constraints
+     * associated with EMLSR operations), provided that the queue is not actually empty.
+     *
+     * \param enable whether to enable backoff generation when no TX is performed in a TXOP
+     */
+    void SetGenerateBackoffOnNoTx(bool enable);
+
+    /**
+     * \return whether the backoff should be invoked when an AC gains the right to start a TXOP
+     *         but it does not transmit any frame (e.g., due to constraints associated with EMLSR
+     *         operations), provided that the queue is not actually empty
+     */
+    bool GetGenerateBackoffOnNoTx() const;
+
+    /**
      * Return the width of the largest primary channel that has been idle for the
      * given time interval before the given time, if any primary channel has been
      * idle, or zero, otherwise.
@@ -294,6 +330,11 @@
     void ResetBackoff(Ptr<Txop> txop);
 
     /**
+     * Reset the backoff for all the DCF/EDCAF. Additionally, cancel the access timeout event.
+     */
+    void ResetAllBackoffs();
+
+    /**
      * Notify that the given PHY is about to switch to the given operating channel, which is
      * used by the given link. This notification is sent by the EMLSR Manager when a PHY object
      * switches operating channel to operate on another link.
@@ -317,7 +358,7 @@
      * \param phy the given PHY
      * \return the current registered listener for PHY events on the given PHY
      */
-    PhyListener* GetPhyListener(Ptr<WifiPhy> phy) const;
+    std::shared_ptr<PhyListener> GetPhyListener(Ptr<WifiPhy> phy) const;
     /**
      * Initialize the structures holding busy end times per channel type (primary,
      * secondary, etc.) and per 20 MHz channel.
@@ -413,15 +454,17 @@
     std::vector<Time> m_lastPer20MHzBusyEnd; /**< the last busy end time per 20 MHz channel
                                                   (HE stations and channel width > 20 MHz only) */
     std::map<WifiChannelListType, Timespan>
-        m_lastIdle;                    //!< the last idle start and end time for each channel type
-    Time m_lastSwitchingEnd;           //!< the last switching end time
-    bool m_usingOtherEmlsrLink;        //!< whether another EMLSR link is being used
-    Time m_lastUsingOtherEmlsrLinkEnd; //!< the last time we were blocked because using another
-                                       //!< EMLSR link
-    bool m_sleeping;                   //!< flag whether it is in sleeping state
-    bool m_off;                        //!< flag whether it is in off state
-    Time m_eifsNoDifs;                 //!< EIFS no DIFS time
-    EventId m_accessTimeout;           //!< the access timeout ID
+        m_lastIdle;               //!< the last idle start and end time for each channel type
+    Time m_lastSwitchingEnd;      //!< the last switching end time
+    bool m_usingOtherEmlsrLink;   //!< whether another EMLSR link is being used
+    bool m_sleeping;              //!< flag whether it is in sleeping state
+    bool m_off;                   //!< flag whether it is in off state
+    Time m_eifsNoDifs;            //!< EIFS no DIFS time
+    EventId m_accessTimeout;      //!< the access timeout ID
+    bool m_generateBackoffOnNoTx; //!< whether the backoff should be invoked when the AC gains the
+                                  //!< right to start a TXOP but it does not transmit any frame
+                                  //!< (e.g., due to constraints associated with EMLSR operations),
+                                  //!< provided that the queue is not actually empty
 
     /// Information associated with each PHY that is going to operate on another EMLSR link
     struct EmlsrLinkSwitchInfo
@@ -434,7 +477,7 @@
     std::unordered_map<Ptr<WifiPhy>, EmlsrLinkSwitchInfo> m_switchingEmlsrLinks;
 
     /// Maps each PHY listener to the associated PHY
-    using PhyListenerMap = std::unordered_map<Ptr<WifiPhy>, std::unique_ptr<PhyListener>>;
+    using PhyListenerMap = std::unordered_map<Ptr<WifiPhy>, std::shared_ptr<PhyListener>>;
 
     PhyListenerMap m_phyListeners;         //!< the PHY listeners
     Ptr<WifiPhy> m_phy;                    //!< pointer to the unique active PHY
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/ctrl-headers.cc ns-3.41/src/wifi/model/ctrl-headers.cc
--- ns-3.40/src/wifi/model/ctrl-headers.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/ctrl-headers.cc	2024-02-09 14:44:08.577794800 -0800
@@ -719,14 +719,27 @@
 
     uint16_t ret = (m_baInfo[index].m_startingSeq << 4) & 0xfff0;
 
-    // The Fragment Number subfield encodes the length of the bitmap for
-    // Compressed and Multi-STA variants (see sections 9.3.1.9.3 and 9.3.1.9.7
-    // of 802.11ax Draft 3.0). Note that Fragmentation Level 3 is not supported.
+    // The Fragment Number subfield encodes the length of the bitmap for Compressed and Multi-STA
+    // variants (see sections 9.3.1.8.2 and 9.3.1.8.7 of 802.11ax-2021 and 802.11be Draft 4.0).
+    // Note that Fragmentation Level 3 is not supported.
     if (m_baType.m_variant == BlockAckType::COMPRESSED)
     {
-        if (m_baType.m_bitmapLen[0] == 32)
+        switch (m_baType.m_bitmapLen[0])
         {
+        case 8:
+            // do nothing
+            break;
+        case 32:
             ret |= 0x0004;
+            break;
+        case 64:
+            ret |= 0x0008;
+            break;
+        case 128:
+            ret |= 0x000a;
+            break;
+        default:
+            NS_ABORT_MSG("Unsupported bitmap length: " << +m_baType.m_bitmapLen[0] << " bytes");
         }
     }
     else if (m_baType.m_variant == BlockAckType::MULTI_STA)
@@ -735,17 +748,28 @@
         NS_ASSERT_MSG(!m_baInfo[index].m_bitmap.empty(),
                       "This Per AID TID Info subfield has no Starting Sequence Control subfield");
 
-        if (m_baType.m_bitmapLen[index] == 16)
+        switch (m_baType.m_bitmapLen[index])
         {
+        case 8:
+            // do nothing
+            break;
+        case 16:
             ret |= 0x0002;
-        }
-        else if (m_baType.m_bitmapLen[index] == 32)
-        {
+            break;
+        case 32:
             ret |= 0x0004;
-        }
-        else if (m_baType.m_bitmapLen[index] == 4)
-        {
+            break;
+        case 4:
             ret |= 0x0006;
+            break;
+        case 64:
+            ret |= 0x0008;
+            break;
+        case 128:
+            ret |= 0x000a;
+            break;
+        default:
+            NS_ABORT_MSG("Unsupported bitmap length: " << +m_baType.m_bitmapLen[index] << " bytes");
         }
     }
     return ret;
@@ -758,54 +782,66 @@
                   "index can only be non null for Multi-STA Block Ack");
     NS_ASSERT(index < m_baInfo.size());
 
-    // The Fragment Number subfield encodes the length of the bitmap for
-    // Compressed and Multi-STA variants (see sections 9.3.1.9.3 and 9.3.1.9.7
-    // of 802.11ax Draft 3.0). Note that Fragmentation Level 3 is not supported.
+    // The Fragment Number subfield encodes the length of the bitmap for Compressed and Multi-STA
+    // variants (see sections 9.3.1.8.2 and 9.3.1.8.7 of 802.11ax-2021 and 802.11be Draft 4.0).
+    // Note that Fragmentation Level 3 is not supported.
     if (m_baType.m_variant == BlockAckType::COMPRESSED)
     {
-        if ((seqControl & 0x0001) == 1)
+        uint16_t fragNumber = seqControl & 0x000f;
+
+        if ((fragNumber & 0x0001) == 1)
         {
             NS_FATAL_ERROR("Fragmentation Level 3 unsupported");
         }
-        if (((seqControl >> 3) & 0x0001) == 0 && ((seqControl >> 1) & 0x0003) == 0)
+        switch (fragNumber)
         {
+        case 0:
             SetType({BlockAckType::COMPRESSED, {8}});
-        }
-        else if (((seqControl >> 3) & 0x0001) == 0 && ((seqControl >> 1) & 0x0003) == 2)
-        {
+            break;
+        case 4:
             SetType({BlockAckType::COMPRESSED, {32}});
-        }
-        else
-        {
-            NS_FATAL_ERROR("Reserved configurations");
+            break;
+        case 8:
+            SetType({BlockAckType::COMPRESSED, {64}});
+            break;
+        case 10:
+            SetType({BlockAckType::COMPRESSED, {128}});
+            break;
+        default:
+            NS_ABORT_MSG("Unsupported fragment number: " << fragNumber);
         }
     }
     else if (m_baType.m_variant == BlockAckType::MULTI_STA)
     {
-        if ((seqControl & 0x0001) == 1)
+        uint16_t fragNumber = seqControl & 0x000f;
+
+        if ((fragNumber & 0x0001) == 1)
         {
             NS_FATAL_ERROR("Fragmentation Level 3 unsupported");
         }
         uint8_t bitmapLen = 0;
-        if (((seqControl >> 3) & 0x0001) == 0 && ((seqControl >> 1) & 0x0003) == 0)
+        switch (fragNumber)
         {
+        case 0:
             bitmapLen = 8;
-        }
-        else if (((seqControl >> 3) & 0x0001) == 0 && ((seqControl >> 1) & 0x0003) == 1)
-        {
+            break;
+        case 2:
             bitmapLen = 16;
-        }
-        else if (((seqControl >> 3) & 0x0001) == 0 && ((seqControl >> 1) & 0x0003) == 2)
-        {
+            break;
+        case 4:
             bitmapLen = 32;
-        }
-        else if (((seqControl >> 3) & 0x0001) == 0 && ((seqControl >> 1) & 0x0003) == 3)
-        {
+            break;
+        case 6:
             bitmapLen = 4;
-        }
-        else
-        {
-            NS_FATAL_ERROR("Reserved configurations");
+            break;
+        case 8:
+            bitmapLen = 64;
+            break;
+        case 10:
+            bitmapLen = 128;
+            break;
+        default:
+            NS_ABORT_MSG("Unsupported fragment number: " << fragNumber);
         }
         m_baType.m_bitmapLen[index] = bitmapLen;
         m_baInfo[index].m_bitmap.assign(bitmapLen, 0);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/default-emlsr-manager.cc ns-3.41/src/wifi/model/eht/default-emlsr-manager.cc
--- ns-3.40/src/wifi/model/eht/default-emlsr-manager.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/default-emlsr-manager.cc	2024-02-09 14:44:08.581795000 -0800
@@ -23,6 +23,7 @@
 #include "ns3/channel-access-manager.h"
 #include "ns3/log.h"
 #include "ns3/wifi-mpdu.h"
+#include "ns3/wifi-net-device.h"
 #include "ns3/wifi-phy.h"
 
 namespace ns3
@@ -42,7 +43,10 @@
             .AddConstructor<DefaultEmlsrManager>()
             .AddAttribute("SwitchAuxPhy",
                           "Whether Aux PHY should switch channel to operate on the link on which "
-                          "the Main PHY was operating before moving to the link of the Aux PHY.",
+                          "the Main PHY was operating before moving to the link of the Aux PHY. "
+                          "Note that, if the Aux PHY does not switch channel, the main PHY will "
+                          "switch back to its previous link once the TXOP terminates (otherwise, "
+                          "no PHY will be listening on that EMLSR link).",
                           BooleanValue(true),
                           MakeBooleanAccessor(&DefaultEmlsrManager::m_switchAuxPhy),
                           MakeBooleanChecker());
@@ -94,25 +98,79 @@
 {
     NS_LOG_FUNCTION(this << currLinkId << nextLinkId);
 
-    if (!m_switchAuxPhy)
+    if (m_switchAuxPhy)
     {
-        return; // nothing to do
+        // switch channel on Aux PHY so that it operates on the link on which the main PHY was
+        // operating
+        SwitchAuxPhy(nextLinkId, currLinkId);
+        return;
     }
 
-    // switch channel on Aux PHY so that it operates on the link on which the main PHY was operating
-    auto auxPhy = GetStaMac()->GetWifiPhy(nextLinkId);
+    if (currLinkId != GetMainPhyId())
+    {
+        // the main PHY is leaving a non-primary link, hence an aux PHY needs to be reconnected
+        NS_ASSERT_MSG(
+            m_auxPhyToReconnect,
+            "There should be an aux PHY to reconnect when the main PHY leaves a non-primary link");
+
+        // the Aux PHY is not actually switching (hence no switching delay)
+        GetStaMac()->NotifySwitchingEmlsrLink(m_auxPhyToReconnect, currLinkId, Seconds(0));
+        SetCcaEdThresholdOnLinkSwitch(m_auxPhyToReconnect, currLinkId);
+        m_auxPhyToReconnect = nullptr;
+    }
 
-    auto newAuxPhyChannel = GetChannelForAuxPhy(currLinkId);
+    if (nextLinkId != GetMainPhyId())
+    {
+        // the main PHY is moving to a non-primary link and the aux PHY does not switch link
+        m_auxPhyToReconnect = GetStaMac()->GetWifiPhy(nextLinkId);
+    }
+}
 
-    NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << newAuxPhyChannel
-                             << " to operate on link " << +currLinkId);
+void
+DefaultEmlsrManager::DoNotifyIcfReceived(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+}
 
-    GetStaMac()
-        ->GetChannelAccessManager(nextLinkId)
-        ->NotifySwitchingEmlsrLink(auxPhy, newAuxPhyChannel, currLinkId);
+void
+DefaultEmlsrManager::DoNotifyUlTxopStart(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+}
+
+void
+DefaultEmlsrManager::DoNotifyTxopEnd(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    // switch main PHY to the previous link, if needed
+    if (!m_switchAuxPhy && m_auxPhyToReconnect)
+    {
+        auto mainPhy = GetStaMac()->GetDevice()->GetPhy(m_mainPhyId);
 
-    void (WifiPhy::*fp)(const WifiPhyOperatingChannel&) = &WifiPhy::SetOperatingChannel;
-    Simulator::ScheduleNow(fp, auxPhy, newAuxPhyChannel);
+        // the main PHY may be switching at the end of a TXOP when, e.g., the main PHY starts
+        // switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
+        // is not received and the UL TXOP ends before the main PHY channel switch is completed.
+        // In such cases, wait until the main PHY channel switch is completed before requesting
+        // a new channel switch.
+        // Backoff shall not be reset on the link left by the main PHY because a TXOP ended and
+        // a new backoff value must be generated.
+        if (!mainPhy->IsStateSwitching())
+        {
+            SwitchMainPhy(GetMainPhyId(), false, DONT_RESET_BACKOFF, REQUEST_ACCESS);
+        }
+        else
+        {
+            Simulator::Schedule(mainPhy->GetDelayUntilIdle(),
+                                &DefaultEmlsrManager::SwitchMainPhy,
+                                this,
+                                GetMainPhyId(),
+                                false,
+                                DONT_RESET_BACKOFF,
+                                REQUEST_ACCESS);
+        }
+        return;
+    }
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/default-emlsr-manager.h ns-3.41/src/wifi/model/eht/default-emlsr-manager.h
--- ns-3.40/src/wifi/model/eht/default-emlsr-manager.h	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/default-emlsr-manager.h	2024-02-09 14:44:08.581795000 -0800
@@ -52,9 +52,15 @@
     void DoNotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId) override;
     void NotifyEmlsrModeChanged() override;
     void NotifyMainPhySwitch(uint8_t currLinkId, uint8_t nextLinkId) override;
+    void DoNotifyIcfReceived(uint8_t linkId) override;
+    void DoNotifyUlTxopStart(uint8_t linkId) override;
+    void DoNotifyTxopEnd(uint8_t linkId) override;
 
     bool m_switchAuxPhy; /**< whether Aux PHY should switch channel to operate on the link on which
                               the Main PHY was operating before moving to the link of the Aux PHY */
+    Ptr<WifiPhy> m_auxPhyToReconnect; //!< Aux PHY the ChannelAccessManager of the link on which
+                                      //!< the main PHY is operating has to connect a listener to
+                                      //!< when the main PHY is back operating on its previous link
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/eht-capabilities.h ns-3.41/src/wifi/model/eht/eht-capabilities.h
--- ns-3.40/src/wifi/model/eht/eht-capabilities.h	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/eht-capabilities.h	2024-02-09 14:44:08.581795000 -0800
@@ -273,7 +273,7 @@
     /**
      * Set the maximum A-MPDU length.
      *
-     * \param maxAmpduLength 2^(23 + x) - 1, x in the range 0 to 3
+     * \param maxAmpduLength 2^(23 + x) - 1, x in the range 0 to 1
      */
     void SetMaxAmpduLength(uint32_t maxAmpduLength);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/eht-configuration.cc ns-3.41/src/wifi/model/eht/eht-configuration.cc
--- ns-3.40/src/wifi/model/eht/eht-configuration.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/eht-configuration.cc	2024-02-09 14:44:08.581795000 -0800
@@ -23,6 +23,7 @@
 #include "ns3/attribute-container.h"
 #include "ns3/boolean.h"
 #include "ns3/enum.h"
+#include "ns3/integer.h"
 #include "ns3/log.h"
 #include "ns3/pair.h"
 #include "ns3/string.h"
@@ -71,10 +72,34 @@
                           MakeTimeAccessor(&EhtConfiguration::m_transitionTimeout),
                           MakeTimeChecker(MicroSeconds(0), MicroSeconds(65536)))
             .AddAttribute(
+                "MediumSyncDuration",
+                "The duration of the MediumSyncDelay timer (must be a multiple of 32 us). "
+                "The value of this attribute is only used by AP MLDs with EMLSR activated.",
+                TimeValue(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
+                MakeTimeAccessor(&EhtConfiguration::m_mediumSyncDuration),
+                MakeTimeChecker(MicroSeconds(0), MicroSeconds(255 * 32)))
+            .AddAttribute(
+                "MsdOfdmEdThreshold",
+                "Threshold (dBm) to be used instead of the normal CCA sensitivity for the primary "
+                "20 MHz channel if the MediumSyncDelay timer has a nonzero value. "
+                "The value of this attribute is only used by AP MLDs with EMLSR activated.",
+                IntegerValue(DEFAULT_MSD_OFDM_ED_THRESH),
+                MakeIntegerAccessor(&EhtConfiguration::m_msdOfdmEdThreshold),
+                MakeIntegerChecker<int8_t>(-72, -62))
+            .AddAttribute(
+                "MsdMaxNTxops",
+                "Maximum number of TXOPs that an EMLSR client is allowed to attempt to initiate "
+                "while the MediumSyncDelay timer is running (zero indicates no limit). "
+                "The value of this attribute is only used by AP MLDs with EMLSR activated.",
+                UintegerValue(DEFAULT_MSD_MAX_N_TXOPS),
+                MakeUintegerAccessor(&EhtConfiguration::m_msdMaxNTxops),
+                MakeUintegerChecker<uint8_t>(0, 15))
+            .AddAttribute(
                 "TidToLinkMappingNegSupport",
                 "TID-to-Link Mapping Negotiation Support.",
                 EnumValue(WifiTidToLinkMappingNegSupport::WIFI_TID_TO_LINK_MAPPING_ANY_LINK_SET),
-                MakeEnumAccessor(&EhtConfiguration::m_tidLinkMappingSupport),
+                MakeEnumAccessor<WifiTidToLinkMappingNegSupport>(
+                    &EhtConfiguration::m_tidLinkMappingSupport),
                 MakeEnumChecker(
                     WifiTidToLinkMappingNegSupport::WIFI_TID_TO_LINK_MAPPING_NOT_SUPPORTED,
                     "NOT_SUPPORTED",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/eht-configuration.h ns-3.41/src/wifi/model/eht/eht-configuration.h
--- ns-3.40/src/wifi/model/eht/eht-configuration.h	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/eht-configuration.h	2024-02-09 14:44:08.581795000 -0800
@@ -31,6 +31,15 @@
 namespace ns3
 {
 
+/// default MediumSyncDelay timer duration (max PPDU TX time rounded to a multiple of 32 us)
+static constexpr uint16_t DEFAULT_MSD_DURATION_USEC = 5484 / 32 * 32;
+
+/// default MediumSyncDelay timer OFDM ED threshold
+static constexpr int8_t DEFAULT_MSD_OFDM_ED_THRESH = -72;
+
+/// default MediumSyncDelay max number of TXOP attempts
+static constexpr uint8_t DEFAULT_MSD_MAX_N_TXOPS = 1;
+
 /**
  * \brief TID-to-Link Mapping Negotiation Support
  */
@@ -77,8 +86,11 @@
                            const std::map<std::list<uint8_t>, std::list<uint8_t>>& mapping);
 
   private:
-    bool m_emlsrActivated;    //!< whether EMLSR option is activated
-    Time m_transitionTimeout; //!< Transition timeout
+    bool m_emlsrActivated;       //!< whether EMLSR option is activated
+    Time m_transitionTimeout;    //!< Transition timeout
+    Time m_mediumSyncDuration;   //!< duration of the MediumSyncDelay timer
+    int8_t m_msdOfdmEdThreshold; //!< MediumSyncDelay OFDM ED threshold
+    uint8_t m_msdMaxNTxops;      //!< MediumSyncDelay max number of TXOPs
     WifiTidToLinkMappingNegSupport
         m_tidLinkMappingSupport; //!< TID-to-Link Mapping Negotiation Support
     std::map<std::list<uint64_t>, std::list<uint64_t>>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/eht-frame-exchange-manager.cc ns-3.41/src/wifi/model/eht/eht-frame-exchange-manager.cc
--- ns-3.40/src/wifi/model/eht/eht-frame-exchange-manager.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/eht-frame-exchange-manager.cc	2024-02-09 14:44:08.581795000 -0800
@@ -25,8 +25,10 @@
 #include "ns3/abort.h"
 #include "ns3/ap-wifi-mac.h"
 #include "ns3/log.h"
+#include "ns3/mgt-action-headers.h"
 #include "ns3/sta-wifi-mac.h"
 #include "ns3/wifi-mac-queue.h"
+#include "ns3/wifi-net-device.h"
 
 #undef NS_LOG_APPEND_CONTEXT
 #define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "][mac=" << m_self << "] "
@@ -34,9 +36,25 @@
 namespace ns3
 {
 
-/// generic value for aRxPHYStartDelay PHY characteristic (used when we do not know the preamble
-/// type of the next frame)
-static constexpr uint8_t RX_PHY_START_DELAY_USEC = 48;
+/// aRxPHYStartDelay value to use when waiting for a new frame in the context of EMLSR operations
+/// (Sec. 35.3.17 of 802.11be D3.1)
+static constexpr uint8_t RX_PHY_START_DELAY_USEC = 20;
+
+/**
+ * Additional time (exceeding 20 us) to wait for a PHY-RXSTART.indication when the PHY is
+ * decoding a PHY header.
+ *
+ * Values for aRxPHYStartDelay:
+ * - OFDM : 20 us (for 20 MHz) [Table 17-21 of 802.11-2020]
+ * - ERP-OFDM : 20 us [Table 18-5 of 802.11-2020]
+ * - HT : 28 us (HT-mixed), 24 us (HT-greenfield) [Table 19-25 of 802.11-2020]
+ * - VHT : 36 + 4 * max N_VHT-LTF + 4 = 72 us [Table 21-28 of 802.11-2020]
+ * - HE : 32 us (for HE SU and HE TB PPDUs)
+ *        32 + 4 * N_HE-SIG-B us (for HE MU PPDUs) [Table 27-54 of 802.11ax-2021]
+ * - EHT : 32 us (for EHT TB PPDUs)
+ *         32 + 4 * N_EHT-SIG us (for EHT MU PPDUs) [Table 36-70 of 802.11be D3.2]
+ */
+static constexpr uint8_t WAIT_FOR_RXSTART_DELAY_USEC = 52;
 
 NS_LOG_COMPONENT_DEFINE("EhtFrameExchangeManager");
 
@@ -139,17 +157,133 @@
 }
 
 bool
+EhtFrameExchangeManager::UsingOtherEmlsrLink() const
+{
+    if (!m_staMac || !m_staMac->IsEmlsrLink(m_linkId))
+    {
+        return false;
+    }
+    auto apAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
+    NS_ASSERT_MSG(apAddress, "MLD address not found for BSSID " << m_bssid);
+    // when EMLSR links are blocked, all TIDs are blocked (we test TID 0 here)
+    WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *apAddress, 0);
+    auto mask = m_staMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, m_linkId);
+    NS_ASSERT_MSG(mask, "No mask for AP " << *apAddress << " on link " << m_linkId);
+    return mask->test(static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK));
+}
+
+bool
 EhtFrameExchangeManager::StartTransmission(Ptr<Txop> edca, uint16_t allowedWidth)
 {
     NS_LOG_FUNCTION(this << edca << allowedWidth);
 
+    std::optional<Time> timeToCtsEnd;
+
+    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
+    {
+        // Cannot start a transmission on a link blocked because another EMLSR link is being used
+        if (UsingOtherEmlsrLink())
+        {
+            NS_LOG_DEBUG("StartTransmission called while another EMLSR link is being used");
+            NotifyChannelReleased(edca);
+            return false;
+        }
+
+        auto emlsrManager = m_staMac->GetEmlsrManager();
+
+        if (auto elapsed = emlsrManager->GetElapsedMediumSyncDelayTimer(m_linkId);
+            elapsed && emlsrManager->MediumSyncDelayNTxopsExceeded(m_linkId))
+        {
+            NS_LOG_DEBUG("No new TXOP attempts allowed while MediumSyncDelay is running");
+            // request channel access if needed when the MediumSyncDelay timer expires; in the
+            // meantime no queued packet can be transmitted
+            Simulator::Schedule(
+                emlsrManager->GetMediumSyncDuration() - *elapsed,
+                &Txop::StartAccessAfterEvent,
+                edca,
+                m_linkId,
+                Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT, // queued frames cannot be transmitted until
+                                                     // MSD expires
+                Txop::DONT_CHECK_MEDIUM_BUSY);       // generate backoff regardless of medium busy
+            NotifyChannelReleased(edca);
+            return false;
+        }
+
+        if (!m_phy)
+        {
+            NS_LOG_DEBUG("No PHY is currently operating on EMLSR link " << +m_linkId);
+            NotifyChannelReleased(edca);
+            return false;
+        }
+
+        if (auto mainPhy = m_staMac->GetDevice()->GetPhy(emlsrManager->GetMainPhyId());
+            mainPhy != m_phy)
+        {
+            // an aux PHY is operating on this link
+            if (!emlsrManager->GetAuxPhyTxCapable())
+            {
+                NS_LOG_DEBUG("Aux PHY is not capable of transmitting a PPDU");
+                NotifyChannelReleased(edca);
+                return false;
+            }
+
+            if (mainPhy->IsStateRx())
+            {
+                NS_LOG_DEBUG(
+                    "Main PHY is receiving a PPDU (may be, e.g., an ICF or a Beacon); do not "
+                    "transmit to avoid dropping that PPDU due to the main PHY switching to this "
+                    "link to take over the TXOP");
+                // Note that we do not prevent a (main or aux) PHY from starting a TXOP when
+                // an(other) aux PHY is receiving a PPDU. The reason is that if the aux PHY is
+                // receiving a Beacon frame, the aux PHY will not be affected by the start of
+                // a TXOP; if the aux PHY is receiving an ICF, the ICF will be dropped by
+                // ReceiveMpdu because another EMLSR link is being used.
+                NotifyChannelReleased(edca);
+                return false;
+            }
+
+            const auto rtsTxVector =
+                GetWifiRemoteStationManager()->GetRtsTxVector(m_bssid, allowedWidth);
+            const auto rtsTxTime =
+                m_phy->CalculateTxDuration(GetRtsSize(), rtsTxVector, m_phy->GetPhyBand());
+            const auto ctsTxVector =
+                GetWifiRemoteStationManager()->GetCtsTxVector(m_bssid, rtsTxVector.GetMode());
+            const auto ctsTxTime =
+                m_phy->CalculateTxDuration(GetCtsSize(), ctsTxVector, m_phy->GetPhyBand());
+
+            // the main PHY shall terminate the channel switch at the end of CTS reception;
+            // the time remaining to the end of CTS reception includes two propagation delays
+            timeToCtsEnd = rtsTxTime + m_phy->GetSifs() + ctsTxTime +
+                           MicroSeconds(2 * MAX_PROPAGATION_DELAY_USEC);
+
+            auto switchingTime = mainPhy->GetChannelSwitchDelay();
+
+            if (mainPhy->IsStateSwitching())
+            {
+                // the main PHY is switching (to another link), hence the remaining time to the
+                // end of the current channel switch needs to be added up
+                switchingTime += mainPhy->GetDelayUntilIdle();
+            }
+
+            if (switchingTime > timeToCtsEnd)
+            {
+                // switching takes longer than RTS/CTS exchange, do not transmit anything to
+                // avoid that the main PHY is requested to switch while already switching
+                NS_LOG_DEBUG("Main PHY will still be switching channel when RTS/CTS ends, thus it "
+                             "will not be able to take over this TXOP");
+                NotifyChannelReleased(edca);
+                return false;
+            }
+        }
+    }
+
     auto started = HeFrameExchangeManager::StartTransmission(edca, allowedWidth);
 
     if (started && m_staMac && m_staMac->IsEmlsrLink(m_linkId))
     {
         // notify the EMLSR Manager of the UL TXOP start on an EMLSR link
         NS_ASSERT(m_staMac->GetEmlsrManager());
-        m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId);
+        m_staMac->GetEmlsrManager()->NotifyUlTxopStart(m_linkId, timeToCtsEnd);
     }
 
     return started;
@@ -171,7 +305,7 @@
     auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
 
     HeFrameExchangeManager::ForwardPsduDown(psdu, txVector);
-    UpdateTxopEndOnTxStart(txDuration);
+    UpdateTxopEndOnTxStart(txDuration, psdu->GetDuration());
 
     if (m_apMac)
     {
@@ -204,7 +338,7 @@
     auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
 
     HeFrameExchangeManager::ForwardPsduMapDown(psduMap, txVector);
-    UpdateTxopEndOnTxStart(txDuration);
+    UpdateTxopEndOnTxStart(txDuration, psduMap.begin()->second->GetDuration());
 
     if (m_apMac)
     {
@@ -232,6 +366,30 @@
 }
 
 void
+EhtFrameExchangeManager::NavResetTimeout()
+{
+    NS_LOG_FUNCTION(this);
+    if (UsingOtherEmlsrLink())
+    {
+        // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
+        return;
+    }
+    HeFrameExchangeManager::NavResetTimeout();
+}
+
+void
+EhtFrameExchangeManager::IntraBssNavResetTimeout()
+{
+    NS_LOG_FUNCTION(this);
+    if (UsingOtherEmlsrLink())
+    {
+        // the CTS may have been missed because another EMLSR link is being used; do not reset NAV
+        return;
+    }
+    HeFrameExchangeManager::IntraBssNavResetTimeout();
+}
+
+void
 EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, const Time& delay)
 {
     NS_LOG_FUNCTION(this << address << delay.As(Time::US));
@@ -243,36 +401,42 @@
     auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
     NS_ASSERT(emlCapabilities);
 
+    std::set<uint8_t> linkIds;
     for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
     {
         if (m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
         {
-            Simulator::Schedule(delay, [=]() {
-                if (linkId != m_linkId)
-                {
-                    // the reason for blocking the other EMLSR links has changed now
-                    m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
-                                                   *mldAddress,
-                                                   {linkId});
-                }
-
-                // block DL transmissions on this link until transition delay elapses
-                m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
-                                             *mldAddress,
-                                             {linkId});
-            });
-
-            // unblock all EMLSR links when the transition delay elapses
-            Simulator::Schedule(delay + CommonInfoBasicMle::DecodeEmlsrTransitionDelay(
-                                            emlCapabilities->get().emlsrTransitionDelay),
-                                [=]() {
-                                    m_mac->UnblockUnicastTxOnLinks(
-                                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
-                                        *mldAddress,
-                                        {linkId});
-                                });
+            linkIds.insert(linkId);
         }
     }
+
+    auto blockLinks = [=, this]() {
+        // the reason for blocking the other EMLSR links has changed now
+        m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                                       *mldAddress,
+                                       linkIds);
+
+        // block DL transmissions on this link until transition delay elapses
+        m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
+                                     *mldAddress,
+                                     linkIds);
+    };
+
+    delay.IsZero() ? blockLinks() : static_cast<void>(Simulator::Schedule(delay, blockLinks));
+
+    // unblock all EMLSR links when the transition delay elapses
+    auto unblockLinks = [=, this]() {
+        m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
+                                       *mldAddress,
+                                       linkIds);
+    };
+
+    auto endDelay = delay + CommonInfoBasicMle::DecodeEmlsrTransitionDelay(
+                                emlCapabilities->get().emlsrTransitionDelay);
+
+    endDelay.IsZero() ? unblockLinks()
+                      : static_cast<void>(m_transDelayTimer[*mldAddress] =
+                                              Simulator::Schedule(endDelay, unblockLinks));
 }
 
 void
@@ -288,12 +452,14 @@
 
     // if we receive the notification from a PHY that is not connected to us, it means that
     // we have been already connected to another PHY operating on this link, hence we do not
-    // have to reset the connected PHY
-    if (phy == m_phy)
+    // have to reset the connected PHY. Similarly, we do not have to reset the connected PHY if
+    // the link does not change (this occurs when changing the channel width of aux PHYs upon
+    // enabling the EMLSR mode).
+    if (phy == m_phy && linkId != m_linkId)
     {
         ResetPhy();
     }
-    m_staMac->NotifySwitchingEmlsrLink(phy, linkId);
+    m_staMac->NotifySwitchingEmlsrLink(phy, linkId, delay);
 }
 
 void
@@ -410,6 +576,45 @@
     HeFrameExchangeManager::SendMuRts(txParams);
 }
 
+void
+EhtFrameExchangeManager::CtsAfterMuRtsTimeout(Ptr<WifiMpdu> muRts, const WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << *muRts << txVector);
+
+    // we blocked transmissions on the other EMLSR links for the EMLSR clients we sent the ICF to.
+    // Given that no client responded, we can unblock transmissions for a client if there is no
+    // ongoing UL TXOP held by that client
+    for (const auto& address : m_sentRtsTo)
+    {
+        if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
+        {
+            continue;
+        }
+
+        auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
+        NS_ASSERT(mldAddress);
+
+        if (m_ongoingTxopEnd.IsRunning() && m_txopHolder &&
+            m_mac->GetMldAddress(*m_txopHolder) == mldAddress)
+        {
+            continue;
+        }
+
+        for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+        {
+            if (linkId != m_linkId &&
+                m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
+            {
+                m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                                               *mldAddress,
+                                               {linkId});
+            }
+        }
+    }
+
+    HeFrameExchangeManager::CtsAfterMuRtsTimeout(muRts, txVector);
+}
+
 bool
 EhtFrameExchangeManager::GetEmlsrSwitchToListening(Ptr<const WifiPsdu> psdu,
                                                    uint16_t aid,
@@ -477,28 +682,32 @@
 }
 
 void
+EhtFrameExchangeManager::TransmissionSucceeded()
+{
+    NS_LOG_FUNCTION(this);
+
+    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
+        m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
+    {
+        NS_LOG_DEBUG("Reset the counter of TXOP attempts allowed while "
+                     "MediumSyncDelay is running");
+        m_staMac->GetEmlsrManager()->ResetMediumSyncDelayNTxops(m_linkId);
+    }
+
+    HeFrameExchangeManager::TransmissionSucceeded();
+}
+
+void
 EhtFrameExchangeManager::TransmissionFailed()
 {
     NS_LOG_FUNCTION(this);
 
-    for (const auto& address : m_txTimer.GetStasExpectedToRespond())
+    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
+        m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
     {
-        if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
-        {
-            // This EMLSR client did not respond to a frame sent by the AP. Specs say:
-            // The AP affiliated with the AP MLD should transmit before the TXNAV timer expires
-            // another initial Control frame addressed to the non-AP STA affiliated with the
-            // non-AP MLD if the AP intends to continue the frame exchanges with the STA and did
-            // not receive the response frame from this STA for the most recently transmitted
-            // frame that requires an immediate response after a SIFS
-            // (Sec. 35.3.17 of 802.11be D3.1)
-            // We let the AP continue the TXOP. TransmissionSucceeded() removes this client from
-            // protected stations, hence next transmission to this client in this TXOP will be
-            // protected by ICF
-            NS_LOG_DEBUG("EMLSR client " << address << " did not respond, continue TXOP");
-            TransmissionSucceeded();
-            return;
-        }
+        NS_LOG_DEBUG("Decrement the remaining number of TXOP attempts allowed while "
+                     "MediumSyncDelay is running");
+        m_staMac->GetEmlsrManager()->DecrementMediumSyncDelayNTxops(m_linkId);
     }
 
     HeFrameExchangeManager::TransmissionFailed();
@@ -511,28 +720,83 @@
 
     if (m_apMac)
     {
-        // the channel has been released; all EMLSR clients will switch back to listening
-        // operation after a timeout interval of aSIFSTime + aSlotTime + aRxPHYStartDelay
-        auto delay = m_phy->GetSifs() + m_phy->GetSlot() + MicroSeconds(RX_PHY_START_DELAY_USEC);
+        // the channel has been released; all EMLSR clients are switching back to
+        // listening operation
         for (const auto& address : m_protectedStas)
         {
             if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
             {
-                EmlsrSwitchToListening(address, delay);
+                EmlsrSwitchToListening(address, Seconds(0));
             }
         }
     }
     else if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
     {
-        // notify the EMLSR Manager of the UL TXOP end
+        // notify the EMLSR Manager of the UL TXOP end, if the TXOP included the transmission of
+        // at least a frame
         NS_ASSERT(m_staMac->GetEmlsrManager());
-        m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
+        auto edca = DynamicCast<QosTxop>(txop);
+        NS_ASSERT(edca);
+        if (auto txopStart = edca->GetTxopStartTime(m_linkId);
+            txopStart && Simulator::Now() > *txopStart)
+        {
+            m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
+        }
     }
 
     HeFrameExchangeManager::NotifyChannelReleased(txop);
 }
 
 void
+EhtFrameExchangeManager::PreProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << psdu << txVector);
+
+    // In addition, the timer resets to zero when any of the following events occur:
+    // — The STA receives an MPDU
+    // (Sec. 35.3.16.8.1 of 802.11be D3.1)
+    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
+        m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
+    {
+        m_staMac->GetEmlsrManager()->CancelMediumSyncDelayTimer(m_linkId);
+    }
+
+    if (m_apMac)
+    {
+        // we iterate over protected STAs to consider only the case when the AP is the TXOP holder.
+        // The AP received a PSDU from a non-AP STA; given that the AP is the TXOP holder, this
+        // PSDU has been likely solicited by the AP. In most of the cases, we identify which EMLSR
+        // clients are no longer involved in the TXOP when the AP transmits the frame soliciting
+        // response(s) from client(s). This is not the case, for example, for the acknowledgment
+        // in SU format of a DL MU PPDU, where all the EMLSR clients (but one) switch to listening
+        // operation after the immediate response (if any) by one of the EMLSR clients.
+        for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
+        {
+            // TB PPDUs are received by the AP at distinct times, so it is difficult to take a
+            // decision based on one of them. However, clients transmitting TB PPDUs are identified
+            // by the soliciting Trigger Frame, thus we have already identified (when sending the
+            // Trigger Frame) which EMLSR clients have switched to listening operation.
+            // If the PSDU is not carried in a TB PPDU, we can determine whether this EMLSR client
+            // is switching to listening operation by checking whether the AP is expecting a
+            // response from it.
+            if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) && !txVector.IsUlMu() &&
+                m_txTimer.GetStasExpectedToRespond().count(*clientIt) == 0)
+            {
+                EmlsrSwitchToListening(*clientIt, Seconds(0));
+                // this client is no longer involved in the current TXOP
+                clientIt = m_protectedStas.erase(clientIt);
+            }
+            else
+            {
+                clientIt++;
+            }
+        }
+    }
+
+    HeFrameExchangeManager::PreProcessFrame(psdu, txVector);
+}
+
+void
 EhtFrameExchangeManager::PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
 {
     NS_LOG_FUNCTION(this << psdu << txVector);
@@ -551,25 +815,10 @@
                 m_phy->GetSifs() + m_phy->GetSlot() + MicroSeconds(RX_PHY_START_DELAY_USEC);
             NS_LOG_DEBUG("Expected TXOP end=" << (Simulator::Now() + delay).As(Time::S));
             m_ongoingTxopEnd = Simulator::Schedule(delay, &EhtFrameExchangeManager::TxopEnd, this);
-
-            // block transmissions on other links
-            auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(psdu->GetAddr2());
-            NS_ASSERT(mldAddress);
-
-            for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
-            {
-                if (linkId != m_linkId &&
-                    m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
-                {
-                    m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
-                                                 *mldAddress,
-                                                 {linkId});
-                }
-            }
         }
         else
         {
-            UpdateTxopEndOnRxEnd();
+            UpdateTxopEndOnRxEnd(psdu->GetDuration());
         }
     }
 
@@ -583,7 +832,7 @@
         }
         else
         {
-            UpdateTxopEndOnRxEnd();
+            UpdateTxopEndOnRxEnd(psdu->GetDuration());
         }
     }
 }
@@ -599,6 +848,42 @@
 
     const auto& hdr = mpdu->GetHeader();
 
+    if (m_apMac && GetWifiRemoteStationManager()->GetEmlsrEnabled(hdr.GetAddr2()))
+    {
+        // the AP MLD received an MPDU from an EMLSR client, which is now involved in an UL TXOP,
+        // hence block transmissions for this EMLSR client on other links
+        auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(hdr.GetAddr2());
+        NS_ASSERT(mldAddress);
+
+        for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+        {
+            if (linkId != m_linkId &&
+                m_mac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*mldAddress))
+            {
+                m_mac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                                             *mldAddress,
+                                             {linkId});
+            }
+        }
+
+        // Make sure that transmissions for this EMLSR client are not blocked on this link
+        // (the AP MLD may have sent an ICF on another link right before receiving this MPDU,
+        // thus transmissions on this link may have been blocked)
+        m_mac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                                       *mldAddress,
+                                       {m_linkId});
+
+        // Stop the transition delay timer for this EMLSR client, if any is running
+        if (auto it = m_transDelayTimer.find(*mldAddress);
+            it != m_transDelayTimer.end() && it->second.IsRunning())
+        {
+            it->second.PeekEventImpl()->Invoke();
+            it->second.Cancel();
+        }
+    }
+
+    bool icfReceived = false;
+
     if (hdr.IsTrigger())
     {
         if (!m_staMac)
@@ -620,14 +905,7 @@
         if (trigger.IsMuRts() && m_staMac->IsEmlsrLink(m_linkId))
         {
             // this is an initial Control frame
-            auto apAddress = GetWifiRemoteStationManager()->GetMldAddress(m_bssid);
-            NS_ASSERT_MSG(apAddress, "MLD address not found for BSSID " << m_bssid);
-            // when EMLSR links are blocked, all TIDs are blocked (we test TID 0 here)
-            WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *apAddress, 0);
-            if (auto mask =
-                    m_staMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, m_linkId);
-                mask && mask->test(static_cast<std::size_t>(
-                            WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK)))
+            if (UsingOtherEmlsrLink())
             {
                 // we received an ICF on a link that is blocked because another EMLSR link is
                 // being used. This is likely because transmission on the other EMLSR link
@@ -637,10 +915,50 @@
                 return;
             }
 
+            /**
+             * It might happen that the AP MLD has not yet received a data frame being transmitted
+             * by us on another link and starts sending an ICF on this link. The transmission of
+             * the ICF might be long enough to terminate after the subsequent acknowledgment, which
+             * terminates the TXOP on the other link. Consequently, no TXOP is ongoing when the
+             * reception of the ICF ends, hence the ICF is not dropped and a DL TXOP can start on
+             * this link. However, even if the aux PHY is able to receive the ICF, we need to allow
+             * enough time for the main PHY to switch to this link. Therefore, we assume that the
+             * ICF is successfully received (by an aux PHY) if the TXOP on the other link ended
+             * before the padding of the ICF. In order to determine when the TXOP ended, we can
+             * determine when the medium sync delay timer started on this link.
+             *
+             *                        TXOP end
+             *                            │
+             *                        ┌───┐                               another
+             *   AP MLD               │ACK│                               link
+             *  ───────────┬─────────┬┴───┴───────────────────────────────────────
+             *   EMLSR     │   QoS   │    │                            main PHY
+             *   client    │  Data   │    │
+             *             └─────────┘    │
+             *                            │- medium sync delay timer -│
+             *                      ┌─────┬───┐                           this
+             *   AP MLD             │ ICF │pad│                           link
+             *  ────────────────────┴─────┴───┴───────────────────────────────────
+             *                                                          aux PHY
+             */
+
+            if (auto elapsed =
+                    m_staMac->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_linkId))
+            {
+                TimeValue padding;
+                m_staMac->GetEmlsrManager()->GetAttribute("EmlsrPaddingDelay", padding);
+
+                if (*elapsed < padding.Get())
+                {
+                    NS_LOG_DEBUG("Drop ICF due to not enough time for the main PHY to switch link");
+                    return;
+                }
+            }
+
             NS_ASSERT(m_staMac->GetEmlsrManager());
-            Simulator::ScheduleNow(&EmlsrManager::NotifyIcfReceived,
-                                   m_staMac->GetEmlsrManager(),
-                                   m_linkId);
+            m_staMac->GetEmlsrManager()->NotifyIcfReceived(m_linkId);
+            icfReceived = true;
+
             // we just got involved in a DL TXOP. Check if we are still involved in the TXOP in a
             // SIFS (we are expected to reply by sending a CTS frame)
             m_ongoingTxopEnd.Cancel();
@@ -651,6 +969,22 @@
         }
     }
 
+    // We impose that an aux PHY is only able to receive an ICF, a CTS or a management frame
+    // (we are interested in receiving mainly Beacon frames). Note that other frames are still
+    // post-processed, e.g., used to set the NAV and the TXOP holder.
+    // The motivation is that, e.g., an AP MLD may send an ICF to EMLSR clients A and B;
+    // A responds while B does not; the AP MLD sends a DL MU PPDU to both clients followed
+    // by an MU-BAR to solicit a BlockAck from both clients. If an aux PHY of client B is
+    // operating on this link, the MU-BAR will be received and a TB PPDU response sent
+    // through the aux PHY.
+    if (m_staMac && m_staMac->IsEmlsrLink(m_linkId) &&
+        m_mac->GetLinkForPhy(m_staMac->GetEmlsrManager()->GetMainPhyId()) != m_linkId &&
+        !icfReceived && !mpdu->GetHeader().IsCts() && !mpdu->GetHeader().IsMgt())
+    {
+        NS_LOG_DEBUG("Dropping " << *mpdu << " received by an aux PHY on link " << +m_linkId);
+        return;
+    }
+
     HeFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
 }
 
@@ -659,6 +993,19 @@
 {
     NS_LOG_FUNCTION(this);
 
+    if (m_phy->IsReceivingPhyHeader())
+    {
+        // we may get here because the PHY has not issued the PHY-RXSTART.indication before
+        // the expiration of the timer started to detect new received frames, but the PHY is
+        // currently decoding the PHY header of a PPDU, so let's wait some more time to check
+        // if we receive a PHY-RXSTART.indication when the PHY is done decoding the PHY header
+        NS_LOG_DEBUG("PHY is decoding the PHY header of PPDU, postpone TXOP end");
+        m_ongoingTxopEnd = Simulator::Schedule(MicroSeconds(WAIT_FOR_RXSTART_DELAY_USEC),
+                                               &EhtFrameExchangeManager::TxopEnd,
+                                               this);
+        return;
+    }
+
     if (m_staMac && m_staMac->IsEmlsrLink(m_linkId))
     {
         m_staMac->GetEmlsrManager()->NotifyTxopEnd(m_linkId);
@@ -672,9 +1019,9 @@
 }
 
 void
-EhtFrameExchangeManager::UpdateTxopEndOnTxStart(Time txDuration)
+EhtFrameExchangeManager::UpdateTxopEndOnTxStart(Time txDuration, Time durationId)
 {
-    NS_LOG_FUNCTION(this << txDuration.As(Time::MS));
+    NS_LOG_FUNCTION(this << txDuration.As(Time::MS) << durationId.As(Time::US));
 
     if (!m_ongoingTxopEnd.IsRunning())
     {
@@ -692,6 +1039,13 @@
         // the response)
         delay = m_txTimer.GetDelayLeft();
     }
+    else if (durationId <= m_phy->GetSifs())
+    {
+        // the TX timer is not running, hence no response is expected, and the Duration/ID value
+        // is less than or equal to a SIFS; the TXOP will end after this transmission
+        NS_LOG_DEBUG("Assume TXOP will end based on Duration/ID value");
+        delay = txDuration;
+    }
     else
     {
         // the TX Timer is not running, hence no response is expected (e.g., we are
@@ -726,9 +1080,9 @@
 }
 
 void
-EhtFrameExchangeManager::UpdateTxopEndOnRxEnd()
+EhtFrameExchangeManager::UpdateTxopEndOnRxEnd(Time durationId)
 {
-    NS_LOG_FUNCTION(this);
+    NS_LOG_FUNCTION(this << durationId.As(Time::US));
 
     if (!m_ongoingTxopEnd.IsRunning())
     {
@@ -738,6 +1092,15 @@
 
     m_ongoingTxopEnd.Cancel();
 
+    // if the Duration/ID of the received frame is less than a SIFS, the TXOP
+    // is terminated
+    if (durationId <= m_phy->GetSifs())
+    {
+        NS_LOG_DEBUG("Assume TXOP ended based on Duration/ID value");
+        TxopEnd();
+        return;
+    }
+
     // we may send a response after a SIFS or we may receive another frame after a SIFS.
     // Postpone the TXOP end by considering the latter (which takes longer)
     auto delay = m_phy->GetSifs() + m_phy->GetSlot() + MicroSeconds(RX_PHY_START_DELAY_USEC);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/eht-frame-exchange-manager.h ns-3.41/src/wifi/model/eht/eht-frame-exchange-manager.h
--- ns-3.40/src/wifi/model/eht/eht-frame-exchange-manager.h	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/eht-frame-exchange-manager.h	2024-02-09 14:44:08.581795000 -0800
@@ -23,9 +23,13 @@
 #include "ns3/he-frame-exchange-manager.h"
 #include "ns3/mgt-headers.h"
 
+#include <unordered_map>
+
 namespace ns3
 {
 
+class MgtEmlOmn;
+
 /**
  * \ingroup wifi
  *
@@ -87,19 +91,30 @@
      */
     void NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId, Time delay);
 
+    /**
+     * \return whether this is an EMLSR link that has been blocked because another EMLSR link
+     *         is being used
+     */
+    bool UsingOtherEmlsrLink() const;
+
   protected:
     void DoDispose() override;
     void RxStartIndication(WifiTxVector txVector, Time psduDuration) override;
     void ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector& txVector) override;
     void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector) override;
     void SendMuRts(const WifiTxParameters& txParams) override;
+    void CtsAfterMuRtsTimeout(Ptr<WifiMpdu> muRts, const WifiTxVector& txVector) override;
+    void TransmissionSucceeded() override;
     void TransmissionFailed() override;
     void NotifyChannelReleased(Ptr<Txop> txop) override;
+    void PreProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
     void PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
     void ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
                      RxSignalInfo rxSignalInfo,
                      const WifiTxVector& txVector,
                      bool inAmpdu) override;
+    void NavResetTimeout() override;
+    void IntraBssNavResetTimeout() override;
 
     /**
      * This method is intended to be called when an AP MLD detects that an EMLSR client previously
@@ -118,8 +133,9 @@
      * Update the TXOP end timer when starting a frame transmission.
      *
      * \param txDuration the TX duration of the frame being transmitted
+     * \param durationId the Duration/ID value carried by the frame being transmitted
      */
-    void UpdateTxopEndOnTxStart(Time txDuration);
+    void UpdateTxopEndOnTxStart(Time txDuration, Time durationId);
 
     /**
      * Update the TXOP end timer when receiving a PHY-RXSTART.indication.
@@ -130,8 +146,10 @@
 
     /**
      * Update the TXOP end timer when a frame reception ends.
+     *
+     * \param durationId the Duration/ID value carried by the received frame
      */
-    void UpdateTxopEndOnRxEnd();
+    void UpdateTxopEndOnRxEnd(Time durationId);
 
     /**
      * Take actions when a TXOP (of which we are not the holder) ends.
@@ -140,6 +158,8 @@
 
     EventId m_ongoingTxopEnd; //!< event indicating the possible end of the current TXOP (of which
                               //!< we are not the holder)
+    std::unordered_map<Mac48Address, EventId, WifiAddressHash>
+        m_transDelayTimer; //!< MLD address-indexed map of transition delay timers
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/eht-phy.cc ns-3.41/src/wifi/model/eht/eht-phy.cc
--- ns-3.40/src/wifi/model/eht/eht-phy.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/eht-phy.cc	2024-02-09 14:44:08.581795000 -0800
@@ -271,7 +271,7 @@
     {                                                                                              \
         static WifiMode mcs = CreateEhtMcs(x);                                                     \
         return mcs;                                                                                \
-    };
+    }
 
 GET_EHT_MCS(0)
 GET_EHT_MCS(1)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/emlsr-manager.cc ns-3.41/src/wifi/model/eht/emlsr-manager.cc
--- ns-3.40/src/wifi/model/eht/emlsr-manager.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/emlsr-manager.cc	2024-02-09 14:44:08.581795000 -0800
@@ -26,6 +26,7 @@
 #include "ns3/assert.h"
 #include "ns3/attribute-container.h"
 #include "ns3/log.h"
+#include "ns3/mgt-action-headers.h"
 #include "ns3/wifi-mpdu.h"
 #include "ns3/wifi-net-device.h"
 #include "ns3/wifi-phy-state-helper.h"
@@ -59,15 +60,47 @@
             .AddAttribute(
                 "MainPhyId",
                 "The ID of the main PHY (position in the vector of PHYs held by "
-                "WifiNetDevice). This attribute cannot be set after initialization.",
+                "WifiNetDevice). This attribute cannot be set after construction.",
+                TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT, // prevent setting after construction
                 UintegerValue(0),
                 MakeUintegerAccessor(&EmlsrManager::SetMainPhyId, &EmlsrManager::GetMainPhyId),
                 MakeUintegerChecker<uint8_t>())
             .AddAttribute("AuxPhyChannelWidth",
-                          "The maximum channel width (MHz) supported by Aux PHYs",
+                          "The maximum channel width (MHz) supported by Aux PHYs. Note that the "
+                          "maximum channel width is capped to the maximum channel width supported "
+                          "by the configured maximum modulation class supported.",
+                          TypeId::ATTR_GET |
+                              TypeId::ATTR_CONSTRUCT, // prevent setting after construction
                           UintegerValue(20),
                           MakeUintegerAccessor(&EmlsrManager::m_auxPhyMaxWidth),
                           MakeUintegerChecker<uint16_t>(20, 160))
+            .AddAttribute("AuxPhyMaxModClass",
+                          "The maximum modulation class supported by Aux PHYs. Use "
+                          "WIFI_MOD_CLASS_OFDM for non-HT.",
+                          TypeId::ATTR_GET |
+                              TypeId::ATTR_CONSTRUCT, // prevent setting after construction
+                          EnumValue(WIFI_MOD_CLASS_OFDM),
+                          MakeEnumAccessor<WifiModulationClass>(&EmlsrManager::m_auxPhyMaxModClass),
+                          MakeEnumChecker(WIFI_MOD_CLASS_HR_DSSS,
+                                          "HR-DSSS",
+                                          WIFI_MOD_CLASS_ERP_OFDM,
+                                          "ERP-OFDM",
+                                          WIFI_MOD_CLASS_OFDM,
+                                          "OFDM",
+                                          WIFI_MOD_CLASS_HT,
+                                          "HT",
+                                          WIFI_MOD_CLASS_VHT,
+                                          "VHT",
+                                          WIFI_MOD_CLASS_HE,
+                                          "HE",
+                                          WIFI_MOD_CLASS_EHT,
+                                          "EHT"))
+            .AddAttribute("AuxPhyTxCapable",
+                          "Whether Aux PHYs are capable of transmitting PPDUs.",
+                          BooleanValue(true),
+                          MakeBooleanAccessor(&EmlsrManager::SetAuxPhyTxCapable,
+                                              &EmlsrManager::GetAuxPhyTxCapable),
+                          MakeBooleanChecker())
             .AddAttribute(
                 "EmlsrLinkSet",
                 "IDs of the links on which EMLSR mode will be enabled. An empty set "
@@ -86,6 +119,13 @@
 }
 
 EmlsrManager::EmlsrManager()
+    // The STA initializes dot11MSDTimerDuration to aPPDUMaxTime defined in Table 36-70
+    // (Sec. 35.3.16.8.1 of 802.11be D3.1)
+    : m_mediumSyncDuration(MicroSeconds(DEFAULT_MSD_DURATION_USEC)),
+      // The default value of dot11MSDOFDMEDthreshold is –72 dBm and the default value of
+      // dot11MSDTXOPMax is 1, respectively (Sec. 35.3.16.8.1 of 802.11be D3.1)
+      m_msdOfdmEdThreshold(DEFAULT_MSD_OFDM_ED_THRESH),
+      m_msdMaxNTxops(DEFAULT_MSD_MAX_N_TXOPS)
 {
     NS_LOG_FUNCTION(this);
 }
@@ -104,6 +144,10 @@
                                             MakeCallback(&EmlsrManager::TxDropped, this));
     m_staMac = nullptr;
     m_transitionTimeoutEvent.Cancel();
+    for (auto& [id, status] : m_mediumSyncDelayStatus)
+    {
+        status.timer.Cancel();
+    }
     Object::DoDispose();
 }
 
@@ -150,6 +194,18 @@
     return m_resetCamState;
 }
 
+void
+EmlsrManager::SetAuxPhyTxCapable(bool capable)
+{
+    m_auxPhyTxCapable = capable;
+}
+
+bool
+EmlsrManager::GetAuxPhyTxCapable() const
+{
+    return m_auxPhyTxCapable;
+}
+
 const std::set<uint8_t>&
 EmlsrManager::GetEmlsrLinks() const
 {
@@ -168,6 +224,17 @@
     return StaticCast<EhtFrameExchangeManager>(m_staMac->GetFrameExchangeManager(linkId));
 }
 
+std::optional<Time>
+EmlsrManager::GetElapsedMediumSyncDelayTimer(uint8_t linkId) const
+{
+    if (const auto statusIt = m_mediumSyncDelayStatus.find(linkId);
+        statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsRunning())
+    {
+        return m_mediumSyncDuration - Simulator::GetDelayLeft(statusIt->second.timer);
+    }
+    return std::nullopt;
+}
+
 void
 EmlsrManager::SetTransitionTimeout(Time timeout)
 {
@@ -182,6 +249,45 @@
 }
 
 void
+EmlsrManager::SetMediumSyncDuration(Time duration)
+{
+    NS_LOG_FUNCTION(this << duration.As(Time::US));
+    m_mediumSyncDuration = duration;
+}
+
+Time
+EmlsrManager::GetMediumSyncDuration() const
+{
+    return m_mediumSyncDuration;
+}
+
+void
+EmlsrManager::SetMediumSyncOfdmEdThreshold(int8_t threshold)
+{
+    NS_LOG_FUNCTION(this << threshold);
+    m_msdOfdmEdThreshold = threshold;
+}
+
+int8_t
+EmlsrManager::GetMediumSyncOfdmEdThreshold() const
+{
+    return m_msdOfdmEdThreshold;
+}
+
+void
+EmlsrManager::SetMediumSyncMaxNTxops(std::optional<uint8_t> nTxops)
+{
+    NS_LOG_FUNCTION(this << nTxops.has_value());
+    m_msdMaxNTxops = nTxops;
+}
+
+std::optional<uint8_t>
+EmlsrManager::GetMediumSyncMaxNTxops() const
+{
+    return m_msdMaxNTxops;
+}
+
+void
 EmlsrManager::SetEmlsrLinks(const std::set<uint8_t>& linkIds)
 {
     NS_LOG_FUNCTION(this);
@@ -265,15 +371,22 @@
         return;
     }
 
-    SwitchMainPhy(linkId, true); // channel switch should occur instantaneously
+    Simulator::ScheduleNow([=, this]() {
+        SwitchMainPhy(linkId,
+                      true, // channel switch should occur instantaneously
+                      RESET_BACKOFF,
+                      DONT_REQUEST_ACCESS);
+
+        // aux PHY received the ICF but main PHY will send the response
+        auto uid = auxPhy->GetPreviouslyRxPpduUid();
+        mainPhy->SetPreviouslyRxPpduUid(uid);
 
-    // aux PHY received the ICF but main PHY will send the response
-    auto uid = auxPhy->GetPreviouslyRxPpduUid();
-    mainPhy->SetPreviouslyRxPpduUid(uid);
+        DoNotifyIcfReceived(linkId);
+    });
 }
 
 void
-EmlsrManager::NotifyUlTxopStart(uint8_t linkId)
+EmlsrManager::NotifyUlTxopStart(uint8_t linkId, std::optional<Time> timeToCtsEnd)
 {
     NS_LOG_FUNCTION(this << linkId);
 
@@ -293,22 +406,36 @@
         }
     }
 
-    // if this TXOP is being started by an aux PHY, wait until the end of RTS transmission and
-    // then have the main PHY (instantaneously) take over the TXOP on this link. We may start the
-    // channel switch now and use the channel switch delay configured for the main PHY, but then
-    // we would have no guarantees that the channel switch is completed in RTS TX time plus SIFS.
+    // if this TXOP is being started by an aux PHY, schedule a channel switch for the main PHY
+    // such that the channel switch is completed by the time the CTS response is received. The
+    // delay has been passed by the FEM.
     if (m_staMac->GetLinkForPhy(m_mainPhyId) != linkId)
     {
         auto stateHelper = m_staMac->GetWifiPhy(linkId)->GetState();
         NS_ASSERT(stateHelper);
         NS_ASSERT_MSG(stateHelper->GetState() == TX,
                       "Expecting the aux PHY to be transmitting (an RTS frame)");
-        Simulator::Schedule(stateHelper->GetDelayUntilIdle(),
+        NS_ASSERT_MSG(timeToCtsEnd.has_value(),
+                      "Aux PHY is sending RTS, expected to get the time to CTS end");
+
+        auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
+
+        // the main PHY shall terminate the channel switch at the end of CTS reception;
+        // the time remaining to the end of CTS reception includes two propagation delays
+        const auto delay = *timeToCtsEnd - mainPhy->GetChannelSwitchDelay();
+
+        NS_ASSERT(delay.IsPositive());
+        NS_LOG_DEBUG("Schedule main Phy switch in " << delay.As(Time::US));
+        Simulator::Schedule(delay,
                             &EmlsrManager::SwitchMainPhy,
                             this,
                             linkId,
-                            true); // channel switch should occur instantaneously
+                            false,
+                            RESET_BACKOFF,
+                            DONT_REQUEST_ACCESS);
     }
+
+    DoNotifyUlTxopStart(linkId);
 }
 
 void
@@ -322,21 +449,59 @@
         return;
     }
 
-    // unblock transmissions and resume medium access on other EMLSR links
-    for (auto id : m_staMac->GetLinkIds())
-    {
-        if (id != linkId && m_staMac->IsEmlsrLink(id))
+    DoNotifyTxopEnd(linkId);
+
+    Simulator::ScheduleNow([=, this]() {
+        // unblock transmissions and resume medium access on other EMLSR links
+        for (auto id : m_staMac->GetLinkIds())
         {
-            m_staMac->UnblockTxOnLink(id, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
-            m_staMac->GetChannelAccessManager(id)->NotifyStopUsingOtherEmlsrLink();
+            if (m_staMac->IsEmlsrLink(id))
+            {
+                m_staMac->UnblockTxOnLink(id, WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
+                m_staMac->GetChannelAccessManager(id)->NotifyStopUsingOtherEmlsrLink();
+            }
         }
+
+        StartMediumSyncDelayTimer(linkId);
+    });
+}
+
+void
+EmlsrManager::SetCcaEdThresholdOnLinkSwitch(Ptr<WifiPhy> phy, uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << phy << linkId);
+
+    // if a MediumSyncDelay timer is running for the link on which the main PHY is going to
+    // operate, set the CCA ED threshold to the MediumSyncDelay OFDM ED threshold
+    if (auto statusIt = m_mediumSyncDelayStatus.find(linkId);
+        statusIt != m_mediumSyncDelayStatus.cend() && statusIt->second.timer.IsRunning())
+    {
+        NS_LOG_DEBUG("Setting CCA ED threshold of PHY " << phy << " to " << +m_msdOfdmEdThreshold
+                                                        << " on link " << +linkId);
+
+        // store the current CCA ED threshold in the m_prevCcaEdThreshold map, if not present
+        m_prevCcaEdThreshold.try_emplace(phy, phy->GetCcaEdThreshold());
+
+        phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
+    }
+    // otherwise, restore the previous value for the CCA ED threshold (if any)
+    else if (auto threshIt = m_prevCcaEdThreshold.find(phy);
+             threshIt != m_prevCcaEdThreshold.cend())
+    {
+        NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
+                                                          << " on link " << +linkId);
+        phy->SetCcaEdThreshold(threshIt->second);
+        m_prevCcaEdThreshold.erase(threshIt);
     }
 }
 
 void
-EmlsrManager::SwitchMainPhy(uint8_t linkId, bool noSwitchDelay)
+EmlsrManager::SwitchMainPhy(uint8_t linkId,
+                            bool noSwitchDelay,
+                            bool resetBackoff,
+                            bool requestAccess)
 {
-    NS_LOG_FUNCTION(this << linkId << noSwitchDelay);
+    NS_LOG_FUNCTION(this << linkId << noSwitchDelay << resetBackoff << requestAccess);
 
     auto mainPhy = m_staMac->GetDevice()->GetPhy(m_mainPhyId);
 
@@ -361,25 +526,206 @@
                   "We should not ask the main PHY to switch channel while transmitting");
 
     // request the main PHY to switch channel
-    Simulator::ScheduleNow([=]() {
-        auto delay = mainPhy->GetChannelSwitchDelay();
-        NS_ASSERT_MSG(noSwitchDelay || delay <= m_lastAdvTransitionDelay,
-                      "Transition delay (" << m_lastAdvTransitionDelay.As(Time::US)
-                                           << ") should exceed the channel switch delay ("
-                                           << delay.As(Time::US) << ")");
-        if (noSwitchDelay)
-        {
-            mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
-        }
-        mainPhy->SetOperatingChannel(newMainPhyChannel);
-        // restore previous channel switch delay
-        if (noSwitchDelay)
+    auto delay = mainPhy->GetChannelSwitchDelay();
+    NS_ASSERT_MSG(noSwitchDelay || delay <= m_lastAdvTransitionDelay,
+                  "Transition delay (" << m_lastAdvTransitionDelay.As(Time::US)
+                                       << ") should exceed the channel switch delay ("
+                                       << delay.As(Time::US) << ")");
+    if (noSwitchDelay)
+    {
+        mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
+    }
+    mainPhy->SetOperatingChannel(newMainPhyChannel);
+    // restore previous channel switch delay
+    if (noSwitchDelay)
+    {
+        mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
+    }
+    // re-enable short time slot, if needed
+    if (m_staMac->GetWifiRemoteStationManager(linkId)->GetShortSlotTimeEnabled())
+    {
+        mainPhy->SetSlot(MicroSeconds(9));
+    }
+
+    if (resetBackoff)
+    {
+        // reset the backoffs on the link left by the main PHY
+        m_staMac->GetChannelAccessManager(*currMainPhyLinkId)->ResetAllBackoffs();
+    }
+
+    const auto timeToSwitchEnd = noSwitchDelay ? Seconds(0) : mainPhy->GetChannelSwitchDelay();
+
+    if (requestAccess)
+    {
+        // schedule channel access request on the new link when switch is completed
+        Simulator::Schedule(timeToSwitchEnd, [=, this]() {
+            for (const auto& [acIndex, ac] : wifiAcList)
+            {
+                m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
+                    linkId,
+                    Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
+                    Txop::CHECK_MEDIUM_BUSY);
+            }
+        });
+    }
+
+    SetCcaEdThresholdOnLinkSwitch(mainPhy, linkId);
+    NotifyMainPhySwitch(*currMainPhyLinkId, linkId);
+}
+
+void
+EmlsrManager::SwitchAuxPhy(uint8_t currLinkId, uint8_t nextLinkId)
+{
+    NS_LOG_FUNCTION(this << currLinkId << nextLinkId);
+
+    auto auxPhy = GetStaMac()->GetWifiPhy(currLinkId);
+
+    auto newAuxPhyChannel = GetChannelForAuxPhy(nextLinkId);
+
+    NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << newAuxPhyChannel
+                             << " to operate on link " << +nextLinkId);
+
+    GetStaMac()
+        ->GetChannelAccessManager(currLinkId)
+        ->NotifySwitchingEmlsrLink(auxPhy, newAuxPhyChannel, nextLinkId);
+
+    auxPhy->SetOperatingChannel(newAuxPhyChannel);
+    // re-enable short time slot, if needed
+    if (m_staMac->GetWifiRemoteStationManager(nextLinkId)->GetShortSlotTimeEnabled())
+    {
+        auxPhy->SetSlot(MicroSeconds(9));
+    }
+
+    // schedule channel access request on the new link when switch is completed
+    Simulator::Schedule(auxPhy->GetChannelSwitchDelay(), [=, this]() {
+        for (const auto& [acIndex, ac] : wifiAcList)
         {
-            mainPhy->SetAttribute("ChannelSwitchDelay", TimeValue(delay));
+            m_staMac->GetQosTxop(acIndex)->StartAccessAfterEvent(
+                nextLinkId,
+                Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
+                Txop::CHECK_MEDIUM_BUSY);
         }
     });
 
-    NotifyMainPhySwitch(*currMainPhyLinkId, linkId);
+    SetCcaEdThresholdOnLinkSwitch(auxPhy, nextLinkId);
+}
+
+void
+EmlsrManager::StartMediumSyncDelayTimer(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    // iterate over all the other EMLSR links
+    for (auto id : m_staMac->GetLinkIds())
+    {
+        if (id != linkId && m_staMac->IsEmlsrLink(id))
+        {
+            const auto [it, inserted] = m_mediumSyncDelayStatus.try_emplace(id);
+
+            // reset the max number of TXOP attempts
+            it->second.msdNTxopsLeft = m_msdMaxNTxops;
+
+            // there are cases in which no PHY is operating on a link; e.g., the main PHY starts
+            // switching to a link on which an aux PHY gained a TXOP and sent an RTS, but the CTS
+            // is not received and the UL TXOP ends before the main PHY channel switch is
+            // completed. The MSD timer is started on the link left "uncovered" by the main PHY
+            if (auto phy = m_staMac->GetWifiPhy(id); phy && !it->second.timer.IsRunning())
+            {
+                NS_LOG_DEBUG("Setting CCA ED threshold on link "
+                             << +id << " to " << +m_msdOfdmEdThreshold << " PHY " << phy);
+                m_prevCcaEdThreshold[phy] = phy->GetCcaEdThreshold();
+                phy->SetCcaEdThreshold(m_msdOfdmEdThreshold);
+            }
+
+            // (re)start the timer
+            it->second.timer.Cancel();
+            it->second.timer = Simulator::Schedule(m_mediumSyncDuration,
+                                                   &EmlsrManager::MediumSyncDelayTimerExpired,
+                                                   this,
+                                                   id);
+        }
+    }
+}
+
+void
+EmlsrManager::CancelMediumSyncDelayTimer(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
+
+    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
+
+    timerIt->second.timer.Cancel();
+    MediumSyncDelayTimerExpired(linkId);
+}
+
+void
+EmlsrManager::MediumSyncDelayTimerExpired(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
+
+    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && !timerIt->second.timer.IsRunning());
+
+    // reset the MSD OFDM ED threshold
+    auto phy = m_staMac->GetWifiPhy(linkId);
+
+    if (!phy)
+    {
+        // no PHY is operating on this link. This may happen when a MediumSyncDelay timer expires
+        // on the link left "uncovered" by the main PHY that is operating on another link (and the
+        // aux PHY of that link did not switch). In this case, do nothing, since the CCA ED
+        // threshold on the main PHY will be restored once the main PHY switches back to its link
+        return;
+    }
+
+    auto threshIt = m_prevCcaEdThreshold.find(phy);
+    NS_ASSERT_MSG(threshIt != m_prevCcaEdThreshold.cend(),
+                  "No value to restore for CCA ED threshold on PHY " << phy);
+    NS_LOG_DEBUG("Resetting CCA ED threshold of PHY " << phy << " to " << threshIt->second
+                                                      << " on link " << +linkId);
+    phy->SetCcaEdThreshold(threshIt->second);
+    m_prevCcaEdThreshold.erase(threshIt);
+}
+
+void
+EmlsrManager::DecrementMediumSyncDelayNTxops(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    const auto timerIt = m_mediumSyncDelayStatus.find(linkId);
+
+    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
+    NS_ASSERT(timerIt->second.msdNTxopsLeft != 0);
+
+    if (timerIt->second.msdNTxopsLeft)
+    {
+        --timerIt->second.msdNTxopsLeft.value();
+    }
+}
+
+void
+EmlsrManager::ResetMediumSyncDelayNTxops(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
+
+    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
+    timerIt->second.msdNTxopsLeft.reset();
+}
+
+bool
+EmlsrManager::MediumSyncDelayNTxopsExceeded(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    auto timerIt = m_mediumSyncDelayStatus.find(linkId);
+
+    NS_ASSERT(timerIt != m_mediumSyncDelayStatus.cend() && timerIt->second.timer.IsRunning());
+    return timerIt->second.msdNTxopsLeft == 0;
 }
 
 MgtEmlOmn
@@ -535,13 +881,13 @@
     // active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled)
     m_staMac->NotifyEmlsrModeChanged(m_emlsrLinks);
     // Enforce the limit on the max channel width supported by aux PHYs
-    ApplyMaxChannelWidthOnAuxPhys();
+    ApplyMaxChannelWidthAndModClassOnAuxPhys();
 
     NotifyEmlsrModeChanged();
 }
 
 void
-EmlsrManager::ApplyMaxChannelWidthOnAuxPhys()
+EmlsrManager::ApplyMaxChannelWidthAndModClassOnAuxPhys()
 {
     NS_LOG_FUNCTION(this);
     auto currMainPhyLinkId = m_staMac->GetLinkForPhy(m_mainPhyId);
@@ -558,6 +904,8 @@
             continue;
         }
 
+        auxPhy->SetMaxModulationClassSupported(m_auxPhyMaxModClass);
+
         NS_LOG_DEBUG("Aux PHY (" << auxPhy << ") is about to switch to " << channel
                                  << " to operate on link " << +linkId);
         // We cannot simply set the new channel, because otherwise the MAC will disable
@@ -565,12 +913,22 @@
         // this channel switch must not have such a consequence. We already have a method
         // for doing so, i.e., inform the MAC that the PHY is switching channel to operate
         // on the "same" link.
-        m_staMac->GetChannelAccessManager(linkId)->NotifySwitchingEmlsrLink(auxPhy,
-                                                                            channel,
-                                                                            linkId);
+        auto cam = m_staMac->GetChannelAccessManager(linkId);
+        cam->NotifySwitchingEmlsrLink(auxPhy, channel, linkId);
 
         void (WifiPhy::*fp)(const WifiPhyOperatingChannel&) = &WifiPhy::SetOperatingChannel;
         Simulator::ScheduleNow(fp, auxPhy, channel);
+
+        // the way the ChannelAccessManager handles EMLSR link switch implies that a PHY listener
+        // is removed when the channel switch starts and another one is attached when the channel
+        // switch ends. In the meantime, no PHY is connected to the ChannelAccessManager. Inform
+        // the ChannelAccessManager that this channel switch is related to EMLSR operations, so
+        // that the ChannelAccessManager does not complain if events requiring access to the PHY
+        // occur during the channel switch.
+        cam->NotifyStartUsingOtherEmlsrLink();
+        Simulator::Schedule(auxPhy->GetChannelSwitchDelay(),
+                            &ChannelAccessManager::NotifyStopUsingOtherEmlsrLink,
+                            cam);
     }
 }
 
@@ -590,25 +948,27 @@
         m_mainPhyChannels.emplace(linkId, channel);
 
         auto mainPhyChWidth = channel.GetWidth();
-        if (m_auxPhyMaxWidth >= mainPhyChWidth)
+        auto auxPhyMaxWidth =
+            std::min(m_auxPhyMaxWidth, GetMaximumChannelWidth(m_auxPhyMaxModClass));
+        if (auxPhyMaxWidth >= mainPhyChWidth)
         {
             // same channel can be used by aux PHYs
             m_auxPhyChannels.emplace(linkId, channel);
             continue;
         }
         // aux PHYs will operate on a primary subchannel
-        auto freq = channel.GetPrimaryChannelCenterFrequency(m_auxPhyMaxWidth);
+        auto freq = channel.GetPrimaryChannelCenterFrequency(auxPhyMaxWidth);
         auto chIt = WifiPhyOperatingChannel::FindFirst(0,
                                                        freq,
-                                                       m_auxPhyMaxWidth,
+                                                       auxPhyMaxWidth,
                                                        WIFI_STANDARD_UNSPECIFIED,
                                                        channel.GetPhyBand());
         NS_ASSERT_MSG(chIt != WifiPhyOperatingChannel::m_frequencyChannels.end(),
-                      "Primary" << m_auxPhyMaxWidth << " channel not found");
+                      "Primary" << auxPhyMaxWidth << " channel not found");
         m_auxPhyChannels.emplace(linkId, chIt);
         // find the P20 index for the channel used by the aux PHYs
         auto p20Index = channel.GetPrimaryChannelIndex(20);
-        while (mainPhyChWidth > m_auxPhyMaxWidth)
+        while (mainPhyChWidth > auxPhyMaxWidth)
         {
             mainPhyChWidth /= 2;
             p20Index /= 2;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/emlsr-manager.h ns-3.41/src/wifi/model/eht/emlsr-manager.h
--- ns-3.40/src/wifi/model/eht/emlsr-manager.h	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/emlsr-manager.h	2024-02-09 14:44:08.581795000 -0800
@@ -22,7 +22,6 @@
 
 #include "ns3/ctrl-headers.h"
 #include "ns3/mac48-address.h"
-#include "ns3/mgt-headers.h"
 #include "ns3/object.h"
 #include "ns3/sta-wifi-mac.h"
 #include "ns3/wifi-phy-operating-channel.h"
@@ -35,6 +34,7 @@
 {
 
 class EhtFrameExchangeManager;
+class MgtEmlOmn;
 class WifiMpdu;
 
 /**
@@ -74,12 +74,45 @@
     std::optional<Time> GetTransitionTimeout() const;
 
     /**
-     * Set the ID of main PHY (position in the vector of PHYs held by WifiNetDevice). This
-     * method cannot be called during or after initialization.
+     * Set the duration of the MediumSyncDelay timer.
      *
-     * \param mainPhyId the ID of the main PHY
+     * \param duration the duration of the MediumSyncDelay timer
      */
-    void SetMainPhyId(uint8_t mainPhyId);
+    void SetMediumSyncDuration(Time duration);
+
+    /**
+     * \return the duration of the MediumSyncDelay timer
+     */
+    Time GetMediumSyncDuration() const;
+
+    /**
+     * Set the Medium Synchronization OFDM ED threshold (dBm) to use while the MediumSyncDelay
+     * timer is running.
+     *
+     * \param threshold the threshold in dBm (ranges from -72 to -62 dBm)
+     */
+    void SetMediumSyncOfdmEdThreshold(int8_t threshold);
+
+    /**
+     * \return the Medium Synchronization OFDM ED threshold (dBm) to use while the MediumSyncDelay
+     * timer is running.
+     */
+    int8_t GetMediumSyncOfdmEdThreshold() const;
+
+    /**
+     * Set the maximum number of TXOPs a non-AP STA is allowed to attempt to initiate while
+     * the MediumSyncDelay timer is running. No value indicates no limit.
+     *
+     * \param nTxops the maximum number of TXOPs a non-AP STA is allowed to attempt to
+     *               initiate while the MediumSyncDelay timer is running
+     */
+    void SetMediumSyncMaxNTxops(std::optional<uint8_t> nTxops);
+
+    /**
+     * \return the maximum number of TXOPs a non-AP STA is allowed to attempt to initiate while
+     * the MediumSyncDelay timer is running. No value indicates no limit.
+     */
+    std::optional<uint8_t> GetMediumSyncMaxNTxops() const;
 
     /**
      * \return the ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
@@ -115,6 +148,18 @@
     bool GetCamStateReset() const;
 
     /**
+     * Set the member variable indicating whether Aux PHYs are capable of transmitting PPDUs.
+     *
+     * \param capable whether Aux PHYs are capable of transmitting PPDUs
+     */
+    void SetAuxPhyTxCapable(bool capable);
+
+    /**
+     * \return whether Aux PHYs are capable of transmitting PPDUs
+     */
+    bool GetAuxPhyTxCapable() const;
+
+    /**
      * Notify the reception of a management frame addressed to us.
      *
      * \param mpdu the received MPDU
@@ -133,8 +178,10 @@
      * Notify the start of an UL TXOP on the given link
      *
      * \param linkId the ID of the given link
+     * \param timeToCtsEnd time remaining to the end of CTS reception, in case the UL TXOP is
+     *                     started by an aux PHY
      */
-    void NotifyUlTxopStart(uint8_t linkId);
+    void NotifyUlTxopStart(uint8_t linkId, std::optional<Time> timeToCtsEnd);
 
     /**
      * Notify the end of a TXOP on the given link.
@@ -143,6 +190,54 @@
      */
     void NotifyTxopEnd(uint8_t linkId);
 
+    /**
+     * Check whether the MediumSyncDelay timer is running for the STA operating on the given link.
+     * If so, returns the time elapsed since the timer started.
+     *
+     * \param linkId the ID of the given link
+     * \return the time elapsed since the MediumSyncDelay timer started, if this timer is running
+     *         for the STA operating on the given link
+     */
+    std::optional<Time> GetElapsedMediumSyncDelayTimer(uint8_t linkId) const;
+
+    /**
+     * Cancel the MediumSyncDelay timer associated with the given link and take the appropriate
+     * actions. This function must not be called when the MediumSyncDelay timer is not running
+     * on the given link.
+     *
+     * \param linkId the ID of the link associated with the MediumSyncDelay timer to cancel
+     */
+    void CancelMediumSyncDelayTimer(uint8_t linkId);
+
+    /**
+     * Decrement the counter indicating the number of TXOP attempts left while the MediumSyncDelay
+     * timer is running. This function must not be called when the MediumSyncDelay timer is not
+     * running on the given link.
+     *
+     * \param linkId the ID of the link on which a new TXOP attempt may be carried out
+     */
+    void DecrementMediumSyncDelayNTxops(uint8_t linkId);
+
+    /**
+     * Reset the counter indicating the number of TXOP attempts left while the MediumSyncDelay
+     * timer is running, so as to remove the limit on the number of attempts that can be made
+     * while the MediumSyncDelay timer is running. This function is normally called when a TXOP
+     * attempt is successful. This function must not be called when the MediumSyncDelay timer is
+     * not running on the given link.
+     *
+     * \param linkId the ID of the link for which the counter of the TXOP attempts is reset
+     */
+    void ResetMediumSyncDelayNTxops(uint8_t linkId);
+
+    /**
+     * Return whether no more TXOP attempt is allowed on the given link. This function must not
+     * be called when the MediumSyncDelay timer is not running on the given link.
+     *
+     * \param linkId the ID of the link on which a new TXOP attempt may be carried out
+     * \return whether no more TXOP attempt on the given link is allowed
+     */
+    bool MediumSyncDelayNTxopsExceeded(uint8_t linkId);
+
   protected:
     void DoDispose() override;
 
@@ -186,6 +281,42 @@
     const WifiPhyOperatingChannel& GetChannelForAuxPhy(uint8_t linkId) const;
 
     /**
+     * Switch channel on the Main PHY so that it operates on the given link.
+     *
+     * \param linkId the ID of the link on which the main PHY has to operate
+     * \param noSwitchDelay whether switching delay should be zero
+     * \param resetBackoff whether backoff should be reset on the link on which the main PHY
+     *                     is operating
+     * \param requestAccess whether channel access should be requested on the link on which the
+     *                      main PHY is moving onto
+     */
+    void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay, bool resetBackoff, bool requestAccess);
+
+    static constexpr bool RESET_BACKOFF = true;       //!< reset backoff on main PHY switch
+    static constexpr bool DONT_RESET_BACKOFF = false; //!< do not reset backoff on main PHY switch
+    static constexpr bool REQUEST_ACCESS = true; //!< request channel access when PHY switch ends
+    static constexpr bool DONT_REQUEST_ACCESS =
+        false; //!< do not request channel access when PHY switch ends
+
+    /**
+     * Switch channel on the Aux PHY operating on the given current link so that it operates
+     * on the given next link.
+     *
+     * \param currLinkId the ID of the link on which the aux PHY is currently operating
+     * \param nextLinkId the ID of the link on which the aux PHY will be operating
+     */
+    void SwitchAuxPhy(uint8_t currLinkId, uint8_t nextLinkId);
+
+    /**
+     * Set the CCA ED threshold (if needed) on the given PHY that is switching channel to
+     * operate on the given link.
+     *
+     * \param phy the given PHY
+     * \param linkId the ID of the given link
+     */
+    void SetCcaEdThresholdOnLinkSwitch(Ptr<WifiPhy> phy, uint8_t linkId);
+
+    /**
      * \return the EML Operating Mode Notification to send
      */
     MgtEmlOmn GetEmlOmn();
@@ -193,10 +324,20 @@
     Time m_emlsrPaddingDelay;    //!< EMLSR Padding delay
     Time m_emlsrTransitionDelay; //!< EMLSR Transition delay
     uint8_t m_mainPhyId; //!< ID of main PHY (position in the vector of PHYs held by WifiNetDevice)
-    uint16_t m_auxPhyMaxWidth; //!< max channel width (MHz) supported by aux PHYs
+    uint16_t m_auxPhyMaxWidth;               //!< max channel width (MHz) supported by aux PHYs
+    WifiModulationClass m_auxPhyMaxModClass; //!< max modulation class supported by aux PHYs
+    bool m_auxPhyTxCapable;                  //!< whether Aux PHYs are capable of transmitting PPDUs
 
   private:
     /**
+     * Set the ID of main PHY (position in the vector of PHYs held by WifiNetDevice). This
+     * method cannot be called during or after initialization.
+     *
+     * \param mainPhyId the ID of the main PHY
+     */
+    void SetMainPhyId(uint8_t mainPhyId);
+
+    /**
      * Compute the operating channels that the main PHY and the aux PHY(s) must switch to in order
      * to operate on each of the setup links. The operating channels may be different due to
      * limited channel width capabilities of the aux PHY(s). This method shall be called upon
@@ -210,6 +351,22 @@
     void SendEmlOmn();
 
     /**
+     * Start the MediumSyncDelay timer and take the appropriate actions, if the timer is not
+     * already running.
+     *
+     * \param linkId the ID of the link on which a TXOP was carried out that caused the STAs
+     *               operating on other links to lose medium synchronization
+     */
+    void StartMediumSyncDelayTimer(uint8_t linkId);
+
+    /**
+     * Take the appropriate actions when the MediumSyncDelay timer expires or is cancelled.
+     *
+     * \param linkId the ID of the link associated with the MediumSyncDelay timer to cancel
+     */
+    void MediumSyncDelayTimerExpired(uint8_t linkId);
+
+    /**
      * Notify the subclass of the reception of a management frame addressed to us.
      *
      * \param mpdu the received MPDU
@@ -218,6 +375,27 @@
     virtual void DoNotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId) = 0;
 
     /**
+     * Notify the subclass of the reception of an initial Control frame on the given link.
+     *
+     * \param linkId the ID of the link on which the initial Control frame was received
+     */
+    virtual void DoNotifyIcfReceived(uint8_t linkId) = 0;
+
+    /**
+     * Notify the subclass of the start of an UL TXOP on the given link
+     *
+     * \param linkId the ID of the given link
+     */
+    virtual void DoNotifyUlTxopStart(uint8_t linkId) = 0;
+
+    /**
+     * Notify the subclass of the end of a TXOP on the given link.
+     *
+     * \param linkId the ID of the given link
+     */
+    virtual void DoNotifyTxopEnd(uint8_t linkId) = 0;
+
+    /**
      * Notify the acknowledgment of the given MPDU.
      *
      * \param mpdu the acknowledged MPDU
@@ -239,18 +417,10 @@
     void ChangeEmlsrMode();
 
     /**
-     * Switch channel on the Main PHY so that it operates on the given link.
-     *
-     * \param linkId the ID of the link on which the main PHY has to operate
-     * \param noSwitchDelay whether switching delay should be zero
-     */
-    void SwitchMainPhy(uint8_t linkId, bool noSwitchDelay);
-
-    /**
      * Adjust the operating channel of all the aux PHYs to meet the constraint on the maximum
      * channel width supported by aux PHYs.
      */
-    void ApplyMaxChannelWidthOnAuxPhys();
+    void ApplyMaxChannelWidthAndModClassOnAuxPhys();
 
     /**
      * Notify subclass that EMLSR mode changed.
@@ -265,9 +435,30 @@
      */
     virtual void NotifyMainPhySwitch(uint8_t currLinkId, uint8_t nextLinkId) = 0;
 
+    /**
+     * Information about the status of the MediumSyncDelay timer associated with a link.
+     */
+    struct MediumSyncDelayStatus
+    {
+        EventId timer;                        //!< the MediumSyncDelay timer
+        std::optional<uint8_t> msdNTxopsLeft; //!< number of TXOP attempts left while the
+                                              //!< MediumSyncDelay timer is running
+    };
+
     Ptr<StaWifiMac> m_staMac;                     //!< the MAC of the managed non-AP MLD
     std::optional<Time> m_emlsrTransitionTimeout; /**< Transition timeout advertised by APs with
                                                        EMLSR activated */
+    Time m_mediumSyncDuration;                    //!< duration of the MediumSyncDelay timer
+    int8_t m_msdOfdmEdThreshold;                  //!< MediumSyncDelay OFDM ED threshold
+    std::optional<uint8_t> m_msdMaxNTxops;        //!< MediumSyncDelay max number of TXOPs
+
+    std::map<uint8_t, MediumSyncDelayStatus>
+        m_mediumSyncDelayStatus; //!< the status of MediumSyncDelay timers (link ID-indexed)
+    std::map<Ptr<WifiPhy>, double> m_prevCcaEdThreshold; //!< the CCA sensitivity threshold (dBm)
+                                                         //!< to restore once the MediumSyncDelay
+                                                         //!< timer expires or the PHY moves to a
+                                                         //!< link on which the timer is not running
+
     std::set<uint8_t> m_emlsrLinks; //!< ID of the EMLSR links (empty if EMLSR mode is disabled)
     std::optional<std::set<uint8_t>> m_nextEmlsrLinks; /**< ID of the links that will become the
                                                             EMLSR links when the pending
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/multi-link-element.cc ns-3.41/src/wifi/model/eht/multi-link-element.cc
--- ns-3.40/src/wifi/model/eht/multi-link-element.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/multi-link-element.cc	2024-02-09 14:44:08.581795000 -0800
@@ -219,6 +219,79 @@
     return MicroSeconds(1 << (3 + value));
 }
 
+void
+CommonInfoBasicMle::SetMediumSyncDelayTimer(Time delay)
+{
+    int64_t delayUs = delay.GetMicroSeconds();
+    NS_ABORT_MSG_IF(delayUs % 32 != 0, "Delay must be a multiple of 32 microseconds");
+    delayUs /= 32;
+
+    if (!m_mediumSyncDelayInfo.has_value())
+    {
+        m_mediumSyncDelayInfo = CommonInfoBasicMle::MediumSyncDelayInfo{};
+    }
+    m_mediumSyncDelayInfo->mediumSyncDuration = (delayUs & 0xff);
+}
+
+Time
+CommonInfoBasicMle::GetMediumSyncDelayTimer() const
+{
+    NS_ASSERT(m_mediumSyncDelayInfo);
+    return MicroSeconds(m_mediumSyncDelayInfo->mediumSyncDuration * 32);
+}
+
+void
+CommonInfoBasicMle::SetMediumSyncOfdmEdThreshold(int8_t threshold)
+{
+    NS_ABORT_MSG_IF(threshold < -72 || threshold > -62, "Threshold may range from -72 to -62 dBm");
+    uint8_t value = 72 + threshold;
+
+    if (!m_mediumSyncDelayInfo.has_value())
+    {
+        m_mediumSyncDelayInfo = CommonInfoBasicMle::MediumSyncDelayInfo{};
+    }
+    m_mediumSyncDelayInfo->mediumSyncOfdmEdThreshold = value;
+}
+
+int8_t
+CommonInfoBasicMle::GetMediumSyncOfdmEdThreshold() const
+{
+    NS_ASSERT(m_mediumSyncDelayInfo);
+    return (m_mediumSyncDelayInfo->mediumSyncOfdmEdThreshold) - 72;
+}
+
+void
+CommonInfoBasicMle::SetMediumSyncMaxNTxops(uint8_t nTxops)
+{
+    NS_ASSERT_MSG(nTxops < 16, "Value " << +nTxops << "cannot be encoded in 4 bits");
+
+    if (!m_mediumSyncDelayInfo.has_value())
+    {
+        m_mediumSyncDelayInfo = CommonInfoBasicMle::MediumSyncDelayInfo{};
+    }
+
+    if (nTxops == 0)
+    {
+        // no limit on max number of TXOPs
+        m_mediumSyncDelayInfo->mediumSyncMaxNTxops = 15;
+        return;
+    }
+
+    m_mediumSyncDelayInfo->mediumSyncMaxNTxops = --nTxops;
+}
+
+std::optional<uint8_t>
+CommonInfoBasicMle::GetMediumSyncMaxNTxops() const
+{
+    NS_ASSERT(m_mediumSyncDelayInfo);
+    uint8_t nTxops = m_mediumSyncDelayInfo->mediumSyncMaxNTxops;
+    if (nTxops == 15)
+    {
+        return std::nullopt;
+    }
+    return nTxops + 1;
+}
+
 /**
  * MultiLinkElement
  */
@@ -330,81 +403,6 @@
 }
 
 void
-MultiLinkElement::SetMediumSyncDelayTimer(Time delay)
-{
-    int64_t delayUs = delay.GetMicroSeconds();
-    NS_ABORT_MSG_IF(delayUs % 32 != 0, "Delay must be a multiple of 32 microseconds");
-    delayUs /= 32;
-
-    auto& mediumSyncDelayInfo = std::get<BASIC_VARIANT>(m_commonInfo).m_mediumSyncDelayInfo;
-    if (!mediumSyncDelayInfo.has_value())
-    {
-        mediumSyncDelayInfo = CommonInfoBasicMle::MediumSyncDelayInfo{};
-    }
-    mediumSyncDelayInfo.value().mediumSyncDuration = (delayUs & 0xff);
-}
-
-Time
-MultiLinkElement::GetMediumSyncDelayTimer() const
-{
-    return MicroSeconds(
-        (std::get<BASIC_VARIANT>(m_commonInfo).m_mediumSyncDelayInfo.value().mediumSyncDuration) *
-        32);
-}
-
-void
-MultiLinkElement::SetMediumSyncOfdmEdThreshold(int8_t threshold)
-{
-    NS_ABORT_MSG_IF(threshold < -72 || threshold > -62, "Threshold may range from -72 to -62 dBm");
-    uint8_t value = 72 + threshold;
-
-    auto& mediumSyncDelayInfo = std::get<BASIC_VARIANT>(m_commonInfo).m_mediumSyncDelayInfo;
-    if (!mediumSyncDelayInfo.has_value())
-    {
-        mediumSyncDelayInfo = CommonInfoBasicMle::MediumSyncDelayInfo{};
-    }
-    mediumSyncDelayInfo.value().mediumSyncOfdmEdThreshold = value;
-}
-
-int8_t
-MultiLinkElement::GetMediumSyncOfdmEdThreshold() const
-{
-    return (std::get<BASIC_VARIANT>(m_commonInfo)
-                .m_mediumSyncDelayInfo.value()
-                .mediumSyncOfdmEdThreshold) -
-           72;
-}
-
-void
-MultiLinkElement::SetMediumSyncMaxNTxops(uint8_t nTxops)
-{
-    NS_ASSERT(nTxops > 0);
-    nTxops--;
-
-    auto& mediumSyncDelayInfo = std::get<BASIC_VARIANT>(m_commonInfo).m_mediumSyncDelayInfo;
-    if (!mediumSyncDelayInfo.has_value())
-    {
-        mediumSyncDelayInfo = CommonInfoBasicMle::MediumSyncDelayInfo{};
-    }
-    mediumSyncDelayInfo.value().mediumSyncMaxNTxops = (nTxops & 0x0f);
-}
-
-uint8_t
-MultiLinkElement::GetMediumSyncMaxNTxops() const
-{
-    return (std::get<BASIC_VARIANT>(m_commonInfo)
-                .m_mediumSyncDelayInfo.value()
-                .mediumSyncMaxNTxops) +
-           1;
-}
-
-bool
-MultiLinkElement::HasMediumSyncDelayInfo() const
-{
-    return std::get<BASIC_VARIANT>(m_commonInfo).m_mediumSyncDelayInfo.has_value();
-}
-
-void
 MultiLinkElement::SetEmlsrSupported(bool supported)
 {
     auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/eht/multi-link-element.h ns-3.41/src/wifi/model/eht/multi-link-element.h
--- ns-3.40/src/wifi/model/eht/multi-link-element.h	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/eht/multi-link-element.h	2024-02-09 14:44:08.581795000 -0800
@@ -140,6 +140,54 @@
      * \return the corresponding EMLSR Transition delay
      */
     static Time DecodeEmlsrTransitionDelay(uint8_t value);
+
+    /**
+     * Set the Medium Synchronization Duration subfield of the Medium Synchronization
+     * Delay Information in the Common Info field.
+     *
+     * \param delay the timer duration (must be a multiple of 32 microseconds)
+     */
+    void SetMediumSyncDelayTimer(Time delay);
+    /**
+     * Set the Medium Synchronization OFDM ED Threshold subfield of the Medium Synchronization
+     * Delay Information in the Common Info field.
+     *
+     * \param threshold the threshold in dBm (ranges from -72 to -62 dBm)
+     */
+    void SetMediumSyncOfdmEdThreshold(int8_t threshold);
+    /**
+     * Set the Medium Synchronization Maximum Number of TXOPs subfield of the Medium Synchronization
+     * Delay Information in the Common Info field. A value of zero indicates no limit on the
+     * maximum number of TXOPs.
+     *
+     * \param nTxops the maximum number of TXOPs a non-AP STA is allowed to attempt to
+     *               initiate while the MediumSyncDelay timer is running at a non-AP STA
+     */
+    void SetMediumSyncMaxNTxops(uint8_t nTxops);
+    /**
+     * Get the Medium Synchronization Duration subfield of the Medium Synchronization Delay
+     * Information in the Common Info field. Make sure that the Medium Synchronization Delay
+     * Information subfield is present.
+     *
+     * \return the timer duration
+     */
+    Time GetMediumSyncDelayTimer() const;
+    /**
+     * Get the Medium Synchronization OFDM ED Threshold in dBm. Make sure that the Medium
+     * Synchronization Delay Information subfield is present.
+     *
+     * \return the threshold in dBm
+     */
+    int8_t GetMediumSyncOfdmEdThreshold() const;
+    /**
+     * Get the maximum number of TXOPs a non-AP STA is allowed to attempt to initiate
+     * while the MediumSyncDelay timer is running at a non-AP STA. If no value is returned,
+     * no limit is imposed on the number of TXOPs. Make sure that the Medium Synchronization
+     * Delay Information subfield is present.
+     *
+     * \return the number of TXOPs
+     */
+    std::optional<uint8_t> GetMediumSyncMaxNTxops() const;
 };
 
 /**
@@ -286,64 +334,6 @@
     uint8_t GetBssParamsChangeCount() const;
 
     /**
-     * Set the Medium Synchronization Duration subfield of the Medium Synchronization
-     * Delay information in the Common Info field. Make sure that this is a Basic
-     * Multi-Link Element.
-     *
-     * \param delay the timer duration (must be a multiple of 32 microseconds)
-     */
-    void SetMediumSyncDelayTimer(Time delay);
-    /**
-     * Set the Medium Synchronization OFDM ED Threshold subfield of the Medium Synchronization
-     * Delay information in the Common Info field. Make sure that this is a Basic
-     * Multi-Link Element.
-     *
-     * \param threshold the threshold in dBm (ranges from -72 to -62 dBm)
-     */
-    void SetMediumSyncOfdmEdThreshold(int8_t threshold);
-    /**
-     * Set the Medium Synchronization Maximum Number of TXOPs subfield of the Medium Synchronization
-     * Delay information in the Common Info field. Make sure that this is a Basic
-     * Multi-Link Element.
-     *
-     * \param nTxops the maximum number of TXOPs a non-AP STA is allowed to attempt to
-     *               initiate while the MediumSyncDelay timer is running at a non-AP STA
-     */
-    void SetMediumSyncMaxNTxops(uint8_t nTxops);
-    /**
-     * Return true if the Medium Synchronization Delay Information subfield in the
-     * Common Info field is present and false otherwise. Make sure that this is a Basic
-     * Multi-Link Element.
-     *
-     * \return true if the Medium Synchronization Delay Information subfield in the
-     * Common Info field is present and false otherwise
-     */
-    bool HasMediumSyncDelayInfo() const;
-    /**
-     * Get the Medium Synchronization Duration subfield of the Medium Synchronization
-     * Delay information in the Common Info field. Make sure that this is a Basic
-     * Multi-Link Element and the Medium Synchronization Duration subfield is present.
-     *
-     * \return the timer duration
-     */
-    Time GetMediumSyncDelayTimer() const;
-    /**
-     * Get the Medium Synchronization OFDM ED Threshold in dBm. Make sure that this is a Basic
-     * Multi-Link Element and the Medium Synchronization Duration subfield is present.
-     *
-     * \return the threshold in dBm
-     */
-    int8_t GetMediumSyncOfdmEdThreshold() const;
-    /**
-     * Get the maximum number of TXOPs a non-AP STA is allowed to attempt to initiate
-     * while the MediumSyncDelay timer is running at a non-AP STA. Make sure that this is a
-     * Basic Multi-Link Element and the Medium Synchronization Duration subfield is present.
-     *
-     * \return the number of TXOPs
-     */
-    uint8_t GetMediumSyncMaxNTxops() const;
-
-    /**
      * Set the EMLSR Support subfield of the EML Capabilities subfield in the Common Info field
      * to 1 if EMLSR mode is supported and set it to 0 otherwise. Make sure that this is a Basic
      * Multi-Link Element.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/error-rate-model.cc ns-3.41/src/wifi/model/error-rate-model.cc
--- ns-3.40/src/wifi/model/error-rate-model.cc	2023-09-27 13:30:37.902840000 -0700
+++ ns-3.41/src/wifi/model/error-rate-model.cc	2024-02-09 14:44:08.581795000 -0800
@@ -44,7 +44,7 @@
     double precision;
     low = 1e-25;
     high = 1e25;
-    precision = 2e-12;
+    precision = 8e-12;
     while (high - low > precision)
     {
         NS_ASSERT(high >= low);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/fcfs-wifi-queue-scheduler.cc ns-3.41/src/wifi/model/fcfs-wifi-queue-scheduler.cc
--- ns-3.40/src/wifi/model/fcfs-wifi-queue-scheduler.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/fcfs-wifi-queue-scheduler.cc	2024-02-09 14:44:08.581795000 -0800
@@ -66,19 +66,20 @@
 TypeId
 FcfsWifiQueueScheduler::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::FcfsWifiQueueScheduler")
-                            .SetParent<WifiMacQueueSchedulerImpl<Time>>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<FcfsWifiQueueScheduler>()
-                            .AddAttribute("DropPolicy",
-                                          "Upon enqueue with full queue, drop oldest (DropOldest) "
-                                          "or newest (DropNewest) packet",
-                                          EnumValue(DROP_NEWEST),
-                                          MakeEnumAccessor(&FcfsWifiQueueScheduler::m_dropPolicy),
-                                          MakeEnumChecker(FcfsWifiQueueScheduler::DROP_OLDEST,
-                                                          "DropOldest",
-                                                          FcfsWifiQueueScheduler::DROP_NEWEST,
-                                                          "DropNewest"));
+    static TypeId tid =
+        TypeId("ns3::FcfsWifiQueueScheduler")
+            .SetParent<WifiMacQueueSchedulerImpl<Time>>()
+            .SetGroupName("Wifi")
+            .AddConstructor<FcfsWifiQueueScheduler>()
+            .AddAttribute("DropPolicy",
+                          "Upon enqueue with full queue, drop oldest (DropOldest) "
+                          "or newest (DropNewest) packet",
+                          EnumValue(DROP_NEWEST),
+                          MakeEnumAccessor<DropPolicy>(&FcfsWifiQueueScheduler::m_dropPolicy),
+                          MakeEnumChecker(FcfsWifiQueueScheduler::DROP_OLDEST,
+                                          "DropOldest",
+                                          FcfsWifiQueueScheduler::DROP_NEWEST,
+                                          "DropNewest"));
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/frame-exchange-manager.cc ns-3.41/src/wifi/model/frame-exchange-manager.cc
--- ns-3.40/src/wifi/model/frame-exchange-manager.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/frame-exchange-manager.cc	2024-02-09 14:44:08.581795000 -0800
@@ -415,12 +415,6 @@
     }
 
     StartProtection(m_txParams);
-
-    if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
-    {
-        // we are done with frames that do not require acknowledgment
-        m_mpdu = nullptr;
-    }
 }
 
 void
@@ -473,14 +467,17 @@
 
     if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
     {
-        Simulator::Schedule(txDuration, &FrameExchangeManager::TransmissionSucceeded, this);
-
         if (!m_mpdu->GetHeader().IsQosData() ||
             m_mpdu->GetHeader().GetQosAckPolicy() == WifiMacHeader::NO_ACK)
         {
             // No acknowledgment, hence dequeue the MPDU if it is stored in a queue
             DequeueMpdu(m_mpdu);
         }
+
+        Simulator::Schedule(txDuration, [=, this]() {
+            TransmissionSucceeded();
+            m_mpdu = nullptr;
+        });
     }
     else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NORMAL_ACK)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/he-configuration.cc ns-3.41/src/wifi/model/he/he-configuration.cc
--- ns-3.40/src/wifi/model/he/he-configuration.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/he-configuration.cc	2024-02-09 14:44:08.585795000 -0800
@@ -67,11 +67,12 @@
                                            &HeConfiguration::SetMaxTbPpduDelay),
                           MakeTimeChecker(Seconds(0)))
             .AddAttribute("MpduBufferSize",
-                          "The MPDU buffer size for receiving A-MPDUs",
+                          "This attribute is obsolete. Use the WifiMac::MpduBufferSize attribute "
+                          "instead.",
                           UintegerValue(64),
-                          MakeUintegerAccessor(&HeConfiguration::GetMpduBufferSize,
-                                               &HeConfiguration::SetMpduBufferSize),
-                          MakeUintegerChecker<uint16_t>(64, 256))
+                          MakeUintegerAccessor(&HeConfiguration::m_mpduBufferSize),
+                          MakeUintegerChecker<uint16_t>(64, 256),
+                          TypeId::OBSOLETE)
             .AddAttribute("MuBeAifsn",
                           "AIFSN used by BE EDCA when the MU EDCA Timer is running. "
                           "It must be either zero (EDCA disabled) or a value from 2 to 15.",
@@ -223,17 +224,4 @@
     m_maxTbPpduDelay = maxTbPpduDelay;
 }
 
-void
-HeConfiguration::SetMpduBufferSize(uint16_t size)
-{
-    NS_LOG_FUNCTION(this << size);
-    m_mpduBufferSize = size;
-}
-
-uint16_t
-HeConfiguration::GetMpduBufferSize() const
-{
-    return m_mpduBufferSize;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/he-configuration.h ns-3.41/src/wifi/model/he/he-configuration.h
--- ns-3.40/src/wifi/model/he/he-configuration.h	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/he-configuration.h	2024-02-09 14:44:08.585795000 -0800
@@ -68,14 +68,6 @@
      * \return the maximum TB PPDU delay
      */
     Time GetMaxTbPpduDelay() const;
-    /**
-     * \param size the MPDU buffer size to receive A-MPDUs
-     */
-    void SetMpduBufferSize(uint16_t size);
-    /**
-     * \return the MPDU buffer size to receive A-MPDUs
-     */
-    uint16_t GetMpduBufferSize() const;
 
   private:
     Time m_guardInterval;      //!< Supported HE guard interval
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/he-frame-exchange-manager.cc ns-3.41/src/wifi/model/he/he-frame-exchange-manager.cc
--- ns-3.40/src/wifi/model/he/he-frame-exchange-manager.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/he-frame-exchange-manager.cc	2024-02-09 14:44:08.585795000 -0800
@@ -96,17 +96,6 @@
     VhtFrameExchangeManager::Reset();
 }
 
-uint16_t
-HeFrameExchangeManager::GetSupportedBaBufferSize() const
-{
-    NS_ASSERT(m_mac->GetHeConfiguration());
-    if (m_mac->GetHeConfiguration()->GetMpduBufferSize() > 64)
-    {
-        return 256;
-    }
-    return 64;
-}
-
 void
 HeFrameExchangeManager::SetWifiMac(const Ptr<WifiMac> mac)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/he-frame-exchange-manager.h ns-3.41/src/wifi/model/he/he-frame-exchange-manager.h
--- ns-3.40/src/wifi/model/he/he-frame-exchange-manager.h	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/he-frame-exchange-manager.h	2024-02-09 14:44:08.585795000 -0800
@@ -75,7 +75,6 @@
     HeFrameExchangeManager();
     ~HeFrameExchangeManager() override;
 
-    uint16_t GetSupportedBaBufferSize() const override;
     bool StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame) override;
     void SetWifiMac(const Ptr<WifiMac> mac) override;
     void CalculateAcknowledgmentTime(WifiAcknowledgment* acknowledgment) const override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/he-phy.cc ns-3.41/src/wifi/model/he/he-phy.cc
--- ns-3.40/src/wifi/model/he/he-phy.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/he-phy.cc	2024-02-09 14:44:08.585795000 -0800
@@ -461,9 +461,8 @@
     // detection window. If a preamble is received after the preamble detection window, it is stored
     // anyway because this is needed for HE TB PPDUs in order to properly update the received power
     // in InterferenceHelper. The map is cleaned anyway at the end of the current reception.
-    const auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
     const auto& currentPreambleEvents = GetCurrentPreambleEvents();
-    const auto it = currentPreambleEvents.find(uidPreamblePair);
+    const auto it = currentPreambleEvents.find({ppdu->GetUid(), ppdu->GetPreamble()});
     if (const auto isResponseToTrigger = (m_previouslyTxPpduUid == ppdu->GetUid());
         ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU || isResponseToTrigger)
     {
@@ -704,6 +703,12 @@
     m_obssPdAlgorithm = algorithm;
 }
 
+Ptr<ObssPdAlgorithm>
+HePhy::GetObssPdAlgorithm() const
+{
+    return m_obssPdAlgorithm;
+}
+
 void
 HePhy::SetEndOfHeSigACallback(EndOfHeSigACallback callback)
 {
@@ -836,8 +841,8 @@
     {
         NS_LOG_DEBUG("Receiving PSDU in HE TB PPDU");
         uint16_t staId = GetStaId(ppdu);
-        m_signalNoiseMap.insert({std::make_pair(ppdu->GetUid(), staId), SignalNoiseDbm()});
-        m_statusPerMpduMap.insert({std::make_pair(ppdu->GetUid(), staId), std::vector<bool>()});
+        m_signalNoiseMap.insert({{ppdu->GetUid(), staId}, SignalNoiseDbm()});
+        m_statusPerMpduMap.insert({{ppdu->GetUid(), staId}, std::vector<bool>()});
         // for HE TB PPDUs, ScheduleEndOfMpdus and EndReceive are scheduled by
         // StartReceiveMuPayload
         NS_ASSERT(!m_beginMuPayloadRxEvents.empty());
@@ -858,7 +863,6 @@
                           const std::vector<bool>& statusPerMpdu)
 {
     NS_LOG_FUNCTION(this << *psdu << txVector);
-    m_state->NotifyRxPsduSucceeded(psdu, rxSignalInfo, txVector, staId, statusPerMpdu);
     if (!IsUlMu(txVector.GetPreambleType()))
     {
         m_state->SwitchFromRxEndOk();
@@ -873,7 +877,6 @@
 HePhy::RxPayloadFailed(Ptr<const WifiPsdu> psdu, double snr, const WifiTxVector& txVector)
 {
     NS_LOG_FUNCTION(this << *psdu << txVector << snr);
-    m_state->NotifyRxPsduFailed(psdu, snr);
     if (!txVector.IsUlMu())
     {
         m_state->SwitchFromRxEndError();
@@ -959,8 +962,8 @@
     m_endRxPayloadEvents.push_back(
         Simulator::Schedule(payloadDuration, &HePhy::EndReceivePayload, this, event));
     uint16_t staId = GetStaId(ppdu);
-    m_signalNoiseMap.insert({std::make_pair(ppdu->GetUid(), staId), SignalNoiseDbm()});
-    m_statusPerMpduMap.insert({std::make_pair(ppdu->GetUid(), staId), std::vector<bool>()});
+    m_signalNoiseMap.insert({{ppdu->GetUid(), staId}, SignalNoiseDbm()});
+    m_statusPerMpduMap.insert({{ppdu->GetUid(), staId}, std::vector<bool>()});
     // Notify the MAC about the start of a new HE TB PPDU, so that it can reschedule the timeout
     NotifyPayloadBegin(ppdu->GetTxVector(), payloadDuration);
 }
@@ -990,14 +993,12 @@
         channelWidth,
         ru.GetRuType(),
         ru.GetPhyIndex(channelWidth, m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
-    HeRu::SubcarrierRange subcarrierRange =
-        std::make_pair(group.front().first, group.back().second);
     // for a TX spectrum, the guard bandwidth is a function of the transmission channel width
     // and the spectrum width equals the transmission channel width (hence bandIndex equals 0)
     auto indices = ConvertHeRuSubcarriers(channelWidth,
                                           GetGuardBandwidth(channelWidth),
                                           m_wifiPhy->GetSubcarrierSpacing(),
-                                          subcarrierRange,
+                                          {group.front().first, group.back().second},
                                           0);
     auto frequencies = m_wifiPhy->ConvertIndicesToFrequencies(indices);
     return {indices, frequencies};
@@ -1014,15 +1015,13 @@
         channelWidth,
         ru.GetRuType(),
         ru.GetPhyIndex(channelWidth, m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
-    HeRu::SubcarrierRange subcarrierRange =
-        std::make_pair(group.front().first, group.back().second);
     // for an RX spectrum, the guard bandwidth is a function of the operating channel width
     // and the spectrum width equals the operating channel width
     auto indices = ConvertHeRuSubcarriers(
         channelWidth,
         GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
         m_wifiPhy->GetSubcarrierSpacing(),
-        subcarrierRange,
+        {group.front().first, group.back().second},
         m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth));
     auto frequencies = m_wifiPhy->ConvertIndicesToFrequencies(indices);
     return {indices, frequencies};
@@ -1047,13 +1046,11 @@
         nonOfdmaRu.GetRuType(),
         nonOfdmaRu.GetPhyIndex(channelWidth,
                                m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
-    HeRu::SubcarrierRange subcarrierRange =
-        std::make_pair(groupPreamble.front().first, groupPreamble.back().second);
     auto indices = ConvertHeRuSubcarriers(
         channelWidth,
         GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
         m_wifiPhy->GetSubcarrierSpacing(),
-        subcarrierRange,
+        {groupPreamble.front().first, groupPreamble.back().second},
         m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth));
     auto frequencies = m_wifiPhy->ConvertIndicesToFrequencies(indices);
     return {indices, frequencies};
@@ -1580,7 +1577,7 @@
     {                                                                                              \
         static WifiMode mcs = CreateHeMcs(x);                                                      \
         return mcs;                                                                                \
-    };
+    }
 
 GET_HE_MCS(0)
 GET_HE_MCS(1)
@@ -1764,7 +1761,7 @@
         staId = txVector.GetHeMuUserInfoMap().begin()->first;
     }
 
-    return WifiConstPsduMap({std::make_pair(staId, psdu)});
+    return WifiConstPsduMap({{staId, psdu}});
 }
 
 uint32_t
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/he-phy.h ns-3.41/src/wifi/model/he/he-phy.h
--- ns-3.40/src/wifi/model/he/he-phy.h	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/he-phy.h	2024-02-09 14:44:08.585795000 -0800
@@ -233,6 +233,13 @@
     void SetObssPdAlgorithm(const Ptr<ObssPdAlgorithm> algorithm);
 
     /**
+     * Gets the OBSS-PD algorithm.
+     *
+     * \return the OBSS-PD algorithm
+     */
+    Ptr<ObssPdAlgorithm> GetObssPdAlgorithm() const;
+
+    /**
      * Set a callback for a end of HE-SIG-A.
      *
      * \param callback the EndOfHeSigACallback to set
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/multi-user-scheduler.cc ns-3.41/src/wifi/model/he/multi-user-scheduler.cc
--- ns-3.40/src/wifi/model/he/multi-user-scheduler.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/multi-user-scheduler.cc	2024-02-09 14:44:08.585795000 -0800
@@ -57,7 +57,7 @@
                           "The Access Category for which the MultiUserScheduler makes requests "
                           "for channel access.",
                           EnumValue(AcIndex::AC_BE),
-                          MakeEnumAccessor(&MultiUserScheduler::m_accessReqAc),
+                          MakeEnumAccessor<AcIndex>(&MultiUserScheduler::m_accessReqAc),
                           MakeEnumChecker(AcIndex::AC_BE,
                                           "AC_BE",
                                           AcIndex::AC_VI,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/he/rr-multi-user-scheduler.cc ns-3.41/src/wifi/model/he/rr-multi-user-scheduler.cc
--- ns-3.40/src/wifi/model/he/rr-multi-user-scheduler.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/he/rr-multi-user-scheduler.cc	2024-02-09 14:44:08.585795000 -0800
@@ -320,7 +320,8 @@
     m_txParams.Clear();
     // set the TXVECTOR used to send the Trigger Frame
     m_txParams.m_txVector =
-        m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1());
+        m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1(),
+                                                                       m_allowedWidth);
 
     if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
     {
@@ -442,7 +443,8 @@
     m_txParams.Clear();
     // set the TXVECTOR used to send the Trigger Frame
     m_txParams.m_txVector =
-        m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1());
+        m_apMac->GetWifiRemoteStationManager(m_linkId)->GetRtsTxVector(m_triggerMacHdr.GetAddr1(),
+                                                                       m_allowedWidth);
 
     if (!GetHeFem(m_linkId)->TryAddMpdu(item, m_txParams, m_availableTime))
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/ht/ht-frame-exchange-manager.cc ns-3.41/src/wifi/model/ht/ht-frame-exchange-manager.cc
--- ns-3.40/src/wifi/model/ht/ht-frame-exchange-manager.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/ht/ht-frame-exchange-manager.cc	2024-02-09 14:44:08.585795000 -0800
@@ -23,10 +23,11 @@
 #include "ns3/assert.h"
 #include "ns3/ctrl-headers.h"
 #include "ns3/log.h"
-#include "ns3/mgt-headers.h"
+#include "ns3/mgt-action-headers.h"
 #include "ns3/recipient-block-ack-agreement.h"
 #include "ns3/snr-tag.h"
 #include "ns3/sta-wifi-mac.h"
+#include "ns3/vht-configuration.h"
 #include "ns3/wifi-mac-queue.h"
 #include "ns3/wifi-utils.h"
 
@@ -109,7 +110,7 @@
     Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
     bool establish;
 
-    if (!GetWifiRemoteStationManager()->GetHtSupported(recipient))
+    if (!m_mac->GetHtSupported(recipient))
     {
         establish = false;
     }
@@ -126,7 +127,7 @@
             ((qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
              (m_mpduAggregator->GetMaxAmpduSize(recipient, tid, WIFI_MOD_CLASS_HT) > 0 &&
               packets > 1) ||
-             GetWifiRemoteStationManager()->GetVhtSupported());
+             m_mac->GetVhtConfiguration());
     }
 
     NS_LOG_FUNCTION(this << recipient << +tid << establish);
@@ -238,7 +239,8 @@
     auto tid = reqHdr->GetTid();
     respHdr.SetTid(tid);
 
-    respHdr.SetBufferSize(GetSupportedBaBufferSize());
+    auto bufferSize = std::min(m_mac->GetMpduBufferSize(), m_mac->GetMaxBaBufferSize(originator));
+    respHdr.SetBufferSize(bufferSize);
     respHdr.SetTimeout(reqHdr->GetTimeout());
 
     WifiActionHeader actionHdr;
@@ -308,12 +310,6 @@
     m_mac->GetQosTxop(tid)->Queue(mpdu);
 }
 
-uint16_t
-HtFrameExchangeManager::GetSupportedBaBufferSize() const
-{
-    return 64;
-}
-
 void
 HtFrameExchangeManager::SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator)
 {
@@ -329,16 +325,7 @@
 
     MgtDelBaHeader delbaHdr;
     delbaHdr.SetTid(tid);
-    if (byOriginator)
-    {
-        delbaHdr.SetByOriginator();
-        GetBaManager(tid)->DestroyOriginatorAgreement(addr, tid);
-    }
-    else
-    {
-        delbaHdr.SetByRecipient();
-        GetBaManager(tid)->DestroyRecipientAgreement(addr, tid);
-    }
+    byOriginator ? delbaHdr.SetByOriginator() : delbaHdr.SetByRecipient();
 
     WifiActionHeader actionHdr;
     WifiActionHeader::ActionValue action;
@@ -349,7 +336,7 @@
     packet->AddHeader(delbaHdr);
     packet->AddHeader(actionHdr);
 
-    m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(packet, hdr));
+    m_mac->GetQosTxop(tid)->Queue(Create<WifiMpdu>(packet, hdr));
 }
 
 bool
@@ -583,6 +570,12 @@
         return false;
     }
 
+    NS_ABORT_IF(txParams.m_acknowledgment->method != WifiAcknowledgment::BLOCK_ACK);
+
+    // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
+    auto blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
+    txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
+
     // we can transmit the BlockAckReq frame
     SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
     return true;
@@ -1377,8 +1370,6 @@
     else
     {
         isBar = false;
-        GetWifiRemoteStationManager()
-            ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
         std::set<uint8_t> tids = psdu->GetTids();
         NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not handled here");
         NS_ASSERT(!tids.empty());
@@ -1437,6 +1428,8 @@
     else
     {
         // we have to retransmit the data frames, if needed
+        GetWifiRemoteStationManager()
+            ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
         if (!GetWifiRemoteStationManager()->NeedRetransmission(*psdu->begin()))
         {
             NS_LOG_DEBUG("Missed Block Ack, do not retransmit the data frames");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/ht/ht-frame-exchange-manager.h ns-3.41/src/wifi/model/ht/ht-frame-exchange-manager.h
--- ns-3.40/src/wifi/model/ht/ht-frame-exchange-manager.h	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/ht/ht-frame-exchange-manager.h	2024-02-09 14:44:08.585795000 -0800
@@ -26,8 +26,6 @@
 #include "ns3/wifi-psdu.h"
 
 class AmpduAggregationTest;
-class TwoLevelAggregationTest;
-class HeAggregationTest;
 
 namespace ns3
 {
@@ -46,10 +44,6 @@
   public:
     /// allow AmpduAggregationTest class access
     friend class ::AmpduAggregationTest;
-    /// allow TwoLevelAggregationTest class access
-    friend class ::TwoLevelAggregationTest;
-    /// allow HeAggregationTest class access
-    friend class ::HeAggregationTest;
 
     /**
      * \brief Get the type ID.
@@ -148,13 +142,6 @@
      * \param originator the MAC address of the originator.
      */
     void SendAddBaResponse(const MgtAddBaRequestHeader* reqHdr, Mac48Address originator);
-    /**
-     * Get the maximum supported buffer size for a Block Ack agreement. This value
-     * is typically included in ADDBA Response frames.
-     *
-     * \return the maximum supported buffer size for a Block Ack agreement
-     */
-    virtual uint16_t GetSupportedBaBufferSize() const;
 
     /**
      * Sends DELBA frame to cancel a block ack agreement with STA
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/ht/ht-phy.cc ns-3.41/src/wifi/model/ht/ht-phy.cc
--- ns-3.40/src/wifi/model/ht/ht-phy.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/ht/ht-phy.cc	2024-02-09 14:44:08.589794900 -0800
@@ -537,7 +537,7 @@
     {                                                                                              \
         static WifiMode mcs = CreateHtMcs(x);                                                      \
         return mcs;                                                                                \
-    };
+    }
 
 GET_HT_MCS(0)
 GET_HT_MCS(1)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/interference-helper.cc ns-3.41/src/wifi/model/interference-helper.cc
--- ns-3.40/src/wifi/model/interference-helper.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/interference-helper.cc	2024-02-09 14:44:08.589794900 -0800
@@ -53,12 +53,6 @@
 {
 }
 
-Event::~Event()
-{
-    m_ppdu = nullptr;
-    m_rxPowerW.clear();
-}
-
 Ptr<const WifiPpdu>
 Event::GetPpdu() const
 {
@@ -816,7 +810,7 @@
 InterferenceHelper::NiChanges::iterator
 InterferenceHelper::AddNiChangeEvent(Time moment, NiChange change, NiChangesPerBand::iterator niIt)
 {
-    return niIt->second.insert(GetNextPosition(moment, niIt), std::make_pair(moment, change));
+    return niIt->second.insert(GetNextPosition(moment, niIt), {moment, change});
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/interference-helper.h ns-3.41/src/wifi/model/interference-helper.h
--- ns-3.40/src/wifi/model/interference-helper.h	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/interference-helper.h	2024-02-09 14:44:08.589794900 -0800
@@ -48,7 +48,6 @@
      * \param rxPower the received power per band (W)
      */
     Event(Ptr<const WifiPpdu> ppdu, Time duration, RxPowerWattPerChannelBand&& rxPower);
-    ~Event();
 
     /**
      * Return the PPDU.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/mgt-action-headers.cc ns-3.41/src/wifi/model/mgt-action-headers.cc
--- ns-3.40/src/wifi/model/mgt-action-headers.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/wifi/model/mgt-action-headers.cc	2024-02-09 14:44:08.589794900 -0800
@@ -0,0 +1,1352 @@
+/*
+ * Copyright (c) 2006 INRIA
+ * Copyright (c) 2009 MIRKO BANCHI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+ *          Mirko Banchi <mk.banchi@gmail.com>
+ */
+
+#include "mgt-action-headers.h"
+
+#include "addba-extension.h"
+
+#include "ns3/multi-link-element.h"
+#include "ns3/packet.h"
+
+namespace ns3
+{
+
+WifiActionHeader::WifiActionHeader()
+{
+}
+
+WifiActionHeader::~WifiActionHeader()
+{
+}
+
+void
+WifiActionHeader::SetAction(WifiActionHeader::CategoryValue type,
+                            WifiActionHeader::ActionValue action)
+{
+    m_category = static_cast<uint8_t>(type);
+    switch (type)
+    {
+    case SPECTRUM_MANAGEMENT: {
+        break;
+    }
+    case QOS: {
+        m_actionValue = static_cast<uint8_t>(action.qos);
+        break;
+    }
+    case BLOCK_ACK: {
+        m_actionValue = static_cast<uint8_t>(action.blockAck);
+        break;
+    }
+    case PUBLIC: {
+        m_actionValue = static_cast<uint8_t>(action.publicAction);
+        break;
+    }
+    case RADIO_MEASUREMENT: {
+        m_actionValue = static_cast<uint8_t>(action.radioMeasurementAction);
+        break;
+    }
+    case MESH: {
+        m_actionValue = static_cast<uint8_t>(action.meshAction);
+        break;
+    }
+    case MULTIHOP: {
+        m_actionValue = static_cast<uint8_t>(action.multihopAction);
+        break;
+    }
+    case SELF_PROTECTED: {
+        m_actionValue = static_cast<uint8_t>(action.selfProtectedAction);
+        break;
+    }
+    case DMG: {
+        m_actionValue = static_cast<uint8_t>(action.dmgAction);
+        break;
+    }
+    case FST: {
+        m_actionValue = static_cast<uint8_t>(action.fstAction);
+        break;
+    }
+    case UNPROTECTED_DMG: {
+        m_actionValue = static_cast<uint8_t>(action.unprotectedDmgAction);
+        break;
+    }
+    case PROTECTED_EHT: {
+        m_actionValue = static_cast<uint8_t>(action.protectedEhtAction);
+        break;
+    }
+    case VENDOR_SPECIFIC_ACTION: {
+        break;
+    }
+    }
+}
+
+WifiActionHeader::CategoryValue
+WifiActionHeader::GetCategory() const
+{
+    switch (m_category)
+    {
+    case QOS:
+        return QOS;
+    case BLOCK_ACK:
+        return BLOCK_ACK;
+    case PUBLIC:
+        return PUBLIC;
+    case RADIO_MEASUREMENT:
+        return RADIO_MEASUREMENT;
+    case MESH:
+        return MESH;
+    case MULTIHOP:
+        return MULTIHOP;
+    case SELF_PROTECTED:
+        return SELF_PROTECTED;
+    case DMG:
+        return DMG;
+    case FST:
+        return FST;
+    case UNPROTECTED_DMG:
+        return UNPROTECTED_DMG;
+    case PROTECTED_EHT:
+        return PROTECTED_EHT;
+    case VENDOR_SPECIFIC_ACTION:
+        return VENDOR_SPECIFIC_ACTION;
+    default:
+        NS_FATAL_ERROR("Unknown action value");
+        return SELF_PROTECTED;
+    }
+}
+
+WifiActionHeader::ActionValue
+WifiActionHeader::GetAction() const
+{
+    ActionValue retval;
+    retval.selfProtectedAction =
+        PEER_LINK_OPEN; // Needs to be initialized to something to quiet valgrind in default cases
+    switch (m_category)
+    {
+    case QOS:
+        switch (m_actionValue)
+        {
+        case ADDTS_REQUEST:
+            retval.qos = ADDTS_REQUEST;
+            break;
+        case ADDTS_RESPONSE:
+            retval.qos = ADDTS_RESPONSE;
+            break;
+        case DELTS:
+            retval.qos = DELTS;
+            break;
+        case SCHEDULE:
+            retval.qos = SCHEDULE;
+            break;
+        case QOS_MAP_CONFIGURE:
+            retval.qos = QOS_MAP_CONFIGURE;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown qos action code");
+            retval.qos = ADDTS_REQUEST; /* quiet compiler */
+        }
+        break;
+
+    case BLOCK_ACK:
+        switch (m_actionValue)
+        {
+        case BLOCK_ACK_ADDBA_REQUEST:
+            retval.blockAck = BLOCK_ACK_ADDBA_REQUEST;
+            break;
+        case BLOCK_ACK_ADDBA_RESPONSE:
+            retval.blockAck = BLOCK_ACK_ADDBA_RESPONSE;
+            break;
+        case BLOCK_ACK_DELBA:
+            retval.blockAck = BLOCK_ACK_DELBA;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown block ack action code");
+            retval.blockAck = BLOCK_ACK_ADDBA_REQUEST; /* quiet compiler */
+        }
+        break;
+
+    case PUBLIC:
+        switch (m_actionValue)
+        {
+        case QAB_REQUEST:
+            retval.publicAction = QAB_REQUEST;
+            break;
+        case QAB_RESPONSE:
+            retval.publicAction = QAB_RESPONSE;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown public action code");
+            retval.publicAction = QAB_REQUEST; /* quiet compiler */
+        }
+        break;
+
+    case RADIO_MEASUREMENT:
+        switch (m_actionValue)
+        {
+        case RADIO_MEASUREMENT_REQUEST:
+            retval.radioMeasurementAction = RADIO_MEASUREMENT_REQUEST;
+            break;
+        case RADIO_MEASUREMENT_REPORT:
+            retval.radioMeasurementAction = RADIO_MEASUREMENT_REPORT;
+            break;
+        case LINK_MEASUREMENT_REQUEST:
+            retval.radioMeasurementAction = LINK_MEASUREMENT_REQUEST;
+            break;
+        case LINK_MEASUREMENT_REPORT:
+            retval.radioMeasurementAction = LINK_MEASUREMENT_REPORT;
+            break;
+        case NEIGHBOR_REPORT_REQUEST:
+            retval.radioMeasurementAction = NEIGHBOR_REPORT_REQUEST;
+            break;
+        case NEIGHBOR_REPORT_RESPONSE:
+            retval.radioMeasurementAction = NEIGHBOR_REPORT_RESPONSE;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown radio measurement action code");
+            retval.radioMeasurementAction = RADIO_MEASUREMENT_REQUEST; /* quiet compiler */
+        }
+        break;
+
+    case SELF_PROTECTED:
+        switch (m_actionValue)
+        {
+        case PEER_LINK_OPEN:
+            retval.selfProtectedAction = PEER_LINK_OPEN;
+            break;
+        case PEER_LINK_CONFIRM:
+            retval.selfProtectedAction = PEER_LINK_CONFIRM;
+            break;
+        case PEER_LINK_CLOSE:
+            retval.selfProtectedAction = PEER_LINK_CLOSE;
+            break;
+        case GROUP_KEY_INFORM:
+            retval.selfProtectedAction = GROUP_KEY_INFORM;
+            break;
+        case GROUP_KEY_ACK:
+            retval.selfProtectedAction = GROUP_KEY_ACK;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown mesh peering management action code");
+            retval.selfProtectedAction = PEER_LINK_OPEN; /* quiet compiler */
+        }
+        break;
+
+    case MESH:
+        switch (m_actionValue)
+        {
+        case LINK_METRIC_REPORT:
+            retval.meshAction = LINK_METRIC_REPORT;
+            break;
+        case PATH_SELECTION:
+            retval.meshAction = PATH_SELECTION;
+            break;
+        case PORTAL_ANNOUNCEMENT:
+            retval.meshAction = PORTAL_ANNOUNCEMENT;
+            break;
+        case CONGESTION_CONTROL_NOTIFICATION:
+            retval.meshAction = CONGESTION_CONTROL_NOTIFICATION;
+            break;
+        case MDA_SETUP_REQUEST:
+            retval.meshAction = MDA_SETUP_REQUEST;
+            break;
+        case MDA_SETUP_REPLY:
+            retval.meshAction = MDA_SETUP_REPLY;
+            break;
+        case MDAOP_ADVERTISEMENT_REQUEST:
+            retval.meshAction = MDAOP_ADVERTISEMENT_REQUEST;
+            break;
+        case MDAOP_ADVERTISEMENTS:
+            retval.meshAction = MDAOP_ADVERTISEMENTS;
+            break;
+        case MDAOP_SET_TEARDOWN:
+            retval.meshAction = MDAOP_SET_TEARDOWN;
+            break;
+        case TBTT_ADJUSTMENT_REQUEST:
+            retval.meshAction = TBTT_ADJUSTMENT_REQUEST;
+            break;
+        case TBTT_ADJUSTMENT_RESPONSE:
+            retval.meshAction = TBTT_ADJUSTMENT_RESPONSE;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown mesh peering management action code");
+            retval.meshAction = LINK_METRIC_REPORT; /* quiet compiler */
+        }
+        break;
+
+    case MULTIHOP: // not yet supported
+        switch (m_actionValue)
+        {
+        case PROXY_UPDATE: // not used so far
+            retval.multihopAction = PROXY_UPDATE;
+            break;
+        case PROXY_UPDATE_CONFIRMATION: // not used so far
+            retval.multihopAction = PROXY_UPDATE;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown mesh peering management action code");
+            retval.multihopAction = PROXY_UPDATE; /* quiet compiler */
+        }
+        break;
+
+    case DMG:
+        switch (m_actionValue)
+        {
+        case DMG_POWER_SAVE_CONFIGURATION_REQUEST:
+            retval.dmgAction = DMG_POWER_SAVE_CONFIGURATION_REQUEST;
+            break;
+        case DMG_POWER_SAVE_CONFIGURATION_RESPONSE:
+            retval.dmgAction = DMG_POWER_SAVE_CONFIGURATION_RESPONSE;
+            break;
+        case DMG_INFORMATION_REQUEST:
+            retval.dmgAction = DMG_INFORMATION_REQUEST;
+            break;
+        case DMG_INFORMATION_RESPONSE:
+            retval.dmgAction = DMG_INFORMATION_RESPONSE;
+            break;
+        case DMG_HANDOVER_REQUEST:
+            retval.dmgAction = DMG_HANDOVER_REQUEST;
+            break;
+        case DMG_HANDOVER_RESPONSE:
+            retval.dmgAction = DMG_HANDOVER_RESPONSE;
+            break;
+        case DMG_DTP_REQUEST:
+            retval.dmgAction = DMG_DTP_REQUEST;
+            break;
+        case DMG_DTP_RESPONSE:
+            retval.dmgAction = DMG_DTP_RESPONSE;
+            break;
+        case DMG_RELAY_SEARCH_REQUEST:
+            retval.dmgAction = DMG_RELAY_SEARCH_REQUEST;
+            break;
+        case DMG_RELAY_SEARCH_RESPONSE:
+            retval.dmgAction = DMG_RELAY_SEARCH_RESPONSE;
+            break;
+        case DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST:
+            retval.dmgAction = DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST;
+            break;
+        case DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT:
+            retval.dmgAction = DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT;
+            break;
+        case DMG_RLS_REQUEST:
+            retval.dmgAction = DMG_RLS_REQUEST;
+            break;
+        case DMG_RLS_RESPONSE:
+            retval.dmgAction = DMG_RLS_RESPONSE;
+            break;
+        case DMG_RLS_ANNOUNCEMENT:
+            retval.dmgAction = DMG_RLS_ANNOUNCEMENT;
+            break;
+        case DMG_RLS_TEARDOWN:
+            retval.dmgAction = DMG_RLS_TEARDOWN;
+            break;
+        case DMG_RELAY_ACK_REQUEST:
+            retval.dmgAction = DMG_RELAY_ACK_REQUEST;
+            break;
+        case DMG_RELAY_ACK_RESPONSE:
+            retval.dmgAction = DMG_RELAY_ACK_RESPONSE;
+            break;
+        case DMG_TPA_REQUEST:
+            retval.dmgAction = DMG_TPA_REQUEST;
+            break;
+        case DMG_TPA_RESPONSE:
+            retval.dmgAction = DMG_TPA_RESPONSE;
+            break;
+        case DMG_ROC_REQUEST:
+            retval.dmgAction = DMG_ROC_REQUEST;
+            break;
+        case DMG_ROC_RESPONSE:
+            retval.dmgAction = DMG_ROC_RESPONSE;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown DMG management action code");
+            retval.dmgAction = DMG_POWER_SAVE_CONFIGURATION_REQUEST; /* quiet compiler */
+        }
+        break;
+
+    case FST:
+        switch (m_actionValue)
+        {
+        case FST_SETUP_REQUEST:
+            retval.fstAction = FST_SETUP_REQUEST;
+            break;
+        case FST_SETUP_RESPONSE:
+            retval.fstAction = FST_SETUP_RESPONSE;
+            break;
+        case FST_TEAR_DOWN:
+            retval.fstAction = FST_TEAR_DOWN;
+            break;
+        case FST_ACK_REQUEST:
+            retval.fstAction = FST_ACK_REQUEST;
+            break;
+        case FST_ACK_RESPONSE:
+            retval.fstAction = FST_ACK_RESPONSE;
+            break;
+        case ON_CHANNEL_TUNNEL_REQUEST:
+            retval.fstAction = ON_CHANNEL_TUNNEL_REQUEST;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown FST management action code");
+            retval.fstAction = FST_SETUP_REQUEST; /* quiet compiler */
+        }
+        break;
+
+    case UNPROTECTED_DMG:
+        switch (m_actionValue)
+        {
+        case UNPROTECTED_DMG_ANNOUNCE:
+            retval.unprotectedDmgAction = UNPROTECTED_DMG_ANNOUNCE;
+            break;
+        case UNPROTECTED_DMG_BRP:
+            retval.unprotectedDmgAction = UNPROTECTED_DMG_BRP;
+            break;
+        case UNPROTECTED_MIMO_BF_SETUP:
+            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_SETUP;
+            break;
+        case UNPROTECTED_MIMO_BF_POLL:
+            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_POLL;
+            break;
+        case UNPROTECTED_MIMO_BF_FEEDBACK:
+            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_FEEDBACK;
+            break;
+        case UNPROTECTED_MIMO_BF_SELECTION:
+            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_SELECTION;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown Unprotected DMG action code");
+            retval.unprotectedDmgAction = UNPROTECTED_DMG_ANNOUNCE; /* quiet compiler */
+        }
+        break;
+
+    case PROTECTED_EHT:
+        switch (m_actionValue)
+        {
+        case PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST:
+            retval.protectedEhtAction = PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST;
+            break;
+        case PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE:
+            retval.protectedEhtAction = PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE;
+            break;
+        case PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN:
+            retval.protectedEhtAction = PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN;
+            break;
+        case PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST:
+            retval.protectedEhtAction = PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST;
+            break;
+        case PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE:
+            retval.protectedEhtAction = PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE;
+            break;
+        case PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN:
+            retval.protectedEhtAction = PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN;
+            break;
+        case PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION:
+            retval.protectedEhtAction = PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION;
+            break;
+        case PROTECTED_EHT_LINK_RECOMMENDATION:
+            retval.protectedEhtAction = PROTECTED_EHT_LINK_RECOMMENDATION;
+            break;
+        case PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST:
+            retval.protectedEhtAction = PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST;
+            break;
+        case PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE:
+            retval.protectedEhtAction = PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE;
+            break;
+        default:
+            NS_FATAL_ERROR("Unknown Protected EHT action code");
+            retval.protectedEhtAction =
+                PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST; /* quiet compiler */
+        }
+        break;
+
+    default:
+        NS_FATAL_ERROR("Unsupported action");
+        retval.selfProtectedAction = PEER_LINK_OPEN; /* quiet compiler */
+    }
+    return retval;
+}
+
+TypeId
+WifiActionHeader::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::WifiActionHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<WifiActionHeader>();
+    return tid;
+}
+
+TypeId
+WifiActionHeader::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+std::pair<WifiActionHeader::CategoryValue, WifiActionHeader::ActionValue>
+WifiActionHeader::Peek(Ptr<const Packet> pkt)
+{
+    WifiActionHeader actionHdr;
+    pkt->PeekHeader(actionHdr);
+    return {actionHdr.GetCategory(), actionHdr.GetAction()};
+}
+
+std::pair<WifiActionHeader::CategoryValue, WifiActionHeader::ActionValue>
+WifiActionHeader::Remove(Ptr<Packet> pkt)
+{
+    WifiActionHeader actionHdr;
+    pkt->RemoveHeader(actionHdr);
+    return {actionHdr.GetCategory(), actionHdr.GetAction()};
+}
+
+void
+WifiActionHeader::Print(std::ostream& os) const
+{
+#define CASE_ACTION_VALUE(x)                                                                       \
+    case x:                                                                                        \
+        os << #x << "]";                                                                           \
+        break;
+
+    switch (m_category)
+    {
+    case QOS:
+        os << "QOS[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(ADDTS_REQUEST);
+            CASE_ACTION_VALUE(ADDTS_RESPONSE);
+            CASE_ACTION_VALUE(DELTS);
+            CASE_ACTION_VALUE(SCHEDULE);
+            CASE_ACTION_VALUE(QOS_MAP_CONFIGURE);
+        default:
+            NS_FATAL_ERROR("Unknown qos action code");
+        }
+        break;
+    case BLOCK_ACK:
+        os << "BLOCK_ACK[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(BLOCK_ACK_ADDBA_REQUEST);
+            CASE_ACTION_VALUE(BLOCK_ACK_ADDBA_RESPONSE);
+            CASE_ACTION_VALUE(BLOCK_ACK_DELBA);
+        default:
+            NS_FATAL_ERROR("Unknown block ack action code");
+        }
+        break;
+    case PUBLIC:
+        os << "PUBLIC[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(QAB_REQUEST);
+            CASE_ACTION_VALUE(QAB_RESPONSE);
+        default:
+            NS_FATAL_ERROR("Unknown public action code");
+        }
+        break;
+    case RADIO_MEASUREMENT:
+        os << "RADIO_MEASUREMENT[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(RADIO_MEASUREMENT_REQUEST);
+            CASE_ACTION_VALUE(RADIO_MEASUREMENT_REPORT);
+            CASE_ACTION_VALUE(LINK_MEASUREMENT_REQUEST);
+            CASE_ACTION_VALUE(LINK_MEASUREMENT_REPORT);
+            CASE_ACTION_VALUE(NEIGHBOR_REPORT_REQUEST);
+            CASE_ACTION_VALUE(NEIGHBOR_REPORT_RESPONSE);
+        default:
+            NS_FATAL_ERROR("Unknown radio measurement action code");
+        }
+        break;
+    case MESH:
+        os << "MESH[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(LINK_METRIC_REPORT);
+            CASE_ACTION_VALUE(PATH_SELECTION);
+            CASE_ACTION_VALUE(PORTAL_ANNOUNCEMENT);
+            CASE_ACTION_VALUE(CONGESTION_CONTROL_NOTIFICATION);
+            CASE_ACTION_VALUE(MDA_SETUP_REQUEST);
+            CASE_ACTION_VALUE(MDA_SETUP_REPLY);
+            CASE_ACTION_VALUE(MDAOP_ADVERTISEMENT_REQUEST);
+            CASE_ACTION_VALUE(MDAOP_ADVERTISEMENTS);
+            CASE_ACTION_VALUE(MDAOP_SET_TEARDOWN);
+            CASE_ACTION_VALUE(TBTT_ADJUSTMENT_REQUEST);
+            CASE_ACTION_VALUE(TBTT_ADJUSTMENT_RESPONSE);
+        default:
+            NS_FATAL_ERROR("Unknown mesh peering management action code");
+        }
+        break;
+    case MULTIHOP:
+        os << "MULTIHOP[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(PROXY_UPDATE);              // not used so far
+            CASE_ACTION_VALUE(PROXY_UPDATE_CONFIRMATION); // not used so far
+        default:
+            NS_FATAL_ERROR("Unknown mesh peering management action code");
+        }
+        break;
+    case SELF_PROTECTED:
+        os << "SELF_PROTECTED[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(PEER_LINK_OPEN);
+            CASE_ACTION_VALUE(PEER_LINK_CONFIRM);
+            CASE_ACTION_VALUE(PEER_LINK_CLOSE);
+            CASE_ACTION_VALUE(GROUP_KEY_INFORM);
+            CASE_ACTION_VALUE(GROUP_KEY_ACK);
+        default:
+            NS_FATAL_ERROR("Unknown mesh peering management action code");
+        }
+        break;
+    case DMG:
+        os << "DMG[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(DMG_POWER_SAVE_CONFIGURATION_REQUEST);
+            CASE_ACTION_VALUE(DMG_POWER_SAVE_CONFIGURATION_RESPONSE);
+            CASE_ACTION_VALUE(DMG_INFORMATION_REQUEST);
+            CASE_ACTION_VALUE(DMG_INFORMATION_RESPONSE);
+            CASE_ACTION_VALUE(DMG_HANDOVER_REQUEST);
+            CASE_ACTION_VALUE(DMG_HANDOVER_RESPONSE);
+            CASE_ACTION_VALUE(DMG_DTP_REQUEST);
+            CASE_ACTION_VALUE(DMG_DTP_RESPONSE);
+            CASE_ACTION_VALUE(DMG_RELAY_SEARCH_REQUEST);
+            CASE_ACTION_VALUE(DMG_RELAY_SEARCH_RESPONSE);
+            CASE_ACTION_VALUE(DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST);
+            CASE_ACTION_VALUE(DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT);
+            CASE_ACTION_VALUE(DMG_RLS_REQUEST);
+            CASE_ACTION_VALUE(DMG_RLS_RESPONSE);
+            CASE_ACTION_VALUE(DMG_RLS_ANNOUNCEMENT);
+            CASE_ACTION_VALUE(DMG_RLS_TEARDOWN);
+            CASE_ACTION_VALUE(DMG_RELAY_ACK_REQUEST);
+            CASE_ACTION_VALUE(DMG_RELAY_ACK_RESPONSE);
+            CASE_ACTION_VALUE(DMG_TPA_REQUEST);
+            CASE_ACTION_VALUE(DMG_TPA_RESPONSE);
+            CASE_ACTION_VALUE(DMG_ROC_REQUEST);
+            CASE_ACTION_VALUE(DMG_ROC_RESPONSE);
+        default:
+            NS_FATAL_ERROR("Unknown DMG management action code");
+        }
+        break;
+    case FST:
+        os << "FST[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(FST_SETUP_REQUEST);
+            CASE_ACTION_VALUE(FST_SETUP_RESPONSE);
+            CASE_ACTION_VALUE(FST_TEAR_DOWN);
+            CASE_ACTION_VALUE(FST_ACK_REQUEST);
+            CASE_ACTION_VALUE(FST_ACK_RESPONSE);
+            CASE_ACTION_VALUE(ON_CHANNEL_TUNNEL_REQUEST);
+        default:
+            NS_FATAL_ERROR("Unknown FST management action code");
+        }
+        break;
+    case UNPROTECTED_DMG:
+        os << "UNPROTECTED_DMG[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(UNPROTECTED_DMG_ANNOUNCE);
+            CASE_ACTION_VALUE(UNPROTECTED_DMG_BRP);
+            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_SETUP);
+            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_POLL);
+            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_FEEDBACK);
+            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_SELECTION);
+        default:
+            NS_FATAL_ERROR("Unknown Unprotected DMG action code");
+        }
+        break;
+    case PROTECTED_EHT:
+        os << "PROTECTED_EHT[";
+        switch (m_actionValue)
+        {
+            CASE_ACTION_VALUE(PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST);
+            CASE_ACTION_VALUE(PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE);
+            CASE_ACTION_VALUE(PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN);
+            CASE_ACTION_VALUE(PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST);
+            CASE_ACTION_VALUE(PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE);
+            CASE_ACTION_VALUE(PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN);
+            CASE_ACTION_VALUE(PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION);
+            CASE_ACTION_VALUE(PROTECTED_EHT_LINK_RECOMMENDATION);
+            CASE_ACTION_VALUE(PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST);
+            CASE_ACTION_VALUE(PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE);
+        default:
+            NS_FATAL_ERROR("Unknown Protected EHT action code");
+        }
+        break;
+    case VENDOR_SPECIFIC_ACTION:
+        os << "VENDOR_SPECIFIC_ACTION";
+        break;
+    default:
+        NS_FATAL_ERROR("Unknown action value");
+    }
+#undef CASE_ACTION_VALUE
+}
+
+uint32_t
+WifiActionHeader::GetSerializedSize() const
+{
+    return 2;
+}
+
+void
+WifiActionHeader::Serialize(Buffer::Iterator start) const
+{
+    start.WriteU8(m_category);
+    start.WriteU8(m_actionValue);
+}
+
+uint32_t
+WifiActionHeader::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    m_category = i.ReadU8();
+    m_actionValue = i.ReadU8();
+    return i.GetDistanceFrom(start);
+}
+
+/***************************************************
+ *                 ADDBARequest
+ ****************************************************/
+
+NS_OBJECT_ENSURE_REGISTERED(MgtAddBaRequestHeader);
+
+TypeId
+MgtAddBaRequestHeader::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::MgtAddBaRequestHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtAddBaRequestHeader>();
+    return tid;
+}
+
+TypeId
+MgtAddBaRequestHeader::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+void
+MgtAddBaRequestHeader::Print(std::ostream& os) const
+{
+}
+
+uint32_t
+MgtAddBaRequestHeader::GetSerializedSize() const
+{
+    uint32_t size = 0;
+    size += 1; // Dialog token
+    size += 2; // Block ack parameter set
+    size += 2; // Block ack timeout value
+    size += 2; // Starting sequence control
+    if (m_bufferSize >= 1024)
+    {
+        // an ADDBA Extension element has to be added
+        size += AddbaExtension().GetSerializedSize();
+    }
+    return size;
+}
+
+void
+MgtAddBaRequestHeader::Serialize(Buffer::Iterator start) const
+{
+    Buffer::Iterator i = start;
+    i.WriteU8(m_dialogToken);
+    i.WriteHtolsbU16(GetParameterSet());
+    i.WriteHtolsbU16(m_timeoutValue);
+    i.WriteHtolsbU16(GetStartingSequenceControl());
+    if (m_bufferSize >= 1024)
+    {
+        AddbaExtension addbaExt;
+        addbaExt.m_extParamSet.extBufferSize = m_bufferSize / 1024;
+        i = addbaExt.Serialize(i);
+    }
+}
+
+uint32_t
+MgtAddBaRequestHeader::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    m_dialogToken = i.ReadU8();
+    SetParameterSet(i.ReadLsbtohU16());
+    m_timeoutValue = i.ReadLsbtohU16();
+    SetStartingSequenceControl(i.ReadLsbtohU16());
+    AddbaExtension addbaExt;
+    auto tmp = i;
+    i = addbaExt.DeserializeIfPresent(i);
+    if (i.GetDistanceFrom(tmp) != 0)
+    {
+        // the buffer size is Extended Buffer Size × 1024 + Buffer Size
+        // (Sec. 9.4.2.138 of 802.11be D4.0)
+        m_bufferSize += addbaExt.m_extParamSet.extBufferSize * 1024;
+    }
+    return i.GetDistanceFrom(start);
+}
+
+void
+MgtAddBaRequestHeader::SetDelayedBlockAck()
+{
+    m_policy = 0;
+}
+
+void
+MgtAddBaRequestHeader::SetImmediateBlockAck()
+{
+    m_policy = 1;
+}
+
+void
+MgtAddBaRequestHeader::SetTid(uint8_t tid)
+{
+    NS_ASSERT(tid < 16);
+    m_tid = tid;
+}
+
+void
+MgtAddBaRequestHeader::SetTimeout(uint16_t timeout)
+{
+    m_timeoutValue = timeout;
+}
+
+void
+MgtAddBaRequestHeader::SetBufferSize(uint16_t size)
+{
+    m_bufferSize = size;
+}
+
+void
+MgtAddBaRequestHeader::SetStartingSequence(uint16_t seq)
+{
+    m_startingSeq = seq;
+}
+
+void
+MgtAddBaRequestHeader::SetStartingSequenceControl(uint16_t seqControl)
+{
+    m_startingSeq = (seqControl >> 4) & 0x0fff;
+}
+
+void
+MgtAddBaRequestHeader::SetAmsduSupport(bool supported)
+{
+    m_amsduSupport = supported;
+}
+
+uint8_t
+MgtAddBaRequestHeader::GetTid() const
+{
+    return m_tid;
+}
+
+bool
+MgtAddBaRequestHeader::IsImmediateBlockAck() const
+{
+    return m_policy == 1;
+}
+
+uint16_t
+MgtAddBaRequestHeader::GetTimeout() const
+{
+    return m_timeoutValue;
+}
+
+uint16_t
+MgtAddBaRequestHeader::GetBufferSize() const
+{
+    return m_bufferSize;
+}
+
+bool
+MgtAddBaRequestHeader::IsAmsduSupported() const
+{
+    return m_amsduSupport == 1;
+}
+
+uint16_t
+MgtAddBaRequestHeader::GetStartingSequence() const
+{
+    return m_startingSeq;
+}
+
+uint16_t
+MgtAddBaRequestHeader::GetStartingSequenceControl() const
+{
+    return (m_startingSeq << 4) & 0xfff0;
+}
+
+uint16_t
+MgtAddBaRequestHeader::GetParameterSet() const
+{
+    uint16_t res = 0;
+    res |= m_amsduSupport;
+    res |= m_policy << 1;
+    res |= m_tid << 2;
+    res |= (m_bufferSize % 1024) << 6;
+    return res;
+}
+
+void
+MgtAddBaRequestHeader::SetParameterSet(uint16_t params)
+{
+    m_amsduSupport = params & 0x01;
+    m_policy = (params >> 1) & 0x01;
+    m_tid = (params >> 2) & 0x0f;
+    m_bufferSize = (params >> 6) & 0x03ff;
+}
+
+/***************************************************
+ *                 ADDBAResponse
+ ****************************************************/
+
+NS_OBJECT_ENSURE_REGISTERED(MgtAddBaResponseHeader);
+
+TypeId
+MgtAddBaResponseHeader::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::MgtAddBaResponseHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtAddBaResponseHeader>();
+    return tid;
+}
+
+TypeId
+MgtAddBaResponseHeader::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+void
+MgtAddBaResponseHeader::Print(std::ostream& os) const
+{
+    os << "status code=" << m_code;
+}
+
+uint32_t
+MgtAddBaResponseHeader::GetSerializedSize() const
+{
+    uint32_t size = 0;
+    size += 1;                          // Dialog token
+    size += m_code.GetSerializedSize(); // Status code
+    size += 2;                          // Block ack parameter set
+    size += 2;                          // Block ack timeout value
+    if (m_bufferSize >= 1024)
+    {
+        // an ADDBA Extension element has to be added
+        size += AddbaExtension().GetSerializedSize();
+    }
+    return size;
+}
+
+void
+MgtAddBaResponseHeader::Serialize(Buffer::Iterator start) const
+{
+    Buffer::Iterator i = start;
+    i.WriteU8(m_dialogToken);
+    i = m_code.Serialize(i);
+    i.WriteHtolsbU16(GetParameterSet());
+    i.WriteHtolsbU16(m_timeoutValue);
+    if (m_bufferSize >= 1024)
+    {
+        AddbaExtension addbaExt;
+        addbaExt.m_extParamSet.extBufferSize = m_bufferSize / 1024;
+        i = addbaExt.Serialize(i);
+    }
+}
+
+uint32_t
+MgtAddBaResponseHeader::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    m_dialogToken = i.ReadU8();
+    i = m_code.Deserialize(i);
+    SetParameterSet(i.ReadLsbtohU16());
+    m_timeoutValue = i.ReadLsbtohU16();
+    AddbaExtension addbaExt;
+    auto tmp = i;
+    i = addbaExt.DeserializeIfPresent(i);
+    if (i.GetDistanceFrom(tmp) != 0)
+    {
+        // the buffer size is Extended Buffer Size × 1024 + Buffer Size
+        // (Sec. 9.4.2.138 of 802.11be D4.0)
+        m_bufferSize += addbaExt.m_extParamSet.extBufferSize * 1024;
+    }
+    return i.GetDistanceFrom(start);
+}
+
+void
+MgtAddBaResponseHeader::SetDelayedBlockAck()
+{
+    m_policy = 0;
+}
+
+void
+MgtAddBaResponseHeader::SetImmediateBlockAck()
+{
+    m_policy = 1;
+}
+
+void
+MgtAddBaResponseHeader::SetTid(uint8_t tid)
+{
+    NS_ASSERT(tid < 16);
+    m_tid = tid;
+}
+
+void
+MgtAddBaResponseHeader::SetTimeout(uint16_t timeout)
+{
+    m_timeoutValue = timeout;
+}
+
+void
+MgtAddBaResponseHeader::SetBufferSize(uint16_t size)
+{
+    m_bufferSize = size;
+}
+
+void
+MgtAddBaResponseHeader::SetStatusCode(StatusCode code)
+{
+    m_code = code;
+}
+
+void
+MgtAddBaResponseHeader::SetAmsduSupport(bool supported)
+{
+    m_amsduSupport = supported;
+}
+
+StatusCode
+MgtAddBaResponseHeader::GetStatusCode() const
+{
+    return m_code;
+}
+
+uint8_t
+MgtAddBaResponseHeader::GetTid() const
+{
+    return m_tid;
+}
+
+bool
+MgtAddBaResponseHeader::IsImmediateBlockAck() const
+{
+    return m_policy == 1;
+}
+
+uint16_t
+MgtAddBaResponseHeader::GetTimeout() const
+{
+    return m_timeoutValue;
+}
+
+uint16_t
+MgtAddBaResponseHeader::GetBufferSize() const
+{
+    return m_bufferSize;
+}
+
+bool
+MgtAddBaResponseHeader::IsAmsduSupported() const
+{
+    return m_amsduSupport == 1;
+}
+
+uint16_t
+MgtAddBaResponseHeader::GetParameterSet() const
+{
+    uint16_t res = 0;
+    res |= m_amsduSupport;
+    res |= m_policy << 1;
+    res |= m_tid << 2;
+    res |= (m_bufferSize % 1024) << 6;
+    return res;
+}
+
+void
+MgtAddBaResponseHeader::SetParameterSet(uint16_t params)
+{
+    m_amsduSupport = params & 0x01;
+    m_policy = (params >> 1) & 0x01;
+    m_tid = (params >> 2) & 0x0f;
+    m_bufferSize = (params >> 6) & 0x03ff;
+}
+
+/***************************************************
+ *                     DelBa
+ ****************************************************/
+
+NS_OBJECT_ENSURE_REGISTERED(MgtDelBaHeader);
+
+TypeId
+MgtDelBaHeader::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::MgtDelBaHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtDelBaHeader>();
+    return tid;
+}
+
+TypeId
+MgtDelBaHeader::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+void
+MgtDelBaHeader::Print(std::ostream& os) const
+{
+}
+
+uint32_t
+MgtDelBaHeader::GetSerializedSize() const
+{
+    uint32_t size = 0;
+    size += 2; // DelBa parameter set
+    size += 2; // Reason code
+    return size;
+}
+
+void
+MgtDelBaHeader::Serialize(Buffer::Iterator start) const
+{
+    Buffer::Iterator i = start;
+    i.WriteHtolsbU16(GetParameterSet());
+    i.WriteHtolsbU16(m_reasonCode);
+}
+
+uint32_t
+MgtDelBaHeader::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    SetParameterSet(i.ReadLsbtohU16());
+    m_reasonCode = i.ReadLsbtohU16();
+    return i.GetDistanceFrom(start);
+}
+
+bool
+MgtDelBaHeader::IsByOriginator() const
+{
+    return m_initiator == 1;
+}
+
+uint8_t
+MgtDelBaHeader::GetTid() const
+{
+    NS_ASSERT(m_tid < 16);
+    auto tid = static_cast<uint8_t>(m_tid);
+    return tid;
+}
+
+void
+MgtDelBaHeader::SetByOriginator()
+{
+    m_initiator = 1;
+}
+
+void
+MgtDelBaHeader::SetByRecipient()
+{
+    m_initiator = 0;
+}
+
+void
+MgtDelBaHeader::SetTid(uint8_t tid)
+{
+    NS_ASSERT(tid < 16);
+    m_tid = static_cast<uint16_t>(tid);
+}
+
+uint16_t
+MgtDelBaHeader::GetParameterSet() const
+{
+    uint16_t res = 0;
+    res |= m_initiator << 11;
+    res |= m_tid << 12;
+    return res;
+}
+
+void
+MgtDelBaHeader::SetParameterSet(uint16_t params)
+{
+    m_initiator = (params >> 11) & 0x01;
+    m_tid = (params >> 12) & 0x0f;
+}
+
+/***************************************************
+ *     EMLSR Operating Mode Notification
+ ****************************************************/
+
+NS_OBJECT_ENSURE_REGISTERED(MgtEmlOmn);
+
+TypeId
+MgtEmlOmn::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::MgtEmlOperatingModeNotification")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtEmlOmn>();
+    return tid;
+}
+
+TypeId
+MgtEmlOmn::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+void
+MgtEmlOmn::Print(std::ostream& os) const
+{
+    os << "EMLSR Mode=" << +m_emlControl.emlsrMode << " EMLMR Mode=" << +m_emlControl.emlmrMode
+       << " EMLSR Parameter Update Control=" << +m_emlControl.emlsrParamUpdateCtrl;
+    if (m_emlControl.linkBitmap)
+    {
+        os << " Link bitmap=" << std::hex << *m_emlControl.linkBitmap << std::dec;
+    }
+    if (m_emlsrParamUpdate)
+    {
+        os << " EMLSR Padding Delay="
+           << CommonInfoBasicMle::DecodeEmlsrPaddingDelay(m_emlsrParamUpdate->paddingDelay)
+                  .As(Time::US)
+           << " EMLSR Transition Delay="
+           << CommonInfoBasicMle::DecodeEmlsrTransitionDelay(m_emlsrParamUpdate->transitionDelay)
+                  .As(Time::US);
+    }
+}
+
+uint32_t
+MgtEmlOmn::GetSerializedSize() const
+{
+    uint32_t size = 2; // Dialog Token (1) + first byte of EML Control
+    if (m_emlControl.linkBitmap)
+    {
+        size += 2;
+    }
+    if (m_emlControl.mcsMapCountCtrl)
+    {
+        size += 1;
+    }
+    // TODO add size of EMLMR Supported MCS And NSS Set subfield when implemented
+    if (m_emlsrParamUpdate)
+    {
+        size += 1; // EMLSR Parameter Update field
+    }
+    return size;
+}
+
+void
+MgtEmlOmn::Serialize(Buffer::Iterator start) const
+{
+    start.WriteU8(m_dialogToken);
+
+    NS_ABORT_MSG_IF(m_emlControl.emlsrMode == 1 && m_emlControl.emlmrMode == 1,
+                    "EMLSR Mode and EMLMR Mode cannot be both set to 1");
+    uint8_t val = m_emlControl.emlsrMode | (m_emlControl.emlmrMode << 1) |
+                  (m_emlControl.emlsrParamUpdateCtrl << 2);
+    start.WriteU8(val);
+
+    NS_ABORT_MSG_IF(m_emlControl.linkBitmap.has_value() !=
+                        (m_emlControl.emlsrMode == 1 || m_emlControl.emlmrMode == 1),
+                    "The EMLSR/EMLMR Link Bitmap is present if and only if either of the EMLSR "
+                    "Mode and EMLMR Mode subfields are set to 1");
+    if (m_emlControl.linkBitmap)
+    {
+        start.WriteHtolsbU16(*m_emlControl.linkBitmap);
+    }
+    // TODO serialize MCS Map Count Control and EMLMR Supported MCS And NSS Set subfields
+    // when implemented
+
+    NS_ABORT_MSG_IF(m_emlsrParamUpdate.has_value() != (m_emlControl.emlsrParamUpdateCtrl == 1),
+                    "The EMLSR Parameter Update field is present "
+                        << std::boolalpha << m_emlsrParamUpdate.has_value()
+                        << " if and only if the EMLSR "
+                           "Parameter Update Control subfield is set to 1 "
+                        << +m_emlControl.emlsrParamUpdateCtrl);
+    if (m_emlsrParamUpdate)
+    {
+        val = m_emlsrParamUpdate->paddingDelay | (m_emlsrParamUpdate->transitionDelay << 3);
+        start.WriteU8(val);
+    }
+}
+
+uint32_t
+MgtEmlOmn::Deserialize(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+
+    m_dialogToken = i.ReadU8();
+
+    uint8_t val = i.ReadU8();
+    m_emlControl.emlsrMode = val & 0x01;
+    m_emlControl.emlmrMode = (val >> 1) & 0x01;
+    m_emlControl.emlsrParamUpdateCtrl = (val >> 2) & 0x01;
+
+    NS_ABORT_MSG_IF(m_emlControl.emlsrMode == 1 && m_emlControl.emlmrMode == 1,
+                    "EMLSR Mode and EMLMR Mode cannot be both set to 1");
+
+    if (m_emlControl.emlsrMode == 1 || m_emlControl.emlmrMode == 1)
+    {
+        m_emlControl.linkBitmap = i.ReadLsbtohU16();
+    }
+    // TODO deserialize MCS Map Count Control and EMLMR Supported MCS And NSS Set subfields
+    // when implemented
+
+    if (m_emlControl.emlsrParamUpdateCtrl == 1)
+    {
+        val = i.ReadU8();
+        m_emlsrParamUpdate = EmlsrParamUpdate{};
+        m_emlsrParamUpdate->paddingDelay = val & 0x07;
+        m_emlsrParamUpdate->transitionDelay = (val >> 3) & 0x07;
+    }
+
+    return i.GetDistanceFrom(start);
+}
+
+void
+MgtEmlOmn::SetLinkIdInBitmap(uint8_t linkId)
+{
+    NS_ABORT_MSG_IF(linkId > 15, "Link ID must not exceed 15");
+    if (!m_emlControl.linkBitmap)
+    {
+        m_emlControl.linkBitmap = 0;
+    }
+    m_emlControl.linkBitmap = *m_emlControl.linkBitmap | (1 << linkId);
+}
+
+std::list<uint8_t>
+MgtEmlOmn::GetLinkBitmap() const
+{
+    std::list<uint8_t> list;
+    NS_ASSERT_MSG(m_emlControl.linkBitmap.has_value(), "No link bitmap");
+    uint16_t bitmap = *m_emlControl.linkBitmap;
+    for (uint8_t linkId = 0; linkId < 16; linkId++)
+    {
+        if ((bitmap & 0x0001) == 1)
+        {
+            list.push_back(linkId);
+        }
+        bitmap >>= 1;
+    }
+    return list;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/mgt-action-headers.h ns-3.41/src/wifi/model/mgt-action-headers.h
--- ns-3.40/src/wifi/model/mgt-action-headers.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/wifi/model/mgt-action-headers.h	2024-02-09 14:44:08.589794900 -0800
@@ -0,0 +1,671 @@
+/*
+ * Copyright (c) 2006 INRIA
+ * Copyright (c) 2009 MIRKO BANCHI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Authors: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+ *          Mirko Banchi <mk.banchi@gmail.com>
+ */
+
+#ifndef MGT_ACTION_HEADERS_H
+#define MGT_ACTION_HEADERS_H
+
+#include "status-code.h"
+
+#include "ns3/header.h"
+
+#include <list>
+#include <optional>
+
+namespace ns3
+{
+
+class Packet;
+
+/**
+ * \ingroup wifi
+ *
+ * See IEEE 802.11 chapter 7.3.1.11
+ * Header format: | category: 1 | action value: 1 |
+ *
+ */
+class WifiActionHeader : public Header
+{
+  public:
+    WifiActionHeader();
+    ~WifiActionHeader() override;
+
+    /*
+     * Compatible with table 8-38 IEEE 802.11, Part11, (Year 2012)
+     * Category values - see 802.11-2012 Table 8-38
+     */
+
+    /// CategoryValue enumeration
+    enum CategoryValue : uint8_t // table 9-51 of IEEE 802.11-2020
+    {
+        SPECTRUM_MANAGEMENT = 0,
+        QOS = 1,
+        BLOCK_ACK = 3,
+        PUBLIC = 4,
+        RADIO_MEASUREMENT = 5, // Category: Radio Measurement
+        MESH = 13,             // Category: Mesh
+        MULTIHOP = 14,         // not used so far
+        SELF_PROTECTED = 15,   // Category: Self Protected
+        DMG = 16,              // Category: DMG
+        FST = 18,              // Category: Fast Session Transfer
+        UNPROTECTED_DMG = 20,  // Category: Unprotected DMG
+        PROTECTED_EHT = 37,    // Category: Protected EHT
+        // Since vendor specific action has no stationary Action value,the parse process is not
+        // here. Refer to vendor-specific-action in wave module.
+        VENDOR_SPECIFIC_ACTION = 127,
+        // values 128 to 255 are illegal
+    };
+
+    /// QosActionValue enumeration
+    enum QosActionValue : uint8_t
+    {
+        ADDTS_REQUEST = 0,
+        ADDTS_RESPONSE = 1,
+        DELTS = 2,
+        SCHEDULE = 3,
+        QOS_MAP_CONFIGURE = 4,
+    };
+
+    /**
+     * Block Ack Action field values
+     * See 802.11 Table 8-202
+     */
+    enum BlockAckActionValue : uint8_t
+    {
+        BLOCK_ACK_ADDBA_REQUEST = 0,
+        BLOCK_ACK_ADDBA_RESPONSE = 1,
+        BLOCK_ACK_DELBA = 2
+    };
+
+    /// PublicActionValue enumeration
+    enum PublicActionValue : uint8_t
+    {
+        QAB_REQUEST = 16,
+        QAB_RESPONSE = 17,
+    };
+
+    /// RadioMeasurementActionValue enumeration
+    enum RadioMeasurementActionValue : uint8_t
+    {
+        RADIO_MEASUREMENT_REQUEST = 0,
+        RADIO_MEASUREMENT_REPORT = 1,
+        LINK_MEASUREMENT_REQUEST = 2,
+        LINK_MEASUREMENT_REPORT = 3,
+        NEIGHBOR_REPORT_REQUEST = 4,
+        NEIGHBOR_REPORT_RESPONSE = 5
+    };
+
+    /// MeshActionValue enumeration
+    enum MeshActionValue : uint8_t
+    {
+        LINK_METRIC_REPORT = 0,              // Action Value:0 in Category 13: Mesh
+        PATH_SELECTION = 1,                  // Action Value:1 in Category 13: Mesh
+        PORTAL_ANNOUNCEMENT = 2,             // Action Value:2 in Category 13: Mesh
+        CONGESTION_CONTROL_NOTIFICATION = 3, // Action Value:3 in Category 13: Mesh
+        MDA_SETUP_REQUEST =
+            4, // Action Value:4 in Category 13: Mesh MCCA-Setup-Request (not used so far)
+        MDA_SETUP_REPLY =
+            5, // Action Value:5 in Category 13: Mesh MCCA-Setup-Reply (not used so far)
+        MDAOP_ADVERTISEMENT_REQUEST =
+            6, // Action Value:6 in Category 13: Mesh MCCA-Advertisement-Request (not used so far)
+        MDAOP_ADVERTISEMENTS = 7,      // Action Value:7 in Category 13: Mesh (not used so far)
+        MDAOP_SET_TEARDOWN = 8,        // Action Value:8 in Category 13: Mesh (not used so far)
+        TBTT_ADJUSTMENT_REQUEST = 9,   // Action Value:9 in Category 13: Mesh (not used so far)
+        TBTT_ADJUSTMENT_RESPONSE = 10, // Action Value:10 in Category 13: Mesh (not used so far)
+    };
+
+    /// MultihopActionValue enumeration
+    enum MultihopActionValue : uint8_t
+    {
+        PROXY_UPDATE = 0,              // not used so far
+        PROXY_UPDATE_CONFIRMATION = 1, // not used so far
+    };
+
+    /// SelfProtectedActionValue enumeration
+    enum SelfProtectedActionValue : uint8_t // Category: 15 (Self Protected)
+    {
+        PEER_LINK_OPEN = 1,    // Mesh Peering Open
+        PEER_LINK_CONFIRM = 2, // Mesh Peering Confirm
+        PEER_LINK_CLOSE = 3,   // Mesh Peering Close
+        GROUP_KEY_INFORM = 4,  // Mesh Group Key Inform
+        GROUP_KEY_ACK = 5,     // Mesh Group Key Acknowledge
+    };
+
+    /**
+     * DMG Action field values
+     * See 802.11ad Table 8-281b
+     */
+    enum DmgActionValue : uint8_t
+    {
+        DMG_POWER_SAVE_CONFIGURATION_REQUEST = 0,
+        DMG_POWER_SAVE_CONFIGURATION_RESPONSE = 1,
+        DMG_INFORMATION_REQUEST = 2,
+        DMG_INFORMATION_RESPONSE = 3,
+        DMG_HANDOVER_REQUEST = 4,
+        DMG_HANDOVER_RESPONSE = 5,
+        DMG_DTP_REQUEST = 6,
+        DMG_DTP_RESPONSE = 7,
+        DMG_RELAY_SEARCH_REQUEST = 8,
+        DMG_RELAY_SEARCH_RESPONSE = 9,
+        DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST = 10,
+        DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT = 11,
+        DMG_RLS_REQUEST = 12,
+        DMG_RLS_RESPONSE = 13,
+        DMG_RLS_ANNOUNCEMENT = 14,
+        DMG_RLS_TEARDOWN = 15,
+        DMG_RELAY_ACK_REQUEST = 16,
+        DMG_RELAY_ACK_RESPONSE = 17,
+        DMG_TPA_REQUEST = 18,
+        DMG_TPA_RESPONSE = 19,
+        DMG_TPA_REPORT = 20,
+        DMG_ROC_REQUEST = 21,
+        DMG_ROC_RESPONSE = 22
+    };
+
+    /**
+     * FST Action field values
+     * See 802.11ad Table 8-281x
+     */
+    enum FstActionValue : uint8_t
+    {
+        FST_SETUP_REQUEST = 0,
+        FST_SETUP_RESPONSE = 1,
+        FST_TEAR_DOWN = 2,
+        FST_ACK_REQUEST = 3,
+        FST_ACK_RESPONSE = 4,
+        ON_CHANNEL_TUNNEL_REQUEST = 5
+    };
+
+    /**
+     * Unprotected DMG action field values
+     * See 802.11ad Table 8-281ae
+     */
+    enum UnprotectedDmgActionValue : uint8_t
+    {
+        UNPROTECTED_DMG_ANNOUNCE = 0,
+        UNPROTECTED_DMG_BRP = 1,
+        UNPROTECTED_MIMO_BF_SETUP = 2,
+        UNPROTECTED_MIMO_BF_POLL = 3,
+        UNPROTECTED_MIMO_BF_FEEDBACK = 4,
+        UNPROTECTED_MIMO_BF_SELECTION = 5,
+    };
+
+    /**
+     * Protected EHT action field values
+     * See 802.11be D3.0 Table 9-623c
+     */
+    enum ProtectedEhtActionValue : uint8_t
+    {
+        PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST = 0,
+        PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE = 1,
+        PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN = 2,
+        PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST = 3,
+        PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE = 4,
+        PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN = 5,
+        PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION = 6,
+        PROTECTED_EHT_LINK_RECOMMENDATION = 7,
+        PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST = 8,
+        PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE = 9,
+    };
+
+    /**
+     * typedef for union of different ActionValues
+     */
+    typedef union {
+        QosActionValue qos;                                 ///< qos
+        BlockAckActionValue blockAck;                       ///< block ack
+        RadioMeasurementActionValue radioMeasurementAction; ///< radio measurement
+        PublicActionValue publicAction;                     ///< public
+        SelfProtectedActionValue selfProtectedAction;       ///< self protected
+        MultihopActionValue multihopAction;                 ///< multi hop
+        MeshActionValue meshAction;                         ///< mesh
+        DmgActionValue dmgAction;                           ///< dmg
+        FstActionValue fstAction;                           ///< fst
+        UnprotectedDmgActionValue unprotectedDmgAction;     ///< unprotected dmg
+        ProtectedEhtActionValue protectedEhtAction;         ///< protected eht
+    } ActionValue;                                          ///< the action value
+
+    /**
+     * Set action for this Action header.
+     *
+     * \param type category
+     * \param action action
+     */
+    void SetAction(CategoryValue type, ActionValue action);
+
+    /**
+     * Return the category value.
+     *
+     * \return CategoryValue
+     */
+    CategoryValue GetCategory() const;
+    /**
+     * Return the action value.
+     *
+     * \return ActionValue
+     */
+    ActionValue GetAction() const;
+
+    /**
+     * Peek an Action header from the given packet.
+     *
+     * \param pkt the given packet
+     * \return the category value and the action value in the peeked Action header
+     */
+    static std::pair<CategoryValue, ActionValue> Peek(Ptr<const Packet> pkt);
+
+    /**
+     * Remove an Action header from the given packet.
+     *
+     * \param pkt the given packet
+     * \return the category value and the action value in the removed Action header
+     */
+    static std::pair<CategoryValue, ActionValue> Remove(Ptr<Packet> pkt);
+
+    /**
+     * Register this type.
+     * \return The TypeId.
+     */
+    static TypeId GetTypeId();
+    TypeId GetInstanceTypeId() const override;
+    void Print(std::ostream& os) const override;
+    uint32_t GetSerializedSize() const override;
+    void Serialize(Buffer::Iterator start) const override;
+    uint32_t Deserialize(Buffer::Iterator start) override;
+
+  private:
+    uint8_t m_category;    //!< Category of the action
+    uint8_t m_actionValue; //!< Action value
+};
+
+/**
+ * \ingroup wifi
+ * Implement the header for management frames of type Add Block Ack request.
+ */
+class MgtAddBaRequestHeader : public Header
+{
+  public:
+    /**
+     * Register this type.
+     * \return The TypeId.
+     */
+    static TypeId GetTypeId();
+    TypeId GetInstanceTypeId() const override;
+    void Print(std::ostream& os) const override;
+    uint32_t GetSerializedSize() const override;
+    void Serialize(Buffer::Iterator start) const override;
+    uint32_t Deserialize(Buffer::Iterator start) override;
+
+    /**
+     * Enable delayed BlockAck.
+     */
+    void SetDelayedBlockAck();
+    /**
+     * Enable immediate BlockAck
+     */
+    void SetImmediateBlockAck();
+    /**
+     * Set Traffic ID (TID).
+     *
+     * \param tid traffic ID
+     */
+    void SetTid(uint8_t tid);
+    /**
+     * Set timeout.
+     *
+     * \param timeout timeout
+     */
+    void SetTimeout(uint16_t timeout);
+    /**
+     * Set buffer size.
+     *
+     * \param size buffer size
+     */
+    void SetBufferSize(uint16_t size);
+    /**
+     * Set the starting sequence number.
+     *
+     * \param seq the starting sequence number
+     */
+    void SetStartingSequence(uint16_t seq);
+    /**
+     * Enable or disable A-MSDU support.
+     *
+     * \param supported enable or disable A-MSDU support
+     */
+    void SetAmsduSupport(bool supported);
+
+    /**
+     * Return the starting sequence number.
+     *
+     * \return the starting sequence number
+     */
+    uint16_t GetStartingSequence() const;
+    /**
+     * Return the Traffic ID (TID).
+     *
+     * \return TID
+     */
+    uint8_t GetTid() const;
+    /**
+     * Return whether the Block Ack policy is immediate Block Ack.
+     *
+     * \return true if immediate Block Ack is being used, false otherwise
+     */
+    bool IsImmediateBlockAck() const;
+    /**
+     * Return the timeout.
+     *
+     * \return timeout
+     */
+    uint16_t GetTimeout() const;
+    /**
+     * Return the buffer size.
+     *
+     * \return the buffer size.
+     */
+    uint16_t GetBufferSize() const;
+    /**
+     * Return whether A-MSDU capability is supported.
+     *
+     * \return true is A-MSDU is supported, false otherwise
+     */
+    bool IsAmsduSupported() const;
+
+  private:
+    /**
+     * Return the raw parameter set.
+     *
+     * \return the raw parameter set
+     */
+    uint16_t GetParameterSet() const;
+    /**
+     * Set the parameter set from the given raw value.
+     *
+     * \param params raw parameter set value
+     */
+    void SetParameterSet(uint16_t params);
+    /**
+     * Return the raw sequence control.
+     *
+     * \return the raw sequence control
+     */
+    uint16_t GetStartingSequenceControl() const;
+    /**
+     * Set sequence control with the given raw value.
+     *
+     * \param seqControl the raw sequence control
+     */
+    void SetStartingSequenceControl(uint16_t seqControl);
+
+    uint8_t m_dialogToken{1};   //!< Not used for now
+    uint8_t m_amsduSupport{1};  //!< Flag if A-MSDU is supported
+    uint8_t m_policy{1};        //!< Block Ack policy
+    uint8_t m_tid{0};           //!< Traffic ID
+    uint16_t m_bufferSize{0};   //!< Buffer size
+    uint16_t m_timeoutValue{0}; //!< Timeout
+    uint16_t m_startingSeq{0};  //!< Starting sequence number
+};
+
+/**
+ * \ingroup wifi
+ * Implement the header for management frames of type Add Block Ack response.
+ */
+class MgtAddBaResponseHeader : public Header
+{
+  public:
+    /**
+     * Register this type.
+     * \return The TypeId.
+     */
+    static TypeId GetTypeId();
+    TypeId GetInstanceTypeId() const override;
+    void Print(std::ostream& os) const override;
+    uint32_t GetSerializedSize() const override;
+    void Serialize(Buffer::Iterator start) const override;
+    uint32_t Deserialize(Buffer::Iterator start) override;
+
+    /**
+     * Enable delayed BlockAck.
+     */
+    void SetDelayedBlockAck();
+    /**
+     * Enable immediate BlockAck.
+     */
+    void SetImmediateBlockAck();
+    /**
+     * Set Traffic ID (TID).
+     *
+     * \param tid traffic ID
+     */
+    void SetTid(uint8_t tid);
+    /**
+     * Set timeout.
+     *
+     * \param timeout timeout
+     */
+    void SetTimeout(uint16_t timeout);
+    /**
+     * Set buffer size.
+     *
+     * \param size buffer size
+     */
+    void SetBufferSize(uint16_t size);
+    /**
+     * Set the status code.
+     *
+     * \param code the status code
+     */
+    void SetStatusCode(StatusCode code);
+    /**
+     * Enable or disable A-MSDU support.
+     *
+     * \param supported enable or disable A-MSDU support
+     */
+    void SetAmsduSupport(bool supported);
+
+    /**
+     * Return the status code.
+     *
+     * \return the status code
+     */
+    StatusCode GetStatusCode() const;
+    /**
+     * Return the Traffic ID (TID).
+     *
+     * \return TID
+     */
+    uint8_t GetTid() const;
+    /**
+     * Return whether the Block Ack policy is immediate Block Ack.
+     *
+     * \return true if immediate Block Ack is being used, false otherwise
+     */
+    bool IsImmediateBlockAck() const;
+    /**
+     * Return the timeout.
+     *
+     * \return timeout
+     */
+    uint16_t GetTimeout() const;
+    /**
+     * Return the buffer size.
+     *
+     * \return the buffer size.
+     */
+    uint16_t GetBufferSize() const;
+    /**
+     * Return whether A-MSDU capability is supported.
+     *
+     * \return true is A-MSDU is supported, false otherwise
+     */
+    bool IsAmsduSupported() const;
+
+  private:
+    /**
+     * Return the raw parameter set.
+     *
+     * \return the raw parameter set
+     */
+    uint16_t GetParameterSet() const;
+    /**
+     * Set the parameter set from the given raw value.
+     *
+     * \param params raw parameter set value
+     */
+    void SetParameterSet(uint16_t params);
+
+    uint8_t m_dialogToken{1};   //!< Not used for now
+    StatusCode m_code{};        //!< Status code
+    uint8_t m_amsduSupport{1};  //!< Flag if A-MSDU is supported
+    uint8_t m_policy{1};        //!< Block ACK policy
+    uint8_t m_tid{0};           //!< Traffic ID
+    uint16_t m_bufferSize{0};   //!< Buffer size
+    uint16_t m_timeoutValue{0}; //!< Timeout
+};
+
+/**
+ * \ingroup wifi
+ * Implement the header for management frames of type Delete Block Ack.
+ */
+class MgtDelBaHeader : public Header
+{
+  public:
+    /**
+     * Register this type.
+     * \return The TypeId.
+     */
+    static TypeId GetTypeId();
+
+    TypeId GetInstanceTypeId() const override;
+    void Print(std::ostream& os) const override;
+    uint32_t GetSerializedSize() const override;
+    void Serialize(Buffer::Iterator start) const override;
+    uint32_t Deserialize(Buffer::Iterator start) override;
+
+    /**
+     * Check if the initiator bit in the DELBA is set.
+     *
+     * \return true if the initiator bit in the DELBA is set,
+     *         false otherwise
+     */
+    bool IsByOriginator() const;
+    /**
+     * Return the Traffic ID (TID).
+     *
+     * \return TID
+     */
+    uint8_t GetTid() const;
+    /**
+     * Set Traffic ID (TID).
+     *
+     * \param tid traffic ID
+     */
+    void SetTid(uint8_t tid);
+    /**
+     * Set the initiator bit in the DELBA.
+     */
+    void SetByOriginator();
+    /**
+     * Un-set the initiator bit in the DELBA.
+     */
+    void SetByRecipient();
+
+  private:
+    /**
+     * Return the raw parameter set.
+     *
+     * \return the raw parameter set
+     */
+    uint16_t GetParameterSet() const;
+    /**
+     * Set the parameter set from the given raw value.
+     *
+     * \param params raw parameter set value
+     */
+    void SetParameterSet(uint16_t params);
+
+    uint16_t m_initiator{0};  //!< initiator
+    uint16_t m_tid{0};        //!< Traffic ID
+    uint16_t m_reasonCode{1}; //!< Not used for now. Always set to 1: "Unspecified reason"
+};
+
+/**
+ * \ingroup wifi
+ * Implement the header for Action frames of type EML Operating Mode Notification.
+ */
+class MgtEmlOmn : public Header
+{
+  public:
+    MgtEmlOmn() = default;
+
+    /**
+     * Register this type.
+     * \return The TypeId.
+     */
+    static TypeId GetTypeId();
+    TypeId GetInstanceTypeId() const override;
+    void Print(std::ostream& os) const override;
+    uint32_t GetSerializedSize() const override;
+    void Serialize(Buffer::Iterator start) const override;
+    uint32_t Deserialize(Buffer::Iterator start) override;
+
+    /**
+     * EML Control field.
+     */
+    struct EmlControl
+    {
+        uint8_t emlsrMode : 1;                  //!< EMLSR Mode
+        uint8_t emlmrMode : 1;                  //!< EMLMR Mode
+        uint8_t emlsrParamUpdateCtrl : 1;       //!< EMLSR Parameter Update Control
+        uint8_t : 5;                            //!< reserved
+        std::optional<uint16_t> linkBitmap;     //!< EMLSR/EMLMR Link Bitmap
+        std::optional<uint8_t> mcsMapCountCtrl; //!< MCS Map Count Control
+        // TODO Add EMLMR Supported MCS And NSS Set subfield when EMLMR is supported
+    };
+
+    /**
+     * EMLSR Parameter Update field.
+     */
+    struct EmlsrParamUpdate
+    {
+        uint8_t paddingDelay : 3;    //!< EMLSR Padding Delay
+        uint8_t transitionDelay : 3; //!< EMLSR Transition Delay
+    };
+
+    /**
+     * Set the bit position in the link bitmap corresponding to the given link.
+     *
+     * \param linkId the ID of the given link
+     */
+    void SetLinkIdInBitmap(uint8_t linkId);
+    /**
+     * \return the ID of the links whose bit position in the link bitmap is set to 1
+     */
+    std::list<uint8_t> GetLinkBitmap() const;
+
+    uint8_t m_dialogToken{0};                             //!< Dialog Token
+    EmlControl m_emlControl{};                            //!< EML Control field
+    std::optional<EmlsrParamUpdate> m_emlsrParamUpdate{}; //!< EMLSR Parameter Update field
+};
+
+} // namespace ns3
+
+#endif /* MGT_ACTION_HEADERS_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/mgt-headers.cc ns-3.41/src/wifi/model/mgt-headers.cc
--- ns-3.40/src/wifi/model/mgt-headers.cc	2023-09-27 13:30:37.906839800 -0700
+++ ns-3.41/src/wifi/model/mgt-headers.cc	2024-02-09 14:44:08.589794900 -0800
@@ -22,7 +22,6 @@
 #include "mgt-headers.h"
 
 #include "ns3/address-utils.h"
-#include "ns3/packet.h"
 #include "ns3/simulator.h"
 
 namespace ns3
@@ -586,1303 +585,4 @@
                           DeserializeFromPerStaProfileImpl(i, length - distance, frame);
 }
 
-/**********************************************************
- *   ActionFrame
- **********************************************************/
-WifiActionHeader::WifiActionHeader()
-{
-}
-
-WifiActionHeader::~WifiActionHeader()
-{
-}
-
-void
-WifiActionHeader::SetAction(WifiActionHeader::CategoryValue type,
-                            WifiActionHeader::ActionValue action)
-{
-    m_category = static_cast<uint8_t>(type);
-    switch (type)
-    {
-    case QOS: {
-        m_actionValue = static_cast<uint8_t>(action.qos);
-        break;
-    }
-    case BLOCK_ACK: {
-        m_actionValue = static_cast<uint8_t>(action.blockAck);
-        break;
-    }
-    case PUBLIC: {
-        m_actionValue = static_cast<uint8_t>(action.publicAction);
-        break;
-    }
-    case RADIO_MEASUREMENT: {
-        m_actionValue = static_cast<uint8_t>(action.radioMeasurementAction);
-        break;
-    }
-    case MESH: {
-        m_actionValue = static_cast<uint8_t>(action.meshAction);
-        break;
-    }
-    case MULTIHOP: {
-        m_actionValue = static_cast<uint8_t>(action.multihopAction);
-        break;
-    }
-    case SELF_PROTECTED: {
-        m_actionValue = static_cast<uint8_t>(action.selfProtectedAction);
-        break;
-    }
-    case DMG: {
-        m_actionValue = static_cast<uint8_t>(action.dmgAction);
-        break;
-    }
-    case FST: {
-        m_actionValue = static_cast<uint8_t>(action.fstAction);
-        break;
-    }
-    case UNPROTECTED_DMG: {
-        m_actionValue = static_cast<uint8_t>(action.unprotectedDmgAction);
-        break;
-    }
-    case PROTECTED_EHT: {
-        m_actionValue = static_cast<uint8_t>(action.protectedEhtAction);
-        break;
-    }
-    case VENDOR_SPECIFIC_ACTION: {
-        break;
-    }
-    }
-}
-
-WifiActionHeader::CategoryValue
-WifiActionHeader::GetCategory() const
-{
-    switch (m_category)
-    {
-    case QOS:
-        return QOS;
-    case BLOCK_ACK:
-        return BLOCK_ACK;
-    case PUBLIC:
-        return PUBLIC;
-    case RADIO_MEASUREMENT:
-        return RADIO_MEASUREMENT;
-    case MESH:
-        return MESH;
-    case MULTIHOP:
-        return MULTIHOP;
-    case SELF_PROTECTED:
-        return SELF_PROTECTED;
-    case DMG:
-        return DMG;
-    case FST:
-        return FST;
-    case UNPROTECTED_DMG:
-        return UNPROTECTED_DMG;
-    case PROTECTED_EHT:
-        return PROTECTED_EHT;
-    case VENDOR_SPECIFIC_ACTION:
-        return VENDOR_SPECIFIC_ACTION;
-    default:
-        NS_FATAL_ERROR("Unknown action value");
-        return SELF_PROTECTED;
-    }
-}
-
-WifiActionHeader::ActionValue
-WifiActionHeader::GetAction() const
-{
-    ActionValue retval;
-    retval.selfProtectedAction =
-        PEER_LINK_OPEN; // Needs to be initialized to something to quiet valgrind in default cases
-    switch (m_category)
-    {
-    case QOS:
-        switch (m_actionValue)
-        {
-        case ADDTS_REQUEST:
-            retval.qos = ADDTS_REQUEST;
-            break;
-        case ADDTS_RESPONSE:
-            retval.qos = ADDTS_RESPONSE;
-            break;
-        case DELTS:
-            retval.qos = DELTS;
-            break;
-        case SCHEDULE:
-            retval.qos = SCHEDULE;
-            break;
-        case QOS_MAP_CONFIGURE:
-            retval.qos = QOS_MAP_CONFIGURE;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown qos action code");
-            retval.qos = ADDTS_REQUEST; /* quiet compiler */
-        }
-        break;
-
-    case BLOCK_ACK:
-        switch (m_actionValue)
-        {
-        case BLOCK_ACK_ADDBA_REQUEST:
-            retval.blockAck = BLOCK_ACK_ADDBA_REQUEST;
-            break;
-        case BLOCK_ACK_ADDBA_RESPONSE:
-            retval.blockAck = BLOCK_ACK_ADDBA_RESPONSE;
-            break;
-        case BLOCK_ACK_DELBA:
-            retval.blockAck = BLOCK_ACK_DELBA;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown block ack action code");
-            retval.blockAck = BLOCK_ACK_ADDBA_REQUEST; /* quiet compiler */
-        }
-        break;
-
-    case PUBLIC:
-        switch (m_actionValue)
-        {
-        case QAB_REQUEST:
-            retval.publicAction = QAB_REQUEST;
-            break;
-        case QAB_RESPONSE:
-            retval.publicAction = QAB_RESPONSE;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown public action code");
-            retval.publicAction = QAB_REQUEST; /* quiet compiler */
-        }
-        break;
-
-    case RADIO_MEASUREMENT:
-        switch (m_actionValue)
-        {
-        case RADIO_MEASUREMENT_REQUEST:
-            retval.radioMeasurementAction = RADIO_MEASUREMENT_REQUEST;
-            break;
-        case RADIO_MEASUREMENT_REPORT:
-            retval.radioMeasurementAction = RADIO_MEASUREMENT_REPORT;
-            break;
-        case LINK_MEASUREMENT_REQUEST:
-            retval.radioMeasurementAction = LINK_MEASUREMENT_REQUEST;
-            break;
-        case LINK_MEASUREMENT_REPORT:
-            retval.radioMeasurementAction = LINK_MEASUREMENT_REPORT;
-            break;
-        case NEIGHBOR_REPORT_REQUEST:
-            retval.radioMeasurementAction = NEIGHBOR_REPORT_REQUEST;
-            break;
-        case NEIGHBOR_REPORT_RESPONSE:
-            retval.radioMeasurementAction = NEIGHBOR_REPORT_RESPONSE;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown radio measurement action code");
-            retval.radioMeasurementAction = RADIO_MEASUREMENT_REQUEST; /* quiet compiler */
-        }
-        break;
-
-    case SELF_PROTECTED:
-        switch (m_actionValue)
-        {
-        case PEER_LINK_OPEN:
-            retval.selfProtectedAction = PEER_LINK_OPEN;
-            break;
-        case PEER_LINK_CONFIRM:
-            retval.selfProtectedAction = PEER_LINK_CONFIRM;
-            break;
-        case PEER_LINK_CLOSE:
-            retval.selfProtectedAction = PEER_LINK_CLOSE;
-            break;
-        case GROUP_KEY_INFORM:
-            retval.selfProtectedAction = GROUP_KEY_INFORM;
-            break;
-        case GROUP_KEY_ACK:
-            retval.selfProtectedAction = GROUP_KEY_ACK;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown mesh peering management action code");
-            retval.selfProtectedAction = PEER_LINK_OPEN; /* quiet compiler */
-        }
-        break;
-
-    case MESH:
-        switch (m_actionValue)
-        {
-        case LINK_METRIC_REPORT:
-            retval.meshAction = LINK_METRIC_REPORT;
-            break;
-        case PATH_SELECTION:
-            retval.meshAction = PATH_SELECTION;
-            break;
-        case PORTAL_ANNOUNCEMENT:
-            retval.meshAction = PORTAL_ANNOUNCEMENT;
-            break;
-        case CONGESTION_CONTROL_NOTIFICATION:
-            retval.meshAction = CONGESTION_CONTROL_NOTIFICATION;
-            break;
-        case MDA_SETUP_REQUEST:
-            retval.meshAction = MDA_SETUP_REQUEST;
-            break;
-        case MDA_SETUP_REPLY:
-            retval.meshAction = MDA_SETUP_REPLY;
-            break;
-        case MDAOP_ADVERTISEMENT_REQUEST:
-            retval.meshAction = MDAOP_ADVERTISEMENT_REQUEST;
-            break;
-        case MDAOP_ADVERTISEMENTS:
-            retval.meshAction = MDAOP_ADVERTISEMENTS;
-            break;
-        case MDAOP_SET_TEARDOWN:
-            retval.meshAction = MDAOP_SET_TEARDOWN;
-            break;
-        case TBTT_ADJUSTMENT_REQUEST:
-            retval.meshAction = TBTT_ADJUSTMENT_REQUEST;
-            break;
-        case TBTT_ADJUSTMENT_RESPONSE:
-            retval.meshAction = TBTT_ADJUSTMENT_RESPONSE;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown mesh peering management action code");
-            retval.meshAction = LINK_METRIC_REPORT; /* quiet compiler */
-        }
-        break;
-
-    case MULTIHOP: // not yet supported
-        switch (m_actionValue)
-        {
-        case PROXY_UPDATE: // not used so far
-            retval.multihopAction = PROXY_UPDATE;
-            break;
-        case PROXY_UPDATE_CONFIRMATION: // not used so far
-            retval.multihopAction = PROXY_UPDATE;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown mesh peering management action code");
-            retval.multihopAction = PROXY_UPDATE; /* quiet compiler */
-        }
-        break;
-
-    case DMG:
-        switch (m_actionValue)
-        {
-        case DMG_POWER_SAVE_CONFIGURATION_REQUEST:
-            retval.dmgAction = DMG_POWER_SAVE_CONFIGURATION_REQUEST;
-            break;
-        case DMG_POWER_SAVE_CONFIGURATION_RESPONSE:
-            retval.dmgAction = DMG_POWER_SAVE_CONFIGURATION_RESPONSE;
-            break;
-        case DMG_INFORMATION_REQUEST:
-            retval.dmgAction = DMG_INFORMATION_REQUEST;
-            break;
-        case DMG_INFORMATION_RESPONSE:
-            retval.dmgAction = DMG_INFORMATION_RESPONSE;
-            break;
-        case DMG_HANDOVER_REQUEST:
-            retval.dmgAction = DMG_HANDOVER_REQUEST;
-            break;
-        case DMG_HANDOVER_RESPONSE:
-            retval.dmgAction = DMG_HANDOVER_RESPONSE;
-            break;
-        case DMG_DTP_REQUEST:
-            retval.dmgAction = DMG_DTP_REQUEST;
-            break;
-        case DMG_DTP_RESPONSE:
-            retval.dmgAction = DMG_DTP_RESPONSE;
-            break;
-        case DMG_RELAY_SEARCH_REQUEST:
-            retval.dmgAction = DMG_RELAY_SEARCH_REQUEST;
-            break;
-        case DMG_RELAY_SEARCH_RESPONSE:
-            retval.dmgAction = DMG_RELAY_SEARCH_RESPONSE;
-            break;
-        case DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST:
-            retval.dmgAction = DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST;
-            break;
-        case DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT:
-            retval.dmgAction = DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT;
-            break;
-        case DMG_RLS_REQUEST:
-            retval.dmgAction = DMG_RLS_REQUEST;
-            break;
-        case DMG_RLS_RESPONSE:
-            retval.dmgAction = DMG_RLS_RESPONSE;
-            break;
-        case DMG_RLS_ANNOUNCEMENT:
-            retval.dmgAction = DMG_RLS_ANNOUNCEMENT;
-            break;
-        case DMG_RLS_TEARDOWN:
-            retval.dmgAction = DMG_RLS_TEARDOWN;
-            break;
-        case DMG_RELAY_ACK_REQUEST:
-            retval.dmgAction = DMG_RELAY_ACK_REQUEST;
-            break;
-        case DMG_RELAY_ACK_RESPONSE:
-            retval.dmgAction = DMG_RELAY_ACK_RESPONSE;
-            break;
-        case DMG_TPA_REQUEST:
-            retval.dmgAction = DMG_TPA_REQUEST;
-            break;
-        case DMG_TPA_RESPONSE:
-            retval.dmgAction = DMG_TPA_RESPONSE;
-            break;
-        case DMG_ROC_REQUEST:
-            retval.dmgAction = DMG_ROC_REQUEST;
-            break;
-        case DMG_ROC_RESPONSE:
-            retval.dmgAction = DMG_ROC_RESPONSE;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown DMG management action code");
-            retval.dmgAction = DMG_POWER_SAVE_CONFIGURATION_REQUEST; /* quiet compiler */
-        }
-        break;
-
-    case FST:
-        switch (m_actionValue)
-        {
-        case FST_SETUP_REQUEST:
-            retval.fstAction = FST_SETUP_REQUEST;
-            break;
-        case FST_SETUP_RESPONSE:
-            retval.fstAction = FST_SETUP_RESPONSE;
-            break;
-        case FST_TEAR_DOWN:
-            retval.fstAction = FST_TEAR_DOWN;
-            break;
-        case FST_ACK_REQUEST:
-            retval.fstAction = FST_ACK_REQUEST;
-            break;
-        case FST_ACK_RESPONSE:
-            retval.fstAction = FST_ACK_RESPONSE;
-            break;
-        case ON_CHANNEL_TUNNEL_REQUEST:
-            retval.fstAction = ON_CHANNEL_TUNNEL_REQUEST;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown FST management action code");
-            retval.fstAction = FST_SETUP_REQUEST; /* quiet compiler */
-        }
-        break;
-
-    case UNPROTECTED_DMG:
-        switch (m_actionValue)
-        {
-        case UNPROTECTED_DMG_ANNOUNCE:
-            retval.unprotectedDmgAction = UNPROTECTED_DMG_ANNOUNCE;
-            break;
-        case UNPROTECTED_DMG_BRP:
-            retval.unprotectedDmgAction = UNPROTECTED_DMG_BRP;
-            break;
-        case UNPROTECTED_MIMO_BF_SETUP:
-            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_SETUP;
-            break;
-        case UNPROTECTED_MIMO_BF_POLL:
-            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_POLL;
-            break;
-        case UNPROTECTED_MIMO_BF_FEEDBACK:
-            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_FEEDBACK;
-            break;
-        case UNPROTECTED_MIMO_BF_SELECTION:
-            retval.unprotectedDmgAction = UNPROTECTED_MIMO_BF_SELECTION;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown Unprotected DMG action code");
-            retval.unprotectedDmgAction = UNPROTECTED_DMG_ANNOUNCE; /* quiet compiler */
-        }
-        break;
-
-    case PROTECTED_EHT:
-        switch (m_actionValue)
-        {
-        case PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST:
-            retval.protectedEhtAction = PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST;
-            break;
-        case PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE:
-            retval.protectedEhtAction = PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE;
-            break;
-        case PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN:
-            retval.protectedEhtAction = PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN;
-            break;
-        case PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST:
-            retval.protectedEhtAction = PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST;
-            break;
-        case PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE:
-            retval.protectedEhtAction = PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE;
-            break;
-        case PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN:
-            retval.protectedEhtAction = PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN;
-            break;
-        case PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION:
-            retval.protectedEhtAction = PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION;
-            break;
-        case PROTECTED_EHT_LINK_RECOMMENDATION:
-            retval.protectedEhtAction = PROTECTED_EHT_LINK_RECOMMENDATION;
-            break;
-        case PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST:
-            retval.protectedEhtAction = PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST;
-            break;
-        case PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE:
-            retval.protectedEhtAction = PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE;
-            break;
-        default:
-            NS_FATAL_ERROR("Unknown Protected EHT action code");
-            retval.protectedEhtAction =
-                PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST; /* quiet compiler */
-        }
-        break;
-
-    default:
-        NS_FATAL_ERROR("Unsupported action");
-        retval.selfProtectedAction = PEER_LINK_OPEN; /* quiet compiler */
-    }
-    return retval;
-}
-
-TypeId
-WifiActionHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::WifiActionHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<WifiActionHeader>();
-    return tid;
-}
-
-TypeId
-WifiActionHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-std::pair<WifiActionHeader::CategoryValue, WifiActionHeader::ActionValue>
-WifiActionHeader::Peek(Ptr<const Packet> pkt)
-{
-    WifiActionHeader actionHdr;
-    pkt->PeekHeader(actionHdr);
-    return {actionHdr.GetCategory(), actionHdr.GetAction()};
-}
-
-std::pair<WifiActionHeader::CategoryValue, WifiActionHeader::ActionValue>
-WifiActionHeader::Remove(Ptr<Packet> pkt)
-{
-    WifiActionHeader actionHdr;
-    pkt->RemoveHeader(actionHdr);
-    return {actionHdr.GetCategory(), actionHdr.GetAction()};
-}
-
-void
-WifiActionHeader::Print(std::ostream& os) const
-{
-#define CASE_ACTION_VALUE(x)                                                                       \
-    case x:                                                                                        \
-        os << #x << "]";                                                                           \
-        break;
-
-    switch (m_category)
-    {
-    case QOS:
-        os << "QOS[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(ADDTS_REQUEST);
-            CASE_ACTION_VALUE(ADDTS_RESPONSE);
-            CASE_ACTION_VALUE(DELTS);
-            CASE_ACTION_VALUE(SCHEDULE);
-            CASE_ACTION_VALUE(QOS_MAP_CONFIGURE);
-        default:
-            NS_FATAL_ERROR("Unknown qos action code");
-        }
-        break;
-    case BLOCK_ACK:
-        os << "BLOCK_ACK[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(BLOCK_ACK_ADDBA_REQUEST);
-            CASE_ACTION_VALUE(BLOCK_ACK_ADDBA_RESPONSE);
-            CASE_ACTION_VALUE(BLOCK_ACK_DELBA);
-        default:
-            NS_FATAL_ERROR("Unknown block ack action code");
-        }
-        break;
-    case PUBLIC:
-        os << "PUBLIC[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(QAB_REQUEST);
-            CASE_ACTION_VALUE(QAB_RESPONSE);
-        default:
-            NS_FATAL_ERROR("Unknown public action code");
-        }
-        break;
-    case RADIO_MEASUREMENT:
-        os << "RADIO_MEASUREMENT[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(RADIO_MEASUREMENT_REQUEST);
-            CASE_ACTION_VALUE(RADIO_MEASUREMENT_REPORT);
-            CASE_ACTION_VALUE(LINK_MEASUREMENT_REQUEST);
-            CASE_ACTION_VALUE(LINK_MEASUREMENT_REPORT);
-            CASE_ACTION_VALUE(NEIGHBOR_REPORT_REQUEST);
-            CASE_ACTION_VALUE(NEIGHBOR_REPORT_RESPONSE);
-        default:
-            NS_FATAL_ERROR("Unknown radio measurement action code");
-        }
-        break;
-    case MESH:
-        os << "MESH[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(LINK_METRIC_REPORT);
-            CASE_ACTION_VALUE(PATH_SELECTION);
-            CASE_ACTION_VALUE(PORTAL_ANNOUNCEMENT);
-            CASE_ACTION_VALUE(CONGESTION_CONTROL_NOTIFICATION);
-            CASE_ACTION_VALUE(MDA_SETUP_REQUEST);
-            CASE_ACTION_VALUE(MDA_SETUP_REPLY);
-            CASE_ACTION_VALUE(MDAOP_ADVERTISEMENT_REQUEST);
-            CASE_ACTION_VALUE(MDAOP_ADVERTISEMENTS);
-            CASE_ACTION_VALUE(MDAOP_SET_TEARDOWN);
-            CASE_ACTION_VALUE(TBTT_ADJUSTMENT_REQUEST);
-            CASE_ACTION_VALUE(TBTT_ADJUSTMENT_RESPONSE);
-        default:
-            NS_FATAL_ERROR("Unknown mesh peering management action code");
-        }
-        break;
-    case MULTIHOP:
-        os << "MULTIHOP[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(PROXY_UPDATE);              // not used so far
-            CASE_ACTION_VALUE(PROXY_UPDATE_CONFIRMATION); // not used so far
-        default:
-            NS_FATAL_ERROR("Unknown mesh peering management action code");
-        }
-        break;
-    case SELF_PROTECTED:
-        os << "SELF_PROTECTED[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(PEER_LINK_OPEN);
-            CASE_ACTION_VALUE(PEER_LINK_CONFIRM);
-            CASE_ACTION_VALUE(PEER_LINK_CLOSE);
-            CASE_ACTION_VALUE(GROUP_KEY_INFORM);
-            CASE_ACTION_VALUE(GROUP_KEY_ACK);
-        default:
-            NS_FATAL_ERROR("Unknown mesh peering management action code");
-        }
-        break;
-    case DMG:
-        os << "DMG[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(DMG_POWER_SAVE_CONFIGURATION_REQUEST);
-            CASE_ACTION_VALUE(DMG_POWER_SAVE_CONFIGURATION_RESPONSE);
-            CASE_ACTION_VALUE(DMG_INFORMATION_REQUEST);
-            CASE_ACTION_VALUE(DMG_INFORMATION_RESPONSE);
-            CASE_ACTION_VALUE(DMG_HANDOVER_REQUEST);
-            CASE_ACTION_VALUE(DMG_HANDOVER_RESPONSE);
-            CASE_ACTION_VALUE(DMG_DTP_REQUEST);
-            CASE_ACTION_VALUE(DMG_DTP_RESPONSE);
-            CASE_ACTION_VALUE(DMG_RELAY_SEARCH_REQUEST);
-            CASE_ACTION_VALUE(DMG_RELAY_SEARCH_RESPONSE);
-            CASE_ACTION_VALUE(DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST);
-            CASE_ACTION_VALUE(DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT);
-            CASE_ACTION_VALUE(DMG_RLS_REQUEST);
-            CASE_ACTION_VALUE(DMG_RLS_RESPONSE);
-            CASE_ACTION_VALUE(DMG_RLS_ANNOUNCEMENT);
-            CASE_ACTION_VALUE(DMG_RLS_TEARDOWN);
-            CASE_ACTION_VALUE(DMG_RELAY_ACK_REQUEST);
-            CASE_ACTION_VALUE(DMG_RELAY_ACK_RESPONSE);
-            CASE_ACTION_VALUE(DMG_TPA_REQUEST);
-            CASE_ACTION_VALUE(DMG_TPA_RESPONSE);
-            CASE_ACTION_VALUE(DMG_ROC_REQUEST);
-            CASE_ACTION_VALUE(DMG_ROC_RESPONSE);
-        default:
-            NS_FATAL_ERROR("Unknown DMG management action code");
-        }
-        break;
-    case FST:
-        os << "FST[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(FST_SETUP_REQUEST);
-            CASE_ACTION_VALUE(FST_SETUP_RESPONSE);
-            CASE_ACTION_VALUE(FST_TEAR_DOWN);
-            CASE_ACTION_VALUE(FST_ACK_REQUEST);
-            CASE_ACTION_VALUE(FST_ACK_RESPONSE);
-            CASE_ACTION_VALUE(ON_CHANNEL_TUNNEL_REQUEST);
-        default:
-            NS_FATAL_ERROR("Unknown FST management action code");
-        }
-        break;
-    case UNPROTECTED_DMG:
-        os << "UNPROTECTED_DMG[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(UNPROTECTED_DMG_ANNOUNCE);
-            CASE_ACTION_VALUE(UNPROTECTED_DMG_BRP);
-            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_SETUP);
-            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_POLL);
-            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_FEEDBACK);
-            CASE_ACTION_VALUE(UNPROTECTED_MIMO_BF_SELECTION);
-        default:
-            NS_FATAL_ERROR("Unknown Unprotected DMG action code");
-        }
-        break;
-    case PROTECTED_EHT:
-        os << "PROTECTED_EHT[";
-        switch (m_actionValue)
-        {
-            CASE_ACTION_VALUE(PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST);
-            CASE_ACTION_VALUE(PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE);
-            CASE_ACTION_VALUE(PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN);
-            CASE_ACTION_VALUE(PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST);
-            CASE_ACTION_VALUE(PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE);
-            CASE_ACTION_VALUE(PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN);
-            CASE_ACTION_VALUE(PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION);
-            CASE_ACTION_VALUE(PROTECTED_EHT_LINK_RECOMMENDATION);
-            CASE_ACTION_VALUE(PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST);
-            CASE_ACTION_VALUE(PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE);
-        default:
-            NS_FATAL_ERROR("Unknown Protected EHT action code");
-        }
-        break;
-    case VENDOR_SPECIFIC_ACTION:
-        os << "VENDOR_SPECIFIC_ACTION";
-        break;
-    default:
-        NS_FATAL_ERROR("Unknown action value");
-    }
-#undef CASE_ACTION_VALUE
-}
-
-uint32_t
-WifiActionHeader::GetSerializedSize() const
-{
-    return 2;
-}
-
-void
-WifiActionHeader::Serialize(Buffer::Iterator start) const
-{
-    start.WriteU8(m_category);
-    start.WriteU8(m_actionValue);
-}
-
-uint32_t
-WifiActionHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    m_category = i.ReadU8();
-    m_actionValue = i.ReadU8();
-    return i.GetDistanceFrom(start);
-}
-
-/***************************************************
- *                 ADDBARequest
- ****************************************************/
-
-NS_OBJECT_ENSURE_REGISTERED(MgtAddBaRequestHeader);
-
-MgtAddBaRequestHeader::MgtAddBaRequestHeader()
-    : m_dialogToken(1),
-      m_amsduSupport(1),
-      m_bufferSize(0)
-{
-}
-
-TypeId
-MgtAddBaRequestHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::MgtAddBaRequestHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtAddBaRequestHeader>();
-    return tid;
-}
-
-TypeId
-MgtAddBaRequestHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-MgtAddBaRequestHeader::Print(std::ostream& os) const
-{
-}
-
-uint32_t
-MgtAddBaRequestHeader::GetSerializedSize() const
-{
-    uint32_t size = 0;
-    size += 1; // Dialog token
-    size += 2; // Block ack parameter set
-    size += 2; // Block ack timeout value
-    size += 2; // Starting sequence control
-    return size;
-}
-
-void
-MgtAddBaRequestHeader::Serialize(Buffer::Iterator start) const
-{
-    Buffer::Iterator i = start;
-    i.WriteU8(m_dialogToken);
-    i.WriteHtolsbU16(GetParameterSet());
-    i.WriteHtolsbU16(m_timeoutValue);
-    i.WriteHtolsbU16(GetStartingSequenceControl());
-}
-
-uint32_t
-MgtAddBaRequestHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    m_dialogToken = i.ReadU8();
-    SetParameterSet(i.ReadLsbtohU16());
-    m_timeoutValue = i.ReadLsbtohU16();
-    SetStartingSequenceControl(i.ReadLsbtohU16());
-    return i.GetDistanceFrom(start);
-}
-
-void
-MgtAddBaRequestHeader::SetDelayedBlockAck()
-{
-    m_policy = 0;
-}
-
-void
-MgtAddBaRequestHeader::SetImmediateBlockAck()
-{
-    m_policy = 1;
-}
-
-void
-MgtAddBaRequestHeader::SetTid(uint8_t tid)
-{
-    NS_ASSERT(tid < 16);
-    m_tid = tid;
-}
-
-void
-MgtAddBaRequestHeader::SetTimeout(uint16_t timeout)
-{
-    m_timeoutValue = timeout;
-}
-
-void
-MgtAddBaRequestHeader::SetBufferSize(uint16_t size)
-{
-    m_bufferSize = size;
-}
-
-void
-MgtAddBaRequestHeader::SetStartingSequence(uint16_t seq)
-{
-    m_startingSeq = seq;
-}
-
-void
-MgtAddBaRequestHeader::SetStartingSequenceControl(uint16_t seqControl)
-{
-    m_startingSeq = (seqControl >> 4) & 0x0fff;
-}
-
-void
-MgtAddBaRequestHeader::SetAmsduSupport(bool supported)
-{
-    m_amsduSupport = supported;
-}
-
-uint8_t
-MgtAddBaRequestHeader::GetTid() const
-{
-    return m_tid;
-}
-
-bool
-MgtAddBaRequestHeader::IsImmediateBlockAck() const
-{
-    return m_policy == 1;
-}
-
-uint16_t
-MgtAddBaRequestHeader::GetTimeout() const
-{
-    return m_timeoutValue;
-}
-
-uint16_t
-MgtAddBaRequestHeader::GetBufferSize() const
-{
-    return m_bufferSize;
-}
-
-bool
-MgtAddBaRequestHeader::IsAmsduSupported() const
-{
-    return m_amsduSupport == 1;
-}
-
-uint16_t
-MgtAddBaRequestHeader::GetStartingSequence() const
-{
-    return m_startingSeq;
-}
-
-uint16_t
-MgtAddBaRequestHeader::GetStartingSequenceControl() const
-{
-    return (m_startingSeq << 4) & 0xfff0;
-}
-
-uint16_t
-MgtAddBaRequestHeader::GetParameterSet() const
-{
-    uint16_t res = 0;
-    res |= m_amsduSupport;
-    res |= m_policy << 1;
-    res |= m_tid << 2;
-    res |= m_bufferSize << 6;
-    return res;
-}
-
-void
-MgtAddBaRequestHeader::SetParameterSet(uint16_t params)
-{
-    m_amsduSupport = (params)&0x01;
-    m_policy = (params >> 1) & 0x01;
-    m_tid = (params >> 2) & 0x0f;
-    m_bufferSize = (params >> 6) & 0x03ff;
-}
-
-/***************************************************
- *                 ADDBAResponse
- ****************************************************/
-
-NS_OBJECT_ENSURE_REGISTERED(MgtAddBaResponseHeader);
-
-MgtAddBaResponseHeader::MgtAddBaResponseHeader()
-    : m_dialogToken(1),
-      m_amsduSupport(1),
-      m_bufferSize(0)
-{
-}
-
-TypeId
-MgtAddBaResponseHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::MgtAddBaResponseHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtAddBaResponseHeader>();
-    return tid;
-}
-
-TypeId
-MgtAddBaResponseHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-MgtAddBaResponseHeader::Print(std::ostream& os) const
-{
-    os << "status code=" << m_code;
-}
-
-uint32_t
-MgtAddBaResponseHeader::GetSerializedSize() const
-{
-    uint32_t size = 0;
-    size += 1;                          // Dialog token
-    size += m_code.GetSerializedSize(); // Status code
-    size += 2;                          // Block ack parameter set
-    size += 2;                          // Block ack timeout value
-    return size;
-}
-
-void
-MgtAddBaResponseHeader::Serialize(Buffer::Iterator start) const
-{
-    Buffer::Iterator i = start;
-    i.WriteU8(m_dialogToken);
-    i = m_code.Serialize(i);
-    i.WriteHtolsbU16(GetParameterSet());
-    i.WriteHtolsbU16(m_timeoutValue);
-}
-
-uint32_t
-MgtAddBaResponseHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    m_dialogToken = i.ReadU8();
-    i = m_code.Deserialize(i);
-    SetParameterSet(i.ReadLsbtohU16());
-    m_timeoutValue = i.ReadLsbtohU16();
-    return i.GetDistanceFrom(start);
-}
-
-void
-MgtAddBaResponseHeader::SetDelayedBlockAck()
-{
-    m_policy = 0;
-}
-
-void
-MgtAddBaResponseHeader::SetImmediateBlockAck()
-{
-    m_policy = 1;
-}
-
-void
-MgtAddBaResponseHeader::SetTid(uint8_t tid)
-{
-    NS_ASSERT(tid < 16);
-    m_tid = tid;
-}
-
-void
-MgtAddBaResponseHeader::SetTimeout(uint16_t timeout)
-{
-    m_timeoutValue = timeout;
-}
-
-void
-MgtAddBaResponseHeader::SetBufferSize(uint16_t size)
-{
-    m_bufferSize = size;
-}
-
-void
-MgtAddBaResponseHeader::SetStatusCode(StatusCode code)
-{
-    m_code = code;
-}
-
-void
-MgtAddBaResponseHeader::SetAmsduSupport(bool supported)
-{
-    m_amsduSupport = supported;
-}
-
-StatusCode
-MgtAddBaResponseHeader::GetStatusCode() const
-{
-    return m_code;
-}
-
-uint8_t
-MgtAddBaResponseHeader::GetTid() const
-{
-    return m_tid;
-}
-
-bool
-MgtAddBaResponseHeader::IsImmediateBlockAck() const
-{
-    return m_policy == 1;
-}
-
-uint16_t
-MgtAddBaResponseHeader::GetTimeout() const
-{
-    return m_timeoutValue;
-}
-
-uint16_t
-MgtAddBaResponseHeader::GetBufferSize() const
-{
-    return m_bufferSize;
-}
-
-bool
-MgtAddBaResponseHeader::IsAmsduSupported() const
-{
-    return m_amsduSupport == 1;
-}
-
-uint16_t
-MgtAddBaResponseHeader::GetParameterSet() const
-{
-    uint16_t res = 0;
-    res |= m_amsduSupport;
-    res |= m_policy << 1;
-    res |= m_tid << 2;
-    res |= m_bufferSize << 6;
-    return res;
-}
-
-void
-MgtAddBaResponseHeader::SetParameterSet(uint16_t params)
-{
-    m_amsduSupport = (params)&0x01;
-    m_policy = (params >> 1) & 0x01;
-    m_tid = (params >> 2) & 0x0f;
-    m_bufferSize = (params >> 6) & 0x03ff;
-}
-
-/***************************************************
- *                     DelBa
- ****************************************************/
-
-NS_OBJECT_ENSURE_REGISTERED(MgtDelBaHeader);
-
-MgtDelBaHeader::MgtDelBaHeader()
-    : m_reasonCode(1)
-{
-}
-
-TypeId
-MgtDelBaHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::MgtDelBaHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtDelBaHeader>();
-    return tid;
-}
-
-TypeId
-MgtDelBaHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-MgtDelBaHeader::Print(std::ostream& os) const
-{
-}
-
-uint32_t
-MgtDelBaHeader::GetSerializedSize() const
-{
-    uint32_t size = 0;
-    size += 2; // DelBa parameter set
-    size += 2; // Reason code
-    return size;
-}
-
-void
-MgtDelBaHeader::Serialize(Buffer::Iterator start) const
-{
-    Buffer::Iterator i = start;
-    i.WriteHtolsbU16(GetParameterSet());
-    i.WriteHtolsbU16(m_reasonCode);
-}
-
-uint32_t
-MgtDelBaHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    SetParameterSet(i.ReadLsbtohU16());
-    m_reasonCode = i.ReadLsbtohU16();
-    return i.GetDistanceFrom(start);
-}
-
-bool
-MgtDelBaHeader::IsByOriginator() const
-{
-    return m_initiator == 1;
-}
-
-uint8_t
-MgtDelBaHeader::GetTid() const
-{
-    NS_ASSERT(m_tid < 16);
-    auto tid = static_cast<uint8_t>(m_tid);
-    return tid;
-}
-
-void
-MgtDelBaHeader::SetByOriginator()
-{
-    m_initiator = 1;
-}
-
-void
-MgtDelBaHeader::SetByRecipient()
-{
-    m_initiator = 0;
-}
-
-void
-MgtDelBaHeader::SetTid(uint8_t tid)
-{
-    NS_ASSERT(tid < 16);
-    m_tid = static_cast<uint16_t>(tid);
-}
-
-uint16_t
-MgtDelBaHeader::GetParameterSet() const
-{
-    uint16_t res = 0;
-    res |= m_initiator << 11;
-    res |= m_tid << 12;
-    return res;
-}
-
-void
-MgtDelBaHeader::SetParameterSet(uint16_t params)
-{
-    m_initiator = (params >> 11) & 0x01;
-    m_tid = (params >> 12) & 0x0f;
-}
-
-/***************************************************
- *     EMLSR Operating Mode Notification
- ****************************************************/
-
-NS_OBJECT_ENSURE_REGISTERED(MgtEmlOmn);
-
-TypeId
-MgtEmlOmn::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::MgtEmlOperatingModeNotification")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtEmlOmn>();
-    return tid;
-}
-
-TypeId
-MgtEmlOmn::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-MgtEmlOmn::Print(std::ostream& os) const
-{
-    os << "EMLSR Mode=" << +m_emlControl.emlsrMode << " EMLMR Mode=" << +m_emlControl.emlmrMode
-       << " EMLSR Parameter Update Control=" << +m_emlControl.emlsrParamUpdateCtrl;
-    if (m_emlControl.linkBitmap)
-    {
-        os << " Link bitmap=" << std::hex << *m_emlControl.linkBitmap << std::dec;
-    }
-    if (m_emlsrParamUpdate)
-    {
-        os << " EMLSR Padding Delay="
-           << CommonInfoBasicMle::DecodeEmlsrPaddingDelay(m_emlsrParamUpdate->paddingDelay)
-                  .As(Time::US)
-           << " EMLSR Transition Delay="
-           << CommonInfoBasicMle::DecodeEmlsrTransitionDelay(m_emlsrParamUpdate->transitionDelay)
-                  .As(Time::US);
-    }
-}
-
-uint32_t
-MgtEmlOmn::GetSerializedSize() const
-{
-    uint32_t size = 2; // Dialog Token (1) + first byte of EML Control
-    if (m_emlControl.linkBitmap)
-    {
-        size += 2;
-    }
-    if (m_emlControl.mcsMapCountCtrl)
-    {
-        size += 1;
-    }
-    // TODO add size of EMLMR Supported MCS And NSS Set subfield when implemented
-    if (m_emlsrParamUpdate)
-    {
-        size += 1; // EMLSR Parameter Update field
-    }
-    return size;
-}
-
-void
-MgtEmlOmn::Serialize(Buffer::Iterator start) const
-{
-    start.WriteU8(m_dialogToken);
-
-    NS_ABORT_MSG_IF(m_emlControl.emlsrMode == 1 && m_emlControl.emlmrMode == 1,
-                    "EMLSR Mode and EMLMR Mode cannot be both set to 1");
-    uint8_t val = m_emlControl.emlsrMode | (m_emlControl.emlmrMode << 1) |
-                  (m_emlControl.emlsrParamUpdateCtrl << 2);
-    start.WriteU8(val);
-
-    NS_ABORT_MSG_IF(m_emlControl.linkBitmap.has_value() !=
-                        (m_emlControl.emlsrMode == 1 || m_emlControl.emlmrMode == 1),
-                    "The EMLSR/EMLMR Link Bitmap is present if and only if either of the EMLSR "
-                    "Mode and EMLMR Mode subfields are set to 1");
-    if (m_emlControl.linkBitmap)
-    {
-        start.WriteHtolsbU16(*m_emlControl.linkBitmap);
-    }
-    // TODO serialize MCS Map Count Control and EMLMR Supported MCS And NSS Set subfields
-    // when implemented
-
-    NS_ABORT_MSG_IF(m_emlsrParamUpdate.has_value() != (m_emlControl.emlsrParamUpdateCtrl == 1),
-                    "The EMLSR Parameter Update field is present "
-                        << std::boolalpha << m_emlsrParamUpdate.has_value()
-                        << " if and only if the EMLSR "
-                           "Parameter Update Control subfield is set to 1 "
-                        << +m_emlControl.emlsrParamUpdateCtrl);
-    if (m_emlsrParamUpdate)
-    {
-        val = m_emlsrParamUpdate->paddingDelay | (m_emlsrParamUpdate->transitionDelay << 3);
-        start.WriteU8(val);
-    }
-}
-
-uint32_t
-MgtEmlOmn::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-
-    m_dialogToken = i.ReadU8();
-
-    uint8_t val = i.ReadU8();
-    m_emlControl.emlsrMode = val & 0x01;
-    m_emlControl.emlmrMode = (val >> 1) & 0x01;
-    m_emlControl.emlsrParamUpdateCtrl = (val >> 2) & 0x01;
-
-    NS_ABORT_MSG_IF(m_emlControl.emlsrMode == 1 && m_emlControl.emlmrMode == 1,
-                    "EMLSR Mode and EMLMR Mode cannot be both set to 1");
-
-    if (m_emlControl.emlsrMode == 1 || m_emlControl.emlmrMode == 1)
-    {
-        m_emlControl.linkBitmap = i.ReadLsbtohU16();
-    }
-    // TODO deserialize MCS Map Count Control and EMLMR Supported MCS And NSS Set subfields
-    // when implemented
-
-    if (m_emlControl.emlsrParamUpdateCtrl == 1)
-    {
-        val = i.ReadU8();
-        m_emlsrParamUpdate = EmlsrParamUpdate{};
-        m_emlsrParamUpdate->paddingDelay = val & 0x07;
-        m_emlsrParamUpdate->transitionDelay = (val >> 3) & 0x07;
-    }
-
-    return i.GetDistanceFrom(start);
-}
-
-void
-MgtEmlOmn::SetLinkIdInBitmap(uint8_t linkId)
-{
-    NS_ABORT_MSG_IF(linkId > 15, "Link ID must not exceed 15");
-    if (!m_emlControl.linkBitmap)
-    {
-        m_emlControl.linkBitmap = 0;
-    }
-    m_emlControl.linkBitmap = *m_emlControl.linkBitmap | (1 << linkId);
-}
-
-std::list<uint8_t>
-MgtEmlOmn::GetLinkBitmap() const
-{
-    std::list<uint8_t> list;
-    NS_ASSERT_MSG(m_emlControl.linkBitmap.has_value(), "No link bitmap");
-    uint16_t bitmap = *m_emlControl.linkBitmap;
-    for (uint8_t linkId = 0; linkId < 16; linkId++)
-    {
-        if ((bitmap & 0x0001) == 1)
-        {
-            list.push_back(linkId);
-        }
-        bitmap >>= 1;
-    }
-    return list;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/mgt-headers.h ns-3.41/src/wifi/model/mgt-headers.h
--- ns-3.40/src/wifi/model/mgt-headers.h	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/mgt-headers.h	2024-02-09 14:44:08.589794900 -0800
@@ -46,13 +46,9 @@
 #include "ns3/vht-capabilities.h"
 #include "ns3/vht-operation.h"
 
-#include <list>
-
 namespace ns3
 {
 
-class Packet;
-
 /**
  * Indicate which Information Elements cannot be included in a Per-STA Profile subelement of
  * a Basic Multi-Link Element (see Sec. 35.3.3.4 of 802.11be D3.1):
@@ -524,646 +520,6 @@
     static TypeId GetTypeId();
 };
 
-/****************************
- *     Action frames
- *****************************/
-
-/**
- * \ingroup wifi
- *
- * See IEEE 802.11 chapter 7.3.1.11
- * Header format: | category: 1 | action value: 1 |
- *
- */
-class WifiActionHeader : public Header
-{
-  public:
-    WifiActionHeader();
-    ~WifiActionHeader() override;
-
-    /*
-     * Compatible with table 8-38 IEEE 802.11, Part11, (Year 2012)
-     * Category values - see 802.11-2012 Table 8-38
-     */
-
-    /// CategoryValue enumeration
-    enum CategoryValue // table 9-51 of IEEE 802.11-2020
-    {
-        QOS = 1,
-        BLOCK_ACK = 3,
-        PUBLIC = 4,
-        RADIO_MEASUREMENT = 5, // Category: Radio Measurement
-        MESH = 13,             // Category: Mesh
-        MULTIHOP = 14,         // not used so far
-        SELF_PROTECTED = 15,   // Category: Self Protected
-        DMG = 16,              // Category: DMG
-        FST = 18,              // Category: Fast Session Transfer
-        UNPROTECTED_DMG = 20,  // Category: Unprotected DMG
-        PROTECTED_EHT = 37,    // Category: Protected EHT
-        // Since vendor specific action has no stationary Action value,the parse process is not
-        // here. Refer to vendor-specific-action in wave module.
-        VENDOR_SPECIFIC_ACTION = 127,
-    };
-
-    /// QosActionValue enumeration
-    enum QosActionValue
-    {
-        ADDTS_REQUEST = 0,
-        ADDTS_RESPONSE = 1,
-        DELTS = 2,
-        SCHEDULE = 3,
-        QOS_MAP_CONFIGURE = 4,
-    };
-
-    /**
-     * Block Ack Action field values
-     * See 802.11 Table 8-202
-     */
-    enum BlockAckActionValue
-    {
-        BLOCK_ACK_ADDBA_REQUEST = 0,
-        BLOCK_ACK_ADDBA_RESPONSE = 1,
-        BLOCK_ACK_DELBA = 2
-    };
-
-    /// PublicActionValue enumeration
-    enum PublicActionValue
-    {
-        QAB_REQUEST = 16,
-        QAB_RESPONSE = 17,
-    };
-
-    /// RadioMeasurementActionValue enumeration
-    enum RadioMeasurementActionValue
-    {
-        RADIO_MEASUREMENT_REQUEST = 0,
-        RADIO_MEASUREMENT_REPORT = 1,
-        LINK_MEASUREMENT_REQUEST = 2,
-        LINK_MEASUREMENT_REPORT = 3,
-        NEIGHBOR_REPORT_REQUEST = 4,
-        NEIGHBOR_REPORT_RESPONSE = 5
-    };
-
-    /// MeshActionValue enumeration
-    enum MeshActionValue
-    {
-        LINK_METRIC_REPORT = 0,              // Action Value:0 in Category 13: Mesh
-        PATH_SELECTION = 1,                  // Action Value:1 in Category 13: Mesh
-        PORTAL_ANNOUNCEMENT = 2,             // Action Value:2 in Category 13: Mesh
-        CONGESTION_CONTROL_NOTIFICATION = 3, // Action Value:3 in Category 13: Mesh
-        MDA_SETUP_REQUEST =
-            4, // Action Value:4 in Category 13: Mesh MCCA-Setup-Request (not used so far)
-        MDA_SETUP_REPLY =
-            5, // Action Value:5 in Category 13: Mesh MCCA-Setup-Reply (not used so far)
-        MDAOP_ADVERTISEMENT_REQUEST =
-            6, // Action Value:6 in Category 13: Mesh MCCA-Advertisement-Request (not used so far)
-        MDAOP_ADVERTISEMENTS = 7,      // Action Value:7 in Category 13: Mesh (not used so far)
-        MDAOP_SET_TEARDOWN = 8,        // Action Value:8 in Category 13: Mesh (not used so far)
-        TBTT_ADJUSTMENT_REQUEST = 9,   // Action Value:9 in Category 13: Mesh (not used so far)
-        TBTT_ADJUSTMENT_RESPONSE = 10, // Action Value:10 in Category 13: Mesh (not used so far)
-    };
-
-    /// MultihopActionValue enumeration
-    enum MultihopActionValue
-    {
-        PROXY_UPDATE = 0,              // not used so far
-        PROXY_UPDATE_CONFIRMATION = 1, // not used so far
-    };
-
-    /// SelfProtectedActionValue enumeration
-    enum SelfProtectedActionValue // Category: 15 (Self Protected)
-    {
-        PEER_LINK_OPEN = 1,    // Mesh Peering Open
-        PEER_LINK_CONFIRM = 2, // Mesh Peering Confirm
-        PEER_LINK_CLOSE = 3,   // Mesh Peering Close
-        GROUP_KEY_INFORM = 4,  // Mesh Group Key Inform
-        GROUP_KEY_ACK = 5,     // Mesh Group Key Acknowledge
-    };
-
-    /**
-     * DMG Action field values
-     * See 802.11ad Table 8-281b
-     */
-    enum DmgActionValue
-    {
-        DMG_POWER_SAVE_CONFIGURATION_REQUEST = 0,
-        DMG_POWER_SAVE_CONFIGURATION_RESPONSE = 1,
-        DMG_INFORMATION_REQUEST = 2,
-        DMG_INFORMATION_RESPONSE = 3,
-        DMG_HANDOVER_REQUEST = 4,
-        DMG_HANDOVER_RESPONSE = 5,
-        DMG_DTP_REQUEST = 6,
-        DMG_DTP_RESPONSE = 7,
-        DMG_RELAY_SEARCH_REQUEST = 8,
-        DMG_RELAY_SEARCH_RESPONSE = 9,
-        DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REQUEST = 10,
-        DMG_MULTI_RELAY_CHANNEL_MEASUREMENT_REPORT = 11,
-        DMG_RLS_REQUEST = 12,
-        DMG_RLS_RESPONSE = 13,
-        DMG_RLS_ANNOUNCEMENT = 14,
-        DMG_RLS_TEARDOWN = 15,
-        DMG_RELAY_ACK_REQUEST = 16,
-        DMG_RELAY_ACK_RESPONSE = 17,
-        DMG_TPA_REQUEST = 18,
-        DMG_TPA_RESPONSE = 19,
-        DMG_TPA_REPORT = 20,
-        DMG_ROC_REQUEST = 21,
-        DMG_ROC_RESPONSE = 22
-    };
-
-    /**
-     * FST Action field values
-     * See 802.11ad Table 8-281x
-     */
-    enum FstActionValue
-    {
-        FST_SETUP_REQUEST = 0,
-        FST_SETUP_RESPONSE = 1,
-        FST_TEAR_DOWN = 2,
-        FST_ACK_REQUEST = 3,
-        FST_ACK_RESPONSE = 4,
-        ON_CHANNEL_TUNNEL_REQUEST = 5
-    };
-
-    /**
-     * Unprotected DMG action field values
-     * See 802.11ad Table 8-281ae
-     */
-    enum UnprotectedDmgActionValue
-    {
-        UNPROTECTED_DMG_ANNOUNCE = 0,
-        UNPROTECTED_DMG_BRP = 1,
-        UNPROTECTED_MIMO_BF_SETUP = 2,
-        UNPROTECTED_MIMO_BF_POLL = 3,
-        UNPROTECTED_MIMO_BF_FEEDBACK = 4,
-        UNPROTECTED_MIMO_BF_SELECTION = 5,
-    };
-
-    /**
-     * Protected EHT action field values
-     * See 802.11be D3.0 Table 9-623c
-     */
-    enum ProtectedEhtActionValue
-    {
-        PROTECTED_EHT_TID_TO_LINK_MAPPING_REQUEST = 0,
-        PROTECTED_EHT_TID_TO_LINK_MAPPING_RESPONSE = 1,
-        PROTECTED_EHT_TID_TO_LINK_MAPPING_TEARDOWN = 2,
-        PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_REQUEST = 3,
-        PROTECTED_EHT_EPCS_PRIORITY_ACCESS_ENABLE_RESPONSE = 4,
-        PROTECTED_EHT_EPCS_PRIORITY_ACCESS_TEARDOWN = 5,
-        PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION = 6,
-        PROTECTED_EHT_LINK_RECOMMENDATION = 7,
-        PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_REQUEST = 8,
-        PROTECTED_EHT_MULTI_LINK_OPERATION_UPDATE_RESPONSE = 9,
-    };
-
-    /**
-     * typedef for union of different ActionValues
-     */
-    typedef union {
-        QosActionValue qos;                                 ///< qos
-        BlockAckActionValue blockAck;                       ///< block ack
-        RadioMeasurementActionValue radioMeasurementAction; ///< radio measurement
-        PublicActionValue publicAction;                     ///< public
-        SelfProtectedActionValue selfProtectedAction;       ///< self protected
-        MultihopActionValue multihopAction;                 ///< multi hop
-        MeshActionValue meshAction;                         ///< mesh
-        DmgActionValue dmgAction;                           ///< dmg
-        FstActionValue fstAction;                           ///< fst
-        UnprotectedDmgActionValue unprotectedDmgAction;     ///< unprotected dmg
-        ProtectedEhtActionValue protectedEhtAction;         ///< protected eht
-    } ActionValue;                                          ///< the action value
-
-    /**
-     * Set action for this Action header.
-     *
-     * \param type category
-     * \param action action
-     */
-    void SetAction(CategoryValue type, ActionValue action);
-
-    /**
-     * Return the category value.
-     *
-     * \return CategoryValue
-     */
-    CategoryValue GetCategory() const;
-    /**
-     * Return the action value.
-     *
-     * \return ActionValue
-     */
-    ActionValue GetAction() const;
-
-    /**
-     * Peek an Action header from the given packet.
-     *
-     * \param pkt the given packet
-     * \return the category value and the action value in the peeked Action header
-     */
-    static std::pair<CategoryValue, ActionValue> Peek(Ptr<const Packet> pkt);
-
-    /**
-     * Remove an Action header from the given packet.
-     *
-     * \param pkt the given packet
-     * \return the category value and the action value in the removed Action header
-     */
-    static std::pair<CategoryValue, ActionValue> Remove(Ptr<Packet> pkt);
-
-    /**
-     * Register this type.
-     * \return The TypeId.
-     */
-    static TypeId GetTypeId();
-    TypeId GetInstanceTypeId() const override;
-    void Print(std::ostream& os) const override;
-    uint32_t GetSerializedSize() const override;
-    void Serialize(Buffer::Iterator start) const override;
-    uint32_t Deserialize(Buffer::Iterator start) override;
-
-  private:
-    uint8_t m_category;    //!< Category of the action
-    uint8_t m_actionValue; //!< Action value
-};
-
-/**
- * \ingroup wifi
- * Implement the header for management frames of type Add Block Ack request.
- */
-class MgtAddBaRequestHeader : public Header
-{
-  public:
-    MgtAddBaRequestHeader();
-
-    /**
-     * Register this type.
-     * \return The TypeId.
-     */
-    static TypeId GetTypeId();
-    TypeId GetInstanceTypeId() const override;
-    void Print(std::ostream& os) const override;
-    uint32_t GetSerializedSize() const override;
-    void Serialize(Buffer::Iterator start) const override;
-    uint32_t Deserialize(Buffer::Iterator start) override;
-
-    /**
-     * Enable delayed BlockAck.
-     */
-    void SetDelayedBlockAck();
-    /**
-     * Enable immediate BlockAck
-     */
-    void SetImmediateBlockAck();
-    /**
-     * Set Traffic ID (TID).
-     *
-     * \param tid traffic ID
-     */
-    void SetTid(uint8_t tid);
-    /**
-     * Set timeout.
-     *
-     * \param timeout timeout
-     */
-    void SetTimeout(uint16_t timeout);
-    /**
-     * Set buffer size.
-     *
-     * \param size buffer size
-     */
-    void SetBufferSize(uint16_t size);
-    /**
-     * Set the starting sequence number.
-     *
-     * \param seq the starting sequence number
-     */
-    void SetStartingSequence(uint16_t seq);
-    /**
-     * Enable or disable A-MSDU support.
-     *
-     * \param supported enable or disable A-MSDU support
-     */
-    void SetAmsduSupport(bool supported);
-
-    /**
-     * Return the starting sequence number.
-     *
-     * \return the starting sequence number
-     */
-    uint16_t GetStartingSequence() const;
-    /**
-     * Return the Traffic ID (TID).
-     *
-     * \return TID
-     */
-    uint8_t GetTid() const;
-    /**
-     * Return whether the Block Ack policy is immediate Block Ack.
-     *
-     * \return true if immediate Block Ack is being used, false otherwise
-     */
-    bool IsImmediateBlockAck() const;
-    /**
-     * Return the timeout.
-     *
-     * \return timeout
-     */
-    uint16_t GetTimeout() const;
-    /**
-     * Return the buffer size.
-     *
-     * \return the buffer size.
-     */
-    uint16_t GetBufferSize() const;
-    /**
-     * Return whether A-MSDU capability is supported.
-     *
-     * \return true is A-MSDU is supported, false otherwise
-     */
-    bool IsAmsduSupported() const;
-
-  private:
-    /**
-     * Return the raw parameter set.
-     *
-     * \return the raw parameter set
-     */
-    uint16_t GetParameterSet() const;
-    /**
-     * Set the parameter set from the given raw value.
-     *
-     * \param params raw parameter set value
-     */
-    void SetParameterSet(uint16_t params);
-    /**
-     * Return the raw sequence control.
-     *
-     * \return the raw sequence control
-     */
-    uint16_t GetStartingSequenceControl() const;
-    /**
-     * Set sequence control with the given raw value.
-     *
-     * \param seqControl the raw sequence control
-     */
-    void SetStartingSequenceControl(uint16_t seqControl);
-
-    uint8_t m_dialogToken;   //!< Not used for now
-    uint8_t m_amsduSupport;  //!< Flag if A-MSDU is supported
-    uint8_t m_policy;        //!< Block Ack policy
-    uint8_t m_tid;           //!< Traffic ID
-    uint16_t m_bufferSize;   //!< Buffer size
-    uint16_t m_timeoutValue; //!< Timeout
-    uint16_t m_startingSeq;  //!< Starting sequence number
-};
-
-/**
- * \ingroup wifi
- * Implement the header for management frames of type Add Block Ack response.
- */
-class MgtAddBaResponseHeader : public Header
-{
-  public:
-    MgtAddBaResponseHeader();
-
-    /**
-     * Register this type.
-     * \return The TypeId.
-     */
-    static TypeId GetTypeId();
-    TypeId GetInstanceTypeId() const override;
-    void Print(std::ostream& os) const override;
-    uint32_t GetSerializedSize() const override;
-    void Serialize(Buffer::Iterator start) const override;
-    uint32_t Deserialize(Buffer::Iterator start) override;
-
-    /**
-     * Enable delayed BlockAck.
-     */
-    void SetDelayedBlockAck();
-    /**
-     * Enable immediate BlockAck.
-     */
-    void SetImmediateBlockAck();
-    /**
-     * Set Traffic ID (TID).
-     *
-     * \param tid traffic ID
-     */
-    void SetTid(uint8_t tid);
-    /**
-     * Set timeout.
-     *
-     * \param timeout timeout
-     */
-    void SetTimeout(uint16_t timeout);
-    /**
-     * Set buffer size.
-     *
-     * \param size buffer size
-     */
-    void SetBufferSize(uint16_t size);
-    /**
-     * Set the status code.
-     *
-     * \param code the status code
-     */
-    void SetStatusCode(StatusCode code);
-    /**
-     * Enable or disable A-MSDU support.
-     *
-     * \param supported enable or disable A-MSDU support
-     */
-    void SetAmsduSupport(bool supported);
-
-    /**
-     * Return the status code.
-     *
-     * \return the status code
-     */
-    StatusCode GetStatusCode() const;
-    /**
-     * Return the Traffic ID (TID).
-     *
-     * \return TID
-     */
-    uint8_t GetTid() const;
-    /**
-     * Return whether the Block Ack policy is immediate Block Ack.
-     *
-     * \return true if immediate Block Ack is being used, false otherwise
-     */
-    bool IsImmediateBlockAck() const;
-    /**
-     * Return the timeout.
-     *
-     * \return timeout
-     */
-    uint16_t GetTimeout() const;
-    /**
-     * Return the buffer size.
-     *
-     * \return the buffer size.
-     */
-    uint16_t GetBufferSize() const;
-    /**
-     * Return whether A-MSDU capability is supported.
-     *
-     * \return true is A-MSDU is supported, false otherwise
-     */
-    bool IsAmsduSupported() const;
-
-  private:
-    /**
-     * Return the raw parameter set.
-     *
-     * \return the raw parameter set
-     */
-    uint16_t GetParameterSet() const;
-    /**
-     * Set the parameter set from the given raw value.
-     *
-     * \param params raw parameter set value
-     */
-    void SetParameterSet(uint16_t params);
-
-    uint8_t m_dialogToken;   //!< Not used for now
-    StatusCode m_code;       //!< Status code
-    uint8_t m_amsduSupport;  //!< Flag if A-MSDU is supported
-    uint8_t m_policy;        //!< Block ACK policy
-    uint8_t m_tid;           //!< Traffic ID
-    uint16_t m_bufferSize;   //!< Buffer size
-    uint16_t m_timeoutValue; //!< Timeout
-};
-
-/**
- * \ingroup wifi
- * Implement the header for management frames of type Delete Block Ack.
- */
-class MgtDelBaHeader : public Header
-{
-  public:
-    MgtDelBaHeader();
-
-    /**
-     * Register this type.
-     * \return The TypeId.
-     */
-    static TypeId GetTypeId();
-
-    TypeId GetInstanceTypeId() const override;
-    void Print(std::ostream& os) const override;
-    uint32_t GetSerializedSize() const override;
-    void Serialize(Buffer::Iterator start) const override;
-    uint32_t Deserialize(Buffer::Iterator start) override;
-
-    /**
-     * Check if the initiator bit in the DELBA is set.
-     *
-     * \return true if the initiator bit in the DELBA is set,
-     *         false otherwise
-     */
-    bool IsByOriginator() const;
-    /**
-     * Return the Traffic ID (TID).
-     *
-     * \return TID
-     */
-    uint8_t GetTid() const;
-    /**
-     * Set Traffic ID (TID).
-     *
-     * \param tid traffic ID
-     */
-    void SetTid(uint8_t tid);
-    /**
-     * Set the initiator bit in the DELBA.
-     */
-    void SetByOriginator();
-    /**
-     * Un-set the initiator bit in the DELBA.
-     */
-    void SetByRecipient();
-
-  private:
-    /**
-     * Return the raw parameter set.
-     *
-     * \return the raw parameter set
-     */
-    uint16_t GetParameterSet() const;
-    /**
-     * Set the parameter set from the given raw value.
-     *
-     * \param params raw parameter set value
-     */
-    void SetParameterSet(uint16_t params);
-
-    uint16_t m_initiator;  //!< initiator
-    uint16_t m_tid;        //!< Traffic ID
-    uint16_t m_reasonCode; //!< Not used for now. Always set to 1: "Unspecified reason"
-};
-
-/**
- * \ingroup wifi
- * Implement the header for Action frames of type EML Operating Mode Notification.
- */
-class MgtEmlOmn : public Header
-{
-  public:
-    MgtEmlOmn() = default;
-
-    /**
-     * Register this type.
-     * \return The TypeId.
-     */
-    static TypeId GetTypeId();
-    TypeId GetInstanceTypeId() const override;
-    void Print(std::ostream& os) const override;
-    uint32_t GetSerializedSize() const override;
-    void Serialize(Buffer::Iterator start) const override;
-    uint32_t Deserialize(Buffer::Iterator start) override;
-
-    /**
-     * EML Control field.
-     */
-    struct EmlControl
-    {
-        uint8_t emlsrMode : 1;                  //!< EMLSR Mode
-        uint8_t emlmrMode : 1;                  //!< EMLMR Mode
-        uint8_t emlsrParamUpdateCtrl : 1;       //!< EMLSR Parameter Update Control
-        uint8_t : 5;                            //!< reserved
-        std::optional<uint16_t> linkBitmap;     //!< EMLSR/EMLMR Link Bitmap
-        std::optional<uint8_t> mcsMapCountCtrl; //!< MCS Map Count Control
-        // TODO Add EMLMR Supported MCS And NSS Set subfield when EMLMR is supported
-    };
-
-    /**
-     * EMLSR Parameter Update field.
-     */
-    struct EmlsrParamUpdate
-    {
-        uint8_t paddingDelay : 3;    //!< EMLSR Padding Delay
-        uint8_t transitionDelay : 3; //!< EMLSR Transition Delay
-    };
-
-    /**
-     * Set the bit position in the link bitmap corresponding to the given link.
-     *
-     * \param linkId the ID of the given link
-     */
-    void SetLinkIdInBitmap(uint8_t linkId);
-    /**
-     * \return the ID of the links whose bit position in the link bitmap is set to 1
-     */
-    std::list<uint8_t> GetLinkBitmap() const;
-
-    uint8_t m_dialogToken{0};                             //!< Dialog Token
-    EmlControl m_emlControl{};                            //!< EML Control field
-    std::optional<EmlsrParamUpdate> m_emlsrParamUpdate{}; //!< EMLSR Parameter Update field
-};
-
 } // namespace ns3
 
 #endif /* MGT_HEADERS_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/mpdu-aggregator.cc ns-3.41/src/wifi/model/mpdu-aggregator.cc
--- ns-3.40/src/wifi/model/mpdu-aggregator.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/mpdu-aggregator.cc	2024-02-09 14:44:08.589794900 -0800
@@ -33,6 +33,7 @@
 #include "wifi-tx-parameters.h"
 #include "wifi-tx-vector.h"
 
+#include "ns3/eht-capabilities.h"
 #include "ns3/he-capabilities.h"
 #include "ns3/ht-capabilities.h"
 #include "ns3/ht-frame-exchange-manager.h"
@@ -147,14 +148,20 @@
     NS_ASSERT(stationManager);
 
     // Retrieve the Capabilities elements advertised by the recipient
-    Ptr<const HeCapabilities> heCapabilities = stationManager->GetStationHeCapabilities(recipient);
-    Ptr<const VhtCapabilities> vhtCapabilities =
-        stationManager->GetStationVhtCapabilities(recipient);
-    Ptr<const HtCapabilities> htCapabilities = stationManager->GetStationHtCapabilities(recipient);
+    auto ehtCapabilities = stationManager->GetStationEhtCapabilities(recipient);
+    auto heCapabilities = stationManager->GetStationHeCapabilities(recipient);
+    auto vhtCapabilities = stationManager->GetStationVhtCapabilities(recipient);
+    auto htCapabilities = stationManager->GetStationHtCapabilities(recipient);
 
     // Determine the constraint imposed by the recipient based on the PPDU
     // format used to transmit the A-MPDU
-    if (modulation >= WIFI_MOD_CLASS_HE)
+    if (modulation >= WIFI_MOD_CLASS_EHT)
+    {
+        NS_ABORT_MSG_IF(!ehtCapabilities, "EHT Capabilities element not received");
+
+        maxAmpduSize = std::min(maxAmpduSize, ehtCapabilities->GetMaxAmpduLength());
+    }
+    else if (modulation >= WIFI_MOD_CLASS_HE)
     {
         NS_ABORT_MSG_IF(!heCapabilities, "HE Capabilities element not received");
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/msdu-aggregator.cc ns-3.41/src/wifi/model/msdu-aggregator.cc
--- ns-3.40/src/wifi/model/msdu-aggregator.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/msdu-aggregator.cc	2024-02-09 14:44:08.589794900 -0800
@@ -127,11 +127,16 @@
     uint8_t nMsdu = 1;
     peekedItem = queue->PeekByTidAndAddress(tid, recipient, peekedItem->GetOriginal());
 
-    while (peekedItem &&
+    // stop aggregation if we find an A-MSDU in the queue. This likely happens when an A-MSDU is
+    // prepared but not transmitted due to RTS/CTS failure
+    while (peekedItem && !peekedItem->GetHeader().IsQosAmsdu() &&
            m_htFem->TryAggregateMsdu(peekedItem = m_htFem->CreateAliasIfNeeded(peekedItem),
                                      txParams,
                                      availableTime))
     {
+        NS_ASSERT_MSG(!peekedItem->HasSeqNoAssigned(),
+                      "Found item with sequence number assignment after one without: perhaps "
+                      "sequence numbers were not released correctly?");
         // find the next MPDU before dequeuing the current one
         Ptr<const WifiMpdu> msdu = peekedItem->GetOriginal();
         peekedItem = queue->PeekByTidAndAddress(tid, recipient, msdu);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/non-ht/dsss-phy.cc ns-3.41/src/wifi/model/non-ht/dsss-phy.cc
--- ns-3.40/src/wifi/model/non-ht/dsss-phy.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/non-ht/dsss-phy.cc	2024-02-09 14:44:08.589794900 -0800
@@ -74,7 +74,7 @@
 GetDsssRatesBpsList()
 {
     return s_dsssRatesBpsList;
-};
+}
 
 DsssPhy::DsssPhy()
 {
@@ -311,7 +311,7 @@
     {                                                                                              \
         static WifiMode mode = CreateDsssMode(#x, WIFI_MOD_CLASS_##m);                             \
         return mode;                                                                               \
-    };
+    }
 
 // Clause 15 rates (DSSS)
 GET_DSSS_MODE(DsssRate1Mbps, DSSS)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/non-ht/erp-ofdm-phy.cc ns-3.41/src/wifi/model/non-ht/erp-ofdm-phy.cc
--- ns-3.40/src/wifi/model/non-ht/erp-ofdm-phy.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/non-ht/erp-ofdm-phy.cc	2024-02-09 14:44:08.589794900 -0800
@@ -69,7 +69,7 @@
 GetErpOfdmRatesBpsList()
 {
     return s_erpOfdmRatesBpsList;
-};
+}
 
 ErpOfdmPhy::ErpOfdmPhy()
     : OfdmPhy(OFDM_PHY_DEFAULT, false) // don't add OFDM modes to list
@@ -162,7 +162,7 @@
     {                                                                                              \
         static WifiMode mode = CreateErpOfdmMode(#x, f);                                           \
         return mode;                                                                               \
-    };
+    }
 
 GET_ERP_OFDM_MODE(ErpOfdmRate6Mbps, true)
 GET_ERP_OFDM_MODE(ErpOfdmRate9Mbps, false)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/non-ht/ofdm-phy.cc ns-3.41/src/wifi/model/non-ht/ofdm-phy.cc
--- ns-3.40/src/wifi/model/non-ht/ofdm-phy.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/non-ht/ofdm-phy.cc	2024-02-09 14:44:08.589794900 -0800
@@ -101,7 +101,7 @@
 GetOfdmRatesBpsList()
 {
     return s_ofdmRatesBpsList;
-};
+}
 
 OfdmPhy::OfdmPhy(OfdmPhyVariant variant /* = OFDM_PHY_DEFAULT */, bool buildModeList /* = true */)
 {
@@ -495,7 +495,7 @@
     {                                                                                              \
         static WifiMode mode = CreateOfdmMode(#x, f);                                              \
         return mode;                                                                               \
-    };
+    }
 
 // 20 MHz channel rates (default)
 GET_OFDM_MODE(OfdmRate6Mbps, true)
@@ -690,7 +690,7 @@
     const auto txWidth = ppdu->GetTxChannelWidth();
     const auto& txVector = ppdu->GetTxVector();
     // Update channel width in TXVECTOR for non-HT duplicate PPDUs.
-    if ((txVector.IsNonHtDuplicate() && (txWidth > m_wifiPhy->GetChannelWidth())))
+    if (txVector.IsNonHtDuplicate() && txWidth > m_wifiPhy->GetChannelWidth())
     {
         // We also do a copy of the PPDU for non-HT duplicate PPDUs since other
         // PHYs might set a different channel width in the reconstructed TXVECTOR.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/phy-entity.cc ns-3.41/src/wifi/model/phy-entity.cc
--- ns-3.40/src/wifi/model/phy-entity.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/phy-entity.cc	2024-02-09 14:44:08.593795000 -0800
@@ -210,7 +210,7 @@
 WifiConstPsduMap
 PhyEntity::GetWifiConstPsduMap(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) const
 {
-    return WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)});
+    return WifiConstPsduMap({{SU_STA_ID, psdu}});
 }
 
 Ptr<const WifiPsdu>
@@ -229,8 +229,7 @@
     while (field != WIFI_PPDU_FIELD_DATA)
     {
         Time duration = GetDuration(field, txVector);
-        map[field] =
-            std::make_pair(std::make_pair(start, start + duration), GetSigMode(field, txVector));
+        map[field] = {{start, start + duration}, GetSigMode(field, txVector)};
         // Move to next field
         start += duration;
         field = GetNextField(field, txVector.GetPreambleType());
@@ -415,17 +414,6 @@
     }
 
     Time endRx = Simulator::Now() + rxDuration;
-    if (m_state->GetState() == WifiPhyState::OFF)
-    {
-        NS_LOG_DEBUG("Cannot start RX because device is OFF");
-        if (endRx > (Simulator::Now() + m_state->GetDelayUntilIdle()))
-        {
-            m_wifiPhy->SwitchMaybeToCcaBusy(nullptr);
-        }
-        DropPreambleEvent(ppdu, WifiPhyRxfailureReason::POWERED_OFF, endRx);
-        return;
-    }
-
     if (ppdu->IsTruncatedTx())
     {
         NS_LOG_DEBUG("Packet reception stopped because transmitter has been switched off");
@@ -513,6 +501,10 @@
         NS_LOG_DEBUG("Drop packet because in sleep mode");
         DropPreambleEvent(ppdu, SLEEPING, endRx);
         break;
+    case WifiPhyState::OFF:
+        NS_LOG_DEBUG("Drop packet because in switched off");
+        DropPreambleEvent(ppdu, WifiPhyRxfailureReason::POWERED_OFF, endRx);
+        break;
     default:
         NS_FATAL_ERROR("Invalid WifiPhy state.");
         break;
@@ -524,8 +516,7 @@
 {
     NS_LOG_FUNCTION(this << ppdu << reason << endRx);
     m_wifiPhy->NotifyRxDrop(GetAddressedPsduInPpdu(ppdu), reason);
-    auto it = m_wifiPhy->m_currentPreambleEvents.find(
-        std::make_pair(ppdu->GetUid(), ppdu->GetPreamble()));
+    auto it = m_wifiPhy->m_currentPreambleEvents.find({ppdu->GetUid(), ppdu->GetPreamble()});
     if (it != m_wifiPhy->m_currentPreambleEvents.end())
     {
         m_wifiPhy->m_currentPreambleEvents.erase(it);
@@ -542,8 +533,7 @@
 PhyEntity::ErasePreambleEvent(Ptr<const WifiPpdu> ppdu, Time rxDuration)
 {
     NS_LOG_FUNCTION(this << ppdu << rxDuration);
-    auto it = m_wifiPhy->m_currentPreambleEvents.find(
-        std::make_pair(ppdu->GetUid(), ppdu->GetPreamble()));
+    auto it = m_wifiPhy->m_currentPreambleEvents.find({ppdu->GetUid(), ppdu->GetPreamble()});
     if (it != m_wifiPhy->m_currentPreambleEvents.end())
     {
         m_wifiPhy->m_currentPreambleEvents.erase(it);
@@ -583,8 +573,8 @@
     Ptr<const WifiPpdu> ppdu = event->GetPpdu();
     NS_LOG_DEBUG("Receiving PSDU");
     uint16_t staId = GetStaId(ppdu);
-    m_signalNoiseMap.insert({std::make_pair(ppdu->GetUid(), staId), SignalNoiseDbm()});
-    m_statusPerMpduMap.insert({std::make_pair(ppdu->GetUid(), staId), std::vector<bool>()});
+    m_signalNoiseMap.insert({{ppdu->GetUid(), staId}, SignalNoiseDbm()});
+    m_statusPerMpduMap.insert({{ppdu->GetUid(), staId}, std::vector<bool>()});
     ScheduleEndOfMpdus(event);
     const auto& txVector = event->GetPpdu()->GetTxVector();
     Time payloadDuration = ppdu->GetTxDuration() - CalculatePhyPreambleAndHeaderDuration(txVector);
@@ -678,7 +668,7 @@
                                     << ", correct reception: " << rxInfo.first << ", Signal/Noise: "
                                     << rxInfo.second.signal << "/" << rxInfo.second.noise << "dBm");
 
-    auto signalNoiseIt = m_signalNoiseMap.find(std::make_pair(ppdu->GetUid(), staId));
+    auto signalNoiseIt = m_signalNoiseMap.find({ppdu->GetUid(), staId});
     NS_ASSERT(signalNoiseIt != m_signalNoiseMap.end());
     signalNoiseIt->second = rxInfo.second;
 
@@ -686,7 +676,7 @@
     rxSignalInfo.snr = rxInfo.second.signal / rxInfo.second.noise;
     rxSignalInfo.rssi = rxInfo.second.signal;
 
-    auto statusPerMpduIt = m_statusPerMpduMap.find(std::make_pair(ppdu->GetUid(), staId));
+    auto statusPerMpduIt = m_statusPerMpduMap.find({ppdu->GetUid(), staId});
     NS_ASSERT(statusPerMpduIt != m_statusPerMpduMap.end());
     statusPerMpduIt->second.push_back(rxInfo.first);
 
@@ -702,9 +692,8 @@
 {
     const auto ppdu = event->GetPpdu();
     const auto& txVector = ppdu->GetTxVector();
-    const auto psduDuration =
-        ppdu->GetTxDuration() - CalculatePhyPreambleAndHeaderDuration(txVector);
-    NS_LOG_FUNCTION(this << *event << psduDuration);
+    NS_LOG_FUNCTION(
+        this << *event << ppdu->GetTxDuration() - CalculatePhyPreambleAndHeaderDuration(txVector));
     NS_ASSERT(event->GetEndTime() == Simulator::Now());
     const auto staId = GetStaId(ppdu);
     const auto channelWidthAndBand = GetChannelWidthAndBand(txVector, staId);
@@ -716,35 +705,48 @@
     Ptr<const WifiPsdu> psdu = GetAddressedPsduInPpdu(ppdu);
     m_wifiPhy->NotifyRxEnd(psdu);
 
-    auto signalNoiseIt = m_signalNoiseMap.find(std::make_pair(ppdu->GetUid(), staId));
+    auto signalNoiseIt = m_signalNoiseMap.find({ppdu->GetUid(), staId});
     NS_ASSERT(signalNoiseIt != m_signalNoiseMap.end());
-    auto statusPerMpduIt = m_statusPerMpduMap.find(std::make_pair(ppdu->GetUid(), staId));
+    auto statusPerMpduIt = m_statusPerMpduMap.find({ppdu->GetUid(), staId});
     NS_ASSERT(statusPerMpduIt != m_statusPerMpduMap.end());
+    // store per-MPDU status, which is cleared by the call to DoEndReceivePayload below
+    auto statusPerMpdu = statusPerMpduIt->second;
 
-    if (std::count(statusPerMpduIt->second.begin(), statusPerMpduIt->second.end(), true))
+    RxSignalInfo rxSignalInfo;
+    bool success;
+
+    if (std::count(statusPerMpdu.cbegin(), statusPerMpdu.cend(), true))
     {
         // At least one MPDU has been successfully received
         m_wifiPhy->NotifyMonitorSniffRx(psdu,
                                         m_wifiPhy->GetFrequency(),
                                         txVector,
                                         signalNoiseIt->second,
-                                        statusPerMpduIt->second,
+                                        statusPerMpdu,
                                         staId);
-        RxSignalInfo rxSignalInfo;
         rxSignalInfo.snr = snr;
         rxSignalInfo.rssi = signalNoiseIt->second.signal; // same information for all MPDUs
-        RxPayloadSucceeded(psdu, rxSignalInfo, txVector, staId, statusPerMpduIt->second);
+        RxPayloadSucceeded(psdu, rxSignalInfo, txVector, staId, statusPerMpdu);
         m_wifiPhy->m_previouslyRxPpduUid =
             ppdu->GetUid(); // store UID only if reception is successful (because otherwise trigger
                             // won't be read by MAC layer)
+        success = true;
     }
     else
     {
         RxPayloadFailed(psdu, snr, txVector);
+        success = false;
     }
 
     DoEndReceivePayload(ppdu);
     m_wifiPhy->SwitchMaybeToCcaBusy(ppdu);
+
+    // notify the MAC through the PHY state helper as the last action. Indeed, the notification
+    // of the RX end may lead the MAC to request a PHY state change (e.g., channel switch, sleep).
+    // Hence, all actions the PHY has to perform when RX ends should be completed before
+    // notifying the MAC.
+    success ? m_state->NotifyRxPsduSucceeded(psdu, rxSignalInfo, txVector, staId, statusPerMpdu)
+            : m_state->NotifyRxPsduFailed(psdu, snr);
 }
 
 void
@@ -755,7 +757,6 @@
                               const std::vector<bool>& statusPerMpdu)
 {
     NS_LOG_FUNCTION(this << *psdu << txVector);
-    m_state->NotifyRxPsduSucceeded(psdu, rxSignalInfo, txVector, staId, statusPerMpdu);
     m_state->SwitchFromRxEndOk();
 }
 
@@ -763,7 +764,6 @@
 PhyEntity::RxPayloadFailed(Ptr<const WifiPsdu> psdu, double snr, const WifiTxVector& txVector)
 {
     NS_LOG_FUNCTION(this << *psdu << txVector << snr);
-    m_state->NotifyRxPsduFailed(psdu, snr);
     m_state->SwitchFromRxEndError();
 }
 
@@ -793,7 +793,7 @@
         channelWidthAndBand.first,
         channelWidthAndBand.second,
         staId,
-        std::make_pair(relativeMpduStart, relativeMpduStart + mpduDuration));
+        {relativeMpduStart, relativeMpduStart + mpduDuration});
 
     WifiMode mode = event->GetPpdu()->GetTxVector().GetMode(staId);
     NS_LOG_DEBUG("rate=" << (mode.GetDataRate(event->GetPpdu()->GetTxVector(), staId))
@@ -814,12 +814,12 @@
           m_wifiPhy->m_postReceptionErrorModel->IsCorrupt(psdu->GetPacket()->Copy())))
     {
         NS_LOG_DEBUG("Reception succeeded: " << psdu);
-        return std::make_pair(true, signalNoise);
+        return {true, signalNoise};
     }
     else
     {
         NS_LOG_DEBUG("Reception failed: " << psdu);
-        return std::make_pair(false, signalNoise);
+        return {false, signalNoise};
     }
 }
 
@@ -827,7 +827,7 @@
 PhyEntity::GetChannelWidthAndBand(const WifiTxVector& txVector, uint16_t /* staId */) const
 {
     uint16_t channelWidth = GetRxChannelWidth(txVector);
-    return std::make_pair(channelWidth, GetPrimaryBand(channelWidth));
+    return {channelWidth, GetPrimaryBand(channelWidth)};
 }
 
 const std::map<std::pair<uint64_t, WifiPreamble>, Ptr<Event>>&
@@ -841,8 +841,7 @@
 {
     NS_LOG_FUNCTION(this << *event);
     Ptr<const WifiPpdu> ppdu = event->GetPpdu();
-    m_wifiPhy->m_currentPreambleEvents.insert(
-        {std::make_pair(ppdu->GetUid(), ppdu->GetPreamble()), event});
+    m_wifiPhy->m_currentPreambleEvents.insert({{ppdu->GetUid(), ppdu->GetPreamble()}, event});
 }
 
 Ptr<Event>
@@ -850,9 +849,8 @@
 {
     // We store all incoming preamble events, and a decision is made at the end of the preamble
     // detection window.
-    const auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
     const auto& currentPreambleEvents = GetCurrentPreambleEvents();
-    const auto it = currentPreambleEvents.find(uidPreamblePair);
+    const auto it = currentPreambleEvents.find({ppdu->GetUid(), ppdu->GetPreamble()});
     if (it != currentPreambleEvents.cend())
     {
         // received another signal with the same content
@@ -983,7 +981,7 @@
                      << event->GetPpdu()->GetUid());
         m_wifiPhy->NotifyRxDrop(GetAddressedPsduInPpdu(event->GetPpdu()), BUSY_DECODING_PREAMBLE);
         auto it = m_wifiPhy->m_currentPreambleEvents.find(
-            std::make_pair(event->GetPpdu()->GetUid(), event->GetPpdu()->GetPreamble()));
+            {event->GetPpdu()->GetUid(), event->GetPpdu()->GetPreamble()});
         m_wifiPhy->m_currentPreambleEvents.erase(it);
         // This is needed to cleanup the m_firstPowerPerBand so that the first power corresponds to
         // the power at the start of the PPDU
@@ -1012,7 +1010,7 @@
         // A bit convoluted but it enables to sync all PHYs
         for (auto& it : m_wifiPhy->m_phyEntities)
         {
-            it.second->CancelRunningEndPreambleDetectionEvents(true);
+            it.second->CancelRunningEndPreambleDetectionEvents();
         }
 
         for (auto it = m_wifiPhy->m_currentPreambleEvents.begin();
@@ -1102,11 +1100,7 @@
 PhyEntity::CancelAllEvents()
 {
     NS_LOG_FUNCTION(this);
-    for (auto& endPreambleDetectionEvent : m_endPreambleDetectionEvents)
-    {
-        endPreambleDetectionEvent.Cancel();
-    }
-    m_endPreambleDetectionEvents.clear();
+    CancelRunningEndPreambleDetectionEvents();
     for (auto& endRxPayloadEvent : m_endRxPayloadEvents)
     {
         endRxPayloadEvent.Cancel();
@@ -1126,20 +1120,14 @@
 }
 
 void
-PhyEntity::CancelRunningEndPreambleDetectionEvents(bool clear /* = false */)
+PhyEntity::CancelRunningEndPreambleDetectionEvents()
 {
-    NS_LOG_FUNCTION(this << clear);
+    NS_LOG_FUNCTION(this);
     for (auto& endPreambleDetectionEvent : m_endPreambleDetectionEvents)
     {
-        if (endPreambleDetectionEvent.IsRunning())
-        {
-            endPreambleDetectionEvent.Cancel();
-        }
-    }
-    if (clear)
-    {
-        m_endPreambleDetectionEvents.clear();
+        endPreambleDetectionEvent.Cancel();
     }
+    m_endPreambleDetectionEvents.clear();
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/phy-entity.h ns-3.41/src/wifi/model/phy-entity.h
--- ns-3.40/src/wifi/model/phy-entity.h	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/phy-entity.h	2024-02-09 14:44:08.593795000 -0800
@@ -437,11 +437,9 @@
      */
     bool NoEndPreambleDetectionEvents() const;
     /**
-     * Cancel and eventually clear all end preamble detection events.
-     *
-     * \param clear whether to clear the end preamble detection events' list
+     * Cancel all end preamble detection events.
      */
-    void CancelRunningEndPreambleDetectionEvents(bool clear = false);
+    void CancelRunningEndPreambleDetectionEvents();
 
     /**
      * Return the STA ID that has been assigned to the station this PHY belongs to.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/qos-frame-exchange-manager.cc ns-3.41/src/wifi/model/qos-frame-exchange-manager.cc
--- ns-3.40/src/wifi/model/qos-frame-exchange-manager.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/qos-frame-exchange-manager.cc	2024-02-09 14:44:08.593795000 -0800
@@ -97,7 +97,8 @@
     cfEnd.SetAddr1(Mac48Address::GetBroadcast());
     cfEnd.SetAddr2(m_self);
 
-    WifiTxVector cfEndTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(cfEnd.GetAddr1());
+    WifiTxVector cfEndTxVector =
+        GetWifiRemoteStationManager()->GetRtsTxVector(cfEnd.GetAddr1(), m_allowedWidth);
 
     auto mpdu = Create<WifiMpdu>(Create<Packet>(), cfEnd);
     auto txDuration =
@@ -125,11 +126,14 @@
 {
     NS_LOG_FUNCTION(this);
     NS_ASSERT(m_edca);
-    NS_ASSERT(m_edca->IsTxopStarted(m_linkId));
+    NS_ASSERT(m_edca->GetTxopStartTime(m_linkId).has_value());
 
     // Release the channel if it has not been idle for the last PIFS interval
-    if (m_channelAccessManager->GetAccessGrantStart() - m_phy->GetSifs() >
-        Simulator::Now() - m_phy->GetPifs())
+    m_allowedWidth = std::min(
+        m_allowedWidth,
+        m_channelAccessManager->GetLargestIdlePrimaryChannel(m_phy->GetPifs(), Simulator::Now()));
+
+    if (m_allowedWidth == 0)
     {
         NotifyChannelReleased(m_edca);
         m_edca = nullptr;
@@ -203,7 +207,7 @@
     if (backingOff)
     {
         NS_ASSERT(m_edca->GetTxopLimit(m_linkId).IsStrictlyPositive());
-        NS_ASSERT(m_edca->IsTxopStarted(m_linkId));
+        NS_ASSERT(m_edca->GetTxopStartTime(m_linkId));
         NS_ASSERT(!m_pifsRecovery);
         NS_ASSERT(!m_initialFrame);
 
@@ -219,7 +223,7 @@
         // TXOP. In such a case, we assume that a new TXOP is being started if it
         // elapsed more than TXOPlimit since the start of the paused TXOP. Note
         // that GetRemainingTxop returns 0 iff Now - TXOPstart >= TXOPlimit
-        if (!m_edca->IsTxopStarted(m_linkId) ||
+        if (!m_edca->GetTxopStartTime(m_linkId) ||
             (backingOff && m_edca->GetRemainingTxop(m_linkId).IsZero()))
         {
             // starting a new TXOP
@@ -613,6 +617,13 @@
     }
     else
     {
+        // some STA(s) did not respond, they are no longer protected
+        for (const auto& address : m_txTimer.GetStasExpectedToRespond())
+        {
+            NS_LOG_DEBUG(address << " did not respond, hence it is no longer protected");
+            m_protectedStas.erase(address);
+        }
+
         NS_ASSERT_MSG(m_edca->GetTxopLimit(m_linkId).IsStrictlyPositive(),
                       "Cannot transmit more than one frame if TXOP Limit is zero");
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/qos-txop.cc ns-3.41/src/wifi/model/qos-txop.cc
--- ns-3.40/src/wifi/model/qos-txop.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/qos-txop.cc	2024-02-09 14:44:08.593795000 -0800
@@ -25,7 +25,7 @@
 #include "channel-access-manager.h"
 #include "ctrl-headers.h"
 #include "mac-tx-middle.h"
-#include "mgt-headers.h"
+#include "mgt-action-headers.h"
 #include "mpdu-aggregator.h"
 #include "msdu-aggregator.h"
 #include "wifi-mac-queue-scheduler.h"
@@ -72,7 +72,7 @@
             .AddAttribute("AddBaResponseTimeout",
                           "The timeout to wait for ADDBA response after the Ack to "
                           "ADDBA request is received.",
-                          TimeValue(MilliSeconds(1)),
+                          TimeValue(MilliSeconds(5)),
                           MakeTimeAccessor(&QosTxop::SetAddBaResponseTimeout,
                                            &QosTxop::GetAddBaResponseTimeout),
                           MakeTimeChecker())
@@ -121,12 +121,25 @@
         }));
     m_baManager->SetUnblockDestinationCallback(
         Callback<void, Mac48Address, uint8_t>([this](Mac48Address recipient, uint8_t tid) {
+            // save the status of AC queues before unblocking the transmissions to the recipient
+            std::map<uint8_t, bool> hasFramesToTransmit;
+            for (const auto& [id, link] : GetLinks())
+            {
+                hasFramesToTransmit[id] = HasFramesToTransmit(id);
+            }
+
             m_mac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::WAITING_ADDBA_RESP,
                                                          m_ac,
                                                          {WIFI_QOSDATA_QUEUE},
                                                          recipient,
                                                          m_mac->GetLocalAddress(recipient),
                                                          {tid});
+
+            // start access (if needed) on all the links
+            for (const auto& [id, link] : GetLinks())
+            {
+                StartAccessAfterEvent(id, hasFramesToTransmit.at(id), CHECK_MEDIUM_BUSY);
+            }
         }));
     m_queue->TraceConnectWithoutContext(
         "Expired",
@@ -520,9 +533,11 @@
                       GetBaStartingSequence(peekedItem->GetOriginal()->GetHeader().GetAddr1(), tid),
                       GetBaBufferSize(peekedItem->GetOriginal()->GetHeader().GetAddr1(), tid)));
 
-        // try A-MSDU aggregation
+        // try A-MSDU aggregation if the MPDU does not contain an A-MSDU and does not already
+        // have a sequence number assigned (may be a retransmission)
         if (m_mac->GetHtSupported() && !recipient.IsBroadcast() &&
-            !peekedItem->HasSeqNoAssigned() && !peekedItem->IsFragment())
+            !peekedItem->GetHeader().IsQosAmsdu() && !peekedItem->HasSeqNoAssigned() &&
+            !peekedItem->IsFragment())
         {
             auto htFem = StaticCast<HtFrameExchangeManager>(qosFem);
             mpdu = htFem->GetMsduAggregator()->GetNextAmsdu(peekedItem, txParams, availableTime);
@@ -572,12 +587,12 @@
     Txop::NotifyChannelAccessed(linkId);
 }
 
-bool
-QosTxop::IsTxopStarted(uint8_t linkId) const
+std::optional<Time>
+QosTxop::GetTxopStartTime(uint8_t linkId) const
 {
     auto& link = GetLink(linkId);
-    NS_LOG_FUNCTION(this << !link.startTxop.IsZero());
-    return (!link.startTxop.IsZero());
+    NS_LOG_FUNCTION(this << link.startTxop.has_value());
+    return link.startTxop;
 }
 
 void
@@ -586,23 +601,45 @@
     NS_LOG_FUNCTION(this << +linkId);
     auto& link = GetLink(linkId);
 
-    if (link.startTxop.IsStrictlyPositive())
+    if (link.startTxop)
     {
-        NS_LOG_DEBUG("Terminating TXOP. Duration = " << Simulator::Now() - link.startTxop);
-        m_txopTrace(link.startTxop, Simulator::Now() - link.startTxop, linkId);
+        NS_LOG_DEBUG("Terminating TXOP. Duration = " << Simulator::Now() - *link.startTxop);
+        m_txopTrace(*link.startTxop, Simulator::Now() - *link.startTxop, linkId);
     }
-    link.startTxop = Seconds(0);
-    Txop::NotifyChannelReleased(linkId);
+
+    // generate a new backoff value if either the TXOP duration is not null (i.e., some frames
+    // were transmitted) or no frame was transmitted but the queue actually contains frame to
+    // transmit and the user indicated that a backoff value should be generated in this situation.
+    // This behavior reflects the following specs text (Sec. 35.3.16.4 of 802.11be D4.0):
+    // An AP or non-AP STA affiliated with an MLD that has gained the right to initiate the
+    // transmission of a frame as described in 10.23.2.4 (Obtaining an EDCA TXOP) for an AC but
+    // does not transmit any frame corresponding to that AC for the reasons stated above may:
+    // - invoke a backoff for the EDCAF associated with that AC as allowed per h) of 10.23.2.2
+    //   (EDCA backoff procedure).
+    auto hasTransmitted = link.startTxop.has_value() && Simulator::Now() > *link.startTxop;
+
+    m_queue->WipeAllExpiredMpdus();
+    if ((hasTransmitted) ||
+        (!m_queue->IsEmpty() && m_mac->GetChannelAccessManager(linkId)->GetGenerateBackoffOnNoTx()))
+    {
+        GenerateBackoff(linkId);
+        if (!m_queue->IsEmpty())
+        {
+            Simulator::ScheduleNow(&QosTxop::RequestAccess, this, linkId);
+        }
+    }
+    link.startTxop.reset();
+    GetLink(linkId).access = NOT_REQUESTED;
 }
 
 Time
 QosTxop::GetRemainingTxop(uint8_t linkId) const
 {
     auto& link = GetLink(linkId);
-    NS_ASSERT(link.startTxop.IsStrictlyPositive());
+    NS_ASSERT(link.startTxop.has_value());
 
     Time remainingTxop = link.txopDuration;
-    remainingTxop -= (Simulator::Now() - link.startTxop);
+    remainingTxop -= (Simulator::Now() - *link.startTxop);
     if (remainingTxop.IsStrictlyNegative())
     {
         remainingTxop = Seconds(0);
@@ -616,6 +653,7 @@
 {
     NS_LOG_FUNCTION(this << respHdr << recipient);
     uint8_t tid = respHdr.GetTid();
+
     if (respHdr.GetStatusCode().IsSuccess())
     {
         NS_LOG_DEBUG("block ack agreement established with " << recipient << " tid " << +tid);
@@ -639,11 +677,6 @@
         NS_LOG_DEBUG("discard ADDBA response" << recipient);
         m_baManager->NotifyOriginatorAgreementRejected(recipient, tid);
     }
-
-    for (const auto& [id, link] : GetLinks())
-    {
-        StartAccessIfNeeded(id);
-    }
 }
 
 void
@@ -658,14 +691,7 @@
 QosTxop::NotifyOriginatorAgreementNoReply(const Mac48Address& recipient, uint8_t tid)
 {
     NS_LOG_FUNCTION(this << recipient << tid);
-
     m_baManager->NotifyOriginatorAgreementNoReply(recipient, tid);
-    // the recipient has been "unblocked" and transmissions can resume using normal
-    // acknowledgment, hence start access (if needed) on all the links
-    for (const auto& [id, link] : GetLinks())
-    {
-        StartAccessIfNeeded(id);
-    }
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/qos-txop.h ns-3.41/src/wifi/model/qos-txop.h
--- ns-3.40/src/wifi/model/qos-txop.h	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/qos-txop.h	2024-02-09 14:44:08.593795000 -0800
@@ -28,6 +28,8 @@
 
 #include "ns3/traced-value.h"
 
+#include <optional>
+
 namespace ns3
 {
 
@@ -324,12 +326,10 @@
     uint8_t GetQosQueueSize(uint8_t tid, Mac48Address receiver) const;
 
     /**
-     * Return true if a TXOP has started on the given link.
-     *
      * \param linkId the ID of the given link
-     * \return true if a TXOP has started, false otherwise.
+     * \return the TXOP start time, if a TXOP is ongoing on the given link
      */
-    virtual bool IsTxopStarted(uint8_t linkId) const;
+    virtual std::optional<Time> GetTxopStartTime(uint8_t linkId) const;
     /**
      * Return the remaining duration in the current TXOP on the given link.
      *
@@ -429,13 +429,13 @@
         /// Destructor (a virtual method is needed to make this struct polymorphic)
         ~QosLinkEntity() override = default;
 
-        Time startTxop{0};            //!< the start TXOP time
-        Time txopDuration{0};         //!< the duration of a TXOP
-        uint32_t muCwMin{0};          //!< the MU CW minimum
-        uint32_t muCwMax{0};          //!< the MU CW maximum
-        uint8_t muAifsn{0};           //!< the MU AIFSN
-        Time muEdcaTimer{0};          //!< the MU EDCA Timer
-        Time muEdcaTimerStartTime{0}; //!< last start time of the MU EDCA Timer
+        std::optional<Time> startTxop; //!< the start TXOP time
+        Time txopDuration{0};          //!< the duration of a TXOP
+        uint32_t muCwMin{0};           //!< the MU CW minimum
+        uint32_t muCwMax{0};           //!< the MU CW maximum
+        uint8_t muAifsn{0};            //!< the MU AIFSN
+        Time muEdcaTimer{0};           //!< the MU EDCA Timer
+        Time muEdcaTimerStartTime{0};  //!< last start time of the MU EDCA Timer
     };
 
     void DoDispose() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/qos-utils.cc ns-3.41/src/wifi/model/qos-utils.cc
--- ns-3.40/src/wifi/model/qos-utils.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/qos-utils.cc	2024-02-09 14:44:08.593795000 -0800
@@ -21,7 +21,7 @@
 #include "qos-utils.h"
 
 #include "ctrl-headers.h"
-#include "mgt-headers.h"
+#include "mgt-action-headers.h"
 #include "wifi-mac-header.h"
 
 #include "ns3/queue-item.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/qos-utils.h ns-3.41/src/wifi/model/qos-utils.h
--- ns-3.40/src/wifi/model/qos-utils.h	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/qos-utils.h	2024-02-09 14:44:08.593795000 -0800
@@ -20,6 +20,7 @@
 #ifndef QOS_UTILS_H
 #define QOS_UTILS_H
 
+#include "ns3/fatal-error.h"
 #include "ns3/ptr.h"
 
 #include <map>
@@ -87,6 +88,38 @@
 };
 
 /**
+ * \brief Stream insertion operator.
+ *
+ * \param os the stream
+ * \param acIndex the AC index
+ * \returns a reference to the stream
+ */
+inline std::ostream&
+operator<<(std::ostream& os, const AcIndex& acIndex)
+{
+    switch (acIndex)
+    {
+    case AC_BE:
+        return (os << "AC BE");
+    case AC_BK:
+        return (os << "AC BK");
+    case AC_VI:
+        return (os << "AC VI");
+    case AC_VO:
+        return (os << "AC VO");
+    case AC_BE_NQOS:
+        return (os << "AC BE NQOS");
+    case AC_BEACON:
+        return (os << "AC BEACON");
+    case AC_UNDEF:
+        return (os << "AC Undefined");
+    default:
+        NS_FATAL_ERROR("Unknown AC index");
+        return (os << "Unknown");
+    }
+}
+
+/**
  * \ingroup wifi
  * This class stores the pair of TIDs of an Access Category.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/rate-control/cara-wifi-manager.cc ns-3.41/src/wifi/model/rate-control/cara-wifi-manager.cc
--- ns-3.40/src/wifi/model/rate-control/cara-wifi-manager.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/rate-control/cara-wifi-manager.cc	2024-02-09 14:44:08.593795000 -0800
@@ -179,7 +179,7 @@
     station->m_failed = 0;
     NS_LOG_DEBUG("self=" << station << " data ok success=" << station->m_success
                          << ", timer=" << station->m_timer);
-    if ((station->m_success == m_successThreshold || station->m_timer >= m_timerTimeout))
+    if (station->m_success == m_successThreshold || station->m_timer >= m_timerTimeout)
     {
         if (station->m_rate < GetNSupported(station) - 1)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/rate-control/ideal-wifi-manager.cc ns-3.41/src/wifi/model/rate-control/ideal-wifi-manager.cc
--- ns-3.40/src/wifi/model/rate-control/ideal-wifi-manager.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/rate-control/ideal-wifi-manager.cc	2024-02-09 14:44:08.593795000 -0800
@@ -95,9 +95,7 @@
 uint16_t
 IdealWifiManager::GetChannelWidthForNonHtMode(WifiMode mode) const
 {
-    NS_ASSERT(mode.GetModulationClass() != WIFI_MOD_CLASS_HT &&
-              mode.GetModulationClass() != WIFI_MOD_CLASS_VHT &&
-              mode.GetModulationClass() != WIFI_MOD_CLASS_HE);
+    NS_ASSERT(mode.GetModulationClass() < WIFI_MOD_CLASS_HT);
     if (mode.GetModulationClass() == WIFI_MOD_CLASS_DSSS ||
         mode.GetModulationClass() == WIFI_MOD_CLASS_HR_DSSS)
     {
@@ -151,7 +149,7 @@
                     txVector.SetMode(mode);
                     AddSnrThreshold(txVector, GetPhy()->CalculateSnr(txVector, m_ber));
                 }
-                else // VHT or HE
+                else
                 {
                     uint16_t guardInterval;
                     if (mode.GetModulationClass() == WIFI_MOD_CLASS_VHT)
@@ -342,7 +340,6 @@
     // to ensure correct packet delivery.
     WifiMode maxMode = GetDefaultModeForSta(st);
     WifiTxVector txVector;
-    WifiMode mode;
     uint64_t bestRate = 0;
     uint8_t selectedNss = 1;
     uint16_t guardInterval;
@@ -367,24 +364,26 @@
         {
             for (uint8_t i = 0; i < GetNMcsSupported(station); i++)
             {
-                mode = GetMcsSupported(station, i);
+                auto mode = GetMcsSupported(station, i);
+                if (!IsCandidateModulationClass(mode.GetModulationClass(), station))
+                {
+                    continue;
+                }
                 txVector.SetMode(mode);
-                if (mode.GetModulationClass() == WIFI_MOD_CLASS_HT)
+                uint16_t guardInterval;
+                if (mode.GetModulationClass() >= WIFI_MOD_CLASS_HE)
+                {
+                    guardInterval = std::max(GetGuardInterval(station), GetGuardInterval());
+                }
+                else
                 {
                     guardInterval = static_cast<uint16_t>(
                         std::max(GetShortGuardIntervalSupported(station) ? 400 : 800,
                                  GetShortGuardIntervalSupported() ? 400 : 800));
-                    txVector.SetGuardInterval(guardInterval);
-                    // If the node and peer are both VHT capable, only search VHT modes
-                    if (GetVhtSupported() && GetVhtSupported(station))
-                    {
-                        continue;
-                    }
-                    // If the node and peer are both HE capable, only search HE modes
-                    if (GetHeSupported() && GetHeSupported(station))
-                    {
-                        continue;
-                    }
+                }
+                txVector.SetGuardInterval(guardInterval);
+                if (mode.GetModulationClass() == WIFI_MOD_CLASS_HT)
+                {
                     // Derive NSS from the MCS index. There is a different mode for each possible
                     // NSS value.
                     uint8_t nss = (mode.GetMcsValue() / 8) + 1;
@@ -417,64 +416,8 @@
                         selectedNss = nss;
                     }
                 }
-                else if (mode.GetModulationClass() == WIFI_MOD_CLASS_VHT)
-                {
-                    guardInterval = static_cast<uint16_t>(
-                        std::max(GetShortGuardIntervalSupported(station) ? 400 : 800,
-                                 GetShortGuardIntervalSupported() ? 400 : 800));
-                    txVector.SetGuardInterval(guardInterval);
-                    // If the node and peer are both HE capable, only search HE modes
-                    if (GetHeSupported() && GetHeSupported(station))
-                    {
-                        continue;
-                    }
-                    // If the node and peer are not both VHT capable, only search HT modes
-                    if (!GetVhtSupported() || !GetVhtSupported(station))
-                    {
-                        continue;
-                    }
-                    for (uint8_t nss = 1; nss <= std::min(GetMaxNumberOfTransmitStreams(),
-                                                          GetNumberOfSupportedStreams(station));
-                         nss++)
-                    {
-                        txVector.SetNss(nss);
-                        if (!txVector.IsValid())
-                        {
-                            NS_LOG_DEBUG("Skipping mode " << mode.GetUniqueName() << " nss " << +nss
-                                                          << " width "
-                                                          << txVector.GetChannelWidth());
-                            continue;
-                        }
-                        double threshold = GetSnrThreshold(txVector);
-                        uint64_t dataRate = mode.GetDataRate(txVector.GetChannelWidth(),
-                                                             txVector.GetGuardInterval(),
-                                                             nss);
-                        NS_LOG_DEBUG("Testing mode = " << mode.GetUniqueName() << " data rate "
-                                                       << dataRate << " threshold " << threshold
-                                                       << " last snr observed "
-                                                       << station->m_lastSnrObserved << " cached "
-                                                       << station->m_lastSnrCached);
-                        double snr = GetLastObservedSnr(station, channelWidth, nss);
-                        if (dataRate > bestRate && threshold < snr)
-                        {
-                            NS_LOG_DEBUG("Candidate mode = "
-                                         << mode.GetUniqueName() << " data rate " << dataRate
-                                         << " channel width " << channelWidth << " snr " << snr);
-                            bestRate = dataRate;
-                            maxMode = mode;
-                            selectedNss = nss;
-                        }
-                    }
-                }
-                else // HE
+                else
                 {
-                    guardInterval = std::max(GetGuardInterval(station), GetGuardInterval());
-                    txVector.SetGuardInterval(guardInterval);
-                    // If the node and peer are not both HE capable, only search (V)HT modes
-                    if (!GetHeSupported() || !GetHeSupported(station))
-                    {
-                        continue;
-                    }
                     for (uint8_t nss = 1; nss <= std::min(GetMaxNumberOfTransmitStreams(),
                                                           GetNumberOfSupportedStreams(station));
                          nss++)
@@ -517,7 +460,7 @@
             selectedNss = 1;
             for (uint8_t i = 0; i < GetNSupported(station); i++)
             {
-                mode = GetSupported(station, i);
+                auto mode = GetSupported(station, i);
                 txVector.SetMode(mode);
                 txVector.SetNss(selectedNss);
                 uint16_t channelWidth = GetChannelWidthForNonHtMode(mode);
@@ -548,12 +491,11 @@
     NS_LOG_DEBUG("Found maxMode: " << maxMode << " channelWidth: " << channelWidth
                                    << " nss: " << +selectedNss);
     station->m_lastChannelWidth = channelWidth;
-    if (maxMode.GetModulationClass() == WIFI_MOD_CLASS_HE)
+    if ((maxMode.GetModulationClass() >= WIFI_MOD_CLASS_HE))
     {
         guardInterval = std::max(GetGuardInterval(station), GetGuardInterval());
     }
-    else if ((maxMode.GetModulationClass() == WIFI_MOD_CLASS_HT) ||
-             (maxMode.GetModulationClass() == WIFI_MOD_CLASS_VHT))
+    else if ((maxMode.GetModulationClass() >= WIFI_MOD_CLASS_HT))
     {
         guardInterval =
             static_cast<uint16_t>(std::max(GetShortGuardIntervalSupported(station) ? 400 : 800,
@@ -643,4 +585,62 @@
     return snr;
 }
 
+bool
+IdealWifiManager::IsModulationClassSupported(WifiModulationClass mc,
+                                             IdealWifiRemoteStation* station)
+{
+    switch (mc)
+    {
+    case WIFI_MOD_CLASS_HT:
+        return (GetHtSupported() && GetHtSupported(station));
+    case WIFI_MOD_CLASS_VHT:
+        return (GetVhtSupported() && GetVhtSupported(station));
+    case WIFI_MOD_CLASS_HE:
+        return (GetHeSupported() && GetHeSupported(station));
+    case WIFI_MOD_CLASS_EHT:
+        return (GetEhtSupported() && GetEhtSupported(station));
+    default:
+        NS_ABORT_MSG("Unknown modulation class: " << mc);
+    }
+}
+
+bool
+IdealWifiManager::IsCandidateModulationClass(WifiModulationClass mc,
+                                             IdealWifiRemoteStation* station)
+{
+    if (!IsModulationClassSupported(mc, station))
+    {
+        return false;
+    }
+    switch (mc)
+    {
+    case WIFI_MOD_CLASS_HT:
+        // If the node and peer are both VHT capable, skip non-VHT modes
+        if (GetVhtSupported() && GetVhtSupported(station))
+        {
+            return false;
+        }
+        [[fallthrough]];
+    case WIFI_MOD_CLASS_VHT:
+        // If the node and peer are both HE capable, skip non-HE modes
+        if (GetHeSupported() && GetHeSupported(station))
+        {
+            return false;
+        }
+        [[fallthrough]];
+    case WIFI_MOD_CLASS_HE:
+        // If the node and peer are both EHT capable, skip non-EHT modes
+        if (GetEhtSupported() && GetEhtSupported(station))
+        {
+            return false;
+        }
+        break;
+    case WIFI_MOD_CLASS_EHT:
+        break;
+    default:
+        NS_ABORT_MSG("Unknown modulation class: " << mc);
+    }
+    return true;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/rate-control/ideal-wifi-manager.h ns-3.41/src/wifi/model/rate-control/ideal-wifi-manager.h
--- ns-3.40/src/wifi/model/rate-control/ideal-wifi-manager.h	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/rate-control/ideal-wifi-manager.h	2024-02-09 14:44:08.593795000 -0800
@@ -139,6 +139,23 @@
                               uint8_t nss) const;
 
     /**
+     * Check whether a given modulation class is supported by both the node and the peer
+     * \param mc the modulation class
+     * \param station the peer station
+     * \return true if the modulation class can be used, false otherwise
+     */
+    bool IsModulationClassSupported(WifiModulationClass mc, IdealWifiRemoteStation* station);
+
+    /**
+     * Check whether a given modulation class is supported and that there are no higher modulation
+     * classes that should instead be candidates
+     * \param mc the modulation class
+     * \param station the peer station
+     * \return true if the modulation class is a candidate, false otherwise
+     */
+    bool IsCandidateModulationClass(WifiModulationClass mc, IdealWifiRemoteStation* station);
+
+    /**
      * A vector of <snr, WifiTxVector> pair holding the minimum SNR for the
      * WifiTxVector
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/rate-control/minstrel-wifi-manager.cc ns-3.41/src/wifi/model/rate-control/minstrel-wifi-manager.cc
--- ns-3.40/src/wifi/model/rate-control/minstrel-wifi-manager.cc	2023-09-27 13:30:37.910839800 -0700
+++ ns-3.41/src/wifi/model/rate-control/minstrel-wifi-manager.cc	2024-02-09 14:44:08.593795000 -0800
@@ -644,7 +644,7 @@
 
             // calculating throughput
             station->m_minstrelTable[i].throughput =
-                tempProb * static_cast<uint32_t>((1000000 / txTime.GetMicroSeconds()));
+                tempProb * static_cast<uint32_t>(1000000 / txTime.GetMicroSeconds());
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/sta-wifi-mac.cc ns-3.41/src/wifi/model/sta-wifi-mac.cc
--- ns-3.40/src/wifi/model/sta-wifi-mac.cc	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/sta-wifi-mac.cc	2024-02-09 14:44:08.597795000 -0800
@@ -23,6 +23,7 @@
 
 #include "channel-access-manager.h"
 #include "frame-exchange-manager.h"
+#include "mgt-action-headers.h"
 #include "mgt-headers.h"
 #include "qos-txop.h"
 #include "snr-tag.h"
@@ -135,7 +136,9 @@
                             "A link setup in the context of ML setup with an AP MLD was torn down. "
                             "Provides ID of the setup link and AP MAC address",
                             MakeTraceSourceAccessor(&StaWifiMac::m_setupCanceled),
-                            "ns3::StaWifiMac::LinkSetupCallback")
+                            "ns3::StaWifiMac::LinkSetupCallback",
+                            TypeId::OBSOLETE,
+                            "Disassociation only occurs at MLD level; use DeAssoc trace.")
             .AddTraceSource("BeaconArrival",
                             "Time of beacons arrival from associated AP",
                             MakeTraceSourceAccessor(&StaWifiMac::m_beaconArrival),
@@ -463,6 +466,9 @@
         // When the Transition Timeout subfield is included in a frame sent by a non-AP STA
         // affiliated with a non-AP MLD, the Transition Timeout subfield is reserved
         // (Section 9.4.2.312.2.3 of 802.11be D2.3)
+        // The Medium Synchronization Delay Information subfield in the Common Info subfield is
+        // not present if the Basic Multi-Link element is sent by a non-AP STA. (Section
+        // 9.4.2.312.2.3 of 802.11be D3.1)
     }
 
     // The MLD Capabilities And Operations subfield is present in the Common Info field of the
@@ -475,7 +481,7 @@
 
     auto ehtConfiguration = GetEhtConfiguration();
     NS_ASSERT(ehtConfiguration);
-    EnumValue negSupport;
+    EnumValue<WifiTidToLinkMappingNegSupport> negSupport;
     ehtConfiguration->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport);
 
     mldCapabilities->tidToLinkMappingSupport = negSupport.Get();
@@ -523,7 +529,7 @@
     auto ehtConfig = GetEhtConfiguration();
     NS_ASSERT(ehtConfig);
 
-    EnumValue negSupport;
+    EnumValue<WifiTidToLinkMappingNegSupport> negSupport;
     ehtConfig->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport);
 
     NS_ABORT_MSG_IF(negSupport.Get() == 0,
@@ -809,14 +815,9 @@
     };
     Time beaconInterval = std::visit(getBeaconInterval, bestAp->m_frame);
     Time delay = beaconInterval * m_maxMissedBeacons;
-    // restart beacon watchdog for all links to setup
-    for (const auto& [id, link] : GetLinks())
-    {
-        if (GetStaLink(link).bssid.has_value() || GetNLinks() == 1)
-        {
-            RestartBeaconWatchdog(delay, id);
-        }
-    }
+    // restart beacon watchdog
+    RestartBeaconWatchdog(delay);
+
     SetState(WAIT_ASSOC_RESP);
     SendAssociationRequest(false);
 }
@@ -830,92 +831,73 @@
 }
 
 void
-StaWifiMac::MissedBeacons(uint8_t linkId)
+StaWifiMac::MissedBeacons()
 {
-    NS_LOG_FUNCTION(this << +linkId);
-    auto& link = GetLink(linkId);
-    if (link.beaconWatchdogEnd > Simulator::Now())
+    NS_LOG_FUNCTION(this);
+
+    if (m_beaconWatchdogEnd > Simulator::Now())
     {
-        if (link.beaconWatchdog.IsRunning())
+        if (m_beaconWatchdog.IsRunning())
         {
-            link.beaconWatchdog.Cancel();
+            m_beaconWatchdog.Cancel();
         }
-        link.beaconWatchdog = Simulator::Schedule(link.beaconWatchdogEnd - Simulator::Now(),
-                                                  &StaWifiMac::MissedBeacons,
-                                                  this,
-                                                  linkId);
+        m_beaconWatchdog = Simulator::Schedule(m_beaconWatchdogEnd - Simulator::Now(),
+                                               &StaWifiMac::MissedBeacons,
+                                               this);
         return;
     }
     NS_LOG_DEBUG("beacon missed");
-    // We need to switch to the UNASSOCIATED state. However, if we are receiving
-    // a frame, wait until the RX is completed (otherwise, crashes may occur if
-    // we are receiving a MU frame because its reception requires the STA-ID)
+    // We need to switch to the UNASSOCIATED state. However, if we are receiving a frame, wait
+    // until the RX is completed (otherwise, crashes may occur if we are receiving a MU frame
+    // because its reception requires the STA-ID). We need to check that a PHY is operating on
+    // the given link, because this may (temporarily) not be the case for EMLSR clients.
     Time delay = Seconds(0);
-    if (GetWifiPhy(linkId)->IsStateRx())
+    for (const auto& [id, link] : GetLinks())
     {
-        delay = GetWifiPhy(linkId)->GetDelayUntilIdle();
+        if (link->phy && link->phy->IsStateRx())
+        {
+            delay = std::max(delay, link->phy->GetDelayUntilIdle());
+        }
     }
-    Simulator::Schedule(delay, &StaWifiMac::Disassociated, this, linkId);
+    Simulator::Schedule(delay, &StaWifiMac::Disassociated, this);
 }
 
 void
-StaWifiMac::Disassociated(uint8_t linkId)
+StaWifiMac::Disassociated()
 {
-    NS_LOG_FUNCTION(this << +linkId);
-
-    auto& link = GetLink(linkId);
-    if (link.bssid.has_value())
-    {
-        // this is a link setup in an ML setup
-        m_setupCanceled(linkId, GetBssid(linkId));
-    }
+    NS_LOG_FUNCTION(this);
 
-    // disable the given link
-    link.bssid = std::nullopt;
-    link.phy->SetOffMode();
+    Mac48Address apAddr; // the AP address to trace (MLD address in case of ML setup)
 
-    for (const auto& [id, lnk] : GetLinks())
+    for (const auto& [id, link] : GetLinks())
     {
-        if (GetStaLink(lnk).bssid.has_value())
+        auto& bssid = GetStaLink(link).bssid;
+        if (bssid)
         {
-            // found an enabled link
-            return;
+            apAddr = GetWifiRemoteStationManager(id)->GetMldAddress(*bssid).value_or(*bssid);
         }
+        bssid = std::nullopt; // link is no longer setup
     }
 
     NS_LOG_DEBUG("Set state to UNASSOCIATED and start scanning");
     SetState(UNASSOCIATED);
     // cancel the association request timer (see issue #862)
     m_assocRequestEvent.Cancel();
-    auto mldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(GetBssid(linkId));
-    if (GetNLinks() > 1 && mldAddress.has_value())
-    {
-        // trace the AP MLD address
-        m_deAssocLogger(*mldAddress);
-    }
-    else
-    {
-        m_deAssocLogger(GetBssid(linkId));
-    }
+    m_deAssocLogger(apAddr);
     m_aid = 0; // reset AID
-    // ensure all links are on
-    for (const auto& [id, lnk] : GetLinks())
-    {
-        lnk->phy->ResumeFromOff();
-    }
     TryToEnsureAssociated();
 }
 
 void
-StaWifiMac::RestartBeaconWatchdog(Time delay, uint8_t linkId)
+StaWifiMac::RestartBeaconWatchdog(Time delay)
 {
-    NS_LOG_FUNCTION(this << delay << +linkId);
-    auto& link = GetLink(linkId);
-    link.beaconWatchdogEnd = std::max(Simulator::Now() + delay, link.beaconWatchdogEnd);
-    if (Simulator::GetDelayLeft(link.beaconWatchdog) < delay && link.beaconWatchdog.IsExpired())
+    NS_LOG_FUNCTION(this << delay);
+
+    m_beaconWatchdogEnd = std::max(Simulator::Now() + delay, m_beaconWatchdogEnd);
+    if (Simulator::GetDelayLeft(m_beaconWatchdog) < delay && m_beaconWatchdog.IsExpired())
     {
         NS_LOG_DEBUG("really restart watchdog.");
-        link.beaconWatchdog = Simulator::Schedule(delay, &StaWifiMac::MissedBeacons, this, linkId);
+        m_beaconWatchdog = Simulator::Schedule(delay, &StaWifiMac::MissedBeacons, this);
     }
 }
 
@@ -962,7 +944,7 @@
 bool
 StaWifiMac::CanForwardPacketsTo(Mac48Address to) const
 {
-    return (IsAssociated());
+    return IsAssociated();
 }
 
 void
@@ -1060,7 +1042,7 @@
 
     BlockUnicastTxOnLinks(reason, apAddress, {linkId});
     // the only type of broadcast frames that a non-AP STA can send are management frames
-    for (const auto [acIndex, ac] : wifiAcList)
+    for (const auto& [acIndex, ac] : wifiAcList)
     {
         GetMacQueueScheduler()->BlockQueues(reason,
                                             acIndex,
@@ -1082,7 +1064,7 @@
 
     UnblockUnicastTxOnLinks(reason, apAddress, {linkId});
     // the only type of broadcast frames that a non-AP STA can send are management frames
-    for (const auto [acIndex, ac] : wifiAcList)
+    for (const auto& [acIndex, ac] : wifiAcList)
     {
         GetMacQueueScheduler()->UnblockQueues(reason,
                                               acIndex,
@@ -1263,7 +1245,7 @@
         m_beaconArrival(Simulator::Now());
         Time delay = MicroSeconds(std::get<MgtBeaconHeader>(apInfo.m_frame).GetBeaconIntervalUs() *
                                   m_maxMissedBeacons);
-        RestartBeaconWatchdog(delay, linkId);
+        RestartBeaconWatchdog(delay);
         UpdateApInfo(apInfo.m_frame, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
     }
     else
@@ -1461,6 +1443,31 @@
         }
     }
 
+    // the station that associated with the AP may have dissociated and then associated again.
+    // In this case, the station may store packets from the previous period in which it was
+    // associated. Have the station restart access if it has packets queued.
+    for (const auto& [id, link] : GetLinks())
+    {
+        if (GetStaLink(link).bssid)
+        {
+            if (const auto txop = GetTxop())
+            {
+                txop->StartAccessAfterEvent(id,
+                                            Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
+                                            Txop::CHECK_MEDIUM_BUSY);
+            }
+            for (const auto [acIndex, ac] : wifiAcList)
+            {
+                if (const auto edca = GetQosTxop(acIndex))
+                {
+                    edca->StartAccessAfterEvent(id,
+                                                Txop::DIDNT_HAVE_FRAMES_TO_TRANSMIT,
+                                                Txop::CHECK_MEDIUM_BUSY);
+                }
+            }
+        }
+    }
+
     SetPmModeAfterAssociation(linkId);
 }
 
@@ -1473,7 +1480,7 @@
     // acknowledgement of the Association Response. For this purpose, we connect a callback to
     // the PHY TX begin trace to catch the Ack transmitted after the Association Response.
     CallbackBase cb = Callback<void, WifiConstPsduMap, WifiTxVector, double>(
-        [=](WifiConstPsduMap psduMap, WifiTxVector txVector, double /* txPowerW */) {
+        [=, this](WifiConstPsduMap psduMap, WifiTxVector txVector, double /* txPowerW */) {
             NS_ASSERT_MSG(psduMap.size() == 1 && psduMap.begin()->second->GetNMpdus() == 1 &&
                               psduMap.begin()->second->GetHeader(0).IsAck(),
                           "Expected a Normal Ack after Association Response frame");
@@ -1782,10 +1789,18 @@
         // is supported by the peer
         GetWifiRemoteStationManager(linkId)->AddStationEhtCapabilities(apAddr, *ehtCapabilities);
 
-        if (const auto& mle = frame.template Get<MultiLinkElement>();
-            mle && mle->HasEmlCapabilities() && m_emlsrManager)
+        if (const auto& mle = frame.template Get<MultiLinkElement>(); mle && m_emlsrManager)
         {
-            m_emlsrManager->SetTransitionTimeout(mle->GetTransitionTimeout());
+            if (mle->HasEmlCapabilities())
+            {
+                m_emlsrManager->SetTransitionTimeout(mle->GetTransitionTimeout());
+            }
+            if (const auto& common = mle->GetCommonInfoBasic(); common.m_mediumSyncDelayInfo)
+            {
+                m_emlsrManager->SetMediumSyncDuration(common.GetMediumSyncDelayTimer());
+                m_emlsrManager->SetMediumSyncOfdmEdThreshold(common.GetMediumSyncOfdmEdThreshold());
+                m_emlsrManager->SetMediumSyncMaxNTxops(common.GetMediumSyncMaxNTxops());
+            }
         }
     };
 
@@ -2013,34 +2028,52 @@
  */
 
 void
-StaWifiMac::NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId)
+StaWifiMac::NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId, Time delay)
 {
-    NS_LOG_FUNCTION(this << phy << linkId);
+    NS_LOG_FUNCTION(this << phy << linkId << delay.As(Time::US));
 
-    // if any link points to the PHY that switched channel, reset the phy pointer
+    // If the PHY is switching channel to operate on another link, then it is no longer operating
+    // on the current link. If any link (other than the current link) points to the PHY that is
+    // switching channel, reset the phy pointer of the link
     for (auto& [id, link] : GetLinks())
     {
-        // auto& link = GetStaLink(lnk);
-        if (link->phy == phy)
+        if (link->phy == phy && id != linkId)
         {
             link->phy = nullptr;
         }
     }
 
-    auto& newLink = GetLink(linkId);
-    // The MAC stack associated with the new link uses the given PHY
-    newLink.phy = phy;
-    // Setup a PHY listener for the given PHY on the CAM associated with the new link
-    newLink.channelAccessManager->SetupPhyListener(phy);
-    NS_ASSERT(m_emlsrManager);
-    if (m_emlsrManager->GetCamStateReset())
-    {
-        newLink.channelAccessManager->ResetState();
-    }
-    // Disconnect the FEM on the new link from the current PHY
-    newLink.feManager->ResetPhy();
-    // Connect the FEM on the new link to the given PHY
-    newLink.feManager->SetWifiPhy(phy);
+    // lambda to connect the PHY to the new link
+    auto connectPhy = [=, this]() mutable {
+        auto& newLink = GetLink(linkId);
+        // The MAC stack associated with the new link uses the given PHY
+        newLink.phy = phy;
+        // Setup a PHY listener for the given PHY on the CAM associated with the new link
+        newLink.channelAccessManager->SetupPhyListener(phy);
+        NS_ASSERT(m_emlsrManager);
+        if (m_emlsrManager->GetCamStateReset())
+        {
+            newLink.channelAccessManager->ResetState();
+        }
+        // Disconnect the FEM on the new link from the current PHY
+        newLink.feManager->ResetPhy();
+        // Connect the FEM on the new link to the given PHY
+        newLink.feManager->SetWifiPhy(phy);
+        // Connect the station manager on the new link to the given PHY
+        newLink.stationManager->SetupPhy(phy);
+    };
+
+    // if there is no PHY operating on the new link, connect the PHY to the new link now.
+    // Otherwise, wait until the channel switch is completed, so that the PHY operating on the new
+    // link can possibly continue receiving frames in the meantime.
+    if (!GetLink(linkId).phy)
+    {
+        connectPhy();
+    }
+    else
+    {
+        Simulator::Schedule(delay, connectPhy);
+    }
 }
 
 void
@@ -2052,7 +2085,7 @@
 
     if (IsInitialized() && IsAssociated())
     {
-        Disassociated(linkId);
+        Disassociated();
     }
 
     // notify association manager
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/sta-wifi-mac.h ns-3.41/src/wifi/model/sta-wifi-mac.h
--- ns-3.40/src/wifi/model/sta-wifi-mac.h	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/sta-wifi-mac.h	2024-02-09 14:44:08.597795000 -0800
@@ -28,9 +28,7 @@
 #include <set>
 #include <variant>
 
-class TwoLevelAggregationTest;
 class AmpduAggregationTest;
-class HeAggregationTest;
 class MultiLinkOperationsTestBase;
 
 namespace ns3
@@ -144,12 +142,8 @@
 {
   public:
     /// Allow test cases to access private members
-    friend class ::TwoLevelAggregationTest;
-    /// Allow test cases to access private members
     friend class ::AmpduAggregationTest;
     /// Allow test cases to access private members
-    friend class ::HeAggregationTest;
-    /// Allow test cases to access private members
     friend class ::MultiLinkOperationsTestBase;
 
     /// type of the management frames used to get info about APs
@@ -313,8 +307,9 @@
      *
      * \param phy the given PHY
      * \param linkId the ID of the EMLSR link on which the given PHY is operating
+     * \param delay the delay after which the channel switch will be completed
      */
-    void NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId);
+    void NotifySwitchingEmlsrLink(Ptr<WifiPhy> phy, uint8_t linkId, Time delay);
 
     /**
      * Block transmissions on the given link for the given reason.
@@ -357,8 +352,6 @@
         bool sendAssocReq;                 //!< whether this link is used to send the
                                            //!< Association Request frame
         std::optional<Mac48Address> bssid; //!< BSSID of the AP to associate with over this link
-        EventId beaconWatchdog;            //!< beacon watchdog
-        Time beaconWatchdogEnd{0};         //!< beacon watchdog end
         WifiPowerManagementMode pmMode{WIFI_PM_ACTIVE}; /**< the current PM mode, if the STA is
                                                              associated, or the PM mode to switch
                                                              to upon association, otherwise */
@@ -506,27 +499,19 @@
      */
     bool IsWaitAssocResp() const;
     /**
-     * This method is called after we have not received a beacon from the AP
-     * on the given link.
-     *
-     * \param linkId the ID of the given link
+     * This method is called after we have not received a beacon from the AP on any link.
      */
-    void MissedBeacons(uint8_t linkId);
+    void MissedBeacons();
     /**
-     * Restarts the beacon timer for the given link.
+     * Restarts the beacon timer.
      *
      * \param delay the delay before the watchdog fires
-     * \param linkId the ID of the given link
      */
-    void RestartBeaconWatchdog(Time delay, uint8_t linkId);
+    void RestartBeaconWatchdog(Time delay);
     /**
-     * Check if any enabled link remains after the given link is disabled (because,
-     * e.g., the maximum number of beacons is missed or the channel is switched).
-     * If no enabled link remains, proceed with disassociation.
-     *
-     * \param linkId the ID of the given link
+     * Set the state to unassociated and try to associate again.
      */
-    void Disassociated(uint8_t linkId);
+    void Disassociated();
     /**
      * Return an instance of SupportedRates that contains all rates that we support
      * including HT rates.
@@ -632,6 +617,8 @@
     Time m_assocRequestTimeout;             ///< association request timeout
     EventId m_assocRequestEvent;            ///< association request event
     uint32_t m_maxMissedBeacons;            ///< maximum missed beacons
+    EventId m_beaconWatchdog;               //!< beacon watchdog
+    Time m_beaconWatchdogEnd{0};            //!< beacon watchdog end
     bool m_activeProbing;                   ///< active probing
     Ptr<RandomVariableStream> m_probeDelay; ///< RandomVariable used to randomize the time
                                             ///< of the first Probe Response on each channel
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/txop.cc ns-3.41/src/wifi/model/txop.cc
--- ns-3.40/src/wifi/model/txop.cc	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/txop.cc	2024-02-09 14:44:08.597795000 -0800
@@ -230,7 +230,6 @@
 Ptr<WifiMacQueue>
 Txop::GetWifiMacQueue() const
 {
-    NS_LOG_FUNCTION(this);
     return m_queue;
 }
 
@@ -535,17 +534,29 @@
 {
     NS_LOG_FUNCTION(this << *mpdu);
     const auto linkIds = m_mac->GetMacQueueScheduler()->GetLinkIds(m_queue->GetAc(), mpdu);
+    std::map<uint8_t, bool> hasFramesToTransmit;
+
+    // save the status of the AC queues before enqueuing the MPDU (required to determine if
+    // backoff is needed)
     for (const auto linkId : linkIds)
     {
-        if (m_mac->GetChannelAccessManager(linkId)->NeedBackoffUponAccess(this))
-        {
-            GenerateBackoff(linkId);
-        }
+        hasFramesToTransmit[linkId] = HasFramesToTransmit(linkId);
     }
     m_queue->Enqueue(mpdu);
     for (const auto linkId : linkIds)
     {
-        StartAccessIfNeeded(linkId);
+        // schedule a call to StartAccessIfNeeded() to request channel access after that all the
+        // packets of a burst have been enqueued, instead of requesting channel access right after
+        // the first packet. The call to StartAccessIfNeeded() is scheduled only after the first
+        // packet
+        if (auto& event = GetLink(linkId).accessRequest.event; !event.IsRunning())
+        {
+            event = Simulator::ScheduleNow(&Txop::StartAccessAfterEvent,
+                                           this,
+                                           linkId,
+                                           hasFramesToTransmit.at(linkId),
+                                           CHECK_MEDIUM_BUSY);
+        }
     }
 }
 
@@ -558,13 +569,24 @@
 }
 
 void
-Txop::StartAccessIfNeeded(uint8_t linkId)
+Txop::StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy)
 {
-    NS_LOG_FUNCTION(this << +linkId);
-    if (HasFramesToTransmit(linkId) && GetLink(linkId).access == NOT_REQUESTED)
+    NS_LOG_FUNCTION(this << +linkId << hadFramesToTransmit << checkMediumBusy);
+
+    if (GetLink(linkId).access != NOT_REQUESTED || !HasFramesToTransmit(linkId))
     {
-        m_mac->GetChannelAccessManager(linkId)->RequestAccess(this);
+        NS_LOG_DEBUG("No need to request channel access on link " << +linkId);
+        return;
+    }
+
+    if (m_mac->GetChannelAccessManager(linkId)->NeedBackoffUponAccess(this,
+                                                                      hadFramesToTransmit,
+                                                                      checkMediumBusy))
+    {
+        GenerateBackoff(linkId);
     }
+
+    m_mac->GetChannelAccessManager(linkId)->RequestAccess(this);
 }
 
 void
@@ -646,7 +668,8 @@
 Txop::NotifyWakeUp(uint8_t linkId)
 {
     NS_LOG_FUNCTION(this << +linkId);
-    StartAccessIfNeeded(linkId);
+    // before wake up, no packet can be transmitted
+    StartAccessAfterEvent(linkId, DIDNT_HAVE_FRAMES_TO_TRANSMIT, DONT_CHECK_MEDIUM_BUSY);
 }
 
 void
@@ -655,7 +678,8 @@
     NS_LOG_FUNCTION(this);
     for (const auto& [id, link] : m_links)
     {
-        StartAccessIfNeeded(id);
+        // before being turned on, no packet can be transmitted
+        StartAccessAfterEvent(id, DIDNT_HAVE_FRAMES_TO_TRANSMIT, DONT_CHECK_MEDIUM_BUSY);
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/txop.h ns-3.41/src/wifi/model/txop.h
--- ns-3.40/src/wifi/model/txop.h	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/txop.h	2024-02-09 14:44:08.597795000 -0800
@@ -30,6 +30,8 @@
 #include <memory>
 #include <vector>
 
+class EmlsrUlTxopTest;
+
 namespace ns3
 {
 
@@ -394,6 +396,32 @@
     virtual ChannelAccessStatus GetAccessStatus(uint8_t linkId) const;
 
     /**
+     * Request channel access on the given link after the occurrence of an event that possibly
+     * requires to generate a new backoff value. Examples of such an event are: a packet has been
+     * enqueued by the upper layer; the given link has been unblocked after being blocked for some
+     * reason (e.g., wait for ADDBA Response, wait for TX on another EMLSR link to finish, etc.);
+     * the PHY operating on the given link just woke up from sleep mode. The <i>checkMediumBusy</i>
+     * argument is forwarded to the NeedBackoffUponAccess method of the ChannelAccessManager.
+     *
+     * \param linkId the ID of the given link
+     * \param hadFramesToTransmit whether packets available for transmission were queued just
+     *                            before the occurrence of the event causing this channel access
+     *                            request
+     * \param checkMediumBusy whether generation of backoff (also) depends on the busy/idle state
+     *                        of the medium
+     */
+    void StartAccessAfterEvent(uint8_t linkId, bool hadFramesToTransmit, bool checkMediumBusy);
+
+    static constexpr bool HAD_FRAMES_TO_TRANSMIT =
+        true; //!< packets available for transmission were in the queue
+    static constexpr bool DIDNT_HAVE_FRAMES_TO_TRANSMIT =
+        false; //!< no packet available for transmission was in the queue
+    static constexpr bool CHECK_MEDIUM_BUSY =
+        true; //!< generation of backoff (also) depends on the busy/idle state of the medium
+    static constexpr bool DONT_CHECK_MEDIUM_BUSY =
+        false; //!< generation of backoff is independent of the busy/idle state of the medium
+
+    /**
      * \param nSlots the number of slots of the backoff.
      * \param linkId the ID of the given link
      *
@@ -422,6 +450,7 @@
   protected:
     ///< ChannelAccessManager associated class
     friend class ChannelAccessManager;
+    friend class ::EmlsrUlTxopTest;
 
     void DoDispose() override;
     void DoInitialize() override;
@@ -441,12 +470,6 @@
      */
     virtual void GenerateBackoff(uint8_t linkId);
     /**
-     * Request access from Txop on the given link if needed.
-     *
-     * \param linkId the ID of the given link
-     */
-    virtual void StartAccessIfNeeded(uint8_t linkId);
-    /**
      * Request access to the ChannelAccessManager associated with the given link
      *
      * \param linkId the ID of the given link
@@ -505,6 +528,12 @@
         uint8_t aifsn{0};                          //!< the AIFSN
         Time txopLimit{0};                         //!< the TXOP limit time
         ChannelAccessStatus access{NOT_REQUESTED}; //!< channel access status
+
+        mutable class
+        {
+            friend void Txop::Queue(Ptr<WifiMpdu>);
+            EventId event;
+        } accessRequest; //!< access request event, to be used by Txop::Queue() only
     };
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/vht/vht-phy.cc ns-3.41/src/wifi/model/vht/vht-phy.cc
--- ns-3.40/src/wifi/model/vht/vht-phy.cc	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/vht/vht-phy.cc	2024-02-09 14:44:08.597795000 -0800
@@ -369,7 +369,7 @@
     {                                                                                              \
         static WifiMode mcs = CreateVhtMcs(x);                                                     \
         return mcs;                                                                                \
-    };
+    }
 
 GET_VHT_MCS(0)
 GET_VHT_MCS(1)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-assoc-manager.cc ns-3.41/src/wifi/model/wifi-assoc-manager.cc
--- ns-3.40/src/wifi/model/wifi-assoc-manager.cc	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/wifi-assoc-manager.cc	2024-02-09 14:44:08.601795200 -0800
@@ -305,7 +305,7 @@
     {
         auto ehtConfig = m_mac->GetEhtConfiguration();
         NS_ASSERT(ehtConfig);
-        EnumValue negSupport;
+        EnumValue<WifiTidToLinkMappingNegSupport> negSupport;
         ehtConfig->GetAttributeFailSafe("TidToLinkMappingNegSupport", negSupport);
 
         // A non-AP MLD that performs multi-link (re)setup on at least two links with an AP MLD
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-bandwidth-filter.cc ns-3.41/src/wifi/model/wifi-bandwidth-filter.cc
--- ns-3.40/src/wifi/model/wifi-bandwidth-filter.cc	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/wifi-bandwidth-filter.cc	2024-02-09 14:44:08.601795200 -0800
@@ -137,4 +137,10 @@
     return filter;
 }
 
+int64_t
+WifiBandwidthFilter::DoAssignStreams(int64_t stream)
+{
+    return 0;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-bandwidth-filter.h ns-3.41/src/wifi/model/wifi-bandwidth-filter.h
--- ns-3.40/src/wifi/model/wifi-bandwidth-filter.h	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/wifi-bandwidth-filter.h	2024-02-09 14:44:08.601795200 -0800
@@ -45,6 +45,9 @@
 
     bool DoFilter(Ptr<const SpectrumSignalParameters> params,
                   Ptr<const SpectrumPhy> receiverPhy) override;
+
+  protected:
+    int64_t DoAssignStreams(int64_t stream) override;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-default-ack-manager.cc ns-3.41/src/wifi/model/wifi-default-ack-manager.cc
--- ns-3.40/src/wifi/model/wifi-default-ack-manager.cc	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/wifi-default-ack-manager.cc	2024-02-09 14:44:08.601795200 -0800
@@ -60,16 +60,17 @@
                           DoubleValue(0.0),
                           MakeDoubleAccessor(&WifiDefaultAckManager::m_baThreshold),
                           MakeDoubleChecker<double>(0.0, 1.0))
-            .AddAttribute("DlMuAckSequenceType",
-                          "Type of the acknowledgment sequence for DL MU PPDUs.",
-                          EnumValue(WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE),
-                          MakeEnumAccessor(&WifiDefaultAckManager::m_dlMuAckType),
-                          MakeEnumChecker(WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE,
-                                          "DL_MU_BAR_BA_SEQUENCE",
-                                          WifiAcknowledgment::DL_MU_TF_MU_BAR,
-                                          "DL_MU_TF_MU_BAR",
-                                          WifiAcknowledgment::DL_MU_AGGREGATE_TF,
-                                          "DL_MU_AGGREGATE_TF"))
+            .AddAttribute(
+                "DlMuAckSequenceType",
+                "Type of the acknowledgment sequence for DL MU PPDUs.",
+                EnumValue(WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE),
+                MakeEnumAccessor<WifiAcknowledgment::Method>(&WifiDefaultAckManager::m_dlMuAckType),
+                MakeEnumChecker(WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE,
+                                "DL_MU_BAR_BA_SEQUENCE",
+                                WifiAcknowledgment::DL_MU_TF_MU_BAR,
+                                "DL_MU_TF_MU_BAR",
+                                WifiAcknowledgment::DL_MU_AGGREGATE_TF,
+                                "DL_MU_AGGREGATE_TF"))
             .AddAttribute("MaxBlockAckMcs",
                           "The MCS used to send a BlockAck in a TB PPDU is the minimum between "
                           "the MCS used for the PSDU sent in the preceding DL MU PPDU and the "
@@ -572,7 +573,9 @@
                                           m_mac->GetBaTypeAsOriginator(receiver, tid)});
 
         acknowledgment->barTypes.push_back(m_mac->GetBarTypeAsOriginator(receiver, tid));
-        acknowledgment->muBarTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(receiver);
+        acknowledgment->muBarTxVector =
+            GetWifiRemoteStationManager()->GetRtsTxVector(receiver,
+                                                          txParams.m_txVector.GetChannelWidth());
         acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::BLOCK_ACK);
         return std::unique_ptr<WifiDlMuTfMuBar>(acknowledgment);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-default-protection-manager.cc ns-3.41/src/wifi/model/wifi-default-protection-manager.cc
--- ns-3.40/src/wifi/model/wifi-default-protection-manager.cc	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/wifi-default-protection-manager.cc	2024-02-09 14:44:08.601795200 -0800
@@ -190,8 +190,8 @@
         return std::make_unique<WifiNoProtection>();
     }
 
-    // when an EMLSR client starts an UL TXOP on a link on which the main PHY is not operating,
-    // the aux PHY sends an RTS frame
+    // when an EMLSR client starts an UL TXOP on a link while the MediumSyncDelay timer is running
+    // or on a link on which the main PHY is not operating, it needs to send an RTS frame
     bool emlsrNeedRts = false;
 
     if (auto staMac = DynamicCast<StaWifiMac>(m_mac))
@@ -199,14 +199,17 @@
         auto emlsrManager = staMac->GetEmlsrManager();
 
         emlsrNeedRts = emlsrManager && staMac->IsEmlsrLink(m_linkId) &&
-                       m_mac->GetLinkForPhy(emlsrManager->GetMainPhyId()) != m_linkId;
+                       (emlsrManager->GetElapsedMediumSyncDelayTimer(m_linkId) ||
+                        m_mac->GetLinkForPhy(emlsrManager->GetMainPhyId()) != m_linkId);
     }
 
     // check if RTS/CTS is needed
     if (emlsrNeedRts || GetWifiRemoteStationManager()->NeedRts(hdr, size))
     {
         auto protection = std::make_unique<WifiRtsCtsProtection>();
-        protection->rtsTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(hdr.GetAddr1());
+        protection->rtsTxVector =
+            GetWifiRemoteStationManager()->GetRtsTxVector(hdr.GetAddr1(),
+                                                          txVector.GetChannelWidth());
         protection->ctsTxVector =
             GetWifiRemoteStationManager()->GetCtsTxVector(hdr.GetAddr1(),
                                                           protection->rtsTxVector.GetMode());
@@ -300,7 +303,8 @@
             }
 
             // compute the TXVECTOR to use to send the MU-RTS Trigger Frame
-            protection->muRtsTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(receiver);
+            protection->muRtsTxVector =
+                GetWifiRemoteStationManager()->GetRtsTxVector(receiver, txWidth);
             // The transmitter of an MU-RTS Trigger frame shall not request a non-AP STA to send
             // a CTS frame response in a 20 MHz channel that is not occupied by the PPDU that
             // contains the MU-RTS Trigger frame. (Sec. 26.2.6.2 of 802.11ax)
@@ -390,7 +394,7 @@
 
     // compute the TXVECTOR to use to send the MU-RTS Trigger Frame
     protection->muRtsTxVector =
-        GetWifiRemoteStationManager()->GetRtsTxVector(mpdu->GetHeader().GetAddr1());
+        GetWifiRemoteStationManager()->GetRtsTxVector(mpdu->GetHeader().GetAddr1(), txWidth);
     // The transmitter of an MU-RTS Trigger frame shall not request a non-AP STA to send
     // a CTS frame response in a 20 MHz channel that is not occupied by the PPDU that
     // contains the MU-RTS Trigger frame. (Sec. 26.2.6.2 of 802.11ax)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-mac.cc ns-3.41/src/wifi/model/wifi-mac.cc
--- ns-3.40/src/wifi/model/wifi-mac.cc	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-mac.cc	2024-02-09 14:44:08.601795200 -0800
@@ -23,7 +23,7 @@
 #include "extended-capabilities.h"
 #include "mac-rx-middle.h"
 #include "mac-tx-middle.h"
-#include "mgt-headers.h"
+#include "mgt-action-headers.h"
 #include "qos-txop.h"
 #include "ssid.h"
 #include "wifi-mac-queue.h"
@@ -120,6 +120,14 @@
                           PointerValue(),
                           MakePointerAccessor(&WifiMac::GetBKQueue),
                           MakePointerChecker<QosTxop>())
+            .AddAttribute(
+                "MpduBufferSize",
+                "The size (in number of MPDUs) of the buffer used for each BlockAck "
+                "agreement in which this node is a recipient. The provided value is "
+                "capped to the maximum allowed value based on the supported standard.",
+                UintegerValue(1024),
+                MakeUintegerAccessor(&WifiMac::GetMpduBufferSize, &WifiMac::SetMpduBufferSize),
+                MakeUintegerChecker<uint16_t>(1, 1024))
             .AddAttribute("VO_MaxAmsduSize",
                           "Maximum length in bytes of an A-MSDU for AC_VO access class "
                           "(capped to 7935 for HT PPDUs and 11398 for VHT/HE/EHT PPDUs). "
@@ -289,19 +297,6 @@
                             "up from the physical layer.",
                             MakeTraceSourceAccessor(&WifiMac::m_macRxDropTrace),
                             "ns3::Packet::TracedCallback")
-            .AddTraceSource("TxOkHeader",
-                            "The header of successfully transmitted packet.",
-                            MakeTraceSourceAccessor(&WifiMac::m_txOkCallback),
-                            "ns3::WifiMacHeader::TracedCallback",
-                            TypeId::OBSOLETE,
-                            "Use the AckedMpdu trace instead.")
-            .AddTraceSource("TxErrHeader",
-                            "The header of unsuccessfuly transmitted packet.",
-                            MakeTraceSourceAccessor(&WifiMac::m_txErrCallback),
-                            "ns3::WifiMacHeader::TracedCallback",
-                            TypeId::OBSOLETE,
-                            "Depending on the failure type, use the NAckedMpdu trace, the "
-                            "DroppedMpdu trace or one of the traces associated with TX timeouts.")
             .AddTraceSource("AckedMpdu",
                             "An MPDU that was successfully acknowledged, via either a "
                             "Normal Ack or a Block Ack.",
@@ -433,6 +428,11 @@
 WifiMac::SetDevice(const Ptr<WifiNetDevice> device)
 {
     m_device = device;
+    if (GetHtSupported())
+    {
+        // the configured BlockAck buffer size can now be capped
+        m_mpduBufferSize = std::min(m_mpduBufferSize, GetMaxBaBufferSize());
+    }
 }
 
 Ptr<WifiNetDevice>
@@ -587,10 +587,8 @@
     // the PHY dependent parameters. In any case, this makes no harm
     ConfigurePhyDependentParameters(linkId);
 
-    // SetupPhy not only resets the remote station manager, but also sets the
-    // default TX mode and MCS, which is required when switching to a channel
-    // in a different band
-    GetLink(linkId).stationManager->SetupPhy(GetLink(linkId).phy);
+    // Reset remote station manager
+    GetLink(linkId).stationManager->Reset();
 }
 
 void
@@ -1404,7 +1402,7 @@
             continue;
         }
 
-        for (const auto [acIndex, ac] : wifiAcList)
+        for (const auto& [acIndex, ac] : wifiAcList)
         {
             // block queues storing QoS data frames and control frames that use MLD addresses
             m_scheduler->BlockQueues(reason,
@@ -1445,8 +1443,11 @@
             continue;
         }
 
-        for (const auto [acIndex, ac] : wifiAcList)
+        for (const auto& [acIndex, ac] : wifiAcList)
         {
+            // save the status of the AC queues before unblocking the requested queues
+            auto hasFramesToTransmit = GetQosTxop(acIndex)->HasFramesToTransmit(linkId);
+
             // unblock queues storing QoS data frames and control frames that use MLD addresses
             m_scheduler->UnblockQueues(reason,
                                        acIndex,
@@ -1465,14 +1466,11 @@
                                        {linkId});
             // request channel access if needed (schedule now because multiple invocations
             // of this method may be done in a loop at the caller)
-            auto qosTxop = GetQosTxop(acIndex);
-            Simulator::ScheduleNow([=]() {
-                if (qosTxop->GetAccessStatus(linkId) == Txop::NOT_REQUESTED &&
-                    qosTxop->HasFramesToTransmit(linkId))
-                {
-                    GetLink(linkId).channelAccessManager->RequestAccess(qosTxop);
-                }
-            });
+            Simulator::ScheduleNow(&Txop::StartAccessAfterEvent,
+                                   GetQosTxop(acIndex),
+                                   linkId,
+                                   hasFramesToTransmit,
+                                   Txop::CHECK_MEDIUM_BUSY); // generate backoff if medium busy
         }
     }
 }
@@ -1836,6 +1834,36 @@
     return false;
 }
 
+uint16_t
+WifiMac::GetMaxBaBufferSize(std::optional<Mac48Address> address) const
+{
+    if (address ? GetEhtSupported(*address) : GetEhtSupported())
+    {
+        return 1024;
+    }
+    if (address ? GetHeSupported(*address) : GetHeSupported())
+    {
+        return 256;
+    }
+    NS_ASSERT(address ? GetHtSupported(*address) : GetHtSupported());
+    return 64;
+}
+
+void
+WifiMac::SetMpduBufferSize(uint16_t size)
+{
+    NS_LOG_FUNCTION(this << size);
+
+    // the cap can be computed if the device has been configured
+    m_mpduBufferSize = m_device ? std::min(size, GetMaxBaBufferSize()) : size;
+}
+
+uint16_t
+WifiMac::GetMpduBufferSize() const
+{
+    return m_mpduBufferSize;
+}
+
 void
 WifiMac::SetVoBlockAckThreshold(uint8_t threshold)
 {
@@ -1940,7 +1968,7 @@
     capabilities.SetLdpc(htConfiguration->GetLdpcSupported());
     capabilities.SetSupportedChannelWidth(htConfiguration->Get40MHzOperationSupported() ? 1 : 0);
     capabilities.SetShortGuardInterval20(sgiSupported);
-    capabilities.SetShortGuardInterval40(phy->GetChannelWidth() >= 40 && sgiSupported);
+    capabilities.SetShortGuardInterval40(sgiSupported);
     // Set Maximum A-MSDU Length subfield
     uint16_t maxAmsduSize =
         std::max({m_voMaxAmsduSize, m_viMaxAmsduSize, m_beMaxAmsduSize, m_bkMaxAmsduSize});
@@ -1966,7 +1994,9 @@
         capabilities.SetRxMcsBitmask(mcs.GetMcsValue());
         uint8_t nss = (mcs.GetMcsValue() / 8) + 1;
         NS_ASSERT(nss > 0 && nss < 5);
-        uint64_t dataRate = mcs.GetDataRate(phy->GetChannelWidth(), sgiSupported ? 400 : 800, nss);
+        uint64_t dataRate = mcs.GetDataRate(htConfiguration->Get40MHzOperationSupported() ? 40 : 20,
+                                            sgiSupported ? 400 : 800,
+                                            nss);
         if (dataRate > maxSupportedRate)
         {
             maxSupportedRate = dataRate;
@@ -2022,8 +2052,8 @@
     capabilities.SetMaxAmpduLength(std::min(std::max(maxAmpduLength, 8191U), 1048575U));
 
     capabilities.SetRxLdpc(htConfiguration->GetLdpcSupported());
-    capabilities.SetShortGuardIntervalFor80Mhz((phy->GetChannelWidth() == 80) && sgiSupported);
-    capabilities.SetShortGuardIntervalFor160Mhz((phy->GetChannelWidth() == 160) && sgiSupported);
+    capabilities.SetShortGuardIntervalFor80Mhz(sgiSupported);
+    capabilities.SetShortGuardIntervalFor160Mhz(sgiSupported);
     uint8_t maxMcs = 0;
     for (const auto& mcs : phy->GetMcsList(WIFI_MOD_CLASS_VHT))
     {
@@ -2042,15 +2072,16 @@
         capabilities.SetTxMcsMap(maxMcs, nss);
     }
     uint64_t maxSupportedRateLGI = 0; // in bit/s
+    uint16_t maxWidth = vhtConfiguration->Get160MHzOperationSupported() ? 160 : 80;
     for (const auto& mcs : phy->GetMcsList(WIFI_MOD_CLASS_VHT))
     {
-        if (!mcs.IsAllowed(phy->GetChannelWidth(), 1))
+        if (!mcs.IsAllowed(maxWidth, 1))
         {
             continue;
         }
-        if (mcs.GetDataRate(phy->GetChannelWidth()) > maxSupportedRateLGI)
+        if (mcs.GetDataRate(maxWidth) > maxSupportedRateLGI)
         {
-            maxSupportedRateLGI = mcs.GetDataRate(phy->GetChannelWidth());
+            maxSupportedRateLGI = mcs.GetDataRate(maxWidth);
             NS_LOG_DEBUG("Updating maxSupportedRateLGI to " << maxSupportedRateLGI);
         }
     }
@@ -2074,18 +2105,20 @@
 
     Ptr<WifiPhy> phy = GetLink(linkId).phy;
     Ptr<HtConfiguration> htConfiguration = GetHtConfiguration();
+    Ptr<VhtConfiguration> vhtConfiguration = GetVhtConfiguration();
     Ptr<HeConfiguration> heConfiguration = GetHeConfiguration();
     uint8_t channelWidthSet = 0;
-    if ((phy->GetChannelWidth() >= 40) && (phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ))
+    if ((htConfiguration->Get40MHzOperationSupported()) &&
+        (phy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ))
     {
         channelWidthSet |= 0x01;
     }
-    if (((phy->GetChannelWidth() >= 80) || GetEhtSupported()) &&
-        ((phy->GetPhyBand() == WIFI_PHY_BAND_5GHZ) || (phy->GetPhyBand() == WIFI_PHY_BAND_6GHZ)))
+    // we assume that HE stations support 80 MHz operations
+    if ((phy->GetPhyBand() == WIFI_PHY_BAND_5GHZ) || (phy->GetPhyBand() == WIFI_PHY_BAND_6GHZ))
     {
         channelWidthSet |= 0x02;
     }
-    if ((phy->GetChannelWidth() >= 160) &&
+    if ((vhtConfiguration->Get160MHzOperationSupported()) &&
         ((phy->GetPhyBand() == WIFI_PHY_BAND_5GHZ) || (phy->GetPhyBand() == WIFI_PHY_BAND_6GHZ)))
     {
         channelWidthSet |= 0x04;
@@ -2167,7 +2200,7 @@
 
     const uint8_t maxTxNss = phy->GetMaxSupportedTxSpatialStreams();
     const uint8_t maxRxNss = phy->GetMaxSupportedRxSpatialStreams();
-    if (phy->GetChannelWidth() == 20)
+    if (auto htConfig = GetHtConfiguration(); !htConfig->Get40MHzOperationSupported())
     {
         for (auto maxMcs : {7, 9, 11, 13})
         {
@@ -2195,7 +2228,7 @@
                 phy->IsMcsSupported(WIFI_MOD_CLASS_EHT, maxMcs) ? maxTxNss : 0);
         }
     }
-    if (phy->GetChannelWidth() >= 160)
+    if (auto vhtConfig = GetVhtConfiguration(); vhtConfig->Get160MHzOperationSupported())
     {
         for (auto maxMcs : {9, 11, 13})
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-mac.h ns-3.41/src/wifi/model/wifi-mac.h
--- ns-3.40/src/wifi/model/wifi-mac.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-mac.h	2024-02-09 14:44:08.601795200 -0800
@@ -679,6 +679,27 @@
     BlockAckReqType GetBarTypeAsRecipient(Mac48Address originator, uint8_t tid) const;
 
     /**
+     * Get the maximum Block Ack buffer size (in number of MPDUs) supported by the given device,
+     * if any, or by this device, otherwise, based on the supported standard.
+     *
+     * \param address the (MLD or link) address of the given device
+     * \return the maximum supported Block Ack buffer size (in number of MPDUs)
+     */
+    uint16_t GetMaxBaBufferSize(std::optional<Mac48Address> address = std::nullopt) const;
+
+    /**
+     * \param size the size (in number of MPDUs) of the buffer used for each BlockAck
+     *             agreement in which this node is a recipient
+     */
+    void SetMpduBufferSize(uint16_t size);
+
+    /**
+     * \return the size (in number of MPDUs) of the buffer used for each BlockAck
+     *             agreement in which this node is a recipient
+     */
+    uint16_t GetMpduBufferSize() const;
+
+    /**
      * Get the TID-to-Link Mapping negotiated with the given MLD (if any) for the given direction.
      * An empty mapping indicates the default mapping.
      *
@@ -1054,6 +1075,8 @@
     uint32_t m_beMaxAmpduSize; ///< maximum A-MPDU size for AC_BE (in bytes)
     uint32_t m_bkMaxAmpduSize; ///< maximum A-MPDU size for AC_BK (in bytes)
 
+    uint16_t m_mpduBufferSize; //!< BlockAck buffer size (in number of MPDUs)
+
     /// @brief DL TID-to-Link Mapping negotiated with an MLD (identified by its MLD address)
     std::unordered_map<Mac48Address, WifiTidLinkMapping, WifiAddressHash> m_dlTidLinkMappings;
     /// @brief UL TID-to-Link Mapping negotiated with an MLD (identified by its MLD address)
@@ -1099,9 +1122,6 @@
      */
     TracedCallback<Ptr<const Packet>> m_macRxDropTrace;
 
-    TracedCallback<const WifiMacHeader&> m_txOkCallback;  ///< transmit OK callback
-    TracedCallback<const WifiMacHeader&> m_txErrCallback; ///< transmit error callback
-
     /**
      * TracedCallback signature for MPDU drop events.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-mac-queue-scheduler-impl.h ns-3.41/src/wifi/model/wifi-mac-queue-scheduler-impl.h
--- ns-3.40/src/wifi/model/wifi-mac-queue-scheduler-impl.h	2023-09-27 13:30:37.914839700 -0700
+++ ns-3.41/src/wifi/model/wifi-mac-queue-scheduler-impl.h	2024-02-09 14:44:08.601795200 -0800
@@ -450,7 +450,7 @@
     std::list<uint8_t> linkIds;
 
     // include only links that are not blocked in the returned list
-    for (const auto [linkId, mask] : queueInfoIt->second.linkIds)
+    for (const auto& [linkId, mask] : queueInfoIt->second.linkIds)
     {
         if (mask.none())
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-mpdu.cc ns-3.41/src/wifi/model/wifi-mpdu.cc
--- ns-3.40/src/wifi/model/wifi-mpdu.cc	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-mpdu.cc	2024-02-09 14:44:08.601795200 -0800
@@ -174,9 +174,15 @@
 void
 WifiMpdu::Aggregate(Ptr<const WifiMpdu> msdu)
 {
-    NS_ASSERT(msdu);
-    NS_LOG_FUNCTION(this << *msdu);
-    NS_ABORT_MSG_IF(!msdu->GetHeader().IsQosData() || msdu->GetHeader().IsQosAmsdu(),
+    if (msdu)
+    {
+        NS_LOG_FUNCTION(this << *msdu);
+    }
+    else
+    {
+        NS_LOG_FUNCTION(this);
+    }
+    NS_ABORT_MSG_IF(msdu && (!msdu->GetHeader().IsQosData() || msdu->GetHeader().IsQosAmsdu()),
                     "Only QoS data frames that do not contain an A-MSDU can be aggregated");
     NS_ABORT_MSG_IF(!std::holds_alternative<OriginalInfo>(m_instanceInfo),
                     "This method can only be called on the original version of the MPDU");
@@ -206,7 +212,10 @@
         // to be set to the BSSID, but neither Address 1 nor Address 2 contain the
         // BSSID. Hence, it is left up to the caller to set these Address fields.
     }
-    DoAggregate(msdu);
+    if (msdu)
+    {
+        DoAggregate(msdu);
+    }
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-mpdu.h ns-3.41/src/wifi/model/wifi-mpdu.h
--- ns-3.40/src/wifi/model/wifi-mpdu.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-mpdu.h	2024-02-09 14:44:08.601795200 -0800
@@ -131,7 +131,9 @@
     /**
      * \brief Aggregate the MSDU contained in the given MPDU to this MPDU (thus
      *        constituting an A-MSDU). Note that the given MPDU cannot contain
-     *        an A-MSDU.
+     *        an A-MSDU. If the given MPDU is a null pointer, the effect of this
+     *        call is to add only an A-MSDU subframe header, thus producing an A-MSDU
+     *        containing a single MSDU.
      * \param msdu the MPDU containing the MSDU to aggregate
      */
     void Aggregate(Ptr<const WifiMpdu> msdu);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-phy.cc ns-3.41/src/wifi/model/wifi-phy.cc
--- ns-3.40/src/wifi/model/wifi-phy.cc	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-phy.cc	2024-02-09 14:44:08.605795100 -0800
@@ -88,9 +88,15 @@
                 "Note that the channel width can be left unspecified (0) if the channel "
                 "number uniquely identify a frequency channel for the given standard and band.",
                 StringValue("{0, 0, BAND_UNSPECIFIED, 0}"),
-                MakeTupleAccessor<UintegerValue, UintegerValue, EnumValue, UintegerValue>(
-                    (void(WifiPhy::*)(const ChannelTuple&)) & WifiPhy::SetOperatingChannel),
-                MakeTupleChecker<UintegerValue, UintegerValue, EnumValue, UintegerValue>(
+                MakeTupleAccessor<UintegerValue,
+                                  UintegerValue,
+                                  EnumValue<WifiPhyBand>,
+                                  UintegerValue>((void(WifiPhy::*)(const ChannelTuple&)) &
+                                                 WifiPhy::SetOperatingChannel),
+                MakeTupleChecker<UintegerValue,
+                                 UintegerValue,
+                                 EnumValue<WifiPhyBand>,
+                                 UintegerValue>(
                     MakeUintegerChecker<uint8_t>(0, 233),
                     MakeUintegerChecker<uint16_t>(0, 160),
                     MakeEnumChecker(WifiPhyBand::WIFI_PHY_BAND_2_4GHZ,
@@ -349,6 +355,7 @@
       m_currentEvent(nullptr),
       m_previouslyRxPpduUid(UINT64_MAX),
       m_standard(WIFI_STANDARD_UNSPECIFIED),
+      m_maxModClassSupported(WIFI_MOD_CLASS_UNKNOWN),
       m_band(WIFI_PHY_BAND_UNSPECIFIED),
       m_sifs(Seconds(0)),
       m_slot(Seconds(0)),
@@ -401,12 +408,9 @@
 WifiPhy::DoDispose()
 {
     NS_LOG_FUNCTION(this);
-    m_endTxEvent.Cancel();
-    m_endPhyRxEvent.Cancel();
-    for (auto& phyEntity : m_phyEntities)
-    {
-        phyEntity.second->CancelAllEvents();
-    }
+
+    Reset();
+
     m_device = nullptr;
     m_mobility = nullptr;
     m_frameCaptureModel = nullptr;
@@ -420,12 +424,6 @@
     m_interference = nullptr;
     m_random = nullptr;
     m_state = nullptr;
-    m_currentEvent = nullptr;
-    for (auto& preambleEvent : m_currentPreambleEvents)
-    {
-        preambleEvent.second = nullptr;
-    }
-    m_currentPreambleEvents.clear();
 
     for (auto& phyEntity : m_phyEntities)
     {
@@ -460,13 +458,13 @@
 }
 
 void
-WifiPhy::RegisterListener(WifiPhyListener* listener)
+WifiPhy::RegisterListener(const std::shared_ptr<WifiPhyListener>& listener)
 {
     m_state->RegisterListener(listener);
 }
 
 void
-WifiPhy::UnregisterListener(WifiPhyListener* listener)
+WifiPhy::UnregisterListener(const std::shared_ptr<WifiPhyListener>& listener)
 {
     m_state->UnregisterListener(listener);
 }
@@ -947,6 +945,19 @@
 }
 
 void
+WifiPhy::SetMaxModulationClassSupported(WifiModulationClass modClass)
+{
+    NS_LOG_FUNCTION(this << modClass);
+    m_maxModClassSupported = modClass;
+}
+
+WifiModulationClass
+WifiPhy::GetMaxModulationClassSupported() const
+{
+    return m_maxModClassSupported;
+}
+
+void
 WifiPhy::ConfigureStandard(WifiStandard standard)
 {
     NS_LOG_FUNCTION(this << standard);
@@ -956,6 +967,11 @@
 
     m_standard = standard;
 
+    if (m_maxModClassSupported == WIFI_MOD_CLASS_UNKNOWN)
+    {
+        m_maxModClassSupported = GetModulationClassForStandard(m_standard);
+    }
+
     if (!m_operatingChannel.IsSet())
     {
         NS_LOG_DEBUG("Setting the operating channel first");
@@ -1094,37 +1110,30 @@
         return;
     }
 
-    Time delay = Seconds(0);
-
     if (IsInitialized())
     {
-        delay = GetDelayUntilChannelSwitch();
-    }
-
-    if (delay.IsStrictlyNegative())
-    {
-        // switching channel is not possible now
-        return;
-    }
-    if (delay.IsStrictlyPositive())
-    {
-        // switching channel has been postponed
-        void (WifiPhy::*fp)(const ChannelTuple&) = &WifiPhy::SetOperatingChannel;
-        Simulator::Schedule(delay, fp, this, channelTuple);
-        return;
+        const auto delay = GetDelayUntilChannelSwitch();
+        if (!delay.has_value())
+        {
+            // switching channel is not possible now
+            return;
+        }
+        if (delay.value().IsStrictlyPositive())
+        {
+            // switching channel has been postponed
+            void (WifiPhy::*fp)(const ChannelTuple&) = &WifiPhy::SetOperatingChannel;
+            Simulator::Schedule(delay.value(), fp, this, channelTuple);
+            return;
+        }
     }
 
     // channel can be switched now.
     DoChannelSwitch();
 }
 
-Time
+std::optional<Time>
 WifiPhy::GetDelayUntilChannelSwitch()
 {
-    m_powerRestricted = false;
-    m_channelAccessRequested = false;
-    m_currentEvent = nullptr;
-    m_currentPreambleEvents.clear();
     if (!IsInitialized())
     {
         // this is not channel switch, this is initialization
@@ -1132,14 +1141,14 @@
         return Seconds(0);
     }
 
-    Time delay = Seconds(0);
-
     NS_ASSERT(!IsStateSwitching());
+    std::optional<Time> delay;
     switch (m_state->GetState())
     {
     case WifiPhyState::RX:
         NS_LOG_DEBUG("drop packet because of channel switching while reception");
         AbortCurrentReception(CHANNEL_SWITCHING);
+        delay = Seconds(0);
         break;
     case WifiPhyState::TX:
         NS_LOG_DEBUG("channel switching postponed until end of current transmission");
@@ -1147,15 +1156,11 @@
         break;
     case WifiPhyState::CCA_BUSY:
     case WifiPhyState::IDLE:
-        m_endPhyRxEvent.Cancel();
-        for (auto& phyEntity : m_phyEntities)
-        {
-            phyEntity.second->CancelAllEvents();
-        }
+        Reset();
+        delay = Seconds(0);
         break;
     case WifiPhyState::SLEEP:
         NS_LOG_DEBUG("channel switching ignored in sleep mode");
-        delay = Seconds(-1); // negative value to indicate switching not possible
         break;
     default:
         NS_ASSERT(false);
@@ -1170,12 +1175,15 @@
 {
     NS_LOG_FUNCTION(this);
 
+    m_powerRestricted = false;
+    m_channelAccessRequested = false;
+
     // Update unspecified parameters with default values
     {
         auto& [number, width, band, primary20] = m_channelSettings;
-        if (band == static_cast<int>(WIFI_PHY_BAND_UNSPECIFIED))
+        if (band == WIFI_PHY_BAND_UNSPECIFIED)
         {
-            band = static_cast<int>(GetDefaultPhyBand(m_standard));
+            band = GetDefaultPhyBand(m_standard);
         }
         if (width == 0 && number == 0)
         {
@@ -1351,7 +1359,16 @@
     case WifiPhyState::CCA_BUSY:
     case WifiPhyState::IDLE:
         NS_LOG_DEBUG("setting sleep mode");
-        m_state->SwitchToSleep();
+        // The PHY object may be in CCA_BUSY state because it is receiving a preamble. Cancel
+        // preamble events before switching to sleep state
+        Reset();
+        // It may happen that we request to switch to sleep at the same time the reception of
+        // a PPDU ends. In such a case, WifiPhyStateHelper::GetState() does not return RX
+        // (m_endRx equals now), we get here and set the state to SLEEP. However,
+        // WifiPhyStateHelper::DoSwitchFromRx() may be called after this function (at the same
+        // simulation time), thus hitting the assert that checks that the state is IDLE or
+        // CCA_BUSY.
+        Simulator::ScheduleNow(&WifiPhyStateHelper::SwitchToSleep, m_state);
         break;
     case WifiPhyState::SLEEP:
         NS_LOG_DEBUG("already in sleep mode");
@@ -1368,12 +1385,7 @@
     NS_LOG_FUNCTION(this);
     m_powerRestricted = false;
     m_channelAccessRequested = false;
-    m_endPhyRxEvent.Cancel();
-    m_endTxEvent.Cancel();
-    for (auto& phyEntity : m_phyEntities)
-    {
-        phyEntity.second->CancelAllEvents();
-    }
+    Reset();
     m_state->SwitchToOff();
 }
 
@@ -1381,7 +1393,6 @@
 WifiPhy::ResumeFromSleep()
 {
     NS_LOG_FUNCTION(this);
-    m_currentPreambleEvents.clear();
     switch (m_state->GetState())
     {
     case WifiPhyState::TX:
@@ -1395,7 +1406,7 @@
     case WifiPhyState::SLEEP: {
         NS_LOG_DEBUG("resuming from sleep mode");
         m_state->SwitchFromSleep();
-        SwitchMaybeToCcaBusy(nullptr);
+        SwitchMaybeToCcaBusy();
         break;
     }
     default: {
@@ -1423,7 +1434,7 @@
     case WifiPhyState::OFF: {
         NS_LOG_DEBUG("resuming from off mode");
         m_state->SwitchFromOff();
-        SwitchMaybeToCcaBusy(nullptr);
+        SwitchMaybeToCcaBusy();
         break;
     }
     default: {
@@ -1725,7 +1736,7 @@
     NS_ASSERT(!m_state->IsStateTx() && !m_state->IsStateSwitching());
     NS_ASSERT(m_endTxEvent.IsExpired());
 
-    if (!txVector.IsValid())
+    if (!txVector.IsValid(m_band))
     {
         NS_FATAL_ERROR("TX-VECTOR is invalid!");
     }
@@ -1775,13 +1786,6 @@
         AbortCurrentReception(RECEPTION_ABORTED_BY_TX);
     }
 
-    for (auto& it : m_phyEntities)
-    {
-        it.second->CancelRunningEndPreambleDetectionEvents();
-    }
-    m_currentPreambleEvents.clear();
-    m_endPhyRxEvent.Cancel();
-
     if (m_powerRestricted)
     {
         NS_LOG_DEBUG("Transmitting with power restriction for " << txDuration.As(Time::NS));
@@ -1829,6 +1833,8 @@
     m_powerRestricted = false;
 
     Simulator::Schedule(txDuration, &WifiPhy::Reset, this);
+
+    Simulator::Schedule(txDuration, &WifiPhy::SwitchMaybeToCcaBusy, this, nullptr);
 }
 
 uint64_t
@@ -1854,7 +1860,8 @@
     {
         phyEntity.second->CancelAllEvents();
     }
-    SwitchMaybeToCcaBusy(nullptr);
+    m_endPhyRxEvent.Cancel();
+    m_endTxEvent.Cancel();
 }
 
 void
@@ -1864,8 +1871,9 @@
 {
     NS_LOG_FUNCTION(this << ppdu << rxDuration);
     WifiModulationClass modulation = ppdu->GetModulation();
-    auto it = m_phyEntities.find(modulation);
-    if (it != m_phyEntities.end())
+    NS_ASSERT(m_maxModClassSupported != WIFI_MOD_CLASS_UNKNOWN);
+    if (auto it = m_phyEntities.find(modulation);
+        it != m_phyEntities.end() && modulation <= m_maxModClassSupported)
     {
         it->second->StartReceivePreamble(ppdu, rxPowersW, rxDuration);
     }
@@ -1875,10 +1883,16 @@
         // structure)
         NS_LOG_DEBUG("Unsupported modulation received (" << modulation << "), consider as noise");
         m_interference->Add(ppdu, rxDuration, rxPowersW);
-        SwitchMaybeToCcaBusy(nullptr);
+        SwitchMaybeToCcaBusy();
     }
 }
 
+bool
+WifiPhy::IsReceivingPhyHeader() const
+{
+    return m_endPhyRxEvent.IsRunning();
+}
+
 void
 WifiPhy::EndReceiveInterBss()
 {
@@ -2088,7 +2102,7 @@
 }
 
 void
-WifiPhy::SwitchMaybeToCcaBusy(const Ptr<const WifiPpdu> ppdu)
+WifiPhy::SwitchMaybeToCcaBusy(const Ptr<const WifiPpdu> ppdu /* = nullptr */)
 {
     NS_LOG_FUNCTION(this);
     GetLatestPhyEntity()->SwitchMaybeToCcaBusy(ppdu);
@@ -2112,10 +2126,7 @@
         {
             phyEntity.second->CancelAllEvents();
         }
-        if (m_endPhyRxEvent.IsRunning())
-        {
-            m_endPhyRxEvent.Cancel();
-        }
+        m_endPhyRxEvent.Cancel();
         m_interference->NotifyRxEnd(Simulator::Now(), GetCurrentFrequencyRange());
         if (!m_currentEvent)
         {
@@ -2126,15 +2137,23 @@
         {
             m_state->SwitchFromRxAbort(GetChannelWidth());
         }
-        for (auto it = m_currentPreambleEvents.begin(); it != m_currentPreambleEvents.end(); ++it)
+        if (reason == RECEPTION_ABORTED_BY_TX)
         {
-            if (it->second == m_currentEvent)
+            Reset();
+        }
+        else
+        {
+            for (auto it = m_currentPreambleEvents.begin(); it != m_currentPreambleEvents.end();
+                 ++it)
             {
-                it = m_currentPreambleEvents.erase(it);
-                break;
+                if (it->second == m_currentEvent)
+                {
+                    it = m_currentPreambleEvents.erase(it);
+                    break;
+                }
             }
+            m_currentEvent = nullptr;
         }
-        m_currentEvent = nullptr;
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-phy-common.h ns-3.41/src/wifi/model/wifi-phy-common.h
--- ns-3.40/src/wifi/model/wifi-phy-common.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-phy-common.h	2024-02-09 14:44:08.605795100 -0800
@@ -48,6 +48,9 @@
 class WifiMode;
 class Time;
 
+/// maximum propagation delay
+static constexpr uint8_t MAX_PROPAGATION_DELAY_USEC = 1;
+
 /**
  * typedef for a pair of start and stop frequencies in Hz to represent a band
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-phy.h ns-3.41/src/wifi/model/wifi-phy.h
--- ns-3.40/src/wifi/model/wifi-phy.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-phy.h	2024-02-09 14:44:08.605795100 -0800
@@ -86,14 +86,14 @@
      * Add the input listener to the list of objects to be notified of
      * PHY-level events.
      */
-    void RegisterListener(WifiPhyListener* listener);
+    void RegisterListener(const std::shared_ptr<WifiPhyListener>& listener);
     /**
      * \param listener the listener to be unregistered
      *
      * Remove the input listener from the list of objects to be notified of
      * PHY-level events.
      */
-    void UnregisterListener(WifiPhyListener* listener);
+    void UnregisterListener(const std::shared_ptr<WifiPhyListener>& listener);
 
     /**
      * \param callback the callback to invoke when PHY capabilities have changed.
@@ -112,6 +112,11 @@
                               Time rxDuration);
 
     /**
+     * \return whether the PHY is busy decoding the PHY header fields of a PPDU
+     */
+    bool IsReceivingPhyHeader() const;
+
+    /**
      * For HE receptions only, check and possibly modify the transmit power restriction state at
      * the end of PPDU reception.
      */
@@ -509,6 +514,20 @@
     virtual void ConfigureStandard(WifiStandard standard);
 
     /**
+     * Set the maximum modulation class that has to be supported by this PHY object.
+     * The maximum modulation class supported will be the minimum between the given modulation
+     * class and the maximum modulation class supported based on the configured standard.
+     *
+     * \param modClass the given modulation class
+     */
+    void SetMaxModulationClassSupported(WifiModulationClass modClass);
+
+    /**
+     * \return the maximum modulation class that has to be supported by this PHY object.
+     */
+    WifiModulationClass GetMaxModulationClassSupported() const;
+
+    /**
      * Get the configured Wi-Fi standard
      *
      * \return the Wi-Fi standard that has been configured
@@ -868,7 +887,7 @@
     using ChannelTuple =
         std::tuple<uint8_t /* channel number */,
                    uint16_t /* channel width */,
-                   int /* WifiPhyBand */,
+                   WifiPhyBand /* WifiPhyBand */,
                    uint8_t /* primary20 index*/>; //!< Tuple identifying an operating channel
 
     /**
@@ -1201,12 +1220,10 @@
      * Perform any actions necessary when user changes operating channel after
      * initialization.
      *
-     * \return zero if the PHY can immediately switch channel, a positive value
-     *         indicating the amount of time to wait until the channel switch can
-     *         be performed or a negative value indicating that channel switch is
-     *         currently not possible (i.e., the radio is in sleep mode)
+     * \return the amount of time to wait until the channel switch can be performed or
+     * std::nullopt if channel switch is currently not possible (i.e., the radio is in sleep mode)
      */
-    Time GetDelayUntilChannelSwitch();
+    std::optional<Time> GetDelayUntilChannelSwitch();
     /**
      * Actually switch channel based on the stored channel settings.
      */
@@ -1218,7 +1235,7 @@
      *
      * \param ppdu the incoming PPDU or nullptr for any signal
      */
-    void SwitchMaybeToCcaBusy(const Ptr<const WifiPpdu> ppdu);
+    void SwitchMaybeToCcaBusy(const Ptr<const WifiPpdu> ppdu = nullptr);
     /**
      * Notify PHY state helper to switch to CCA busy state,
      *
@@ -1470,8 +1487,9 @@
      */
     static std::map<WifiModulationClass, Ptr<PhyEntity>>& GetStaticPhyEntities();
 
-    WifiStandard m_standard;        //!< WifiStandard
-    WifiPhyBand m_band;             //!< WifiPhyBand
+    WifiStandard m_standard;                    //!< WifiStandard
+    WifiModulationClass m_maxModClassSupported; //!< max modulation class supported
+    WifiPhyBand m_band;                         //!< WifiPhyBand
     ChannelTuple m_channelSettings; //!< Store operating channel settings until initialization
     WifiPhyOperatingChannel m_operatingChannel; //!< Operating channel
     bool m_fixedPhyBand; //!< True to prohibit changing PHY band after initialization
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-phy-state-helper.cc ns-3.41/src/wifi/model/wifi-phy-state-helper.cc
--- ns-3.40/src/wifi/model/wifi-phy-state-helper.cc	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-phy-state-helper.cc	2024-02-09 14:44:08.605795100 -0800
@@ -29,6 +29,7 @@
 #include "ns3/simulator.h"
 
 #include <algorithm>
+#include <functional>
 #include <iterator>
 
 namespace ns3
@@ -66,7 +67,8 @@
 }
 
 WifiPhyStateHelper::WifiPhyStateHelper()
-    : m_sleeping(false),
+    : NS_LOG_TEMPLATE_DEFINE("WifiPhyStateHelper"),
+      m_sleeping(false),
       m_isOff(false),
       m_endTx(Seconds(0)),
       m_endRx(Seconds(0)),
@@ -95,19 +97,15 @@
 }
 
 void
-WifiPhyStateHelper::RegisterListener(WifiPhyListener* listener)
+WifiPhyStateHelper::RegisterListener(const std::shared_ptr<WifiPhyListener>& listener)
 {
-    m_listeners.push_back(listener);
+    m_listeners.emplace_back(listener);
 }
 
 void
-WifiPhyStateHelper::UnregisterListener(WifiPhyListener* listener)
+WifiPhyStateHelper::UnregisterListener(const std::shared_ptr<WifiPhyListener>& listener)
 {
-    auto it = find(m_listeners.begin(), m_listeners.end(), listener);
-    if (it != m_listeners.end())
-    {
-        m_listeners.erase(it);
-    }
+    m_listeners.remove_if([&listener](auto&& weakPtr) { return weakPtr.lock() == listener; });
 }
 
 bool
@@ -231,108 +229,6 @@
 }
 
 void
-WifiPhyStateHelper::NotifyTxStart(Time duration, double txPowerDbm)
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyTxStart(duration, txPowerDbm);
-    }
-}
-
-void
-WifiPhyStateHelper::NotifyRxStart(Time duration)
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyRxStart(duration);
-    }
-}
-
-void
-WifiPhyStateHelper::NotifyRxEndOk()
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyRxEndOk();
-    }
-}
-
-void
-WifiPhyStateHelper::NotifyRxEndError()
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyRxEndError();
-    }
-}
-
-void
-WifiPhyStateHelper::NotifyCcaBusyStart(Time duration,
-                                       WifiChannelListType channelType,
-                                       const std::vector<Time>& per20MhzDurations)
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyCcaBusyStart(duration, channelType, per20MhzDurations);
-    }
-}
-
-void
-WifiPhyStateHelper::NotifySwitchingStart(Time duration)
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifySwitchingStart(duration);
-    }
-}
-
-void
-WifiPhyStateHelper::NotifySleep()
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifySleep();
-    }
-}
-
-void
-WifiPhyStateHelper::NotifyOff()
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyOff();
-    }
-}
-
-void
-WifiPhyStateHelper::NotifyWakeup()
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyWakeup();
-    }
-}
-
-void
-WifiPhyStateHelper::NotifyOn()
-{
-    NS_LOG_FUNCTION(this);
-    for (const auto& listener : m_listeners)
-    {
-        listener->NotifyOn();
-    }
-}
-
-void
 WifiPhyStateHelper::LogPreviousIdleAndCcaBusyStates()
 {
     NS_LOG_FUNCTION(this);
@@ -404,7 +300,7 @@
     m_previousStateChangeTime = now;
     m_endTx = now + txDuration;
     m_startTx = now;
-    NotifyTxStart(txDuration, txPowerDbm);
+    NotifyListeners(&WifiPhyListener::NotifyTxStart, txDuration, txPowerDbm);
 }
 
 void
@@ -427,7 +323,7 @@
     m_previousStateChangeTime = now;
     m_startRx = now;
     m_endRx = now + rxDuration;
-    NotifyRxStart(rxDuration);
+    NotifyListeners(&WifiPhyListener::NotifyRxStart, rxDuration);
     NS_ASSERT(IsStateRx());
 }
 
@@ -460,7 +356,7 @@
     m_previousStateChangeTime = now;
     m_startSwitching = now;
     m_endSwitching = now + switchingDuration;
-    NotifySwitchingStart(switchingDuration);
+    NotifyListeners(&WifiPhyListener::NotifySwitchingStart, switchingDuration);
     NS_ASSERT(switchingDuration.IsZero() || IsStateSwitching());
 }
 
@@ -520,7 +416,7 @@
 {
     NS_LOG_FUNCTION(this);
     NS_ASSERT(m_endRx == Simulator::Now());
-    NotifyRxEndOk();
+    NotifyListeners(&WifiPhyListener::NotifyRxEndOk);
     DoSwitchFromRx();
 }
 
@@ -529,7 +425,7 @@
 {
     NS_LOG_FUNCTION(this);
     NS_ASSERT(m_endRx == Simulator::Now());
-    NotifyRxEndError();
+    NotifyListeners(&WifiPhyListener::NotifyRxEndError);
     DoSwitchFromRx();
 }
 
@@ -554,7 +450,7 @@
     {
         return;
     }
-    NotifyCcaBusyStart(duration, channelType, per20MhzDurations);
+    NotifyListeners(&WifiPhyListener::NotifyCcaBusyStart, duration, channelType, per20MhzDurations);
     if (channelType != WIFI_CHANLIST_PRIMARY)
     {
         // WifiPhyStateHelper only updates CCA start and end durations for the primary channel
@@ -591,7 +487,7 @@
     m_previousStateChangeTime = now;
     m_sleeping = true;
     m_startSleep = now;
-    NotifySleep();
+    NotifyListeners(&WifiPhyListener::NotifySleep);
     NS_ASSERT(IsStateSleep());
 }
 
@@ -604,7 +500,7 @@
     m_stateLogger(m_startSleep, now - m_startSleep, WifiPhyState::SLEEP);
     m_previousStateChangeTime = now;
     m_sleeping = false;
-    NotifyWakeup();
+    NotifyListeners(&WifiPhyListener::NotifyWakeup);
 }
 
 void
@@ -613,7 +509,7 @@
     NS_LOG_FUNCTION(this << operatingWidth);
     NS_ASSERT(IsStateCcaBusy()); // abort is called (with OBSS_PD_CCA_RESET reason) before RX is set
                                  // by payload start
-    NotifyRxEndOk();
+    NotifyListeners(&WifiPhyListener::NotifyRxEndOk);
     DoSwitchFromRx();
     m_endCcaBusy = Simulator::Now();
     std::vector<Time> per20MhzDurations;
@@ -621,7 +517,10 @@
     {
         std::fill_n(std::back_inserter(per20MhzDurations), (operatingWidth / 20), Seconds(0));
     }
-    NotifyCcaBusyStart(Seconds(0), WIFI_CHANLIST_PRIMARY, per20MhzDurations);
+    NotifyListeners(&WifiPhyListener::NotifyCcaBusyStart,
+                    Seconds(0),
+                    WIFI_CHANLIST_PRIMARY,
+                    per20MhzDurations);
     NS_ASSERT(IsStateIdle());
 }
 
@@ -657,7 +556,7 @@
     }
     m_previousStateChangeTime = now;
     m_isOff = true;
-    NotifyOff();
+    NotifyListeners(&WifiPhyListener::NotifyOff);
     NS_ASSERT(IsStateOff());
 }
 
@@ -669,7 +568,7 @@
     Time now = Simulator::Now();
     m_previousStateChangeTime = now;
     m_isOff = false;
-    NotifyOn();
+    NotifyListeners(&WifiPhyListener::NotifyOn);
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-phy-state-helper.h ns-3.41/src/wifi/model/wifi-phy-state-helper.h
--- ns-3.40/src/wifi/model/wifi-phy-state-helper.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-phy-state-helper.h	2024-02-09 14:44:08.605795100 -0800
@@ -29,6 +29,8 @@
 #include "ns3/object.h"
 #include "ns3/traced-callback.h"
 
+#include <list>
+#include <memory>
 #include <vector>
 
 namespace ns3
@@ -93,13 +95,13 @@
      *
      * \param listener the WifiPhyListener to register
      */
-    void RegisterListener(WifiPhyListener* listener);
+    void RegisterListener(const std::shared_ptr<WifiPhyListener>& listener);
     /**
      * Remove WifiPhyListener from this WifiPhyStateHelper.
      *
      * \param listener the WifiPhyListener to unregister
      */
-    void UnregisterListener(WifiPhyListener* listener);
+    void UnregisterListener(const std::shared_ptr<WifiPhyListener>& listener);
     /**
      * Return the current state of WifiPhy.
      *
@@ -309,11 +311,24 @@
                                      WifiPreamble preamble,
                                      uint8_t power);
 
+    /**
+     * Notify all WifiPhyListener objects of the given PHY event.
+     *
+     * \tparam FUNC \deduced Member function type
+     * \tparam Ts \deduced Function argument types
+     * \param f the member function to invoke
+     * \param args arguments to pass to the member function
+     */
+    template <typename FUNC, typename... Ts>
+    void NotifyListeners(FUNC f, Ts&&... args);
+
   private:
     /**
-     * typedef for a list of WifiPhyListeners
+     * typedef for a list of WifiPhyListeners. We use weak pointers so that unregistering a
+     * listener is not necessary to delete a listener (reference count is not incremented by
+     * weak pointers).
      */
-    typedef std::vector<WifiPhyListener*> Listeners;
+    typedef std::list<std::weak_ptr<WifiPhyListener>> Listeners;
 
     /**
      * Log the idle and CCA busy states.
@@ -321,72 +336,16 @@
     void LogPreviousIdleAndCcaBusyStates();
 
     /**
-     * Notify all WifiPhyListener that the transmission has started for the given duration.
-     *
-     * \param duration the duration of the transmission
-     * \param txPowerDbm the nominal TX power in dBm
-     */
-    void NotifyTxStart(Time duration, double txPowerDbm);
-    /**
-     * Notify all WifiPhyListener that the reception has started for the given duration.
-     *
-     * \param duration the duration of the reception
-     */
-    void NotifyRxStart(Time duration);
-    /**
-     * Notify all WifiPhyListener that the reception was successful.
-     */
-    void NotifyRxEndOk();
-    /**
-     * Notify all WifiPhyListener that the reception was not successful.
-     */
-    void NotifyRxEndError();
-    /**
-     * Notify all WifiPhyListener that the CCA has started for the given duration.
-     *
-     * \param duration the duration of the CCA state
-     * \param channelType the channel type for which the CCA busy state is reported.
-     * \param per20MhzDurations vector that indicates for how long each 20 MHz subchannel
-     *        (corresponding to the index of the element in the vector) is busy and where a zero
-     * duration indicates that the subchannel is idle. The vector is non-empty if  the PHY supports
-     * 802.11ax or later and if the operational channel width is larger than 20 MHz.
-     */
-    void NotifyCcaBusyStart(Time duration,
-                            WifiChannelListType channelType,
-                            const std::vector<Time>& per20MhzDurations);
-    /**
-     * Notify all WifiPhyListener that we are switching channel with the given channel
-     * switching delay.
-     *
-     * \param duration the delay to switch the channel
-     */
-    void NotifySwitchingStart(Time duration);
-    /**
-     * Notify all WifiPhyListener that we are going to sleep
-     */
-    void NotifySleep();
-    /**
-     * Notify all WifiPhyListener that we are going to switch off
-     */
-    void NotifyOff();
-    /**
-     * Notify all WifiPhyListener that we woke up
-     */
-    void NotifyWakeup();
-    /**
      * Switch the state from RX.
      */
     void DoSwitchFromRx();
-    /**
-     * Notify all WifiPhyListener that we are going to switch on
-     */
-    void NotifyOn();
 
     /**
      * The trace source fired when state is changed.
      */
     TracedCallback<Time, Time, WifiPhyState> m_stateLogger;
 
+    NS_LOG_TEMPLATE_DECLARE;        //!< the log component
     bool m_sleeping;                ///< sleeping
     bool m_isOff;                   ///< switched off
     Time m_endTx;                   ///< end transmit
@@ -412,4 +371,38 @@
 
 } // namespace ns3
 
+/***************************************************************
+ *  Implementation of the templates declared above.
+ ***************************************************************/
+
+namespace ns3
+{
+
+template <typename FUNC, typename... Ts>
+void
+WifiPhyStateHelper::NotifyListeners(FUNC f, Ts&&... args)
+{
+    NS_LOG_FUNCTION(this);
+    // In some cases (e.g., when notifying an EMLSR client of a link switch), a notification
+    // to a PHY listener involves the addition and/or removal of a PHY listener, thus modifying
+    // the list we are iterating over. This is dangerous, so ensure that we iterate over a copy
+    // of the list of PHY listeners. The copied list contains shared pointers to the PHY listeners
+    // to prevent them from being deleted.
+    std::list<std::shared_ptr<WifiPhyListener>> listeners;
+    std::transform(m_listeners.cbegin(),
+                   m_listeners.cend(),
+                   std::back_inserter(listeners),
+                   [](auto&& listener) { return listener.lock(); });
+
+    for (const auto& listener : listeners)
+    {
+        if (listener)
+        {
+            std::invoke(f, listener, std::forward<Ts>(args)...);
+        }
+    }
+}
+
+} // namespace ns3
+
 #endif /* WIFI_PHY_STATE_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-radio-energy-model.cc ns-3.41/src/wifi/model/wifi-radio-energy-model.cc
--- ns-3.40/src/wifi/model/wifi-radio-energy-model.cc	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-radio-energy-model.cc	2024-02-09 14:44:08.605795100 -0800
@@ -99,7 +99,7 @@
     NS_LOG_FUNCTION(this);
     m_energyDepletionCallback.Nullify();
     // set callback for WifiPhy listener
-    m_listener = new WifiRadioEnergyModelPhyListener;
+    m_listener = std::make_shared<WifiRadioEnergyModelPhyListener>();
     m_listener->SetChangeStateCallback(MakeCallback(&DeviceEnergyModel::ChangeState, this));
     // set callback for updating the TX current
     m_listener->SetUpdateTxCurrentCallback(
@@ -110,7 +110,7 @@
 {
     NS_LOG_FUNCTION(this);
     m_txCurrentModel = nullptr;
-    delete m_listener;
+    m_listener.reset();
 }
 
 void
@@ -387,7 +387,7 @@
     }
 }
 
-WifiRadioEnergyModelPhyListener*
+std::shared_ptr<WifiRadioEnergyModelPhyListener>
 WifiRadioEnergyModel::GetPhyListener()
 {
     NS_LOG_FUNCTION(this);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-radio-energy-model.h ns-3.41/src/wifi/model/wifi-radio-energy-model.h
--- ns-3.40/src/wifi/model/wifi-radio-energy-model.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-radio-energy-model.h	2024-02-09 14:44:08.605795100 -0800
@@ -331,7 +331,7 @@
     /**
      * \returns Pointer to the PHY listener.
      */
-    WifiRadioEnergyModelPhyListener* GetPhyListener();
+    std::shared_ptr<WifiRadioEnergyModelPhyListener> GetPhyListener();
 
   private:
     void DoDispose() override;
@@ -384,7 +384,7 @@
     WifiRadioEnergyRechargedCallback m_energyRechargedCallback;
 
     /// WifiPhy listener
-    WifiRadioEnergyModelPhyListener* m_listener;
+    std::shared_ptr<WifiRadioEnergyModelPhyListener> m_listener;
 
     EventId m_switchToOffEvent; ///< switch to off event
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-remote-station-manager.cc ns-3.41/src/wifi/model/wifi-remote-station-manager.cc
--- ns-3.40/src/wifi/model/wifi-remote-station-manager.cc	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-remote-station-manager.cc	2024-02-09 14:44:08.605795100 -0800
@@ -71,9 +71,9 @@
                           "If the size of the PSDU is bigger than this value, we use an RTS/CTS "
                           "handshake before sending the data frame."
                           "This value will not have any effect on some rate control algorithms.",
-                          UintegerValue(65535),
+                          UintegerValue(4692480),
                           MakeUintegerAccessor(&WifiRemoteStationManager::SetRtsCtsThreshold),
-                          MakeUintegerChecker<uint32_t>())
+                          MakeUintegerChecker<uint32_t>(0, 4692480))
             .AddAttribute(
                 "FragmentationThreshold",
                 "If the size of the PSDU is bigger than this value, we fragment it such that the "
@@ -100,7 +100,8 @@
                           "Protection mode used when non-ERP STAs are connected to an ERP AP: "
                           "Rts-Cts or Cts-To-Self",
                           EnumValue(WifiRemoteStationManager::CTS_TO_SELF),
-                          MakeEnumAccessor(&WifiRemoteStationManager::m_erpProtectionMode),
+                          MakeEnumAccessor<WifiRemoteStationManager::ProtectionMode>(
+                              &WifiRemoteStationManager::m_erpProtectionMode),
                           MakeEnumChecker(WifiRemoteStationManager::RTS_CTS,
                                           "Rts-Cts",
                                           WifiRemoteStationManager::CTS_TO_SELF,
@@ -109,7 +110,8 @@
                           "Protection mode used when non-HT STAs are connected to a HT AP: Rts-Cts "
                           "or Cts-To-Self",
                           EnumValue(WifiRemoteStationManager::CTS_TO_SELF),
-                          MakeEnumAccessor(&WifiRemoteStationManager::m_htProtectionMode),
+                          MakeEnumAccessor<WifiRemoteStationManager::ProtectionMode>(
+                              &WifiRemoteStationManager::m_htProtectionMode),
                           MakeEnumChecker(WifiRemoteStationManager::RTS_CTS,
                                           "Rts-Cts",
                                           WifiRemoteStationManager::CTS_TO_SELF,
@@ -142,6 +144,8 @@
       m_shortSlotTimeEnabled(false)
 {
     NS_LOG_FUNCTION(this);
+    m_ssrc.fill(0);
+    m_slrc.fill(0);
 }
 
 WifiRemoteStationManager::~WifiRemoteStationManager()
@@ -166,13 +170,6 @@
     // transmit rate for automatic control responses like
     // acknowledgments.
     m_wifiPhy = phy;
-    m_defaultTxMode = phy->GetDefaultMode();
-    NS_ASSERT(m_defaultTxMode.IsMandatory());
-    if (GetHtSupported())
-    {
-        m_defaultTxMcs = HtPhy::GetHtMcs(0);
-    }
-    Reset();
 }
 
 void
@@ -182,7 +179,6 @@
     // We need to track our MAC because it is the object that knows the
     // full set of interframe spaces.
     m_wifiMac = mac;
-    Reset();
 }
 
 int64_t
@@ -701,13 +697,13 @@
 }
 
 WifiTxVector
-WifiRemoteStationManager::GetRtsTxVector(Mac48Address address)
+WifiRemoteStationManager::GetRtsTxVector(Mac48Address address, uint16_t allowedWidth)
 {
-    NS_LOG_FUNCTION(this << address);
+    NS_LOG_FUNCTION(this << address << allowedWidth);
+    WifiTxVector v;
     if (address.IsGroup())
     {
         WifiMode mode = GetNonUnicastMode();
-        WifiTxVector v;
         v.SetMode(mode);
         v.SetPreambleType(
             GetPreambleForTransmission(mode.GetModulationClass(), GetShortPreambleEnabled()));
@@ -717,9 +713,31 @@
         v.SetNTx(GetNumberOfAntennas());
         v.SetNss(1);
         v.SetNess(0);
-        return v;
     }
-    return DoGetRtsTxVector(Lookup(address));
+    else
+    {
+        v = DoGetRtsTxVector(Lookup(address));
+    }
+    auto modulation = v.GetModulationClass();
+
+    if (allowedWidth >= 40 &&
+        (modulation == WIFI_MOD_CLASS_DSSS || modulation == WIFI_MOD_CLASS_HR_DSSS))
+    {
+        // RTS must be sent in a non-HT duplicate PPDU because it must protect a frame being
+        // transmitted on at least 40 MHz. Change the modulation class to ERP-OFDM and the rate
+        // to 6 Mbps
+        v.SetMode(ErpOfdmPhy::GetErpOfdmRate6Mbps());
+        modulation = v.GetModulationClass();
+    }
+    // do not set allowedWidth as the TX width if the modulation class is (HR-)DSSS (allowedWidth
+    // may be >= 40 MHz) or allowedWidth is 22 MHz (the selected modulation class may be OFDM)
+    if (modulation != WIFI_MOD_CLASS_DSSS && modulation != WIFI_MOD_CLASS_HR_DSSS &&
+        allowedWidth != 22)
+    {
+        v.SetChannelWidth(allowedWidth);
+    }
+
+    return v;
 }
 
 WifiTxVector
@@ -1431,6 +1449,8 @@
 WifiRemoteStationManager::Lookup(Mac48Address address) const
 {
     NS_LOG_FUNCTION(this << address);
+    NS_ASSERT(!address.IsGroup());
+    NS_ASSERT(address != m_wifiMac->GetAddress());
     auto stationIt = m_stations.find(address);
 
     if (stationIt != m_stations.end())
@@ -1675,13 +1695,16 @@
 WifiMode
 WifiRemoteStationManager::GetDefaultMode() const
 {
-    return m_defaultTxMode;
+    NS_ASSERT(m_wifiPhy);
+    auto defaultTxMode = m_wifiPhy->GetDefaultMode();
+    NS_ASSERT(defaultTxMode.IsMandatory());
+    return defaultTxMode;
 }
 
 WifiMode
 WifiRemoteStationManager::GetDefaultMcs() const
 {
-    return m_defaultTxMcs;
+    return HtPhy::GetHtMcs0();
 }
 
 WifiMode
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-remote-station-manager.h ns-3.41/src/wifi/model/wifi-remote-station-manager.h
--- ns-3.40/src/wifi/model/wifi-remote-station-manager.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-remote-station-manager.h	2024-02-09 14:44:08.605795100 -0800
@@ -816,11 +816,12 @@
     WifiTxVector GetDataTxVector(const WifiMacHeader& header, uint16_t allowedWidth);
     /**
      * \param address remote address
+     * \param allowedWidth the allowed width in MHz for the data frame being protected
      *
      * \return the TXVECTOR to use to send the RTS prior to the
      *         transmission of the data packet itself.
      */
-    WifiTxVector GetRtsTxVector(Mac48Address address);
+    WifiTxVector GetRtsTxVector(Mac48Address address, uint16_t allowedWidth);
     /**
      * Return a TXVECTOR for the CTS frame given the destination and the mode of the RTS
      * used by the sender.
@@ -1490,9 +1491,6 @@
     StationStates m_states; //!< States of known stations
     Stations m_stations;    //!< Information for each known stations
 
-    WifiMode m_defaultTxMode; //!< The default transmission mode
-    WifiMode m_defaultTxMcs;  //!< The default transmission modulation-coding scheme (MCS)
-
     uint32_t m_maxSsrc;                //!< Maximum STA short retry count (SSRC)
     uint32_t m_maxSlrc;                //!< Maximum STA long retry count (SLRC)
     uint32_t m_rtsCtsThreshold;        //!< Threshold for RTS/CTS
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-tx-vector.cc ns-3.41/src/wifi/model/wifi-tx-vector.cc
--- ns-3.40/src/wifi/model/wifi-tx-vector.cc	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-tx-vector.cc	2024-02-09 14:44:08.609795000 -0800
@@ -123,11 +123,6 @@
     }
 }
 
-WifiTxVector::~WifiTxVector()
-{
-    m_muUserInfos.clear();
-}
-
 bool
 WifiTxVector::GetModeInitialized() const
 {
@@ -451,7 +446,7 @@
 }
 
 bool
-WifiTxVector::IsValid() const
+WifiTxVector::IsValid(WifiPhyBand band) const
 {
     if (!GetModeInitialized())
     {
@@ -522,6 +517,16 @@
             return false;
         }
     }
+
+    if (band != WIFI_PHY_BAND_UNSPECIFIED)
+    {
+        NS_ABORT_MSG_IF(GetModulationClass() == WIFI_MOD_CLASS_OFDM && band == WIFI_PHY_BAND_2_4GHZ,
+                        "Cannot use OFDM modulation class in the 2.4 GHz band");
+        NS_ABORT_MSG_IF(GetModulationClass() == WIFI_MOD_CLASS_ERP_OFDM &&
+                            band != WIFI_PHY_BAND_2_4GHZ,
+                        "ERP-OFDM modulation class can only be used in the 2.4 GHz band");
+    }
+
     return true;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/model/wifi-tx-vector.h ns-3.41/src/wifi/model/wifi-tx-vector.h
--- ns-3.40/src/wifi/model/wifi-tx-vector.h	2023-09-27 13:30:37.918840000 -0700
+++ ns-3.41/src/wifi/model/wifi-tx-vector.h	2024-02-09 14:44:08.609795000 -0800
@@ -22,6 +22,7 @@
 #define WIFI_TX_VECTOR_H
 
 #include "wifi-mode.h"
+#include "wifi-phy-band.h"
 #include "wifi-phy-common.h"
 
 #include "ns3/he-ru.h"
@@ -114,7 +115,6 @@
         HeMuUserInfoMap;
 
     WifiTxVector();
-    ~WifiTxVector();
     /**
      * Create a TXVECTOR with the given parameters.
      *
@@ -357,10 +357,13 @@
      * The standard disallows certain combinations of WifiMode, number of
      * spatial streams, and channel widths.  This method can be used to
      * check whether this WifiTxVector contains an invalid combination.
+     * If a PHY band is specified, it is checked that the PHY band is appropriate for
+     * the modulation class of the TXVECTOR, in case the latter is OFDM or ERP-OFDM.
      *
+     * \param band the PHY band
      * \return true if the WifiTxVector parameters are allowed by the standard
      */
-    bool IsValid() const;
+    bool IsValid(WifiPhyBand band = WIFI_PHY_BAND_UNSPECIFIED) const;
     /**
      * \return true if this TX vector is used for a multi-user (OFDMA and/or MU-MIMO) transmission
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/channel-access-manager-test.cc ns-3.41/src/wifi/test/channel-access-manager-test.cc
--- ns-3.40/src/wifi/test/channel-access-manager-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/channel-access-manager-test.cc	2024-02-09 14:44:08.609795000 -0800
@@ -806,11 +806,12 @@
                                                     uint64_t expectedGrantTime,
                                                     Ptr<TxopTest<TxopType>> state)
 {
-    if (m_ChannelAccessManager->NeedBackoffUponAccess(state))
+    auto hadFramesToTransmit = state->HasFramesToTransmit(SINGLE_LINK_OP_ID);
+    state->QueueTx(txTime, expectedGrantTime);
+    if (m_ChannelAccessManager->NeedBackoffUponAccess(state, hadFramesToTransmit, true))
     {
         state->GenerateBackoff(0);
     }
-    state->QueueTx(txTime, expectedGrantTime);
     m_ChannelAccessManager->RequestAccess(state);
 }
 
@@ -1397,7 +1398,7 @@
                              : ((1 << (busyChannel - 1)) * 20);
 
     Time checkTime1 = start + ccaBusyStartDelay + ccaBusyDuration / 2;
-    Simulator::Schedule(checkTime1 - start, [=]() {
+    Simulator::Schedule(checkTime1 - start, [=, this]() {
         Time interval1 = (ccaBusyStartDelay + ccaBusyDuration) / 2;
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval1, checkTime1),
                               idleWidth,
@@ -1410,7 +1411,7 @@
     // same as the previous case
     Time ccaBusyRxInterval = MilliSeconds(1);
     Time checkTime2 = start + ccaBusyStartDelay + ccaBusyDuration + ccaBusyRxInterval / 2;
-    Simulator::Schedule(checkTime2 - start, [=]() {
+    Simulator::Schedule(checkTime2 - start, [=, this]() {
         Time interval2 = (ccaBusyDuration + ccaBusyRxInterval) / 2;
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval2, checkTime2),
                               idleWidth,
@@ -1429,7 +1430,7 @@
     // At RX end, we check the status of the channel during an interval immediately
     // preceding RX start and overlapping the CCA_BUSY period.
     Time checkTime3 = start + ccaBusyStartDelay + ccaBusyDuration + ccaBusyRxInterval + rxDuration;
-    Simulator::Schedule(checkTime3 - start, [=]() {
+    Simulator::Schedule(checkTime3 - start, [=, this]() {
         Time interval3 = ccaBusyDuration / 2 + ccaBusyRxInterval;
         Time end3 = checkTime3 - rxDuration;
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval3, end3),
@@ -1443,7 +1444,7 @@
     // At RX end, we check the status of the channel during the interval following
     // the CCA_BUSY period and preceding RX start. The entire operating channel is idle.
     const Time& checkTime4 = checkTime3;
-    Simulator::Schedule(checkTime4 - start, [=]() {
+    Simulator::Schedule(checkTime4 - start, [=, this]() {
         const Time& interval4 = ccaBusyRxInterval;
         Time end4 = checkTime4 - rxDuration;
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval4, end4),
@@ -1458,7 +1459,7 @@
     // overlap the RX period
     Time interval5 = MilliSeconds(1);
     Time checkTime5 = checkTime4 + interval5;
-    Simulator::Schedule(checkTime5 - start, [=]() {
+    Simulator::Schedule(checkTime5 - start, [=, this]() {
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval5, checkTime5),
                               chWidth,
                               "Incorrect width of the idle channel in an interval "
@@ -1468,7 +1469,7 @@
 
     // After RX end, no channel is idle if the interval overlaps the RX period
     const Time& checkTime6 = checkTime5;
-    Simulator::Schedule(checkTime6 - start, [=]() {
+    Simulator::Schedule(checkTime6 - start, [=, this]() {
         Time interval6 = interval5 + rxDuration / 2;
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval6, checkTime6),
                               0,
@@ -1492,7 +1493,7 @@
 
         for (const auto busyChannel : busyChannels)
         {
-            Simulator::Schedule(Seconds(delay), [this, chWidth, busyChannel]() {
+            Simulator::Schedule(Seconds(delay), [=, this]() {
                 // reset PHY
                 if (m_phy)
                 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/examples-to-run.py ns-3.41/src/wifi/test/examples-to-run.py
--- ns-3.40/src/wifi/test/examples-to-run.py	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/examples-to-run.py	2024-02-09 14:44:08.609795000 -0800
@@ -29,448 +29,2160 @@
     ("wifi-phy-configuration --testCase=19", "True", "False"),
     ("wifi-phy-configuration --testCase=20", "True", "False"),
     ("wifi-manager-example --wifiManager=Aarf --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Aarf --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Aarf --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Aarf --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Aarf --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarf --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarf --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Aarf --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Aarf --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Aarf --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Aarf --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarf --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Aarfcd --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Aarfcd --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Aarfcd --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Aarfcd --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Aarfcd --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Aarfcd --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Aarfcd --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Aarfcd --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarfcd --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarfcd --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarfcd --standard=802.11g --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarfcd --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Aarfcd --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Amrr --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Amrr --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Amrr --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Amrr --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Amrr --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Amrr --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Amrr --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Amrr --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Amrr --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Amrr --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Amrr --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Amrr --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Arf --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Arf --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Arf --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Arf --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Arf --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Arf --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Arf --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Arf --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Arf --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Arf --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Arf --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Arf --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Cara --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Cara --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Cara --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Cara --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Cara --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Cara --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Cara --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Cara --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Cara --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Cara --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Cara --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Cara --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Onoe --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Onoe --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Onoe --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Onoe --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Onoe --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Onoe --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Onoe --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Onoe --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Onoe --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Onoe --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Onoe --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Onoe --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Rraa --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Rraa --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Rraa --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Rraa --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Rraa --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Rraa --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Rraa --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Rraa --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Rraa --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Rraa --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Minstrel --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Minstrel --standard=802.11a --rtsThreshold=0 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Minstrel --standard=802.11a --maxSlrc=1 --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Minstrel --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Minstrel --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Minstrel --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Minstrel --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
+    (
+        "wifi-manager-example --wifiManager=Rraa --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Rraa --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Minstrel --standard=802.11a --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Minstrel --standard=802.11a --rtsThreshold=0 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Minstrel --standard=802.11a --maxSlrc=1 --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Minstrel --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Minstrel --standard=802.11g --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Minstrel --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Minstrel --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11a --stepTime=0.1",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11g --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Ideal --standard=802.11a --stepTime=0.1", "True", "True"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1", "True", "False"),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11b --serverChannelWidth=22 --clientChannelWidth=22 --stepTime=0.1",
+        "True",
+        "False",
+    ),
     ("wifi-manager-example --wifiManager=Ideal --standard=802.11g --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11p-10MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11p-5MHz --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1", "False", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1", "True", "False"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=17 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=20 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=27 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0", "True", "True"),
-    ("wifi-test-interference-helper --enableCapture=1 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=1", "True", "False"),
-    ("wifi-bianchi --validate --phyMode=OfdmRate54Mbps --nMinStas=5 --nMaxStas=10 --duration=5", "False", "False"), # TODO: run from N=5 to N=50 for 100s (TAKES_FOREVER) when issue #170 is fixed
-    ("wifi-bianchi --validate --phyMode=OfdmRate6Mbps --nMinStas=5 --nMaxStas=10 --duration=15", "True", "False"), # TODO: run from N=5 to N=50 for 400s (TAKES_FOREVER) when issue #170 is fixed
-    ("wifi-bianchi --validate --phyMode=OfdmRate54Mbps --nMinStas=5 --nMaxStas=10 --duration=5 --infra", "False", "False"), # TODO: run from N=5 to N=50 for 100s (TAKES_FOREVER) when issue #170 is fixed
-    ("wifi-bianchi --validate --phyMode=OfdmRate6Mbps --nMinStas=5 --nMaxStas=10 --duration=20 --infra", "False", "False"), # TODO: run from N=5 to N=50 for 600s (TAKES_FOREVER) when issue #170 is fixed
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11p-10MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11p-5MHz --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11n-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ac --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=400 --clientShortGuardInterval=400 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=Ideal --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-5GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-2.4GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=1 --clientNss=1 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=2 --clientNss=2 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=3 --clientNss=3 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=20 --clientChannelWidth=20 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=40 --clientChannelWidth=40 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=80 --clientChannelWidth=80 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=800 --clientShortGuardInterval=800 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "False",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=1600 --clientShortGuardInterval=1600 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-manager-example --wifiManager=MinstrelHt --standard=802.11ax-6GHz --serverChannelWidth=160 --clientChannelWidth=160 --serverShortGuardInterval=3200 --clientShortGuardInterval=3200 --serverNss=4 --clientNss=4 --stepTime=0.1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=17 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=20 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-test-interference-helper --enableCapture=0 --txPowerA=5 --txPowerB=15  --delay=27 --standard=WIFI_PHY_STANDARD_80211ac --preamble=WIFI_PREAMBLE_VHT_SU --txModeA=VhtMcs0 --txModeB=VhtMcs0 --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=0",
+        "True",
+        "True",
+    ),
+    (
+        "wifi-test-interference-helper --enableCapture=1 --txPowerA=5 --txPowerB=15 --delay=10 --txModeA=OfdmRate6Mbps --txModeB=OfdmRate6Mbps --checkResults=1 --expectRxASuccessful=0 --expectRxBSuccessful=1",
+        "True",
+        "False",
+    ),
+    (
+        "wifi-bianchi --validate --phyMode=OfdmRate54Mbps --nMinStas=5 --nMaxStas=10 --duration=5",
+        "False",
+        "False",
+    ),  # TODO: run from N=5 to N=50 for 100s (TAKES_FOREVER) when issue #170 is fixed
+    (
+        "wifi-bianchi --validate --phyMode=OfdmRate6Mbps --nMinStas=5 --nMaxStas=10 --duration=15",
+        "True",
+        "False",
+    ),  # TODO: run from N=5 to N=50 for 400s (TAKES_FOREVER) when issue #170 is fixed
+    (
+        "wifi-bianchi --validate --phyMode=OfdmRate54Mbps --nMinStas=5 --nMaxStas=10 --duration=5 --infra",
+        "False",
+        "False",
+    ),  # TODO: run from N=5 to N=50 for 100s (TAKES_FOREVER) when issue #170 is fixed
+    (
+        "wifi-bianchi --validate --phyMode=OfdmRate6Mbps --nMinStas=5 --nMaxStas=10 --duration=20 --infra",
+        "False",
+        "False",
+    ),  # TODO: run from N=5 to N=50 for 600s (TAKES_FOREVER) when issue #170 is fixed
 ]
 
 # A list of Python examples to run in order to ensure that they remain
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/spectrum-wifi-phy-test.cc ns-3.41/src/wifi/test/spectrum-wifi-phy-test.cc
--- ns-3.40/src/wifi/test/spectrum-wifi-phy-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/spectrum-wifi-phy-test.cc	2024-02-09 14:44:08.609795000 -0800
@@ -366,7 +366,7 @@
   private:
     void DoSetup() override;
     void DoRun() override;
-    TestPhyListener* m_listener; ///< listener
+    std::shared_ptr<TestPhyListener> m_listener; ///< listener
 };
 
 SpectrumWifiPhyListenerTest::SpectrumWifiPhyListenerTest()
@@ -382,7 +382,7 @@
 SpectrumWifiPhyListenerTest::DoSetup()
 {
     SpectrumWifiPhyBasicTest::DoSetup();
-    m_listener = new TestPhyListener;
+    m_listener = std::make_shared<TestPhyListener>();
     m_phy->RegisterListener(m_listener);
 }
 
@@ -403,7 +403,7 @@
     NS_TEST_ASSERT_MSG_EQ(m_listener->m_notifyRxEndOk, 1, "Didn't receive NotifyRxEnd");
 
     Simulator::Destroy();
-    delete m_listener;
+    m_listener.reset();
 }
 
 /**
@@ -635,7 +635,7 @@
                                                                         WIFI_STANDARD_80211ax,
                                                                         WIFI_PHY_BAND_5GHZ));
     m_txPhy->SetOperatingChannel(
-        WifiPhy::ChannelTuple{txChannelNum, m_txChannelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{txChannelNum, m_txChannelWidth, WIFI_PHY_BAND_5GHZ, 0});
 
     uint16_t rxFrequency;
     switch (m_rxChannelWidth)
@@ -660,7 +660,7 @@
                                                                         WIFI_STANDARD_80211ax,
                                                                         WIFI_PHY_BAND_5GHZ));
     m_rxPhy->SetOperatingChannel(
-        WifiPhy::ChannelTuple{rxChannelNum, m_rxChannelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{rxChannelNum, m_rxChannelWidth, WIFI_PHY_BAND_5GHZ, 0});
 
     m_ruBands.clear();
     for (uint16_t bw = 160; bw >= 20; bw = bw / 2)
@@ -893,7 +893,7 @@
     std::vector<Ptr<MultiModelSpectrumChannel>> m_spectrumChannels; //!< Spectrum channels
     std::vector<Ptr<SpectrumWifiPhy>> m_txPhys{};                   //!< TX PHYs
     std::vector<Ptr<SpectrumWifiPhy>> m_rxPhys{};                   //!< RX PHYs
-    std::vector<std::unique_ptr<TestPhyListener>> m_listeners{};    //!< listeners
+    std::vector<std::shared_ptr<TestPhyListener>> m_listeners{};    //!< listeners
 
     std::vector<uint32_t> m_counts{0}; //!< count number of packets received by PHYs
 
@@ -1156,8 +1156,8 @@
             "PhyRxBegin",
             MakeCallback(&SpectrumWifiPhyMultipleInterfacesTest::RxCallback, this).Bind(index));
 
-        auto listener = std::make_unique<TestPhyListener>();
-        rxPhy->RegisterListener(listener.get());
+        auto listener = std::make_shared<TestPhyListener>();
+        rxPhy->RegisterListener(listener);
         m_listeners.push_back(std::move(listener));
 
         m_rxPhys.push_back(rxPhy);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/tx-duration-test.cc ns-3.41/src/wifi/test/tx-duration-test.cc
--- ns-3.40/src/wifi/test/tx-duration-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/tx-duration-test.cc	2024-02-09 14:44:08.609795000 -0800
@@ -591,7 +591,8 @@
                              WIFI_PREAMBLE_LONG,
                              MicroSeconds(8208 + 192));
 
-    // values from http://mailman.isi.edu/pipermail/ns-developers/2009-July/006226.html
+    // values from
+    // https://web.archive.org/web/20100711002639/http://mailman.isi.edu/pipermail/ns-developers/2009-July/006226.html
     retval = retval && CheckTxDuration(14,
                                        DsssPhy::GetDsssRate1Mbps(),
                                        22,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-aggregation-test.cc ns-3.41/src/wifi/test/wifi-aggregation-test.cc
--- ns-3.40/src/wifi/test/wifi-aggregation-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/wifi-aggregation-test.cc	2024-02-09 14:44:08.609795000 -0800
@@ -17,15 +17,18 @@
  * Author: Sébastien Deronne <sebastien.deronne@gmail.com>
  */
 
+#include "ns3/eht-configuration.h"
 #include "ns3/fcfs-wifi-queue-scheduler.h"
 #include "ns3/he-configuration.h"
 #include "ns3/ht-configuration.h"
 #include "ns3/ht-frame-exchange-manager.h"
 #include "ns3/interference-helper.h"
 #include "ns3/mac-tx-middle.h"
+#include "ns3/mgt-action-headers.h"
 #include "ns3/mobility-helper.h"
 #include "ns3/mpdu-aggregator.h"
 #include "ns3/msdu-aggregator.h"
+#include "ns3/multi-link-element.h"
 #include "ns3/node-container.h"
 #include "ns3/packet-socket-client.h"
 #include "ns3/packet-socket-helper.h"
@@ -47,6 +50,7 @@
 
 #include <algorithm>
 #include <iterator>
+#include <vector>
 
 using namespace ns3;
 
@@ -61,6 +65,59 @@
   public:
     AmpduAggregationTest();
 
+    /// Test parameters
+    struct Params
+    {
+        WifiStandard standard; //!< the standard of the device
+        uint8_t nLinks;        //!< number of links (>1 only for EHT)
+        std::string dataMode;  //!< data mode
+        uint16_t bufferSize;   //!< the size (in number of MPDUs) of the BlockAck buffer
+        uint16_t maxAmsduSize; //!< maximum A-MSDU size (bytes)
+        uint32_t maxAmpduSize; //!< maximum A-MPDU size (bytes)
+        Time txopLimit;        //!< TXOP limit duration
+    };
+
+    /**
+     * Construct object with non-default test parameters
+     *
+     * \param name the name of the test case
+     * \param params the test parameters
+     */
+    AmpduAggregationTest(const std::string& name, const Params& params);
+
+  protected:
+    /**
+     * Establish a BlockAck agreement.
+     *
+     * \param recipient the recipient MAC address
+     */
+    void EstablishAgreement(const Mac48Address& recipient);
+
+    /**
+     * Enqueue the given number of packets addressed to the given station and of the given size.
+     *
+     * \param count the number of packets
+     * \param size the size (bytes) of each packet
+     * \param dest the destination address
+     */
+    void EnqueuePkts(std::size_t count, uint32_t size, const Mac48Address& dest);
+
+    /**
+     * \return the Best Effort QosTxop
+     */
+    Ptr<QosTxop> GetBeQueue() const;
+
+    /**
+     * Dequeue a PSDU.
+     *
+     * \param mpduList the MPDUs contained in the PSDU
+     */
+    void DequeueMpdus(const std::vector<Ptr<WifiMpdu>>& mpduList);
+
+    Ptr<StaWifiMac> m_mac;            ///< Mac
+    std::vector<Ptr<WifiPhy>> m_phys; ///< Phys
+    Params m_params;                  //!< test parameters
+
   private:
     /**
      * Fired when the MAC discards an MPDU.
@@ -70,17 +127,31 @@
      */
     void MpduDiscarded(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu);
 
+    void DoSetup() override;
     void DoRun() override;
-    Ptr<WifiNetDevice> m_device;             ///< WifiNetDevice
-    Ptr<StaWifiMac> m_mac;                   ///< Mac
-    Ptr<YansWifiPhy> m_phy;                  ///< Phy
-    Ptr<WifiRemoteStationManager> m_manager; ///< remote station manager
-    ObjectFactory m_factory;                 ///< factory
-    bool m_discarded;                        ///< whether the packet should be discarded
+    void DoTeardown() override;
+
+    Ptr<WifiNetDevice> m_device;                           ///< WifiNetDevice
+    std::vector<Ptr<WifiRemoteStationManager>> m_managers; ///< remote station managers
+    ObjectFactory m_factory;                               ///< factory
+    bool m_discarded; ///< whether the packet should be discarded
 };
 
 AmpduAggregationTest::AmpduAggregationTest()
-    : TestCase("Check the correctness of MPDU aggregation operations"),
+    : AmpduAggregationTest("Check the correctness of MPDU aggregation operations",
+                           Params{.standard = WIFI_STANDARD_80211n,
+                                  .nLinks = 1,
+                                  .dataMode = "HtMcs7",
+                                  .bufferSize = 64,
+                                  .maxAmsduSize = 0,
+                                  .maxAmpduSize = 65535,
+                                  .txopLimit = Seconds(0)})
+{
+}
+
+AmpduAggregationTest::AmpduAggregationTest(const std::string& name, const Params& params)
+    : TestCase(name),
+      m_params(params),
       m_discarded(false)
 {
 }
@@ -92,88 +163,187 @@
 }
 
 void
-AmpduAggregationTest::DoRun()
+AmpduAggregationTest::DoSetup()
 {
     /*
      * Create device and attach HT configuration.
      */
     m_device = CreateObject<WifiNetDevice>();
-    Ptr<HtConfiguration> htConfiguration = CreateObject<HtConfiguration>();
+    m_device->SetStandard(m_params.standard);
+    auto htConfiguration = CreateObject<HtConfiguration>();
     m_device->SetHtConfiguration(htConfiguration);
+    if (m_params.standard >= WIFI_STANDARD_80211ax)
+    {
+        auto vhtConfiguration = CreateObject<VhtConfiguration>();
+        m_device->SetVhtConfiguration(vhtConfiguration);
+        auto heConfiguration = CreateObject<HeConfiguration>();
+        m_device->SetHeConfiguration(heConfiguration);
+    }
+    if (m_params.standard >= WIFI_STANDARD_80211be)
+    {
+        auto ehtConfiguration = CreateObject<EhtConfiguration>();
+        m_device->SetEhtConfiguration(ehtConfiguration);
+    }
 
     /*
      * Create and configure phy layer.
      */
-    m_phy = CreateObject<YansWifiPhy>();
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    m_phy->SetDevice(m_device);
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211n);
-    m_device->SetPhy(m_phy);
+    for (uint8_t i = 0; i < m_params.nLinks; i++)
+    {
+        m_phys.emplace_back(CreateObject<YansWifiPhy>());
+        auto interferenceHelper = CreateObject<InterferenceHelper>();
+        m_phys.back()->SetInterferenceHelper(interferenceHelper);
+        m_phys.back()->SetDevice(m_device);
+        m_phys.back()->ConfigureStandard(m_params.standard);
+    }
+    m_device->SetPhys(m_phys);
 
     /*
      * Create and configure manager.
      */
     m_factory = ObjectFactory();
     m_factory.SetTypeId("ns3::ConstantRateWifiManager");
-    m_factory.Set("DataMode", StringValue("HtMcs7"));
-    m_manager = m_factory.Create<WifiRemoteStationManager>();
-    m_manager->SetupPhy(m_phy);
-    m_device->SetRemoteStationManager(m_manager);
+    m_factory.Set("DataMode", StringValue(m_params.dataMode));
+    for (uint8_t i = 0; i < m_params.nLinks; i++)
+    {
+        m_managers.emplace_back(m_factory.Create<WifiRemoteStationManager>());
+        m_managers.back()->SetupPhy(m_phys.at(i));
+    }
+    m_device->SetRemoteStationManagers(m_managers);
 
     /*
      * Create and configure mac layer.
      */
     m_mac = CreateObjectWithAttributes<StaWifiMac>("QosSupported", BooleanValue(true));
     m_mac->SetDevice(m_device);
-    m_mac->SetWifiRemoteStationManager(m_manager);
+    m_mac->SetWifiRemoteStationManagers(m_managers);
+    for (uint8_t i = 0; i < m_params.nLinks; i++)
+    {
+        m_managers.at(i)->SetupMac(m_mac);
+    }
     m_mac->SetAddress(Mac48Address("00:00:00:00:00:01"));
-    m_mac->SetWifiPhys({m_phy});
-    m_mac->ConfigureStandard(WIFI_STANDARD_80211n);
-    Ptr<FrameExchangeManager> fem = m_mac->GetFrameExchangeManager();
-    Ptr<WifiProtectionManager> protectionManager = CreateObject<WifiDefaultProtectionManager>();
-    protectionManager->SetWifiMac(m_mac);
-    fem->SetProtectionManager(protectionManager);
-    Ptr<WifiAckManager> ackManager = CreateObject<WifiDefaultAckManager>();
-    ackManager->SetWifiMac(m_mac);
-    fem->SetAckManager(ackManager);
     m_device->SetMac(m_mac);
+    m_mac->SetWifiPhys(m_phys);
+    m_mac->ConfigureStandard(m_params.standard);
+    for (uint8_t i = 0; i < m_params.nLinks; i++)
+    {
+        auto fem = m_mac->GetFrameExchangeManager(i);
+        auto protectionManager = CreateObject<WifiDefaultProtectionManager>();
+        protectionManager->SetWifiMac(m_mac);
+        fem->SetProtectionManager(protectionManager);
+        auto ackManager = CreateObject<WifiDefaultAckManager>();
+        ackManager->SetWifiMac(m_mac);
+        fem->SetAckManager(ackManager);
+        // here we should assign link addresses in case of MLDs, but we don't actually use link
+        // addresses in this test
+    }
     m_mac->SetState(StaWifiMac::ASSOCIATED);
+    if (m_params.nLinks > 1)
+    {
+        // the bssid field of StaLinkEntity must hold a value
+        for (const auto& [id, link] : m_mac->GetLinks())
+        {
+            static_cast<StaWifiMac::StaLinkEntity&>(*link).bssid = Mac48Address::GetBroadcast();
+        }
+    }
     m_mac->SetMacQueueScheduler(CreateObject<FcfsWifiQueueScheduler>());
 
     /*
-     * Configure MPDU aggregation.
+     * Configure A-MSDU and A-MPDU aggregation.
      */
-    m_mac->SetAttribute("BE_MaxAmpduSize", UintegerValue(65535));
-    HtCapabilities htCapabilities;
-    htCapabilities.SetMaxAmpduLength(65535);
-    m_manager->AddStationHtCapabilities(Mac48Address("00:00:00:00:00:02"), htCapabilities);
-    m_manager->AddStationHtCapabilities(Mac48Address("00:00:00:00:00:03"), htCapabilities);
+    // Make sure that at least 1024 MPDUs are buffered (to test aggregation on EHT devices)
+    m_mac->GetTxopQueue(AC_BE)->SetAttribute("MaxSize", StringValue("2000p"));
+    m_mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(m_params.maxAmsduSize));
+    m_mac->SetAttribute("BE_MaxAmpduSize", UintegerValue(m_params.maxAmpduSize));
+    GetBeQueue()->SetAttribute(
+        "TxopLimits",
+        AttributeContainerValue<TimeValue>(std::vector<Time>(m_params.nLinks, m_params.txopLimit)));
 
-    /*
-     * Create a dummy packet of 1500 bytes and fill mac header fields.
-     */
-    Ptr<const Packet> pkt = Create<Packet>(1500);
-    Ptr<Packet> currentAggregatedPacket = Create<Packet>();
-    WifiMacHeader hdr;
-    hdr.SetAddr1(Mac48Address("00:00:00:00:00:02"));
-    hdr.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr.SetType(WIFI_MAC_QOSDATA);
-    hdr.SetQosTid(0);
-    hdr.SetFragmentNumber(0);
-    hdr.SetNoMoreFragments();
-    hdr.SetNoRetry();
+    if (m_params.nLinks > 1)
+    {
+        auto mleCommonInfo2 = std::make_shared<CommonInfoBasicMle>();
+        mleCommonInfo2->m_mldMacAddress = Mac48Address("00:00:00:00:00:02");
+        for (uint8_t i = 0; i < m_params.nLinks; i++)
+        {
+            // we don't actually use the link addresses of the receiver, so we just use one address
+            // as both the MLD address and the link address of the receiver (the first argument in
+            // the call below should be the link address)
+            m_managers.at(i)->AddStationMleCommonInfo(mleCommonInfo2->m_mldMacAddress,
+                                                      mleCommonInfo2);
+        }
+
+        auto mleCommonInfo3 = std::make_shared<CommonInfoBasicMle>();
+        mleCommonInfo3->m_mldMacAddress = Mac48Address("00:00:00:00:00:03");
+        for (uint8_t i = 0; i < m_params.nLinks; i++)
+        {
+            m_managers.at(i)->AddStationMleCommonInfo(mleCommonInfo3->m_mldMacAddress,
+                                                      mleCommonInfo3);
+        }
+    }
+
+    for (uint8_t i = 0; i < m_params.nLinks; i++)
+    {
+        HtCapabilities htCapabilities;
+        htCapabilities.SetMaxAmsduLength(7935);
+        htCapabilities.SetMaxAmpduLength(65535);
+        m_managers.at(i)->AddStationHtCapabilities(Mac48Address("00:00:00:00:00:02"),
+                                                   htCapabilities);
+        m_managers.at(i)->AddStationHtCapabilities(Mac48Address("00:00:00:00:00:03"),
+                                                   htCapabilities);
+
+        if (m_params.standard >= WIFI_STANDARD_80211ac)
+        {
+            VhtCapabilities vhtCapabilities;
+            vhtCapabilities.SetMaxMpduLength(11454);
+            m_managers.at(i)->AddStationVhtCapabilities(Mac48Address("00:00:00:00:00:02"),
+                                                        vhtCapabilities);
+        }
+        if (m_params.standard >= WIFI_STANDARD_80211ax)
+        {
+            HeCapabilities heCapabilities;
+            heCapabilities.SetMaxAmpduLength((1 << 23) - 1);
+            m_managers.at(i)->AddStationHeCapabilities(Mac48Address("00:00:00:00:00:02"),
+                                                       heCapabilities);
+        }
+        if (m_params.standard >= WIFI_STANDARD_80211be)
+        {
+            EhtCapabilities ehtCapabilities;
+            ehtCapabilities.SetMaxMpduLength(11454);
+            ehtCapabilities.SetMaxAmpduLength((1 << 24) - 1);
+            m_managers.at(i)->AddStationEhtCapabilities(Mac48Address("00:00:00:00:00:02"),
+                                                        ehtCapabilities);
+        }
+    }
 
     /*
      * Establish agreement.
      */
+    EstablishAgreement(Mac48Address("00:00:00:00:00:02"));
+}
+
+Ptr<QosTxop>
+AmpduAggregationTest::GetBeQueue() const
+{
+    return m_mac->GetBEQueue();
+}
+
+void
+AmpduAggregationTest::DequeueMpdus(const std::vector<Ptr<WifiMpdu>>& mpduList)
+{
+    std::list<Ptr<const WifiMpdu>> mpdus(mpduList.cbegin(), mpduList.cend());
+    m_mac->GetTxopQueue(AC_BE)->DequeueIfQueued(mpdus);
+}
+
+void
+AmpduAggregationTest::EstablishAgreement(const Mac48Address& recipient)
+{
     MgtAddBaRequestHeader reqHdr;
     reqHdr.SetImmediateBlockAck();
     reqHdr.SetTid(0);
-    reqHdr.SetBufferSize(64);
+    reqHdr.SetBufferSize(m_params.bufferSize);
     reqHdr.SetTimeout(0);
     reqHdr.SetStartingSequence(0);
-    m_mac->GetBEQueue()->GetBaManager()->CreateOriginatorAgreement(reqHdr, hdr.GetAddr1());
+    GetBeQueue()->GetBaManager()->CreateOriginatorAgreement(reqHdr, recipient);
 
     MgtAddBaResponseHeader respHdr;
     StatusCode code;
@@ -182,27 +352,49 @@
     respHdr.SetAmsduSupport(reqHdr.IsAmsduSupported());
     respHdr.SetImmediateBlockAck();
     respHdr.SetTid(reqHdr.GetTid());
-    respHdr.SetBufferSize(64);
+    respHdr.SetBufferSize(m_params.bufferSize);
     respHdr.SetTimeout(reqHdr.GetTimeout());
-    m_mac->GetBEQueue()->GetBaManager()->UpdateOriginatorAgreement(respHdr, hdr.GetAddr1(), 0);
+    GetBeQueue()->GetBaManager()->UpdateOriginatorAgreement(respHdr, recipient, 0);
+}
+
+void
+AmpduAggregationTest::EnqueuePkts(std::size_t count, uint32_t size, const Mac48Address& dest)
+{
+    for (std::size_t i = 0; i < count; i++)
+    {
+        auto pkt = Create<Packet>(size);
+        WifiMacHeader hdr;
+
+        hdr.SetAddr1(dest);
+        hdr.SetAddr2(Mac48Address("00:00:00:00:00:01"));
+        hdr.SetType(WIFI_MAC_QOSDATA);
+        hdr.SetQosTid(0);
 
-    //-----------------------------------------------------------------------------------------------------
+        GetBeQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt, hdr));
+    }
+}
 
+void
+AmpduAggregationTest::DoRun()
+{
     /*
      * Test behavior when no other packets are in the queue
      */
-    Ptr<HtFrameExchangeManager> htFem = DynamicCast<HtFrameExchangeManager>(fem);
-    Ptr<MpduAggregator> mpduAggregator = htFem->GetMpduAggregator();
+    auto fem = m_mac->GetFrameExchangeManager(SINGLE_LINK_OP_ID);
+    auto htFem = DynamicCast<HtFrameExchangeManager>(fem);
+    auto mpduAggregator = htFem->GetMpduAggregator();
 
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt, hdr));
+    /*
+     * Create a dummy packet of 1500 bytes and fill mac header fields.
+     */
+    EnqueuePkts(1, 1500, Mac48Address("00:00:00:00:00:02"));
 
-    Ptr<WifiMpdu> peeked = m_mac->GetBEQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    auto peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
     WifiTxParameters txParams;
-    txParams.m_txVector =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(peeked->GetHeader(),
-                                                              m_phy->GetChannelWidth());
-    Ptr<WifiMpdu> item =
-        m_mac->GetBEQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
+    auto item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
 
     auto mpduList = mpduAggregator->GetNextAmpdu(item, txParams, Time::Min());
 
@@ -212,40 +404,24 @@
     m_mac->m_txMiddle->SetSequenceNumberFor(&item->GetHeader());
     item->UnassignSeqNo();
 
-    //-----------------------------------------------------------------------------------------------------
+    //---------------------------------------------------------------------------------------------
 
     /*
      * Test behavior when 2 more packets are in the queue
      */
-    Ptr<const Packet> pkt1 = Create<Packet>(1500);
-    Ptr<const Packet> pkt2 = Create<Packet>(1500);
-    WifiMacHeader hdr1;
-    WifiMacHeader hdr2;
-
-    hdr1.SetAddr1(Mac48Address("00:00:00:00:00:02"));
-    hdr1.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr1.SetType(WIFI_MAC_QOSDATA);
-    hdr1.SetQosTid(0);
-
-    hdr2.SetAddr1(Mac48Address("00:00:00:00:00:02"));
-    hdr2.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr2.SetType(WIFI_MAC_QOSDATA);
-    hdr2.SetQosTid(0);
+    EnqueuePkts(2, 1500, Mac48Address("00:00:00:00:00:02"));
 
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt1, hdr1));
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt2, hdr2));
-
-    item = m_mac->GetBEQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
+    item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
     mpduList = mpduAggregator->GetNextAmpdu(item, txParams, Time::Min());
 
     NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), false, "MPDU aggregation failed");
 
-    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(mpduList);
-    htFem->DequeuePsdu(psdu);
+    auto psdu = Create<WifiPsdu>(mpduList);
+    DequeueMpdus(mpduList);
 
     NS_TEST_EXPECT_MSG_EQ(psdu->GetSize(), 4606, "A-MPDU size is not correct");
     NS_TEST_EXPECT_MSG_EQ(mpduList.size(), 3, "A-MPDU should contain 3 MPDUs");
-    NS_TEST_EXPECT_MSG_EQ(m_mac->GetBEQueue()->GetWifiMacQueue()->GetNPackets(),
+    NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
                           0,
                           "queue should be empty");
 
@@ -254,43 +430,22 @@
         NS_TEST_EXPECT_MSG_EQ(psdu->GetHeader(i).GetSequenceNumber(), i, "wrong sequence number");
     }
 
-    //-----------------------------------------------------------------------------------------------------
+    //---------------------------------------------------------------------------------------------
 
     /*
      * Test behavior when the 802.11n station and another non-QoS station are associated to the AP.
      * The AP sends an A-MPDU to the 802.11n station followed by the last retransmission of a
      * non-QoS data frame to the non-QoS station. This is used to reproduce bug 2224.
      */
-    pkt1 = Create<Packet>(1500);
-    pkt2 = Create<Packet>(1500);
-    hdr1.SetAddr1(Mac48Address("00:00:00:00:00:02"));
-    hdr1.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr1.SetType(WIFI_MAC_QOSDATA);
-    hdr1.SetQosTid(0);
-    hdr1.SetSequenceNumber(3);
-    hdr2.SetAddr1(Mac48Address("00:00:00:00:00:03"));
-    hdr2.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr2.SetType(WIFI_MAC_QOSDATA);
-    hdr2.SetQosTid(0);
-
-    Ptr<const Packet> pkt3 = Create<Packet>(1500);
-    WifiMacHeader hdr3;
-    hdr3.SetSequenceNumber(0);
-    hdr3.SetAddr1(Mac48Address("00:00:00:00:00:03"));
-    hdr3.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr3.SetType(WIFI_MAC_QOSDATA);
-    hdr3.SetQosTid(0);
-
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt1, hdr1));
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt2, hdr2));
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt3, hdr3));
+    EnqueuePkts(1, 1500, Mac48Address("00:00:00:00:00:02"));
+    EnqueuePkts(2, 1500, Mac48Address("00:00:00:00:00:03"));
 
-    peeked = m_mac->GetBEQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
     txParams.Clear();
-    txParams.m_txVector =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(peeked->GetHeader(),
-                                                              m_phy->GetChannelWidth());
-    item = m_mac->GetBEQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
+    item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
 
     mpduList = mpduAggregator->GetNextAmpdu(item, txParams, Time::Min());
 
@@ -298,14 +453,14 @@
                           true,
                           "a single packet for this destination should not result in an A-MPDU");
     // dequeue the MPDU
-    htFem->DequeueMpdu(item);
+    DequeueMpdus({item});
 
-    peeked = m_mac->GetBEQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
     txParams.Clear();
-    txParams.m_txVector =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(peeked->GetHeader(),
-                                                              m_phy->GetChannelWidth());
-    item = m_mac->GetBEQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
+    item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
 
     mpduList = mpduAggregator->GetNextAmpdu(item, txParams, Time::Min());
 
@@ -313,25 +468,31 @@
                           true,
                           "no MPDU aggregation should be performed if there is no agreement");
 
-    m_manager->SetMaxSsrc(
-        0); // set to 0 in order to fake that the maximum number of retries has been reached
+    m_managers.at(SINGLE_LINK_OP_ID)
+        ->SetMaxSsrc(
+            0); // set to 0 in order to fake that the maximum number of retries has been reached
     m_mac->TraceConnectWithoutContext("DroppedMpdu",
                                       MakeCallback(&AmpduAggregationTest::MpduDiscarded, this));
-    htFem->m_dcf = m_mac->GetBEQueue();
+    htFem->m_dcf = GetBeQueue();
     htFem->NormalAckTimeout(item, txParams.m_txVector);
 
     NS_TEST_EXPECT_MSG_EQ(m_discarded, true, "packet should be discarded");
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Flush();
+    GetBeQueue()->GetWifiMacQueue()->Flush();
+}
 
+void
+AmpduAggregationTest::DoTeardown()
+{
     Simulator::Destroy();
 
-    m_manager->Dispose();
-    m_manager = nullptr;
+    for (auto manager : m_managers)
+    {
+        manager->Dispose();
+    }
+    m_managers.clear();
 
     m_device->Dispose();
     m_device = nullptr;
-
-    htConfiguration = nullptr;
 }
 
 /**
@@ -340,22 +501,24 @@
  *
  * \brief Two Level Aggregation Test
  */
-class TwoLevelAggregationTest : public TestCase
+class TwoLevelAggregationTest : public AmpduAggregationTest
 {
   public:
     TwoLevelAggregationTest();
 
   private:
     void DoRun() override;
-    Ptr<WifiNetDevice> m_device;             ///< WifiNetDevice
-    Ptr<StaWifiMac> m_mac;                   ///< Mac
-    Ptr<YansWifiPhy> m_phy;                  ///< Phy
-    Ptr<WifiRemoteStationManager> m_manager; ///< remote station manager
-    ObjectFactory m_factory;                 ///< factory
 };
 
 TwoLevelAggregationTest::TwoLevelAggregationTest()
-    : TestCase("Check the correctness of two-level aggregation operations")
+    : AmpduAggregationTest("Check the correctness of two-level aggregation operations",
+                           Params{.standard = WIFI_STANDARD_80211n,
+                                  .nLinks = 1,
+                                  .dataMode = "HtMcs2", // 19.5Mbps
+                                  .bufferSize = 64,
+                                  .maxAmsduSize = 3050,
+                                  .maxAmpduSize = 65535,
+                                  .txopLimit = MicroSeconds(3008)})
 {
 }
 
@@ -363,190 +526,89 @@
 TwoLevelAggregationTest::DoRun()
 {
     /*
-     * Create device and attach HT configuration.
-     */
-    m_device = CreateObject<WifiNetDevice>();
-    m_device->SetStandard(WIFI_STANDARD_80211n);
-    Ptr<HtConfiguration> htConfiguration = CreateObject<HtConfiguration>();
-    m_device->SetHtConfiguration(htConfiguration);
-
-    /*
-     * Create and configure phy layer.
-     */
-    m_phy = CreateObject<YansWifiPhy>();
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    m_phy->SetDevice(m_device);
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211n);
-    m_device->SetPhy(m_phy);
-
-    /*
-     * Create and configure manager.
-     */
-    m_factory = ObjectFactory();
-    m_factory.SetTypeId("ns3::ConstantRateWifiManager");
-    m_factory.Set("DataMode", StringValue("HtMcs7"));
-    m_manager = m_factory.Create<WifiRemoteStationManager>();
-    m_manager->SetupPhy(m_phy);
-    m_device->SetRemoteStationManager(m_manager);
-
-    /*
-     * Create and configure mac layer.
-     */
-    m_mac = CreateObjectWithAttributes<StaWifiMac>("QosSupported", BooleanValue(true));
-    m_mac->SetDevice(m_device);
-    m_mac->SetWifiRemoteStationManager(m_manager);
-    m_mac->SetAddress(Mac48Address("00:00:00:00:00:01"));
-    m_mac->SetWifiPhys({m_phy});
-    m_mac->ConfigureStandard(WIFI_STANDARD_80211n);
-    Ptr<FrameExchangeManager> fem = m_mac->GetFrameExchangeManager();
-    Ptr<WifiProtectionManager> protectionManager = CreateObject<WifiDefaultProtectionManager>();
-    protectionManager->SetWifiMac(m_mac);
-    fem->SetProtectionManager(protectionManager);
-    Ptr<WifiAckManager> ackManager = CreateObject<WifiDefaultAckManager>();
-    ackManager->SetWifiMac(m_mac);
-    fem->SetAckManager(ackManager);
-    m_device->SetMac(m_mac);
-    m_mac->SetState(StaWifiMac::ASSOCIATED);
-    m_mac->SetMacQueueScheduler(CreateObject<FcfsWifiQueueScheduler>());
-
-    /*
-     * Configure aggregation.
-     */
-    m_mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(4095));
-    m_mac->SetAttribute("BE_MaxAmpduSize", UintegerValue(65535));
-    HtCapabilities htCapabilities;
-    htCapabilities.SetMaxAmsduLength(7935);
-    htCapabilities.SetMaxAmpduLength(65535);
-    m_manager->AddStationHtCapabilities(Mac48Address("00:00:00:00:00:02"), htCapabilities);
-
-    /*
      * Create dummy packets of 1500 bytes and fill mac header fields that will be used for the
      * tests.
      */
-    Ptr<const Packet> pkt = Create<Packet>(1500);
-    WifiMacHeader hdr;
-    hdr.SetAddr1(Mac48Address("00:00:00:00:00:02"));
-    hdr.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr.SetType(WIFI_MAC_QOSDATA);
-    hdr.SetQosTid(0);
+    EnqueuePkts(3, 1500, Mac48Address("00:00:00:00:00:02"));
 
-    //-----------------------------------------------------------------------------------------------------
+    //---------------------------------------------------------------------------------------------
 
     /*
      * Test MSDU and MPDU aggregation. Three MSDUs are in the queue and the maximum A-MSDU size
      * is such that only two MSDUs can be aggregated. Therefore, the first MPDU we get contains
      * an A-MSDU of 2 MSDUs.
      */
-    Ptr<HtFrameExchangeManager> htFem = DynamicCast<HtFrameExchangeManager>(fem);
-    Ptr<MsduAggregator> msduAggregator = htFem->GetMsduAggregator();
-    Ptr<MpduAggregator> mpduAggregator = htFem->GetMpduAggregator();
-
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(Create<Packet>(1500), hdr));
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(Create<Packet>(1500), hdr));
-    m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(Create<Packet>(1500), hdr));
+    auto fem = m_mac->GetFrameExchangeManager(SINGLE_LINK_OP_ID);
+    auto htFem = DynamicCast<HtFrameExchangeManager>(fem);
+    auto msduAggregator = htFem->GetMsduAggregator();
+    auto mpduAggregator = htFem->GetMpduAggregator();
 
-    Ptr<WifiMpdu> peeked = m_mac->GetBEQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    auto peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
     WifiTxParameters txParams;
-    txParams.m_txVector =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(peeked->GetHeader(),
-                                                              m_phy->GetChannelWidth());
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
     htFem->TryAddMpdu(peeked, txParams, Time::Min());
-    Ptr<WifiMpdu> item = msduAggregator->GetNextAmsdu(peeked, txParams, Time::Min());
+    auto item = msduAggregator->GetNextAmsdu(peeked, txParams, Time::Min());
 
     bool result{item};
     NS_TEST_EXPECT_MSG_EQ(result, true, "aggregation failed");
     NS_TEST_EXPECT_MSG_EQ(item->GetPacketSize(), 3030, "wrong packet size");
 
     // dequeue the MSDUs
-    htFem->DequeueMpdu(item);
+    DequeueMpdus({item});
 
-    NS_TEST_EXPECT_MSG_EQ(m_mac->GetBEQueue()->GetWifiMacQueue()->GetNPackets(),
+    NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
                           1,
                           "Unexpected number of MSDUs left in the EDCA queue");
 
-    //-----------------------------------------------------------------------------------------------------
+    //---------------------------------------------------------------------------------------------
 
     /*
      * A-MSDU aggregation fails when there is just one MSDU in the queue.
      */
 
-    peeked = m_mac->GetBEQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
     txParams.Clear();
-    txParams.m_txVector =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(peeked->GetHeader(),
-                                                              m_phy->GetChannelWidth());
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
     htFem->TryAddMpdu(peeked, txParams, Time::Min());
     item = msduAggregator->GetNextAmsdu(peeked, txParams, Time::Min());
 
     NS_TEST_EXPECT_MSG_EQ(item, nullptr, "A-MSDU aggregation did not fail");
 
-    htFem->DequeueMpdu(peeked);
+    DequeueMpdus({peeked});
 
-    NS_TEST_EXPECT_MSG_EQ(m_mac->GetBEQueue()->GetWifiMacQueue()->GetNPackets(),
+    NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
                           0,
                           "queue should be empty");
 
-    //-----------------------------------------------------------------------------------------------------
+    //---------------------------------------------------------------------------------------------
 
     /*
      * Aggregation of MPDUs is stopped to prevent that the PPDU duration exceeds the TXOP limit.
-     * In this test, the VI AC is used, which has a default TXOP limit of 3008 microseconds.
+     * In this test, a TXOP limit of 3008 microseconds is used.
      */
 
-    // Establish agreement.
-    uint8_t tid = 5;
-    MgtAddBaRequestHeader reqHdr;
-    reqHdr.SetImmediateBlockAck();
-    reqHdr.SetTid(tid);
-    reqHdr.SetBufferSize(64);
-    reqHdr.SetTimeout(0);
-    reqHdr.SetStartingSequence(0);
-    m_mac->GetVIQueue()->GetBaManager()->CreateOriginatorAgreement(reqHdr, hdr.GetAddr1());
-
-    MgtAddBaResponseHeader respHdr;
-    StatusCode code;
-    code.SetSuccess();
-    respHdr.SetStatusCode(code);
-    respHdr.SetAmsduSupport(reqHdr.IsAmsduSupported());
-    respHdr.SetImmediateBlockAck();
-    respHdr.SetTid(reqHdr.GetTid());
-    respHdr.SetBufferSize(64);
-    respHdr.SetTimeout(reqHdr.GetTimeout());
-    m_mac->GetVIQueue()->GetBaManager()->UpdateOriginatorAgreement(respHdr, hdr.GetAddr1(), 0);
-
-    m_mac->SetAttribute("VI_MaxAmsduSize", UintegerValue(3050)); // max 2 MSDUs per A-MSDU
-    m_mac->SetAttribute("VI_MaxAmpduSize", UintegerValue(65535));
-    m_mac->GetVIQueue()->SetAttribute(
-        "TxopLimits",
-        AttributeContainerValue<TimeValue>(std::vector<Time>{MicroSeconds(3008)}));
-    m_manager->SetAttribute("DataMode", StringValue("HtMcs2")); // 19.5Mbps
-
-    hdr.SetQosTid(tid);
-
     // Add 10 MSDUs to the EDCA queue
-    for (uint8_t i = 0; i < 10; i++)
-    {
-        m_mac->GetVIQueue()->GetWifiMacQueue()->Enqueue(
-            Create<WifiMpdu>(Create<Packet>(1300), hdr));
-    }
+    EnqueuePkts(10, 1300, Mac48Address("00:00:00:00:00:02"));
 
-    peeked = m_mac->GetVIQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
     txParams.Clear();
-    txParams.m_txVector =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(peeked->GetHeader(),
-                                                              m_phy->GetChannelWidth());
-    Time txopLimit = m_mac->GetVIQueue()->GetTxopLimit(); // 3.008 ms
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
 
     // Compute the first MPDU to be aggregated in an A-MPDU. It must contain an A-MSDU
     // aggregating two MSDUs
-    item = m_mac->GetVIQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, txopLimit, true);
+    item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, m_params.txopLimit, true);
 
     NS_TEST_EXPECT_MSG_EQ(std::distance(item->begin(), item->end()),
                           2,
                           "There must be 2 MSDUs in the A-MSDU");
 
-    auto mpduList = mpduAggregator->GetNextAmpdu(item, txParams, txopLimit);
+    auto mpduList = mpduAggregator->GetNextAmpdu(item, txParams, m_params.txopLimit);
 
     // The maximum number of bytes that can be transmitted in a TXOP is (approximately, as we
     // do not consider that the preamble is transmitted at a different rate):
@@ -561,23 +623,96 @@
     NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), false, "aggregation failed");
     NS_TEST_EXPECT_MSG_EQ(mpduList.size(), 3, "Unexpected number of MPDUs in the A-MPDU");
     NS_TEST_EXPECT_MSG_EQ(mpduList.at(0)->GetSize(), 2660, "Unexpected size of the first MPDU");
+    NS_TEST_EXPECT_MSG_EQ(mpduList.at(0)->GetHeader().IsQosAmsdu(),
+                          true,
+                          "Expecting the first MPDU to contain an A-MSDU");
     NS_TEST_EXPECT_MSG_EQ(mpduList.at(1)->GetSize(), 2660, "Unexpected size of the second MPDU");
-    NS_TEST_EXPECT_MSG_EQ(mpduList.at(2)->GetSize(), 1330, "Unexpected size of the first MPDU");
+    NS_TEST_EXPECT_MSG_EQ(mpduList.at(1)->GetHeader().IsQosAmsdu(),
+                          true,
+                          "Expecting the second MPDU to contain an A-MSDU");
+    NS_TEST_EXPECT_MSG_EQ(mpduList.at(2)->GetSize(), 1330, "Unexpected size of the third MPDU");
+    NS_TEST_EXPECT_MSG_EQ(mpduList.at(2)->GetHeader().IsQosAmsdu(),
+                          false,
+                          "Expecting the third MPDU not to contain an A-MSDU");
 
-    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(mpduList);
-    htFem->DequeuePsdu(psdu);
+    auto psdu = Create<WifiPsdu>(mpduList);
+    NS_TEST_EXPECT_MSG_EQ(psdu->GetSize(), 6662, "Unexpected size of the A-MPDU");
 
-    NS_TEST_EXPECT_MSG_EQ(m_mac->GetVIQueue()->GetWifiMacQueue()->GetNPackets(),
-                          5,
-                          "Unexpected number of MSDUs left in the EDCA queue");
+    // we now have two A-MSDUs and 6 MSDUs in the queue (5 MSDUs with no assigned sequence number)
+    NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
+                          8,
+                          "Unexpected number of items left in the EDCA queue");
 
-    NS_TEST_EXPECT_MSG_EQ(psdu->GetSize(), 6662, "Unexpected size of the A-MPDU");
+    // prepare another A-MPDU (e.g., for transmission on another link)
+    peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID, 0, psdu->GetAddr1(), mpduList.at(2));
+    txParams.Clear();
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
 
-    Simulator::Destroy();
+    // Compute the first MPDU to be aggregated in an A-MPDU. It must contain an A-MSDU
+    // aggregating two MSDUs
+    item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, m_params.txopLimit, true);
 
-    m_device->Dispose();
-    m_device = nullptr;
-    htConfiguration = nullptr;
+    NS_TEST_EXPECT_MSG_EQ(std::distance(item->begin(), item->end()),
+                          2,
+                          "There must be 2 MSDUs in the A-MSDU");
+
+    auto mpduList2 = mpduAggregator->GetNextAmpdu(item, txParams, m_params.txopLimit);
+
+    // we now have two A-MSDUs, one MSDU, two A-MSDUs and one MSDU in the queue (all with assigned
+    // sequence number)
+    NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
+                          6,
+                          "Unexpected number of items left in the EDCA queue");
+
+    // unassign sequence numbers for all MPDUs (emulates an RTS/CTS failure on both links)
+    mpduList.at(0)->UnassignSeqNo();
+    mpduList.at(1)->UnassignSeqNo();
+    mpduList.at(2)->UnassignSeqNo();
+    mpduList2.at(0)->UnassignSeqNo();
+    mpduList2.at(1)->UnassignSeqNo();
+    mpduList2.at(2)->UnassignSeqNo();
+
+    // set A-MSDU max size to a large value
+    m_mac->SetAttribute("BE_MaxAmsduSize", UintegerValue(7000));
+
+    // A-MSDU aggregation now fails because the first item in the queue contain A-MSDUs
+    peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    txParams.Clear();
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
+
+    htFem->TryAddMpdu(peeked, txParams, Time::Min());
+    item = msduAggregator->GetNextAmsdu(peeked, txParams, Time::Min());
+
+    NS_TEST_EXPECT_MSG_EQ(item, nullptr, "Expecting not to be able to aggregate A-MSDUs");
+
+    // remove the first two items in the queue (containing A-MSDUs)
+    DequeueMpdus({mpduList.at(0), mpduList.at(1)});
+
+    // we now have one MSDU, two A-MSDUs and one MSDU in the queue
+    NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
+                          4,
+                          "Unexpected number of items left in the EDCA queue");
+
+    peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    txParams.Clear();
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
+
+    NS_TEST_EXPECT_MSG_EQ(peeked->GetHeader().IsQosAmsdu(),
+                          false,
+                          "Expecting the peeked MPDU not to contain an A-MSDU");
+
+    item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
+
+    // A-MSDU aggregation is not attempted because the next item contains an A-MSDU
+    NS_TEST_EXPECT_MSG_EQ(item->GetHeader().IsQosAmsdu(),
+                          false,
+                          "Expecting the returned MPDU not to contain an A-MSDU");
 }
 
 /**
@@ -587,187 +722,193 @@
  * \brief 802.11ax aggregation test which permits 64 or 256 MPDUs in A-MPDU according to the
  * negotiated buffer size.
  */
-class HeAggregationTest : public TestCase
+class HeAggregationTest : public AmpduAggregationTest
 {
   public:
-    HeAggregationTest();
-
-  private:
-    void DoRun() override;
     /**
-     * Run test for a given buffer size
+     * Constructor.
      *
-     * \param bufferSize the buffer size
+     * \param bufferSize the size (in number of MPDUs) of the BlockAck buffer
      */
-    void DoRunSubTest(uint16_t bufferSize);
-    Ptr<WifiNetDevice> m_device;             ///< WifiNetDevice
-    Ptr<StaWifiMac> m_mac;                   ///< Mac
-    Ptr<YansWifiPhy> m_phy;                  ///< Phy
-    Ptr<WifiRemoteStationManager> m_manager; ///< remote station manager
-    ObjectFactory m_factory;                 ///< factory
+    HeAggregationTest(uint16_t bufferSize);
+
+  private:
+    void DoRun() override;
 };
 
-HeAggregationTest::HeAggregationTest()
-    : TestCase("Check the correctness of 802.11ax aggregation operations")
+HeAggregationTest::HeAggregationTest(uint16_t bufferSize)
+    : AmpduAggregationTest("Check the correctness of 802.11ax aggregation operations, size=" +
+                               std::to_string(bufferSize),
+                           Params{.standard = WIFI_STANDARD_80211ax,
+                                  .nLinks = 1,
+                                  .dataMode = "HeMcs11",
+                                  .bufferSize = bufferSize,
+                                  .maxAmsduSize = 0,
+                                  .maxAmpduSize = 65535,
+                                  .txopLimit = Seconds(0)})
 {
 }
 
 void
-HeAggregationTest::DoRunSubTest(uint16_t bufferSize)
+HeAggregationTest::DoRun()
 {
     /*
-     * Create device and attach configurations.
-     */
-    m_device = CreateObject<WifiNetDevice>();
-    Ptr<HtConfiguration> htConfiguration = CreateObject<HtConfiguration>();
-    m_device->SetHtConfiguration(htConfiguration);
-    Ptr<VhtConfiguration> vhtConfiguration = CreateObject<VhtConfiguration>();
-    m_device->SetVhtConfiguration(vhtConfiguration);
-    Ptr<HeConfiguration> heConfiguration = CreateObject<HeConfiguration>();
-    m_device->SetHeConfiguration(heConfiguration);
-
-    /*
-     * Create and configure phy layer.
+     * Test behavior when 300 packets are ready for transmission
      */
-    m_phy = CreateObject<YansWifiPhy>();
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    m_phy->SetDevice(m_device);
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    m_device->SetPhy(m_phy);
+    EnqueuePkts(300, 100, Mac48Address("00:00:00:00:00:02"));
 
-    /*
-     * Create and configure manager.
-     */
-    m_factory = ObjectFactory();
-    m_factory.SetTypeId("ns3::ConstantRateWifiManager");
-    m_factory.Set("DataMode", StringValue("HeMcs11"));
-    m_manager = m_factory.Create<WifiRemoteStationManager>();
-    m_manager->SetupPhy(m_phy);
-    m_device->SetRemoteStationManager(m_manager);
-
-    /*
-     * Create and configure mac layer.
-     */
-    m_mac = CreateObjectWithAttributes<StaWifiMac>("QosSupported", BooleanValue(true));
-    m_mac->SetDevice(m_device);
-    m_mac->SetWifiRemoteStationManager(m_manager);
-    m_mac->SetAddress(Mac48Address("00:00:00:00:00:01"));
-    m_mac->SetWifiPhys({m_phy});
-    m_mac->ConfigureStandard(WIFI_STANDARD_80211ax);
-    Ptr<FrameExchangeManager> fem = m_mac->GetFrameExchangeManager();
-    Ptr<WifiProtectionManager> protectionManager = CreateObject<WifiDefaultProtectionManager>();
-    protectionManager->SetWifiMac(m_mac);
-    fem->SetProtectionManager(protectionManager);
-    Ptr<WifiAckManager> ackManager = CreateObject<WifiDefaultAckManager>();
-    ackManager->SetWifiMac(m_mac);
-    fem->SetAckManager(ackManager);
-    m_device->SetMac(m_mac);
-    m_mac->SetState(StaWifiMac::ASSOCIATED);
-    m_mac->SetMacQueueScheduler(CreateObject<FcfsWifiQueueScheduler>());
+    auto fem = m_mac->GetFrameExchangeManager(SINGLE_LINK_OP_ID);
+    auto htFem = DynamicCast<HtFrameExchangeManager>(fem);
+    auto mpduAggregator = htFem->GetMpduAggregator();
 
-    /*
-     * Configure aggregation.
-     */
-    HeCapabilities heCapabilities;
-    m_manager->AddStationHeCapabilities(Mac48Address("00:00:00:00:00:02"), heCapabilities);
-
-    /*
-     * Fill mac header fields.
-     */
-    WifiMacHeader hdr;
-    hdr.SetAddr1(Mac48Address("00:00:00:00:00:02"));
-    hdr.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-    hdr.SetType(WIFI_MAC_QOSDATA);
-    hdr.SetQosTid(0);
-    uint16_t sequence = m_mac->m_txMiddle->PeekNextSequenceNumberFor(&hdr);
-    hdr.SetSequenceNumber(sequence);
-    hdr.SetFragmentNumber(0);
-    hdr.SetNoMoreFragments();
-    hdr.SetNoRetry();
-
-    /*
-     * Establish agreement.
-     */
-    MgtAddBaRequestHeader reqHdr;
-    reqHdr.SetImmediateBlockAck();
-    reqHdr.SetTid(0);
-    reqHdr.SetBufferSize(bufferSize);
-    reqHdr.SetTimeout(0);
-    reqHdr.SetStartingSequence(0);
-    m_mac->GetBEQueue()->GetBaManager()->CreateOriginatorAgreement(reqHdr, hdr.GetAddr1());
-
-    MgtAddBaResponseHeader respHdr;
-    StatusCode code;
-    code.SetSuccess();
-    respHdr.SetStatusCode(code);
-    respHdr.SetAmsduSupport(reqHdr.IsAmsduSupported());
-    respHdr.SetImmediateBlockAck();
-    respHdr.SetTid(reqHdr.GetTid());
-    respHdr.SetBufferSize(bufferSize);
-    respHdr.SetTimeout(reqHdr.GetTimeout());
-    m_mac->GetBEQueue()->GetBaManager()->UpdateOriginatorAgreement(respHdr, hdr.GetAddr1(), 0);
-
-    /*
-     * Test behavior when 300 packets are ready for transmission but negotiated buffer size is 64
-     */
-    Ptr<HtFrameExchangeManager> htFem = DynamicCast<HtFrameExchangeManager>(fem);
-    Ptr<MpduAggregator> mpduAggregator = htFem->GetMpduAggregator();
-
-    for (uint16_t i = 0; i < 300; i++)
-    {
-        Ptr<const Packet> pkt = Create<Packet>(100);
-        WifiMacHeader hdr;
-
-        hdr.SetAddr1(Mac48Address("00:00:00:00:00:02"));
-        hdr.SetAddr2(Mac48Address("00:00:00:00:00:01"));
-        hdr.SetType(WIFI_MAC_QOSDATA);
-        hdr.SetQosTid(0);
-
-        m_mac->GetBEQueue()->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(pkt, hdr));
-    }
-
-    Ptr<WifiMpdu> peeked = m_mac->GetBEQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
+    auto peeked = GetBeQueue()->PeekNextMpdu(SINGLE_LINK_OP_ID);
     WifiTxParameters txParams;
-    txParams.m_txVector =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(peeked->GetHeader(),
-                                                              m_phy->GetChannelWidth());
-    Ptr<WifiMpdu> item =
-        m_mac->GetBEQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
+    txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+        peeked->GetHeader(),
+        m_phys.at(SINGLE_LINK_OP_ID)->GetChannelWidth());
+    auto item = GetBeQueue()->GetNextMpdu(SINGLE_LINK_OP_ID, peeked, txParams, Time::Min(), true);
 
     auto mpduList = mpduAggregator->GetNextAmpdu(item, txParams, Time::Min());
-    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(mpduList);
-    htFem->DequeuePsdu(psdu);
+    DequeueMpdus(mpduList);
 
     NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), false, "MPDU aggregation failed");
     NS_TEST_EXPECT_MSG_EQ(mpduList.size(),
-                          bufferSize,
-                          "A-MPDU should contain " << bufferSize << " MPDUs");
-    uint16_t expectedRemainingPacketsInQueue = 300 - bufferSize;
-    NS_TEST_EXPECT_MSG_EQ(m_mac->GetBEQueue()->GetWifiMacQueue()->GetNPackets(),
+                          m_params.bufferSize,
+                          "A-MPDU contains an unexpected number of MPDUs");
+    uint16_t expectedRemainingPacketsInQueue = 300 - m_params.bufferSize;
+    NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
                           expectedRemainingPacketsInQueue,
-                          "queue should contain 300 - " << bufferSize << " = "
-                                                        << expectedRemainingPacketsInQueue
-                                                        << " packets");
-
-    Simulator::Destroy();
+                          "Queue contains an unexpected number of MPDUs");
+}
 
-    m_manager->Dispose();
-    m_manager = nullptr;
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief 802.11be aggregation test which permits up to 1024 MPDUs in A-MPDU according to the
+ * negotiated buffer size.
+ */
+class EhtAggregationTest : public AmpduAggregationTest
+{
+  public:
+    /**
+     * Constructor.
+     *
+     * \param bufferSize the size (in number of MPDUs) of the BlockAck buffer
+     */
+    EhtAggregationTest(uint16_t bufferSize);
 
-    m_device->Dispose();
-    m_device = nullptr;
+  private:
+    void DoRun() override;
+};
 
-    htConfiguration = nullptr;
-    vhtConfiguration = nullptr;
-    heConfiguration = nullptr;
+EhtAggregationTest::EhtAggregationTest(uint16_t bufferSize)
+    : AmpduAggregationTest("Check the correctness of 802.11be aggregation operations, size=" +
+                               std::to_string(bufferSize),
+                           Params{.standard = WIFI_STANDARD_80211be,
+                                  .nLinks = 2,
+                                  .dataMode = "EhtMcs13",
+                                  .bufferSize = bufferSize,
+                                  .maxAmsduSize = 0,
+                                  .maxAmpduSize = 102000,
+                                  .txopLimit = Seconds(0)})
+{
 }
 
 void
-HeAggregationTest::DoRun()
+EhtAggregationTest::DoRun()
 {
-    DoRunSubTest(64);
-    DoRunSubTest(256);
+    /*
+     * Test behavior when 1200 packets of 100 bytes each are ready for transmission. The max
+     * A-MPDU size limit (102000 B) is computed to have at most 750 MPDUs aggregated in a single
+     * A-MPDU (each MPDU is 130 B, plus 4 B of A-MPDU subframe header, plus 2 B of padding).
+     */
+    EnqueuePkts(1200, 100, Mac48Address("00:00:00:00:00:02"));
+    const std::size_t maxNMpdus = 750;
+
+    for (uint8_t linkId = 0; linkId < m_params.nLinks; linkId++)
+    {
+        auto fem = m_mac->GetFrameExchangeManager(linkId);
+        auto htFem = DynamicCast<HtFrameExchangeManager>(fem);
+        auto mpduAggregator = htFem->GetMpduAggregator();
+        std::vector<Ptr<WifiMpdu>> mpduList;
+
+        auto peeked = GetBeQueue()->PeekNextMpdu(linkId);
+        if (peeked)
+        {
+            WifiTxParameters txParams;
+            txParams.m_txVector = m_mac->GetWifiRemoteStationManager()->GetDataTxVector(
+                peeked->GetHeader(),
+                m_phys.at(linkId)->GetChannelWidth());
+            auto item = GetBeQueue()->GetNextMpdu(linkId, peeked, txParams, Time::Min(), true);
+
+            mpduList = mpduAggregator->GetNextAmpdu(item, txParams, Time::Min());
+            DequeueMpdus(mpduList);
+        }
+
+        uint16_t expectedRemainingPacketsInQueue;
+
+        if (m_params.bufferSize >= maxNMpdus)
+        {
+            // two A-MPDUs are transmitted concurrently on the two links and together saturate
+            // the transmit window
+            switch (linkId)
+            {
+            case 0:
+                // the first A-MPDU includes maxNMpdus MPDUs
+                NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), false, "MPDU aggregation failed");
+                NS_TEST_EXPECT_MSG_EQ(mpduList.size(),
+                                      maxNMpdus,
+                                      "A-MPDU contains an unexpected number of MPDUs");
+                expectedRemainingPacketsInQueue = 1200 - maxNMpdus;
+                NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
+                                      expectedRemainingPacketsInQueue,
+                                      "Queue contains an unexpected number of MPDUs");
+                break;
+            case 1:
+                // the second A-MPDU includes bufferSize - maxNMpdus MPDUs
+                NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), false, "MPDU aggregation failed");
+                NS_TEST_EXPECT_MSG_EQ(mpduList.size(),
+                                      m_params.bufferSize - maxNMpdus,
+                                      "A-MPDU contains an unexpected number of MPDUs");
+                expectedRemainingPacketsInQueue = 1200 - m_params.bufferSize;
+                NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
+                                      expectedRemainingPacketsInQueue,
+                                      "Queue contains an unexpected number of MPDUs");
+                break;
+            default:
+                NS_TEST_ASSERT_MSG_EQ(true, false, "Unexpected link ID " << +linkId);
+            }
+        }
+        else
+        {
+            // one A-MPDU is transmitted that saturates the transmit window
+            switch (linkId)
+            {
+            case 0:
+                // the first A-MPDU includes bufferSize MPDUs
+                NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), false, "MPDU aggregation failed");
+                NS_TEST_EXPECT_MSG_EQ(mpduList.size(),
+                                      m_params.bufferSize,
+                                      "A-MPDU contains an unexpected number of MPDUs");
+                expectedRemainingPacketsInQueue = 1200 - m_params.bufferSize;
+                NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
+                                      expectedRemainingPacketsInQueue,
+                                      "Queue contains an unexpected number of MPDUs");
+                break;
+            case 1:
+                // no more MPDUs can be sent, aggregation fails
+                NS_TEST_EXPECT_MSG_EQ(mpduList.empty(), true, "MPDU aggregation did not fail");
+                expectedRemainingPacketsInQueue = 1200 - m_params.bufferSize;
+                NS_TEST_EXPECT_MSG_EQ(GetBeQueue()->GetWifiMacQueue()->GetNPackets(),
+                                      expectedRemainingPacketsInQueue,
+                                      "Queue contains an unexpected number of MPDUs");
+                break;
+            default:
+                NS_TEST_ASSERT_MSG_EQ(true, false, "Unexpected link ID " << +linkId);
+            }
+        }
+    }
 }
 
 /**
@@ -999,7 +1140,10 @@
 {
     AddTestCase(new AmpduAggregationTest, TestCase::QUICK);
     AddTestCase(new TwoLevelAggregationTest, TestCase::QUICK);
-    AddTestCase(new HeAggregationTest, TestCase::QUICK);
+    AddTestCase(new HeAggregationTest(64), TestCase::QUICK);
+    AddTestCase(new HeAggregationTest(256), TestCase::QUICK);
+    AddTestCase(new EhtAggregationTest(512), TestCase::QUICK);
+    AddTestCase(new EhtAggregationTest(1024), TestCase::QUICK);
     AddTestCase(new PreservePacketsInAmpdus, TestCase::QUICK);
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-channel-switching-test.cc ns-3.41/src/wifi/test/wifi-channel-switching-test.cc
--- ns-3.40/src/wifi/test/wifi-channel-switching-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/wifi-channel-switching-test.cc	2024-02-09 14:44:08.609795000 -0800
@@ -45,9 +45,9 @@
  *
  * This test verifies that communication between an AP and a STA resumes
  * after that both switch channel and PHY band. The channel switch is
- * scheduled to happen during the transmission of a frame sent by the AP
- * to the STA. STA discards the frame, associates with the AP again and
- * finally receives the frame successfully.
+ * scheduled to happen during the transmission of a frame sent by the STA
+ * to the AP. AP discards the frame, STA associates with the AP again and
+ * the AP finally receives the frame successfully.
  */
 class WifiChannelSwitchingTest : public TestCase
 {
@@ -69,7 +69,7 @@
     void Associated(Mac48Address bssid);
     /**
      * Callback invoked when PHY receives a PSDU to transmit from the MAC. Tracks the
-     * number of times a QoS data frame is transmitted by the AP.
+     * number of times a QoS data frame is transmitted by the STA.
      *
      * \param psduMap the PSDU map
      * \param txVector the TX vector
@@ -84,7 +84,7 @@
      */
     void L7Receive(Ptr<const Packet> p, const Address& addr);
     /**
-     * Send a packet from the AP to the STA through a packet socket
+     * Send a packet from the STA to the AP through a packet socket
      */
     void SendPacket();
     /**
@@ -172,8 +172,8 @@
 WifiChannelSwitchingTest::SendPacket()
 {
     PacketSocketAddress socket;
-    socket.SetSingleDevice(m_apDevice.Get(0)->GetIfIndex());
-    socket.SetPhysicalAddress(m_staDevice.Get(0)->GetAddress());
+    socket.SetSingleDevice(m_staDevice.Get(0)->GetIfIndex());
+    socket.SetPhysicalAddress(m_apDevice.Get(0)->GetAddress());
     socket.SetProtocol(1);
 
     // give packet socket powers to nodes.
@@ -181,18 +181,18 @@
     packetSocket.Install(m_staNode);
     packetSocket.Install(m_apNode);
 
-    Ptr<PacketSocketClient> client = CreateObject<PacketSocketClient>();
+    auto client = CreateObject<PacketSocketClient>();
     client->SetAttribute("PacketSize", UintegerValue(m_payloadSize));
     client->SetAttribute("MaxPackets", UintegerValue(1));
     client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
     client->SetRemote(socket);
-    m_apNode.Get(0)->AddApplication(client);
+    m_staNode.Get(0)->AddApplication(client);
     client->SetStartTime(Seconds(0.5));
     client->SetStopTime(Seconds(1.0));
 
-    Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
+    auto server = CreateObject<PacketSocketServer>();
     server->SetLocal(socket);
-    m_staNode.Get(0)->AddApplication(server);
+    m_apNode.Get(0)->AddApplication(server);
     server->SetStartTime(Seconds(0.0));
     server->SetStopTime(Seconds(1.0));
 }
@@ -229,11 +229,10 @@
     m_apNode.Create(1);
     m_staNode.Create(1);
 
-    Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
-    Ptr<FriisPropagationLossModel> lossModel = CreateObject<FriisPropagationLossModel>();
+    auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    auto lossModel = CreateObject<FriisPropagationLossModel>();
     spectrumChannel->AddPropagationLossModel(lossModel);
-    Ptr<ConstantSpeedPropagationDelayModel> delayModel =
-        CreateObject<ConstantSpeedPropagationDelayModel>();
+    auto delayModel = CreateObject<ConstantSpeedPropagationDelayModel>();
     spectrumChannel->SetPropagationDelayModel(delayModel);
 
     SpectrumWifiPhyHelper phy;
@@ -293,10 +292,10 @@
     NS_TEST_EXPECT_MSG_EQ(+m_assocCount, 2, "STA did not associate twice");
     NS_TEST_EXPECT_MSG_EQ(+m_txCount,
                           2,
-                          "The QoS Data frame should have been transmitted twice by the AP");
+                          "The QoS Data frame should have been transmitted twice by the STA");
     NS_TEST_EXPECT_MSG_EQ(m_rxBytes,
                           m_payloadSize,
-                          "The QoS Data frame should have been received once by the STA");
+                          "The QoS Data frame should have been received once by the AP");
     NS_TEST_EXPECT_MSG_EQ(+m_channelSwitchCount[0], 1, "AP had to perform one channel switch");
     NS_TEST_EXPECT_MSG_EQ(+m_channelSwitchCount[1], 1, "STA had to perform one channel switch");
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-eht-info-elems-test.cc ns-3.41/src/wifi/test/wifi-eht-info-elems-test.cc
--- ns-3.40/src/wifi/test/wifi-eht-info-elems-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/wifi-eht-info-elems-test.cc	2024-02-09 14:44:08.609795000 -0800
@@ -97,11 +97,12 @@
     }
     if (commonInfo.m_mediumSyncDelayInfo.has_value())
     {
-        mle.SetMediumSyncDelayTimer(
+        mle.GetCommonInfoBasic().SetMediumSyncDelayTimer(
             MicroSeconds(32 * commonInfo.m_mediumSyncDelayInfo->mediumSyncDuration));
-        mle.SetMediumSyncOfdmEdThreshold(
+        mle.GetCommonInfoBasic().SetMediumSyncOfdmEdThreshold(
             commonInfo.m_mediumSyncDelayInfo->mediumSyncOfdmEdThreshold - 72);
-        mle.SetMediumSyncMaxNTxops(commonInfo.m_mediumSyncDelayInfo->mediumSyncMaxNTxops + 1);
+        mle.GetCommonInfoBasic().SetMediumSyncMaxNTxops(
+            commonInfo.m_mediumSyncDelayInfo->mediumSyncMaxNTxops + 1);
     }
     if (commonInfo.m_emlCapabilities.has_value())
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-emlsr-test.cc ns-3.41/src/wifi/test/wifi-emlsr-test.cc
--- ns-3.40/src/wifi/test/wifi-emlsr-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/wifi-emlsr-test.cc	2024-02-09 14:44:08.613795300 -0800
@@ -17,7 +17,8 @@
  * Author: Stefano Avallone <stavallo@unina.it>
  */
 
-#include "ns3/ap-wifi-mac.h"
+#include "wifi-emlsr-test.h"
+
 #include "ns3/attribute-container.h"
 #include "ns3/boolean.h"
 #include "ns3/config.h"
@@ -25,13 +26,11 @@
 #include "ns3/eht-configuration.h"
 #include "ns3/emlsr-manager.h"
 #include "ns3/he-frame-exchange-manager.h"
-#include "ns3/header-serialization-test.h"
 #include "ns3/log.h"
-#include "ns3/mgt-headers.h"
+#include "ns3/mgt-action-headers.h"
 #include "ns3/mobility-helper.h"
 #include "ns3/multi-model-spectrum-channel.h"
 #include "ns3/node-list.h"
-#include "ns3/packet-socket-client.h"
 #include "ns3/packet-socket-helper.h"
 #include "ns3/packet-socket-server.h"
 #include "ns3/qos-txop.h"
@@ -40,11 +39,8 @@
 #include "ns3/simulator.h"
 #include "ns3/spectrum-wifi-helper.h"
 #include "ns3/spectrum-wifi-phy.h"
-#include "ns3/sta-wifi-mac.h"
 #include "ns3/string.h"
 #include "ns3/wifi-net-device.h"
-#include "ns3/wifi-ppdu.h"
-#include "ns3/wifi-psdu.h"
 
 #include <algorithm>
 #include <functional>
@@ -55,25 +51,6 @@
 
 NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
 
-/**
- * \ingroup wifi-test
- * \ingroup tests
- *
- * \brief Test EML Operating Mode Notification frame serialization and deserialization
- */
-class EmlOperatingModeNotificationTest : public HeaderSerializationTestCase
-{
-  public:
-    /**
-     * Constructor
-     */
-    EmlOperatingModeNotificationTest();
-    ~EmlOperatingModeNotificationTest() override = default;
-
-  private:
-    void DoRun() override;
-};
-
 EmlOperatingModeNotificationTest::EmlOperatingModeNotificationTest()
     : HeaderSerializationTestCase(
           "Check serialization and deserialization of the EML Operating Mode Notification frame")
@@ -122,142 +99,6 @@
         "Unexpected EMLSR Transition Delay");
 }
 
-/**
- * \ingroup wifi-test
- * \ingroup tests
- *
- * \brief Base class for EMLSR Operations tests
- *
- * This base class setups and configures one AP MLD, a variable number of non-AP MLDs with
- * EMLSR activated and a variable number of non-AP MLD with EMLSR deactivated. Every MLD has
- * three links, each operating on a distinct PHY band (2.4 GHz, 5 GHz and 6 GHz). Therefore,
- * it is expected that three links are setup by the non-AP MLD(s). The values for the Padding
- * Delay, the Transition Delay and the Transition Timeout are provided as argument to the
- * constructor of this class, along with the IDs of the links on which EMLSR mode must be
- * enabled for the non-AP MLDs (this information is used to set the EmlsrLinkSet attribute
- * of the DefaultEmlsrManager installed on the non-AP MLDs).
- */
-class EmlsrOperationsTestBase : public TestCase
-{
-  public:
-    /**
-     * Constructor
-     *
-     * \param name The name of the new TestCase created
-     */
-    EmlsrOperationsTestBase(const std::string& name);
-    ~EmlsrOperationsTestBase() override = default;
-
-    /// Enumeration for traffic directions
-    enum TrafficDirection : uint8_t
-    {
-        DOWNLINK = 0,
-        UPLINK
-    };
-
-  protected:
-    /**
-     * Callback invoked when a FEM passes PSDUs to the PHY.
-     *
-     * \param mac the MAC transmitting the PSDUs
-     * \param phyId the ID of the PHY transmitting the PSDUs
-     * \param psduMap the PSDU map
-     * \param txVector the TX vector
-     * \param txPowerW the tx power in Watts
-     */
-    virtual void Transmit(Ptr<WifiMac> mac,
-                          uint8_t phyId,
-                          WifiConstPsduMap psduMap,
-                          WifiTxVector txVector,
-                          double txPowerW);
-
-    /**
-     * \param dir the traffic direction (downlink/uplink)
-     * \param staId the index (starting at 0) of the non-AP MLD generating/receiving packets
-     * \param count the number of packets to generate
-     * \param pktSize the size of the packets to generate
-     * \return an application generating the given number packets of the given size from/to the
-     *         AP MLD to/from the given non-AP MLD
-     */
-    Ptr<PacketSocketClient> GetApplication(TrafficDirection dir,
-                                           std::size_t staId,
-                                           std::size_t count,
-                                           std::size_t pktSize) const;
-
-    /**
-     * Check whether QoS data unicast transmissions addressed to the given destination on the
-     * given link are blocked or unblocked for the given reason on the given device.
-     *
-     * \param mac the MAC of the given device
-     * \param dest the MAC address of the given destination
-     * \param linkId the ID of the given link
-     * \param reason the reason for blocking transmissions to test
-     * \param blocked whether transmissions are blocked for the given reason
-     * \param description text indicating when this check is performed
-     * \param testUnblockedForOtherReasons whether to test if transmissions are unblocked for
-     *                                     all the reasons other than the one provided
-     */
-    void CheckBlockedLink(Ptr<WifiMac> mac,
-                          Mac48Address dest,
-                          uint8_t linkId,
-                          WifiQueueBlockedReason reason,
-                          bool blocked,
-                          std::string description,
-                          bool testUnblockedForOtherReasons = true);
-
-    void DoSetup() override;
-
-    /// Information about transmitted frames
-    struct FrameInfo
-    {
-        Time startTx;             ///< TX start time
-        WifiConstPsduMap psduMap; ///< transmitted PSDU map
-        WifiTxVector txVector;    ///< TXVECTOR
-        uint8_t linkId;           ///< link ID
-        uint8_t phyId;            ///< ID of the transmitting PHY
-    };
-
-    uint8_t m_mainPhyId{0};                   //!< ID of the main PHY
-    std::set<uint8_t> m_linksToEnableEmlsrOn; /**< IDs of the links on which EMLSR mode has to
-                                                   be enabled */
-    std::size_t m_nEmlsrStations{1};          ///< number of stations to create that activate EMLSR
-    std::size_t m_nNonEmlsrStations{0};       /**< number of stations to create that do not
-                                                activate EMLSR */
-    Time m_transitionTimeout{MicroSeconds(128)}; ///< Transition Timeout advertised by the AP MLD
-    std::vector<Time> m_paddingDelay{
-        {MicroSeconds(32)}}; ///< Padding Delay advertised by the non-AP MLD
-    std::vector<Time> m_transitionDelay{
-        {MicroSeconds(16)}};                ///< Transition Delay advertised by the non-AP MLD
-    bool m_establishBaDl{false};            /**< whether BA needs to be established (for TID 0)
-                                                 with the AP as originator */
-    bool m_establishBaUl{false};            /**< whether BA needs to be established (for TID 0)
-                                                 with the AP as recipient */
-    std::vector<FrameInfo> m_txPsdus;       ///< transmitted PSDUs
-    Ptr<ApWifiMac> m_apMac;                 ///< AP wifi MAC
-    std::vector<Ptr<StaWifiMac>> m_staMacs; ///< MACs of the non-AP MLDs
-    std::vector<PacketSocketAddress> m_dlSockets; ///< packet socket address for DL traffic
-    std::vector<PacketSocketAddress> m_ulSockets; ///< packet socket address for UL traffic
-    uint16_t m_lastAid{0};                        ///< AID of last associated station
-    Time m_duration{0};                           ///< simulation duration
-
-  private:
-    /**
-     * Set the SSID on the next station that needs to start the association procedure.
-     * This method is connected to the ApWifiMac's AssociatedSta trace source.
-     * Start generating traffic (if needed) when all stations are associated.
-     *
-     * \param aid the AID assigned to the previous associated STA
-     */
-    void SetSsid(uint16_t aid, Mac48Address /* addr */);
-
-    /**
-     * Start the generation of traffic (needs to be overridden)
-     */
-    virtual void StartTraffic()
-    {
-    }
-};
-
 EmlsrOperationsTestBase::EmlsrOperationsTestBase(const std::string& name)
     : TestCase(name)
 {
@@ -315,6 +156,8 @@
     RngSeedManager::SetRun(2);
     int64_t streamNumber = 100;
 
+    Config::SetDefault("ns3::WifiMac::MpduBufferSize", UintegerValue(64));
+
     NodeContainer wifiApNode(1);
     NodeContainer wifiStaNodes(m_nEmlsrStations);
 
@@ -508,7 +351,7 @@
     if (m_establishBaDl)
     {
         // trigger establishment of BA agreement with AP as originator
-        Simulator::Schedule(delay, [=]() {
+        Simulator::Schedule(delay, [=, this]() {
             m_apMac->GetDevice()->GetNode()->AddApplication(
                 GetApplication(DOWNLINK, aid - 1, 4, 1000));
         });
@@ -519,7 +362,7 @@
     if (m_establishBaUl)
     {
         // trigger establishment of BA agreement with AP as recipient
-        Simulator::Schedule(delay, [=]() {
+        Simulator::Schedule(delay, [=, this]() {
             m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
                 GetApplication(UPLINK, aid - 1, 4, 1000));
         });
@@ -527,7 +370,7 @@
         delay += MilliSeconds(5);
     }
 
-    Simulator::Schedule(delay, [=]() {
+    Simulator::Schedule(delay, [=, this]() {
         if (aid < m_nEmlsrStations + m_nNonEmlsrStations)
         {
             // make the next STA start ML discovery & setup
@@ -583,112 +426,6 @@
     }
 }
 
-/**
- * \ingroup wifi-test
- * \ingroup tests
- *
- * \brief Test the exchange of EML Operating Mode Notification frames.
- *
- * This test considers an AP MLD and a non-AP MLD with EMLSR activated. Upon association,
- * the non-AP MLD sends an EML Operating Mode Notification frame, which is however corrupted
- * by using a post reception error model (installed on the AP MLD). We keep corrupting the
- * EML Notification frames transmitted by the non-AP MLD until the frame is dropped due to
- * exceeded max retry limit. It is checked that:
- *
- * - the Association Request contains a Multi-Link Element including an EML Capabilities field
- *   that contains the expected values for Padding Delay and Transition Delay
- * - the Association Response contains a Multi-Link Element including an EML Capabilities field
- *   that contains the expected value for Transition Timeout
- * - all EML Notification frames contain the expected values for EMLSR Mode, EMLMR Mode and
- *   Link Bitmap fields and are transmitted on the link used for association
- * - the correct EMLSR link set is stored by the EMLSR Manager, both when the transition
- *   timeout expires and when an EML Notification response is received from the AP MLD (thus,
- *   the correct EMLSR link set is stored after whichever of the two events occur first)
- */
-class EmlOmnExchangeTest : public EmlsrOperationsTestBase
-{
-  public:
-    /**
-     * Constructor
-     *
-     * \param linksToEnableEmlsrOn IDs of links on which EMLSR mode should be enabled
-     * \param transitionTimeout the Transition Timeout advertised by the AP MLD
-     */
-    EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn, Time transitionTimeout);
-    ~EmlOmnExchangeTest() override = default;
-
-  protected:
-    void DoSetup() override;
-    void DoRun() override;
-    void Transmit(Ptr<WifiMac> mac,
-                  uint8_t phyId,
-                  WifiConstPsduMap psduMap,
-                  WifiTxVector txVector,
-                  double txPowerW) override;
-
-    /**
-     * Callback invoked when the non-AP MLD receives the acknowledgment for a transmitted MPDU.
-     *
-     * \param mpdu the acknowledged MPDU
-     */
-    void TxOk(Ptr<const WifiMpdu> mpdu);
-    /**
-     * Callback invoked when the non-AP MLD drops the given MPDU for the given reason.
-     *
-     * \param reason the reason why the MPDU was dropped
-     * \param mpdu the dropped MPDU
-     */
-    void TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu);
-
-    /**
-     * Check the content of the EML Capabilities subfield of the Multi-Link Element included
-     * in the Association Request frame sent by the non-AP MLD.
-     *
-     * \param mpdu the MPDU containing the Association Request frame
-     * \param txVector the TXVECTOR used to transmit the frame
-     * \param linkId the ID of the link on which the frame was transmitted
-     */
-    void CheckEmlCapabilitiesInAssocReq(Ptr<const WifiMpdu> mpdu,
-                                        const WifiTxVector& txVector,
-                                        uint8_t linkId);
-    /**
-     * Check the content of the EML Capabilities subfield of the Multi-Link Element included
-     * in the Association Response frame sent by the AP MLD to the EMLSR client.
-     *
-     * \param mpdu the MPDU containing the Association Response frame
-     * \param txVector the TXVECTOR used to transmit the frame
-     * \param linkId the ID of the link on which the frame was transmitted
-     */
-    void CheckEmlCapabilitiesInAssocResp(Ptr<const WifiMpdu> mpdu,
-                                         const WifiTxVector& txVector,
-                                         uint8_t linkId);
-    /**
-     * Check the content of a received EML Operating Mode Notification frame.
-     *
-     * \param psdu the PSDU containing the EML Operating Mode Notification frame
-     * \param txVector the TXVECTOR used to transmit the frame
-     * \param linkId the ID of the link on which the frame was transmitted
-     */
-    void CheckEmlNotification(Ptr<const WifiPsdu> psdu,
-                              const WifiTxVector& txVector,
-                              uint8_t linkId);
-    /**
-     * Check that the EMLSR mode has been enabled on the expected EMLSR links.
-     */
-    void CheckEmlsrLinks();
-
-  private:
-    std::size_t m_checkEmlsrLinksCount;        /**< counter for the number of times CheckEmlsrLinks
-                                                    is called (should be two: when the transition
-                                                    timeout expires and when the EML Notification
-                                                    response from the AP MLD is received */
-    std::size_t m_emlNotificationDroppedCount; /**< counter for the number of times the EML
-                                                    Notification frame sent by the non-AP MLD
-                                                    has been dropped due to max retry limit */
-    Ptr<ListErrorModel> m_errorModel;          ///< error rate model to corrupt packets at AP MLD
-    std::list<uint64_t> m_uidList;             ///< list of UIDs of packets to corrupt
-};
-
 EmlOmnExchangeTest::EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
                                        Time transitionTimeout)
     : EmlsrOperationsTestBase("Check EML Notification exchange"),
@@ -960,192 +697,27 @@
     Simulator::Destroy();
 }
 
-/**
- * \ingroup wifi-test
- * \ingroup tests
- *
- * \brief Test the transmission of DL frames to EMLSR clients.
- *
- * This test considers an AP MLD and a configurable number of non-AP MLDs that support EMLSR
- * and a configurable number of non-AP MLDs that do not support EMLSR. All MLDs have three
- * setup links, while the set of EMLSR links for the EMLSR clients is configurable.
- * Block ack agreements (for TID 0, with the AP MLD as originator) are established with all
- * the non-AP MLDs before that EMLSR clients send the EML Operating Mode Notification frame
- * to enable the EMLSR mode on their EMLSR links.
- *
- * Before enabling EMLSR mode, it is checked that:
- *
- * - all EMLSR links (but the link used for ML setup) of the EMLSR clients are considered to
- *   be in power save mode and are blocked by the AP MLD; all the other links have transitioned
- *   to active mode and are not blocked
- * - no MU-RTS Trigger Frame is sent as Initial control frame
- * - In case of EMLSR clients having no link that is not an EMLSR link and is different than
- *   the link used for ML setup, the two A-MPDUs used to trigger BA establishment are
- *   transmitted one after another on the link used for ML setup. Otherwise, the two A-MPDUs
- *   are sent concurrently on two distinct links
- *
- * After enabling EMLSR mode, it is checked that:
- *
- * - all EMLSR links of the EMLSR clients are considered to be in active mode and are not
- *   blocked by the AP MLD
- * - If all setup links are EMLSR links, the first two frame exchanges are both protected by
- *   MU-RTS TF and occur one after another. Otherwise, one frame exchange occurs on the
- *   non-EMLSR link and is not protected by MU-RTS TF; the other frame exchange occurs on an
- *   EMLSR link and is protected by MU-RTS TF
- * - the AP MLD blocks transmission on all other EMLSR links when sending an ICF to an EMLSR client
- * - After completing a frame exchange with an EMLSR client, the AP MLD can start another frame
- *   exchange with that EMLSR client within the same TXOP (after a SIFS) without sending an ICF
- * - During the transition delay, all EMLSR links are not used for DL transmissions
- * - The padding added to Initial Control frames is the largest among all the EMLSR clients
- *   solicited by the ICF
- *
- * After disabling EMLSR mode, it is checked that:
- *
- * - all EMLSR links (but the link used to exchange EML Notification frames) of the EMLSR clients
- *   are considered to be in power save mode and are blocked by the AP MLD
- * - an MU-RTS Trigger Frame is sent by the AP MLD as ICF for sending the EML Notification
- *   response, unless the link used to exchange EML Notification frames is a non-EMLSR link
- * - no MU-RTS Trigger Frame is used as ICF for QoS data frames
- * - In case of EMLSR clients having no link that is not an EMLSR link and is different than
- *   the link used to exchange EML Notification frames, the two A-MPDUs are transmitted one
- *   after another on the link used to exchange EML Notification frames. Otherwise, the two
- *   A-MPDUs are sent concurrently on two distinct links
- */
-class EmlsrDlTxopTest : public EmlsrOperationsTestBase
-{
-  public:
-    /**
-     * Constructor
-     *
-     * \param nEmlsrStations number of non-AP MLDs that support EMLSR
-     * \param nNonEmlsrStations number of non-AP MLDs that do not support EMLSR
-     * \param linksToEnableEmlsrOn IDs of links on which EMLSR mode should be enabled
-     * \param paddingDelay vector (whose size equals <i>nEmlsrStations</i>) of the padding
-     *                     delay values advertised by non-AP MLDs
-     * \param transitionDelay vector (whose size equals <i>nEmlsrStations</i>) of the transition
-     *                        delay values advertised by non-AP MLDs
-     * \param transitionTimeout the Transition Timeout advertised by the AP MLD
-     */
-    EmlsrDlTxopTest(std::size_t nEmlsrStations,
-                    std::size_t nNonEmlsrStations,
-                    const std::set<uint8_t>& linksToEnableEmlsrOn,
-                    const std::vector<Time>& paddingDelay,
-                    const std::vector<Time>& transitionDelay,
-                    Time transitionTimeout);
-    ~EmlsrDlTxopTest() override = default;
-
-  protected:
-    void DoSetup() override;
-    void DoRun() override;
-    void Transmit(Ptr<WifiMac> mac,
-                  uint8_t phyId,
-                  WifiConstPsduMap psduMap,
-                  WifiTxVector txVector,
-                  double txPowerW) override;
-
-    /**
-     * Check that the simulation produced the expected results.
-     */
-    void CheckResults();
-
-    /**
-     * Check that the AP MLD considers the correct Power Management mode for the links setup
-     * with the given non-AP MLD. This method is intended to be called shortly after ML setup.
-     *
-     * \param address a link address of the given non-AP MLD
-     */
-    void CheckPmModeAfterAssociation(const Mac48Address& address);
-
-    /**
-     * Check that appropriate actions are taken by the AP MLD transmitting an EML Operating Mode
-     * Notification response frame to an EMLSR client on the given link.
-     *
-     * \param mpdu the MPDU carrying the EML Operating Mode Notification frame
-     * \param txVector the TXVECTOR used to send the PPDU
-     * \param linkId the ID of the given link
-     */
-    void CheckEmlNotificationFrame(Ptr<const WifiMpdu> mpdu,
-                                   const WifiTxVector& txVector,
-                                   uint8_t linkId);
-
-    /**
-     * Check that appropriate actions are taken by the AP MLD transmitting an initial
-     * Control frame to an EMLSR client on the given link.
-     *
-     * \param mpdu the MPDU carrying the MU-RTS TF
-     * \param txVector the TXVECTOR used to send the PPDU
-     * \param linkId the ID of the given link
-     */
-    void CheckInitialControlFrame(Ptr<const WifiMpdu> mpdu,
-                                  const WifiTxVector& txVector,
-                                  uint8_t linkId);
-
-    /**
-     * Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing
-     * QoS data frames to EMLSR clients on the given link.
-     *
-     * \param psduMap the PSDU(s) carrying QoS data frames
-     * \param txVector the TXVECTOR used to send the PPDU
-     * \param linkId the ID of the given link
-     */
-    void CheckQosFrames(const WifiConstPsduMap& psduMap,
-                        const WifiTxVector& txVector,
-                        uint8_t linkId);
-
-    /**
-     * Check that appropriate actions are taken by the AP MLD receiving a PPDU containing
-     * BlockAck frames from EMLSR clients on the given link.
-     *
-     * \param psduMap the PSDU carrying BlockAck frames
-     * \param txVector the TXVECTOR used to send the PPDU
-     * \param phyId the ID of the PHY transmitting the PSDU(s)
-     */
-    void CheckBlockAck(const WifiConstPsduMap& psduMap,
-                       const WifiTxVector& txVector,
-                       uint8_t phyId);
-
-  private:
-    void StartTraffic() override;
-
-    /**
-     * Enable EMLSR mode on the next EMLSR client
-     */
-    void EnableEmlsrMode();
-
-    std::set<uint8_t> m_emlsrLinks; /**< IDs of the links on which EMLSR mode has to be enabled */
-    Time m_emlsrEnabledTime;        //!< when EMLSR mode has been enabled on all EMLSR clients
-    const Time m_fe2to3delay;       /**< time interval between 2nd and 3rd frame exchange sequences
-                                         after the enablement of EMLSR mode */
-    std::size_t m_countQoSframes;   //!< counter for QoS frames (transition delay test)
-    std::size_t m_countBlockAck;    //!< counter for BlockAck frames (transition delay test)
-    Ptr<ListErrorModel> m_errorModel; ///< error rate model to corrupt BlockAck at AP MLD
-};
-
-EmlsrDlTxopTest::EmlsrDlTxopTest(std::size_t nEmlsrStations,
-                                 std::size_t nNonEmlsrStations,
-                                 const std::set<uint8_t>& linksToEnableEmlsrOn,
-                                 const std::vector<Time>& paddingDelay,
-                                 const std::vector<Time>& transitionDelay,
-                                 Time transitionTimeout)
-    : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" + std::to_string(nEmlsrStations) +
-                              "," + std::to_string(nNonEmlsrStations) + ")"),
-      m_emlsrLinks(linksToEnableEmlsrOn),
+EmlsrDlTxopTest::EmlsrDlTxopTest(const Params& params)
+    : EmlsrOperationsTestBase("Check EML DL TXOP transmissions (" +
+                              std::to_string(params.nEmlsrStations) + "," +
+                              std::to_string(params.nNonEmlsrStations) + ")"),
+      m_emlsrLinks(params.linksToEnableEmlsrOn),
       m_emlsrEnabledTime(0),
       m_fe2to3delay(MilliSeconds(20)),
       m_countQoSframes(0),
       m_countBlockAck(0)
 {
-    m_nEmlsrStations = nEmlsrStations;
-    m_nNonEmlsrStations = nNonEmlsrStations;
+    m_nEmlsrStations = params.nEmlsrStations;
+    m_nNonEmlsrStations = params.nNonEmlsrStations;
     m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
     m_mainPhyId = 1;
-    m_paddingDelay = paddingDelay;
-    m_transitionDelay = transitionDelay;
-    m_transitionTimeout = transitionTimeout;
+    m_paddingDelay = params.paddingDelay;
+    m_transitionDelay = params.transitionDelay;
+    m_transitionTimeout = params.transitionTimeout;
     m_establishBaDl = true;
     m_duration = Seconds(1.5);
 
-    NS_ABORT_MSG_IF(linksToEnableEmlsrOn.size() < 2,
+    NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
                     "This test requires at least two links to be configured as EMLSR links");
 }
 
@@ -1279,13 +851,13 @@
     // and the generation of other packets destined to the EMLSR clients
     for (std::size_t id = 0; id < m_nEmlsrStations; id++)
     {
-        Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=]() {
+        Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (id + 1)), [=, this]() {
             m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
                 "EmlsrLinkSet",
                 AttributeContainerValue<UintegerValue>({}));
         });
 
-        Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (m_nEmlsrStations + 1)), [=]() {
+        Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (m_nEmlsrStations + 1)), [=, this]() {
             m_apMac->GetDevice()->GetNode()->AddApplication(
                 GetApplication(DOWNLINK, id, 8 / m_nEmlsrStations, 650));
         });
@@ -1299,7 +871,7 @@
         "EmlsrLinkSet",
         AttributeContainerValue<UintegerValue>(m_emlsrLinks));
     m_lastAid++;
-    Simulator::Schedule(MilliSeconds(5), [=]() {
+    Simulator::Schedule(MilliSeconds(5), [=, this]() {
         if (m_lastAid < m_nEmlsrStations)
         {
             // make the next STA send EML Notification frame
@@ -1689,7 +1261,7 @@
     /**
      * A and B are two EMLSR clients. No ICF before the second QoS data frame because B
      * has not switched to listening mode. ICF is sent before the third QoS data frame because
-     * A has switched to listening mode.
+     * A has switched to listening mode. C is a non-EMLSR client.
      *
      *                        ┌─────┐          A switches to listening
      *                        │QoS x│          after transition delay
@@ -1701,15 +1273,15 @@
      *                   │CTS│       │BA│       │BA│
      *                   ├───┤       ├──┤       └──┘
      *                   │CTS│       │BA│
-     *                   └───┘       └──┘  AP continues the TXOP despite   A switches to listening
-     *                                     the failure, but sends an ICF    after transition delay
+     *                   └───┘       └──┘        AP continues the TXOP     A switches to listening
+     *                                             after PIFS recovery      after transition delay
      *                                                                │                       │
-     *                                            ┌───┐     ┌─────┐   │┌───┐              ┌───┐
-     *                                            │MU │     │QoS x│   ││MU │     ┌───┐    │CF-│
-     *  [link 1]                                  │RTS│     │ to A│   ││RTS│     │BAR│    │End│
-     *  ──────────────────────────────────────────┴───┴┬───┬┴─────┴┬──┬┴───┴┬───┬┴───┴┬──┬┴───┴─
-     *                                                 │CTS│       │BA│     │CTS│     │BA│
-     *                                                 └───┘       └──x     └───┘     └──┘
+     *                                 ┌─────┐    ┌───┐     ┌─────┐   │┌───┐              ┌───┐
+     *                                 │QoS z│    │MU │     │QoS x│   ││MU │     ┌───┐    │CF-│
+     *  [link 1]                       │ to C│    │RTS│     │ to A│   ││RTS│     │BAR│    │End│
+     *  ───────────────────────────────┴─────┴┬──┬┴───┴┬───┬┴─────┴┬──┬┴───┴┬───┬┴───┴┬──┬┴───┴─
+     *                                        │BA│     │CTS│       │BA│     │CTS│     │BA│
+     *                                        └──┘     └───┘       └──x     └───┘     └──┘
      */
     if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
     {
@@ -1829,14 +1401,14 @@
                                                                            phy->GetPhyBand());
         auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
 
-        // the fourth frame exchange starts a SIFS after the previous one because the AP can
-        // continue the TXOP despite it does not receive the BlockAck (the AP received the
-        // PHY-RXSTART.indication and the frame exchange involves an EMLSR client)
+        // the fourth frame exchange starts a PIFS after the previous one because the AP
+        // performs PIFS recovery (the initial frame in the TXOP was successfully received by
+        // a non-EMLSR client)
         NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
-                                    bAckRespTxEnd + phy->GetSifs(),
-                                    "Transmission started less than a SIFS after BlockAck");
+                                    bAckRespTxEnd + phy->GetPifs(),
+                                    "Transmission started less than a PIFS after BlockAck");
         NS_TEST_EXPECT_MSG_LT(fourthExchangeIt->front()->startTx,
-                              bAckRespTxEnd + phy->GetSifs() +
+                              bAckRespTxEnd + phy->GetPifs() +
                                   MicroSeconds(1) /* propagation delay upper bound */,
                               "Transmission started too much time after BlockAck");
 
@@ -2078,7 +1650,7 @@
                                                     ackTxVector,
                                                     phy->GetPhyBand());
 
-    Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=]() {
+    Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=, this]() {
         if (frame.m_emlControl.emlsrMode == 1)
         {
             // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
@@ -2195,7 +1767,8 @@
                                  id != linkId,
                                  "Checking that AP blocked transmissions on all other EMLSR "
                                  "links after sending ICF to client with AID=" +
-                                     std::to_string(userInfo.GetAid12()));
+                                     std::to_string(userInfo.GetAid12()),
+                                 false);
             }
         }
     }
@@ -2235,7 +1808,7 @@
                 continue;
             }
 
-            Simulator::Schedule(txDuration + NanoSeconds(5), [=]() {
+            Simulator::Schedule(txDuration + NanoSeconds(5), [=, this]() {
                 for (uint8_t id = 0; id < m_staMacs[i]->GetNLinks(); id++)
                 {
                     // non-EMLSR links or links on which ICF is received are not blocked
@@ -2284,7 +1857,7 @@
         // both EMLSR clients are about to receive a QoS data frame
         for (std::size_t clientId : {firstClientId, secondClientId})
         {
-            Simulator::Schedule(txDuration, [=]() {
+            Simulator::Schedule(txDuration, [=, this]() {
                 for (uint8_t id = 0; id < m_staMacs[clientId]->GetNLinks(); id++)
                 {
                     // link on which QoS data is received is not blocked
@@ -2329,7 +1902,7 @@
 
         // immediately before the end of the PPDU, this link only is not blocked for the EMLSR
         // client on the AP MLD
-        Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
+        Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
             for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
             {
                 CheckBlockedLink(m_apMac,
@@ -2345,7 +1918,7 @@
         // immediately before the end of the PPDU, all the links on the EMLSR client that is not
         // the recipient of the second QoS frame are unblocked (they are unblocked when the
         // PHY-RXSTART.indication is not received)
-        Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
+        Simulator::Schedule(txDuration - NanoSeconds(1), [=, this]() {
             for (uint8_t id = 0; id < m_staMacs[secondClientId]->GetNLinks(); id++)
             {
                 CheckBlockedLink(m_staMacs[secondClientId],
@@ -2359,7 +1932,7 @@
             }
         });
         // immediately after the end of the PPDU, all links are blocked for the EMLSR client
-        Simulator::Schedule(txDuration + NanoSeconds(1), [=]() {
+        Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
             for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
             {
                 CheckBlockedLink(m_apMac,
@@ -2375,7 +1948,7 @@
         // immediately before the transition delay, all links are still blocked for the EMLSR client
         Simulator::Schedule(
             txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
-            [=]() {
+            [=, this]() {
                 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
                 {
                     CheckBlockedLink(
@@ -2385,15 +1958,64 @@
                         WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
                         true,
                         "Checking links of EMLSR client " + std::to_string(secondClientId) +
-                            " are all blocked on the AP MLD before the transition delay");
+                            " are all blocked on the AP MLD before the transition delay",
+                        false);
                 }
             });
 
+        // 100 us before the transition delay expires, generate another small packet addressed
+        // to a non-EMLSR client. The AP will start a TXOP to transmit this frame, while the
+        // frame addressed to the EMLSR client is still queued because the transition delay has
+        // not yet elapsed. The transition delay will expire while the AP is transmitting the
+        // frame to the non-EMLSR client, so that the AP continues the TXOP to transmit the frame
+        // to the EMLSR client and we can check that the AP performs PIFS recovery after missing
+        // the BlockAck from the EMLSR client
+        Simulator::Schedule(txDuration + m_transitionDelay.at(secondClientId) - MicroSeconds(100),
+                            [=, this]() {
+                                m_apMac->GetDevice()->GetNode()->AddApplication(
+                                    GetApplication(DOWNLINK, m_nEmlsrStations, 1, 40));
+                            });
+
         break;
     case 3:
-        // at the end of the third QoS frame, this link only is not blocked on the EMLSR
+        // this is the frame addressed to a non-EMLSR client, which is transmitted before the
+        // frame addressed to the EMLSR client, because the links of the latter are still blocked
+        // at the AP because the transition delay has not yet elapsed
+        NS_TEST_EXPECT_MSG_EQ(
+            psduMap.cbegin()->second->GetAddr1(),
+            m_staMacs[m_nEmlsrStations]->GetFrameExchangeManager(linkId)->GetAddress(),
+            "QoS frame not addressed to a non-EMLSR client");
+
+        for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+        {
+            CheckBlockedLink(m_apMac,
+                             addr,
+                             id,
+                             WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
+                             true,
+                             "Checking links of EMLSR client " + std::to_string(secondClientId) +
+                                 " are all blocked on the AP MLD before the transition delay");
+        }
+        // Block transmissions to the EMLSR client on all the links but the one on which this
+        // frame is sent, so that the AP will continue this TXOP to send the queued frame to the
+        // EMLSR client once the transition delay elapses
+        for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+        {
+            if (id != linkId)
+            {
+                m_apMac->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
+            }
+        }
+        break;
+    case 4:
+        // the AP is continuing the TXOP, no need to block transmissions anymore
+        for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+        {
+            m_apMac->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED, addr, {id});
+        }
+        // at the end of the fourth QoS frame, this link only is not blocked on the EMLSR
         // client receiving the frame
-        Simulator::Schedule(txDuration, [=]() {
+        Simulator::Schedule(txDuration, [=, this]() {
             for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
             {
                 CheckBlockedLink(m_staMacs[secondClientId],
@@ -2403,7 +2025,7 @@
                                  id != linkId,
                                  "Checking EMLSR links on EMLSR client " +
                                      std::to_string(secondClientId) +
-                                     " after receiving the third QoS data frame");
+                                     " after receiving the fourth QoS data frame");
             }
         });
         break;
@@ -2422,6 +2044,12 @@
         return;
     }
 
+    if (++m_countBlockAck == 4)
+    {
+        // fourth BlockAck is sent by a non-EMLSR client
+        return;
+    }
+
     auto taddr = psduMap.cbegin()->second->GetAddr2();
     std::size_t clientId;
     if (m_staMacs[0]->GetLinkIdByAddress(taddr))
@@ -2452,14 +2080,13 @@
     auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
     auto cfEndTxDuration = WifiPhy::CalculateTxDuration(
         Create<WifiPsdu>(Create<Packet>(), WifiMacHeader(WIFI_MAC_CTL_END)),
-        m_apMac->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(Mac48Address::GetBroadcast()),
+        m_apMac->GetWifiRemoteStationManager(linkId)->GetRtsTxVector(Mac48Address::GetBroadcast(),
+                                                                     txVector.GetChannelWidth()),
         apPhy->GetPhyBand());
 
-    m_countBlockAck++;
-
     switch (m_countBlockAck)
     {
-    case 4:
+    case 5:
         // the PPDU carrying this BlockAck is corrupted, hence the AP MLD MAC receives the
         // PHY-RXSTART indication but it does not receive any frame from the PHY. Therefore,
         // at the end of the PPDU transmission, the AP MLD realizes that the EMLSR client has
@@ -2467,7 +2094,7 @@
 
         // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
         // the AP MLD
-        Simulator::Schedule(txDuration, [=]() {
+        Simulator::Schedule(txDuration, [=, this]() {
             for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
             {
                 CheckBlockedLink(m_staMacs[clientId],
@@ -2488,7 +2115,7 @@
         });
         // a SIFS after the end of the PPDU, still this link only is not blocked on both the
         // EMLSR client and the AP MLD
-        Simulator::Schedule(txDuration + apPhy->GetSifs(), [=]() {
+        Simulator::Schedule(txDuration + apPhy->GetSifs(), [=, this]() {
             for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
             {
                 CheckBlockedLink(m_staMacs[clientId],
@@ -2513,10 +2140,10 @@
             m_errorModel->SetList({uid});
         }
         break;
-    case 5:
+    case 6:
         // at the end of the PPDU, this link only is not blocked on both the EMLSR client and
         // the AP MLD
-        Simulator::Schedule(txDuration, [=]() {
+        Simulator::Schedule(txDuration, [=, this]() {
             for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
             {
                 CheckBlockedLink(m_staMacs[clientId],
@@ -2539,7 +2166,7 @@
         // EMLSR client and the AP MLD
         Simulator::Schedule(
             txDuration + apPhy->GetSifs() + cfEndTxDuration - MicroSeconds(1),
-            [=]() {
+            [=, this]() {
                 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
                 {
                     CheckBlockedLink(m_staMacs[clientId],
@@ -2562,7 +2189,7 @@
         // AP MLD
         Simulator::Schedule(
             txDuration + apPhy->GetSifs() + cfEndTxDuration + MicroSeconds(1),
-            [=]() {
+            [=, this]() {
                 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
                 {
                     CheckBlockedLink(
@@ -2580,7 +2207,7 @@
         Simulator::Schedule(
             txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) -
                 MicroSeconds(1),
-            [=]() {
+            [=, this]() {
                 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
                 {
                     CheckBlockedLink(
@@ -2597,7 +2224,7 @@
         Simulator::Schedule(
             txDuration + apPhy->GetSifs() + cfEndTxDuration + m_transitionDelay.at(clientId) +
                 MicroSeconds(1),
-            [=]() {
+            [=, this]() {
                 for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
                 {
                     CheckBlockedLink(
@@ -2625,96 +2252,881 @@
     Simulator::Destroy();
 }
 
-/**
- * \ingroup wifi-test
- * \ingroup tests
- *
- * \brief Test the switching of PHYs on EMLSR clients.
- *
- * An AP MLD and an EMLSR client setup 3 links, on which EMLSR mode is enabled. The AP MLD
- * transmits 4 QoS data frames (one after another, each protected by ICF):
- *
- * - the first one on the link used for ML setup, hence no PHY switch occurs
- * - the second one on another link, thus causing the main PHY to switch link
- * - the third one on the remaining link, thus causing the main PHY to switch link again
- * - the fourth one on the link used for ML setup; if the aux PHYs switches link, there is
- *   one aux PHY listening on such a link and the main PHY switches to this link, otherwise
- *   no PHY is listening on such a link and there is no response to the ICF sent by the AP MLD
- */
-class EmlsrLinkSwitchTest : public EmlsrOperationsTestBase
+EmlsrUlTxopTest::EmlsrUlTxopTest(const Params& params)
+    : EmlsrOperationsTestBase("Check EML UL TXOP transmissions"),
+      m_emlsrLinks(params.linksToEnableEmlsrOn),
+      m_channelWidth(params.channelWidth),
+      m_auxPhyChannelWidth(params.auxPhyChannelWidth),
+      m_mediumSyncDuration(params.mediumSyncDuration),
+      m_msdMaxNTxops(params.msdMaxNTxops),
+      m_emlsrEnabledTime(0),
+      m_firstUlPktsGenTime(0),
+      m_unblockMainPhyLinkDelay(MilliSeconds(20)),
+      m_checkBackoffStarted(false),
+      m_countQoSframes(0),
+      m_countBlockAck(0),
+      m_countRtsframes(0),
+      m_genBackoffIfTxopWithoutTx(params.genBackoffIfTxopWithoutTx)
 {
-  public:
-    /**
-     * Constructor
-     *
-     * \param switchAuxPhy whether AUX PHY should switch channel to operate on the link on which
-                          the Main PHY was operating before moving to the link of the Aux PHY
-     * \param resetCamState whether to reset the state of the ChannelAccessManager associated with
-                            the link on which the main PHY has just switched to
-        \param auxPhyMaxChWidth max channel width (MHz) supported by aux PHYs
-     */
-    EmlsrLinkSwitchTest(bool switchAuxPhy, bool resetCamState, uint16_t auxPhyMaxChWidth);
+    m_nEmlsrStations = 1;
+    m_nNonEmlsrStations = 0;
+    m_linksToEnableEmlsrOn = params.linksToEnableEmlsrOn;
+    m_mainPhyId = 1;
+
+    // when aux PHYs do not switch link, the main PHY switches back to its previous link after
+    // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
+    m_transitionDelay = {MicroSeconds(256)};
+    m_establishBaDl = true;
+    m_establishBaUl = true;
+    m_duration = Seconds(1);
 
-    ~EmlsrLinkSwitchTest() override = default;
+    NS_ABORT_MSG_IF(params.linksToEnableEmlsrOn.size() < 2,
+                    "This test requires at least two links to be configured as EMLSR links");
+    for (uint8_t id = 0; id < 3; id++)
+    {
+        if (m_emlsrLinks.count(id) == 0)
+        {
+            // non-EMLSR link found
+            m_nonEmlsrLink = id;
+            break;
+        }
+    }
+}
 
-  protected:
-    void DoSetup() override;
-    void DoRun() override;
-    void Transmit(Ptr<WifiMac> mac,
-                  uint8_t phyId,
-                  WifiConstPsduMap psduMap,
-                  WifiTxVector txVector,
-                  double txPowerW) override;
+void
+EmlsrUlTxopTest::DoSetup()
+{
+    Config::SetDefault("ns3::EmlsrManager::AuxPhyChannelWidth",
+                       UintegerValue(m_auxPhyChannelWidth));
+    Config::SetDefault("ns3::DefaultEmlsrManager::SwitchAuxPhy", BooleanValue(false));
+    Config::SetDefault("ns3::EhtConfiguration::MediumSyncDuration",
+                       TimeValue(m_mediumSyncDuration));
+    Config::SetDefault("ns3::EhtConfiguration::MsdMaxNTxops", UintegerValue(m_msdMaxNTxops));
+    Config::SetDefault("ns3::ChannelAccessManager::GenerateBackoffIfTxopWithoutTx",
+                       BooleanValue(m_genBackoffIfTxopWithoutTx));
+    // Channel switch delay should be less than RTS TX time + SIFS + CTS TX time, otherwise
+    // UL TXOPs cannot be initiated by aux PHYs
+    Config::SetDefault("ns3::WifiPhy::ChannelSwitchDelay", TimeValue(MicroSeconds(75)));
 
-    /**
-     * Check that the simulation produced the expected results.
-     */
-    void CheckResults();
+    EmlsrOperationsTestBase::DoSetup();
 
-    /**
-     * Check that the Main PHY (and possibly the Aux PHY) correctly switches channel when the
-     * reception of an ICF ends.
-     *
-     * \param psduMap the PSDU carrying the MU-RTS TF
-     * \param txVector the TXVECTOR used to send the PPDU
-     * \param linkId the ID of the given link
-     */
-    void CheckInitialControlFrame(const WifiConstPsduMap& psduMap,
-                                  const WifiTxVector& txVector,
-                                  uint8_t linkId);
+    m_staMacs[0]->GetQosTxop(AC_BE)->TraceConnectWithoutContext(
+        "BackoffTrace",
+        MakeCallback(&EmlsrUlTxopTest::BackoffGenerated, this));
+
+    uint8_t linkId = 0;
+    // configure channels of the given width
+    for (auto band : {WIFI_PHY_BAND_2_4GHZ, WIFI_PHY_BAND_5GHZ, WIFI_PHY_BAND_6GHZ})
+    {
+        uint16_t bw = 20;
+        uint8_t number = band == WIFI_PHY_BAND_5GHZ ? 36 : 1;
+
+        auto width = std::min<uint16_t>(m_channelWidth, band == WIFI_PHY_BAND_2_4GHZ ? 40 : 160);
+        while (bw < width)
+        {
+            bw *= 2;
+            number += bw / 20;
+        }
+
+        for (auto mac : std::initializer_list<Ptr<WifiMac>>{m_apMac, m_staMacs[0]})
+        {
+            mac->GetWifiPhy(linkId)->SetOperatingChannel(
+                WifiPhy::ChannelTuple{number, width, band, 0});
+        }
+        linkId++;
+    }
+
+    // install post reception error model on the AP affiliated with the AP MLD and operating on
+    // the same link as the main PHY of the EMLSR client
+    m_errorModel = CreateObject<ListErrorModel>();
+    m_apMac->GetWifiPhy(m_mainPhyId)->SetPostReceptionErrorModel(m_errorModel);
+}
+
+void
+EmlsrUlTxopTest::BackoffGenerated(uint32_t backoff, uint8_t linkId)
+{
+    NS_LOG_INFO("Backoff value " << backoff << " generated by EMLSR client on link " << +linkId
+                                 << "\n");
+    if (linkId != m_mainPhyId)
+    {
+        return; // we are only interested in backoff on main PHY link
+    }
+
+    if (m_backoffEndTime)
+    {
+        if (m_checkBackoffStarted)
+        {
+            // another backoff value while checkBackoffStarted is true is generated only if
+            // GenerateBackoffIfTxopWithoutTx is true
+            NS_TEST_EXPECT_MSG_EQ(
+                m_genBackoffIfTxopWithoutTx,
+                true,
+                "Another backoff value should not be generated while the main PHY link is blocked");
+
+            NS_TEST_EXPECT_MSG_EQ(m_backoffEndTime.value(),
+                                  Simulator::Now(),
+                                  "Backoff generated at unexpected time");
+        }
+        else
+        {
+            // we are done checking the backoff
+            m_backoffEndTime.reset();
+        }
+    }
+
+    if (m_checkBackoffStarted)
+    {
+        m_backoffEndTime = Simulator::Now() +
+                           m_staMacs[0]->GetChannelAccessManager(linkId)->GetSifs() +
+                           (m_staMacs[0]->GetQosTxop(AC_BE)->GetAifsn(linkId) + backoff) *
+                               m_staMacs[0]->GetChannelAccessManager(linkId)->GetSlot();
+    }
+}
+
+void
+EmlsrUlTxopTest::Transmit(Ptr<WifiMac> mac,
+                          uint8_t phyId,
+                          WifiConstPsduMap psduMap,
+                          WifiTxVector txVector,
+                          double txPowerW)
+{
+    EmlsrOperationsTestBase::Transmit(mac, phyId, psduMap, txVector, txPowerW);
+    auto linkId = m_txPsdus.back().linkId;
+
+    auto psdu = psduMap.begin()->second;
+    auto nodeId = mac->GetDevice()->GetNode()->GetId();
+
+    switch (psdu->GetHeader(0).GetType())
+    {
+    case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
+        NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
+        NS_TEST_EXPECT_MSG_EQ(+linkId, +m_mainPhyId, "AssocReq not sent by the main PHY");
+        break;
+
+    case WIFI_MAC_CTL_RTS:
+        CheckRtsFrames(*psdu->begin(), txVector, linkId);
+        break;
+
+    case WIFI_MAC_CTL_CTS:
+        CheckCtsFrames(*psdu->begin(), txVector, linkId);
+        break;
+
+    case WIFI_MAC_QOSDATA:
+        CheckQosFrames(psduMap, txVector, linkId);
+        break;
+
+    case WIFI_MAC_CTL_BACKRESP:
+        CheckBlockAck(psduMap, txVector, linkId);
+        break;
+
+    default:;
+    }
+}
+
+void
+EmlsrUlTxopTest::StartTraffic()
+{
+    // initially, we prevent transmissions on aux PHY links
+    auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
+    auxPhyLinks.erase(m_mainPhyId);
+    if (m_nonEmlsrLink)
+    {
+        auxPhyLinks.erase(*m_nonEmlsrLink);
+    }
+    m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                        m_apMac->GetAddress(),
+                                        auxPhyLinks);
+
+    // Association, Block Ack agreement establishment and enabling EMLSR mode have been done.
+    // After 50ms, schedule:
+    // - block of transmissions on the link where the main PHY is operating and on the non-EMLSR
+    //   link (if any)
+    // - the generation of two UL packets
+    // - after m_unblockMainPhyLinkDelay, unblock transmissions on the link where the main PHY
+    //   is operating, so that the first data frame is transmitted on that link
+    Simulator::Schedule(MilliSeconds(50), [&]() {
+        std::set<uint8_t> linkIds;
+        linkIds.insert(*m_staMacs[0]->GetLinkForPhy(m_mainPhyId));
+        if (m_nonEmlsrLink)
+        {
+            linkIds.insert(*m_nonEmlsrLink);
+        }
+        m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                            m_apMac->GetAddress(),
+                                            linkIds);
+
+        NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
+        m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
+        m_firstUlPktsGenTime = Simulator::Now();
+
+        Simulator::Schedule(m_unblockMainPhyLinkDelay, [&]() {
+            m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                                  m_apMac->GetAddress(),
+                                                  {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
+        });
+    });
+}
+
+void
+EmlsrUlTxopTest::CheckQosFrames(const WifiConstPsduMap& psduMap,
+                                const WifiTxVector& txVector,
+                                uint8_t linkId)
+{
+    m_countQoSframes++;
+
+    auto txDuration =
+        WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
+
+    switch (m_countQoSframes)
+    {
+    case 1:
+    case 2:
+        // do nothing, these are the QoS data frames sent to establish BA agreements in DL and UL
+        // direction
+        break;
+    case 3:
+        // first UL data frame (transmitted by the main PHY)
+        if (m_nonEmlsrLink)
+        {
+            // generate data packets for another UL data frame, which will be sent on the
+            // non-EMLSR link
+            NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
+            m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
+                GetApplication(UPLINK, 0, 2, 1000));
+
+            // unblock transmissions on the non-EMLSR link once the two packets are queued
+            Simulator::ScheduleNow([=, this]() {
+                m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                                      m_apMac->GetAddress(),
+                                                      {*m_nonEmlsrLink});
+            });
+        }
+
+        // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
+        // after this QoS data frame is received
+        Simulator::ScheduleNow([=, this]() {
+            for (auto id : m_staMacs[0]->GetLinkIds())
+            {
+                CheckBlockedLink(
+                    m_staMacs[0],
+                    m_apMac->GetAddress(),
+                    id,
+                    WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                    id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) && m_staMacs[0]->IsEmlsrLink(id),
+                    "Checking EMLSR links on EMLSR client while sending the first data frame",
+                    false);
+
+                Simulator::Schedule(
+                    txDuration + MicroSeconds(1) /* propagation delay */,
+                    [=, this]() {
+                        CheckBlockedLink(
+                            m_apMac,
+                            m_staMacs[0]->GetAddress(),
+                            id,
+                            WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                            id != m_staMacs[0]->GetLinkForPhy(m_mainPhyId) &&
+                                m_staMacs[0]->IsEmlsrLink(id),
+                            "Checking EMLSR links on AP MLD while sending the first data frame");
+                    });
+            }
+        });
+
+        if (m_nonEmlsrLink)
+        {
+            break;
+        }
+        m_countQoSframes++; // if all EMLSR links, next case is already executed now
+        [[fallthrough]];
+    case 4:
+        // check that other EMLSR links are now blocked on the EMLSR client and on the AP MLD
+        // after this QoS data frame is received
+        Simulator::ScheduleNow([=, this]() {
+            // make aux PHYs capable of transmitting frames
+            auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
+            auxPhyLinks.erase(m_mainPhyId);
+            if (m_nonEmlsrLink)
+            {
+                auxPhyLinks.erase(*m_nonEmlsrLink);
+            }
+            m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                                  m_apMac->GetAddress(),
+                                                  auxPhyLinks);
+
+            // block transmissions on the link where the main PHY is operating
+            m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                                m_apMac->GetAddress(),
+                                                {*m_staMacs[0]->GetLinkForPhy(m_mainPhyId)});
+
+            // generate data packets for another UL data frame, which will be sent on a link on
+            // which an aux PHY is operating
+            NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
+            m_staMacs[0]->GetDevice()->GetNode()->AddApplication(
+                GetApplication(UPLINK, 0, 2, 1000));
+        });
+        break;
+    case 5:
+        // check that other EMLSR links are now blocked on both the EMLSR client and the AP MLD
+        Simulator::ScheduleNow([=, this]() {
+            for (auto id : m_staMacs[0]->GetLinkIds())
+            {
+                CheckBlockedLink(
+                    m_staMacs[0],
+                    m_apMac->GetAddress(),
+                    id,
+                    WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                    id != linkId && m_staMacs[0]->IsEmlsrLink(id),
+                    "Checking EMLSR links on EMLSR client while sending the second data frame",
+                    false);
+
+                CheckBlockedLink(
+                    m_apMac,
+                    m_staMacs[0]->GetAddress(),
+                    id,
+                    WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK,
+                    id != linkId && m_staMacs[0]->IsEmlsrLink(id),
+                    "Checking EMLSR links on AP MLD while sending the second data frame",
+                    false);
+            }
+
+            // unblock transmission on the link where the main PHY is operating
+            m_staMacs[0]->GetMacQueueScheduler()->UnblockQueues(
+                WifiQueueBlockedReason::TID_NOT_MAPPED,
+                AC_BE,
+                {WIFI_QOSDATA_QUEUE},
+                m_apMac->GetAddress(),
+                m_staMacs[0]->GetAddress(),
+                {0},
+                {m_mainPhyId});
+        });
+        break;
+    }
+}
+
+void
+EmlsrUlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
+                               const WifiTxVector& txVector,
+                               uint8_t linkId)
+{
+    m_countBlockAck++;
+
+    auto auxPhyLinks = m_staMacs[0]->GetSetupLinkIds();
+    auxPhyLinks.erase(m_mainPhyId);
+    if (m_nonEmlsrLink)
+    {
+        auxPhyLinks.erase(*m_nonEmlsrLink);
+    }
+
+    // lambda to check that the MediumSyncDelay timer is correctly running/not running and
+    // the CCA ED threshold is set to the correct value on all the links
+    auto checkMediumSyncDelayTimerActive = [=, this]() {
+        for (auto id : m_staMacs[0]->GetLinkIds())
+        {
+            // timer only started on EMLSR links other than the link on which TXOP was carried
+            // out
+            auto isTimerActive = m_staMacs[0]->IsEmlsrLink(id) && id != linkId;
+            auto time = m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(id);
+            NS_TEST_EXPECT_MSG_EQ(time.has_value(),
+                                  isTimerActive,
+                                  Simulator::Now().As(Time::MS)
+                                      << " Unexpected status for MediumSyncDelay timer on link "
+                                      << +id << " after terminating a TXOP on link " << +linkId);
+            auto currThreshold = m_staMacs[0]->GetWifiPhy(id)->GetCcaEdThreshold();
+            NS_TEST_EXPECT_MSG_EQ((static_cast<int8_t>(currThreshold) ==
+                                   m_staMacs[0]->GetEmlsrManager()->GetMediumSyncOfdmEdThreshold()),
+                                  isTimerActive,
+                                  Simulator::Now().As(Time::MS)
+                                      << " Unexpected value (" << currThreshold
+                                      << ") for CCA ED threshold on link " << +id
+                                      << " when MediumSyncDelay is "
+                                      << (isTimerActive ? "active" : "inactive"));
+        }
+    };
+
+    auto txDuration =
+        WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
+
+    switch (m_countBlockAck)
+    {
+    case 1:
+    case 2:
+        // do nothing, these are BlockAcks in response to the QoS data frames sent to establish
+        // BA agreements in DL and UL direction
+        break;
+    case 3:
+        if (linkId == m_nonEmlsrLink)
+        {
+            // this BlockAck has been sent on the non-EMLSR link, ignore it
+            break;
+        }
+        m_checkBackoffStarted = true;
+        if (!m_nonEmlsrLink)
+        {
+            m_countBlockAck++; // if all EMLSR links, next case is already executed now
+        }
+        [[fallthrough]];
+    case 4:
+        if (m_nonEmlsrLink && m_countBlockAck == 4)
+        {
+            // block transmissions on the non-EMLSR link
+            Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
+                m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                                    m_apMac->GetAddress(),
+                                                    {*m_nonEmlsrLink});
+            });
+        }
+        if (linkId == m_nonEmlsrLink)
+        {
+            // this BlockAck has been sent on the non-EMLSR link, ignore it
+            break;
+        }
+        m_checkBackoffStarted = true;
+        // check MediumSyncDelay timer on the EMLSR client after receiving BlockAck
+        Simulator::Schedule(txDuration + NanoSeconds(1), checkMediumSyncDelayTimerActive);
+        break;
+    case 5:
+        // Block Ack in response to the second data frame sent by the EMLSR client on EMLSR links.
+        // Check that MediumSyncDelay timer is running on the link where the main PHY is operating
+        // and that the number of backoff slots is not changed since the beginning of the TXOP
+        Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
+            checkMediumSyncDelayTimerActive();
+            auto elapsed =
+                m_staMacs[0]->GetEmlsrManager()->GetElapsedMediumSyncDelayTimer(m_mainPhyId);
+            NS_TEST_EXPECT_MSG_EQ(
+                elapsed.has_value(),
+                true,
+                "MediumSyncDelay timer not running on link where main PHY is operating");
+            m_lastMsdExpiryTime = Simulator::Now() +
+                                  m_staMacs[0]->GetEmlsrManager()->GetMediumSyncDuration() -
+                                  *elapsed;
+        });
+
+        Simulator::Schedule(txDuration, [=, this]() {
+            m_checkBackoffStarted = false;
+            NS_TEST_ASSERT_MSG_EQ(m_backoffEndTime.has_value(),
+                                  true,
+                                  "Backoff end time should have been calculated");
+            // when this BlockAck is received, the TXOP ends and the main PHY link is unblocked,
+            // which causes a new backoff timer to be generated if the backoff timer is not
+            // already running
+            *m_backoffEndTime = Max(*m_backoffEndTime, Simulator::Now());
+        });
+
+        // make aux PHYs not capable of transmitting frames
+        m_staMacs[0]->BlockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                            m_apMac->GetAddress(),
+                                            auxPhyLinks);
+
+        // generate data packets for another UL data frame, which will be sent on the link where
+        // the main PHY is operating
+        NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
+        m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
+        break;
+    case 6:
+        // block transmission on the main PHY link and on the non-EMLSR link (if any), so that
+        // the next QoS frames are sent on a link where an aux PHY is operating
+        std::set<uint8_t> linkIds{m_mainPhyId};
+        if (m_nonEmlsrLink)
+        {
+            linkIds.insert(*m_nonEmlsrLink);
+        }
+        m_staMacs[0]->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                                          AC_BE,
+                                                          {WIFI_QOSDATA_QUEUE},
+                                                          m_apMac->GetAddress(),
+                                                          m_staMacs[0]->GetAddress(),
+                                                          {0},
+                                                          linkIds);
+        // make sure aux PHYs are capable of transmitting frames
+        m_staMacs[0]->UnblockUnicastTxOnLinks(WifiQueueBlockedReason::TID_NOT_MAPPED,
+                                              m_apMac->GetAddress(),
+                                              auxPhyLinks);
+
+        // generate data packets for another UL data frame
+        NS_LOG_INFO("Enqueuing two packets at the EMLSR client\n");
+        m_staMacs[0]->GetDevice()->GetNode()->AddApplication(GetApplication(UPLINK, 0, 2, 1000));
+
+        break;
+    }
+}
+
+void
+EmlsrUlTxopTest::CheckRtsFrames(Ptr<const WifiMpdu> mpdu,
+                                const WifiTxVector& txVector,
+                                uint8_t linkId)
+{
+    if (m_firstUlPktsGenTime.IsZero())
+    {
+        // this function only considers RTS frames sent after the first QoS data frame
+        return;
+    }
+
+    if (linkId != m_mainPhyId)
+    {
+        if (m_countRtsframes > 0 && !m_corruptCts.has_value())
+        {
+            // we get here for the frame exchange in which the CTS response must be corrupted.
+            // Install post reception error model on the STA affiliated with the EMLSR client that
+            // is transmitting this RTS frame
+            m_errorModel = CreateObject<ListErrorModel>();
+            m_staMacs[0]->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
+            m_corruptCts = true;
+        }
+
+        return;
+    }
+
+    // we get here for RTS frames sent by the main PHY while the MediumSyncDelay timer is running
+    m_countRtsframes++;
+
+    NS_TEST_EXPECT_MSG_EQ(txVector.GetChannelWidth(),
+                          m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
+                          "RTS sent by main PHY on an unexpected width");
+
+    // corrupt reception at AP MLD
+    NS_LOG_INFO("CORRUPTED");
+    m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
+}
+
+void
+EmlsrUlTxopTest::CheckCtsFrames(Ptr<const WifiMpdu> mpdu,
+                                const WifiTxVector& txVector,
+                                uint8_t linkId)
+{
+    if (m_corruptCts.has_value() && *m_corruptCts)
+    {
+        // corrupt reception at EMLSR client
+        NS_LOG_INFO("CORRUPTED");
+        m_errorModel->SetList({mpdu->GetPacket()->GetUid()});
+        m_corruptCts = false;
+
+        auto txDuration = WifiPhy::CalculateTxDuration(mpdu->GetSize(),
+                                                       txVector,
+                                                       m_apMac->GetWifiPhy(linkId)->GetPhyBand());
+        // main PHY is about to complete channel switch when CTS ends
+        Simulator::Schedule(txDuration, [=, this]() {
+            const auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
+            NS_TEST_EXPECT_MSG_EQ(mainPhy->IsStateSwitching(),
+                                  true,
+                                  "Expecting the main PHY to be switching link");
+        });
+    }
+}
+
+void
+EmlsrUlTxopTest::DoRun()
+{
+    Simulator::Stop(m_duration);
+    Simulator::Run();
+
+    CheckResults();
+
+    Simulator::Destroy();
+}
+
+void
+EmlsrUlTxopTest::CheckResults()
+{
+    if (m_msdMaxNTxops > 0)
+    {
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(
+            m_countRtsframes,
+            m_msdMaxNTxops,
+            "Unexpected number of RTS frames sent while the MediumSyncDelay timer is running");
+    }
+
+    auto psduIt = m_txPsdus.cbegin();
+
+    // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame or RTS transmitted
+    // to/by an EMLSR client
+    auto jumpToQosDataOrMuRts = [&]() {
+        while (psduIt != m_txPsdus.cend() &&
+               !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData() &&
+               !psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts())
+        {
+            auto psdu = psduIt->psduMap.cbegin()->second;
+            if (psdu->GetHeader(0).IsTrigger())
+            {
+                CtrlTriggerHeader trigger;
+                psdu->GetPayload(0)->PeekHeader(trigger);
+                if (trigger.IsMuRts())
+                {
+                    break;
+                }
+            }
+            psduIt++;
+        }
+    };
 
     /**
-     * Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing
-     * QoS data frames to the EMLSR client on the given link.
+     * EMLSR client with EMLSR mode enabled on all links (main PHY ID = 1).
      *
-     * \param psduMap the PSDU(s) carrying QoS data frames
-     * \param txVector the TXVECTOR used to send the PPDU
-     * \param linkId the ID of the given link
-     */
-    void CheckQosFrames(const WifiConstPsduMap& psduMap,
-                        const WifiTxVector& txVector,
-                        uint8_t linkId);
-
-  private:
-    bool m_switchAuxPhy;  /**< whether AUX PHY should switch channel to operate on the link on which
-                               the Main PHY was operating before moving to the link of Aux PHY */
-    bool m_resetCamState; /**< whether to reset the state of the ChannelAccessManager associated
-                               with the link on which the main PHY has just switched to */
-    uint16_t m_auxPhyMaxChWidth;  //!< max channel width (MHz) supported by aux PHYs
-    std::size_t m_countQoSframes; //!< counter for QoS data frames
-    std::size_t m_txPsdusPos;     //!< a position in the vector of TX PSDUs
-};
-
-EmlsrLinkSwitchTest::EmlsrLinkSwitchTest(bool switchAuxPhy,
-                                         bool resetCamState,
-                                         uint16_t auxPhyMaxChWidth)
+     *  main PHY│
+     *  blocked,│
+     *  aux PHYs││main PHY blocked│
+     *  cannot  │
+     *  transmit│
+     *          │                    ┌───┐         ┌──┐
+     *  [link 0]                     │CTS│         │BA│
+     *  ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
+     *                          │RTS│     │QoS│QoS│
+     *                          └───┘     │ 6 │ 7 │
+     *                                    └───┴───┘
+     *                             gen backoff      gen backoff if     MediumSyncDelay
+     *                    ┌──┐    (also many times)  not running       timer expired ┌──┐
+     *  [link 1]          │BA│  │   if allowed        │                   │          │BA│
+     *  ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
+     *           │QoS│QoS│                               │RTS│ ... │RTS│    │QoS│QoS│
+     *           │ 4 │ 5 │                               └───┘     └───┘    │ 8 │ 9 │
+     *           └───┴───┘                                                  └───┴───┘
+     *
+     *  [link 2]
+     *  ───────────────────────────────────────────────────────────────────────────
+     *
+     *
+     *
+     * EMLSR client with EMLSR mode enabled on links 0 and 1 (main PHY ID = 1).
+     *
+     * main PHY │
+     *   and    │
+     * non-EMLSR│
+     *   link   │
+     *  blocked,│
+     *  aux PHYs││main PHY blocked│
+     *  cannot  │
+     *  transmit│
+     *          │                    ┌───┐         ┌──┐
+     *  [link 0]                     │CTS│         │BA│
+     *  ────────────────────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────────
+     *                          │RTS│     │QoS│QoS│
+     *                          └───┘     │ 8 │ 9 │
+     *                                    └───┴───┘
+     *                             gen backoff      gen backoff if     MediumSyncDelay
+     *                    ┌──┐    (also many times)  not running       timer expired ┌──┐
+     *  [link 1]          │BA│  │   if allowed        │                   │          │BA│
+     *  ─────────┬───┬───┬┴──┴───────────────────────────┬───┬─────┬───┬────┬───┬───┬┴──┴───────
+     *           │QoS│QoS│                               │RTS│ ... │RTS│    │QoS│QoS│
+     *           │ 4 │ 5 │                               └───┘     └───┘    │ 10│ 11│
+     *           └───┴───┘                                                  └───┴───┘
+     *                     ┌──┐
+     *  [link 2]           │BA│
+     *  ──────────┬───┬───┬┴──┴────────────────────────────────────────────────────────────
+     *            │QoS│QoS│
+     *            │ 6 │ 7 │
+     *            └───┴───┘
+     *
+     * For both scenarios, after the last frame exchange on the main PHY link, we have the
+     * following frame exchange on an EMLSR link where an aux PHY is operating on:
+     *
+     *             ┌───┐              ┌───┐         ┌──┐
+     *             │CTS│              │CTS│         │BA│
+     *  ──────┬───┬┴───X─────────┬───┬┴───┴┬───┬───┬┴──┴─────────────────────────────────────
+     *        │RTS│              │RTS│     │QoS│QoS│
+     *        └───┘              └───┘     │ X │ Y │
+     *                                     └───┴───┘
+     * For all EMLSR links scenario, X=10, Y=11; otherwise, X=12, Y=13.
+     */
+
+    // jump to the first (non-Beacon) frame transmitted after establishing BA agreements and
+    // enabling EMLSR mode
+    while (psduIt != m_txPsdus.cend() &&
+           (psduIt->startTx < m_firstUlPktsGenTime ||
+            psduIt->psduMap.cbegin()->second->GetHeader(0).IsBeacon()))
+    {
+        ++psduIt;
+    }
+
+    // the first QoS data frame is transmitted by the main PHY without RTS protection as soon
+    // as transmissions on the link where the main PHY is operating are unblocked (at this
+    // moment, aux PHYs cannot transmit)
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "First QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                          true,
+                          "First QoS data frame should be transmitted without protection");
+    NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
+                          +m_mainPhyId,
+                          "First QoS data frame should be transmitted by the main PHY");
+    NS_TEST_EXPECT_MSG_GT_OR_EQ(psduIt->startTx,
+                                m_firstUlPktsGenTime + m_unblockMainPhyLinkDelay,
+                                "First QoS data frame sent too early");
+
+    auto prevPsduIt = psduIt++;
+    jumpToQosDataOrMuRts();
+
+    if (m_nonEmlsrLink)
+    {
+        // an additional data frame is sent concurrently on the non-EMLSR link
+        NS_TEST_ASSERT_MSG_EQ(
+            (psduIt != m_txPsdus.cend()),
+            true,
+            "Expected another QoS data frame sent concurrently with the first frame");
+        NS_TEST_EXPECT_MSG_EQ(
+            psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+            true,
+            "First data frame on non-EMLSR link should be transmitted without protection");
+        NS_TEST_EXPECT_MSG_EQ(+psduIt->linkId,
+                              +m_nonEmlsrLink.value(),
+                              "First data frame expected to be transmitted on the non-EMLSR link");
+        const auto txDuration =
+            WifiPhy::CalculateTxDuration(prevPsduIt->psduMap,
+                                         prevPsduIt->txVector,
+                                         m_staMacs[0]->GetWifiPhy(prevPsduIt->phyId)->GetPhyBand());
+        NS_TEST_EXPECT_MSG_LT(psduIt->startTx,
+                              prevPsduIt->startTx + txDuration,
+                              "First data frame on the non-EMLSR link not sent concurrently");
+        psduIt++;
+        jumpToQosDataOrMuRts();
+    }
+
+    // the second QoS data frame is transmitted by the main PHY after that the aux PHY has
+    // obtained a TXOP and sent an RTS
+    // RTS
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "RTS before second QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
+                          true,
+                          "Second QoS data frame should be transmitted with protection");
+    NS_TEST_EXPECT_MSG_NE(
+        +psduIt->phyId,
+        +m_mainPhyId,
+        "RTS before second QoS data frame should not be transmitted by the main PHY");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
+                          m_auxPhyChannelWidth,
+                          "RTS before second data frame transmitted on an unexpected width");
+    psduIt++;
+    // CTS
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "CTS before second QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
+                          true,
+                          "CTS before second QoS data frame has not been transmitted");
+    psduIt++;
+    // QoS Data
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "Second QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                          true,
+                          "Second QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
+                          +m_mainPhyId,
+                          "Second QoS data frame should be transmitted by the main PHY");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
+                          m_auxPhyChannelWidth,
+                          "Second data frame not transmitted on the same width as RTS");
+
+    bool moreQosDataFound = false;
+
+    while (++psduIt != m_txPsdus.cend())
+    {
+        jumpToQosDataOrMuRts();
+        if (psduIt != m_txPsdus.cend() &&
+            psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
+        {
+            moreQosDataFound = true;
+
+            NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
+                                  +m_mainPhyId,
+                                  "Last QoS data frame should be transmitted by the main PHY");
+            NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
+                                  m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId)->GetChannelWidth(),
+                                  "Expecting TX width of last data frame to equal the channel "
+                                  "width used by the main PHY");
+            NS_TEST_EXPECT_MSG_GT_OR_EQ(
+                psduIt->startTx,
+                m_lastMsdExpiryTime,
+                "Last QoS data frame sent before MediumSyncDelay timer expired");
+
+            break;
+        }
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(moreQosDataFound,
+                          true,
+                          "Last QoS data frame transmitted by the main PHY not found");
+
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()), true, "Expected more frames");
+    ++psduIt;
+    jumpToQosDataOrMuRts();
+
+    // the first attempt at transmitting the last QoS data frame fails because CTS is corrupted
+    // RTS
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "RTS before last QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
+                          true,
+                          "Last QoS data frame should be transmitted with protection");
+    NS_TEST_EXPECT_MSG_NE(
+        +psduIt->phyId,
+        +m_mainPhyId,
+        "RTS before last QoS data frame should not be transmitted by the main PHY");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
+                          m_auxPhyChannelWidth,
+                          "RTS before last data frame transmitted on an unexpected width");
+    psduIt++;
+    // CTS
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "CTS before last QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
+                          true,
+                          "CTS before last QoS data frame has not been transmitted");
+    psduIt++;
+    jumpToQosDataOrMuRts();
+
+    // the last QoS data frame is transmitted by an aux PHY after that the aux PHY has
+    // obtained a TXOP and sent an RTS
+    // RTS
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "RTS before last QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsRts(),
+                          true,
+                          "Last QoS data frame should be transmitted with protection");
+    NS_TEST_EXPECT_MSG_NE(
+        +psduIt->phyId,
+        +m_mainPhyId,
+        "RTS before last QoS data frame should not be transmitted by the main PHY");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
+                          m_auxPhyChannelWidth,
+                          "RTS before last data frame transmitted on an unexpected width");
+    psduIt++;
+    // CTS
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "CTS before last QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsCts(),
+                          true,
+                          "CTS before last QoS data frame has not been transmitted");
+    psduIt++;
+    // QoS Data
+    NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend()),
+                          true,
+                          "Last QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                          true,
+                          "Last QoS data frame has not been transmitted");
+    NS_TEST_EXPECT_MSG_EQ(+psduIt->phyId,
+                          +m_mainPhyId,
+                          "Last QoS data frame should be transmitted by the main PHY");
+    NS_TEST_EXPECT_MSG_EQ(psduIt->txVector.GetChannelWidth(),
+                          m_auxPhyChannelWidth,
+                          "Last data frame not transmitted on the same width as RTS");
+}
+
+EmlsrLinkSwitchTest::EmlsrLinkSwitchTest(const Params& params)
     : EmlsrOperationsTestBase(std::string("Check EMLSR link switching (switchAuxPhy=") +
-                              std::to_string(switchAuxPhy) +
-                              ", resetCamState=" + std::to_string(resetCamState) +
-                              ", auxPhyMaxChWidth=" + std::to_string(auxPhyMaxChWidth) + "MHz )"),
-      m_switchAuxPhy(switchAuxPhy),
-      m_resetCamState(resetCamState),
-      m_auxPhyMaxChWidth(auxPhyMaxChWidth),
+                              std::to_string(params.switchAuxPhy) +
+                              ", resetCamState=" + std::to_string(params.resetCamState) +
+                              ", auxPhyMaxChWidth=" + std::to_string(params.auxPhyMaxChWidth) +
+                              "MHz )"),
+      m_switchAuxPhy(params.switchAuxPhy),
+      m_resetCamState(params.resetCamState),
+      m_auxPhyMaxChWidth(params.auxPhyMaxChWidth),
       m_countQoSframes(0),
       m_txPsdusPos(0)
 {
@@ -2724,6 +3136,9 @@
     m_mainPhyId = 1;
     m_establishBaDl = true;
     m_duration = Seconds(1.0);
+    // when aux PHYs do not switch link, the main PHY switches back to its previous link after
+    // a TXOP, hence the transition delay must exceed the channel switch delay (default: 250us)
+    m_transitionDelay = {MicroSeconds(256)};
 }
 
 void
@@ -2896,10 +3311,10 @@
 /**
  * AUX PHY switching enabled
  *
- *  |--------- aux PHY A ---------|------ main PHY ------|-------------- aux PHY B --------------
- *                            ┌───┐     ┌───┐
- *                            │ICF│     │QoS│
- *  ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
+ *  |--------- aux PHY A ---------|------ main PHY ------|-------------- aux PHY B -------------
+ *                           ┌───┐     ┌───┐
+ *                           │ICF│     │QoS│
+ * ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
  *  [link 0]                       │CTS│     │BA│
  *                                 └───┘     └──┘
  *
@@ -2920,25 +3335,27 @@
  *                                                        └───┘     └──┘
  *
  *
- * AUX PHY switching disabled
+ * AUX PHY switching disabled (X = main PHY channel switch delay)
  *
- *  |--------- aux PHY A ---------|------ main PHY ------|
+ *  |------------------------------------------ aux PHY A ---------------------------------------
+ *                                |-- main PHY --|X|
  *                            ┌───┐     ┌───┐
  *                            │ICF│     │QoS│
  *  ──────────────────────────┴───┴┬───┬┴───┴┬──┬────────────────────────────────────────────────
  *  [link 0]                       │CTS│     │BA│
  *                                 └───┘     └──┘
  *
- *
- *  |--------- main PHY ----------|
- *     ┌───┐     ┌───┐                                                      ┌───┐  ┌───┐  ┌───┐
- *     │ICF│     │QoS│                                                      │ICF│  │ICF│  │ICF│
- *  ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴──┴───┴──┴───┴──
- *  [link 1]│CTS│     │BA│
- *          └───┘     └──┘
+ *                                                 |-main|
+ *  |--------- main PHY ----------|                |-PHY-|                |------ main PHY ------
+ *     ┌───┐     ┌───┐                                                      ┌───┐     ┌───┐
+ *     │ICF│     │QoS│                                                      │ICF│     │QoS│
+ *  ───┴───┴┬───┬┴───┴┬──┬──────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬──
+ *  [link 1]│CTS│     │BA│                                                       │CTS│     │BA│
+ *          └───┘     └──┘                                                       └───┘     └──┘
  *
  *
- *  |--------------------- aux PHY B --------------------|--------------- main PHY --------------
+ *  |------------------------------------------ aux PHY B ---------------------------------------
+ *                                                       |-- main PHY --|X|
  *                                                   ┌───┐     ┌───┐
  *                                                   │ICF│     │QoS│
  *  ─────────────────────────────────────────────────┴───┴┬───┬┴───┴┬──┬─────────────────────────
@@ -2958,6 +3375,8 @@
         return;
     }
 
+    NS_TEST_EXPECT_MSG_LT(m_countQoSframes, 4, "Unexpected number of ICFs");
+
     auto mainPhy = m_staMacs[0]->GetDevice()->GetPhy(m_mainPhyId);
     auto phyRecvIcf = m_staMacs[0]->GetWifiPhy(linkId); // PHY receiving the ICF
 
@@ -2965,66 +3384,68 @@
     NS_TEST_ASSERT_MSG_EQ(currMainPhyLinkId.has_value(),
                           true,
                           "Didn't find the link on which the Main PHY is operating");
+    NS_TEST_ASSERT_MSG_NE(phyRecvIcf,
+                          nullptr,
+                          "No PHY on the link where ICF " << m_countQoSframes << " was sent");
 
-    if (phyRecvIcf && phyRecvIcf != mainPhy)
+    if (phyRecvIcf != mainPhy)
     {
         NS_TEST_EXPECT_MSG_LT_OR_EQ(
             phyRecvIcf->GetChannelWidth(),
             m_auxPhyMaxChWidth,
-            "Aux PHY that received ICF is operating on a channel whose width exceeds the limit "
-                << m_countQoSframes);
+            "Aux PHY that received ICF "
+                << m_countQoSframes << " is operating on a channel whose width exceeds the limit");
     }
 
+    // if aux PHYs switch links, only the first ICF is received by the main PHY; otherwise,
+    // the first ICF and the last ICF are received by the main PHY
+    NS_TEST_EXPECT_MSG_EQ((phyRecvIcf == mainPhy),
+                          (m_countQoSframes == 0 || (!m_switchAuxPhy && m_countQoSframes == 3)),
+                          "Expecting that the ICF was received by the main PHY");
+
+    // if aux PHYs do not switch links, the main PHY is operating on its original link when
+    // the transmission of an ICF starts
+    NS_TEST_EXPECT_MSG_EQ(m_switchAuxPhy || currMainPhyLinkId == m_mainPhyId,
+                          true,
+                          "Main PHY is operating on an unexpected link ("
+                              << +currMainPhyLinkId.value() << ", expected " << +m_mainPhyId
+                              << ")");
+
     auto txDuration =
         WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
 
     // check that PHYs are operating on the expected link after the reception of the ICF
-    Simulator::Schedule(txDuration + NanoSeconds(1), [=]() {
-        std::size_t nRxOk = m_switchAuxPhy ? 4 : 3; // successfully received ICFs
-
-        if (m_countQoSframes < nRxOk)
-        {
-            // the main PHY must be operating on the link where ICF was sent
-            NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId),
-                                  mainPhy,
-                                  "PHY operating on link where ICF was sent is not the main PHY");
-        }
-
-        // the first ICF is received by the main PHY and no channel switch occurs
-        if (m_countQoSframes == 0)
-        {
-            NS_TEST_EXPECT_MSG_EQ(phyRecvIcf,
-                                  mainPhy,
-                                  "PHY that received the ICF is not the main PHY");
-            return;
-        }
+    Simulator::Schedule(txDuration + NanoSeconds(1), [=, this]() {
+        // the main PHY must be operating on the link where ICF was sent
+        NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(linkId),
+                              mainPhy,
+                              "PHY operating on link where ICF was sent is not the main PHY");
 
         // the behavior of Aux PHYs depends on whether they switch channel or not
         if (m_switchAuxPhy)
         {
-            NS_TEST_EXPECT_MSG_LT(m_countQoSframes, nRxOk, "Unexpected number of ICFs");
-            Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=]() {
+            if (mainPhy != phyRecvIcf)
+            {
+                NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
+                                      true,
+                                      "Aux PHY expected to switch channel");
+            }
+            Simulator::Schedule(phyRecvIcf->GetChannelSwitchDelay(), [=, this]() {
                 NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
                                       phyRecvIcf,
-                                      "PHY operating on link where Main PHY was before switching "
-                                      "channel is not the aux PHY that received the ICF");
+                                      "The Aux PHY that received the ICF is expected to operate "
+                                      "on the link where Main PHY was before switching channel");
             });
         }
-        else if (m_countQoSframes < nRxOk)
-        {
-            // the first 3 ICFs are actually received by some PHY
-            NS_TEST_EXPECT_MSG_NE(phyRecvIcf, nullptr, "Expected some PHY to receive the ICF");
-            NS_TEST_EXPECT_MSG_EQ(m_staMacs[0]->GetWifiPhy(*currMainPhyLinkId),
-                                  nullptr,
-                                  "No PHY expected to operate on link where Main PHY was "
-                                  "before switching channel");
-        }
         else
         {
-            // no PHY received the ICF
-            NS_TEST_EXPECT_MSG_EQ(phyRecvIcf,
-                                  nullptr,
-                                  "Expected no PHY to operate on link where ICF is sent");
+            NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->IsStateSwitching(),
+                                  false,
+                                  "Aux PHY is not expected to switch channel");
+            NS_TEST_EXPECT_MSG_EQ(phyRecvIcf->GetPhyBand(),
+                                  mainPhy->GetPhyBand(),
+                                  "The Aux PHY that received the ICF is expected to operate "
+                                  "on the same band as the Main PHY");
         }
     });
 }
@@ -3034,7 +3455,7 @@
 {
     NS_TEST_ASSERT_MSG_NE(m_txPsdusPos, 0, "BA agreement establishment not completed");
 
-    std::size_t nRxOk = m_switchAuxPhy ? 4 : 3; // successfully received ICFs
+    const std::size_t nRxOk = 4; // successfully received ICFs
 
     NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(),
                                 m_txPsdusPos + 3 + nRxOk * 4,
@@ -3068,18 +3489,6 @@
     }
 }
 
-/**
- * \ingroup wifi-test
- * \ingroup tests
- *
- * \brief wifi EMLSR Test Suite
- */
-class WifiEmlsrTestSuite : public TestSuite
-{
-  public:
-    WifiEmlsrTestSuite();
-};
-
 WifiEmlsrTestSuite::WifiEmlsrTestSuite()
     : TestSuite("wifi-emlsr", UNIT)
 {
@@ -3091,27 +3500,31 @@
     for (const auto& emlsrLinks :
          {std::set<uint8_t>{0, 1, 2}, std::set<uint8_t>{1, 2}, std::set<uint8_t>{0, 1}})
     {
-        AddTestCase(new EmlsrDlTxopTest(1,
-                                        0,
-                                        emlsrLinks,
-                                        {MicroSeconds(32)},
-                                        {MicroSeconds(32)},
-                                        MicroSeconds(512)),
-                    TestCase::QUICK);
-        AddTestCase(new EmlsrDlTxopTest(1,
-                                        1,
-                                        emlsrLinks,
-                                        {MicroSeconds(64)},
-                                        {MicroSeconds(64)},
-                                        MicroSeconds(512)),
+        AddTestCase(
+            new EmlsrDlTxopTest(
+                {1, 0, emlsrLinks, {MicroSeconds(32)}, {MicroSeconds(32)}, MicroSeconds(512)}),
+            TestCase::QUICK);
+        AddTestCase(
+            new EmlsrDlTxopTest(
+                {1, 1, emlsrLinks, {MicroSeconds(64)}, {MicroSeconds(64)}, MicroSeconds(512)}),
+            TestCase::QUICK);
+        AddTestCase(new EmlsrDlTxopTest({2,
+                                         2,
+                                         emlsrLinks,
+                                         {MicroSeconds(128), MicroSeconds(256)},
+                                         {MicroSeconds(128), MicroSeconds(256)},
+                                         MicroSeconds(512)}),
                     TestCase::QUICK);
-        AddTestCase(new EmlsrDlTxopTest(2,
-                                        2,
-                                        emlsrLinks,
-                                        {MicroSeconds(128), MicroSeconds(256)},
-                                        {MicroSeconds(128), MicroSeconds(256)},
-                                        MicroSeconds(512)),
+    }
+
+    for (auto genBackoffIfTxopWithoutTx : {true, false})
+    {
+        AddTestCase(new EmlsrUlTxopTest(
+                        {{0, 1, 2}, 40, 20, MicroSeconds(5504), 3, genBackoffIfTxopWithoutTx}),
                     TestCase::QUICK);
+        AddTestCase(
+            new EmlsrUlTxopTest({{0, 1}, 40, 20, MicroSeconds(5504), 1, genBackoffIfTxopWithoutTx}),
+            TestCase::QUICK);
     }
 
     for (bool switchAuxPhy : {true, false})
@@ -3120,8 +3533,9 @@
         {
             for (uint16_t auxPhyMaxChWidth : {20, 40, 80, 160})
             {
-                AddTestCase(new EmlsrLinkSwitchTest(switchAuxPhy, resetCamState, auxPhyMaxChWidth),
-                            TestCase::QUICK);
+                AddTestCase(
+                    new EmlsrLinkSwitchTest({switchAuxPhy, resetCamState, auxPhyMaxChWidth}),
+                    TestCase::QUICK);
             }
         }
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-emlsr-test.h ns-3.41/src/wifi/test/wifi-emlsr-test.h
--- ns-3.40/src/wifi/test/wifi-emlsr-test.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/src/wifi/test/wifi-emlsr-test.h	2024-02-09 14:44:08.613795300 -0800
@@ -0,0 +1,734 @@
+/*
+ * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Stefano Avallone <stavallo@unina.it>
+ */
+
+#ifndef WIFI_EMLSR_TEST_H
+#define WIFI_EMLSR_TEST_H
+
+#include "ns3/ap-wifi-mac.h"
+#include "ns3/error-model.h"
+#include "ns3/header-serialization-test.h"
+#include "ns3/packet-socket-address.h"
+#include "ns3/packet-socket-client.h"
+#include "ns3/sta-wifi-mac.h"
+#include "ns3/test.h"
+#include "ns3/wifi-mac-queue-scheduler.h"
+#include "ns3/wifi-mac.h"
+#include "ns3/wifi-ppdu.h"
+#include "ns3/wifi-psdu.h"
+
+using namespace ns3;
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test EML Operating Mode Notification frame serialization and deserialization
+ */
+class EmlOperatingModeNotificationTest : public HeaderSerializationTestCase
+{
+  public:
+    /**
+     * Constructor
+     */
+    EmlOperatingModeNotificationTest();
+    ~EmlOperatingModeNotificationTest() override = default;
+
+  private:
+    void DoRun() override;
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Base class for EMLSR Operations tests
+ *
+ * This base class setups and configures one AP MLD, a variable number of non-AP MLDs with
+ * EMLSR activated and a variable number of non-AP MLD with EMLSR deactivated. Every MLD has
+ * three links, each operating on a distinct PHY band (2.4 GHz, 5 GHz and 6 GHz). Therefore,
+ * it is expected that three links are setup by the non-AP MLD(s). The values for the Padding
+ * Delay, the Transition Delay and the Transition Timeout are provided as argument to the
+ * constructor of this class, along with the IDs of the links on which EMLSR mode must be
+ * enabled for the non-AP MLDs (this information is used to set the EmlsrLinkSet attribute
+ * of the DefaultEmlsrManager installed on the non-AP MLDs).
+ */
+class EmlsrOperationsTestBase : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param name The name of the new TestCase created
+     */
+    EmlsrOperationsTestBase(const std::string& name);
+    ~EmlsrOperationsTestBase() override = default;
+
+    /// Enumeration for traffic directions
+    enum TrafficDirection : uint8_t
+    {
+        DOWNLINK = 0,
+        UPLINK
+    };
+
+  protected:
+    /**
+     * Callback invoked when a FEM passes PSDUs to the PHY.
+     *
+     * \param mac the MAC transmitting the PSDUs
+     * \param phyId the ID of the PHY transmitting the PSDUs
+     * \param psduMap the PSDU map
+     * \param txVector the TX vector
+     * \param txPowerW the tx power in Watts
+     */
+    virtual void Transmit(Ptr<WifiMac> mac,
+                          uint8_t phyId,
+                          WifiConstPsduMap psduMap,
+                          WifiTxVector txVector,
+                          double txPowerW);
+
+    /**
+     * \param dir the traffic direction (downlink/uplink)
+     * \param staId the index (starting at 0) of the non-AP MLD generating/receiving packets
+     * \param count the number of packets to generate
+     * \param pktSize the size of the packets to generate
+     * \return an application generating the given number packets of the given size from/to the
+     *         AP MLD to/from the given non-AP MLD
+     */
+    Ptr<PacketSocketClient> GetApplication(TrafficDirection dir,
+                                           std::size_t staId,
+                                           std::size_t count,
+                                           std::size_t pktSize) const;
+
+    /**
+     * Check whether QoS data unicast transmissions addressed to the given destination on the
+     * given link are blocked or unblocked for the given reason on the given device.
+     *
+     * \param mac the MAC of the given device
+     * \param dest the MAC address of the given destination
+     * \param linkId the ID of the given link
+     * \param reason the reason for blocking transmissions to test
+     * \param blocked whether transmissions are blocked for the given reason
+     * \param description text indicating when this check is performed
+     * \param testUnblockedForOtherReasons whether to test if transmissions are unblocked for
+     *                                     all the reasons other than the one provided
+     */
+    void CheckBlockedLink(Ptr<WifiMac> mac,
+                          Mac48Address dest,
+                          uint8_t linkId,
+                          WifiQueueBlockedReason reason,
+                          bool blocked,
+                          std::string description,
+                          bool testUnblockedForOtherReasons = true);
+
+    void DoSetup() override;
+
+    /// Information about transmitted frames
+    struct FrameInfo
+    {
+        Time startTx;             ///< TX start time
+        WifiConstPsduMap psduMap; ///< transmitted PSDU map
+        WifiTxVector txVector;    ///< TXVECTOR
+        uint8_t linkId;           ///< link ID
+        uint8_t phyId;            ///< ID of the transmitting PHY
+    };
+
+    uint8_t m_mainPhyId{0};                   //!< ID of the main PHY
+    std::set<uint8_t> m_linksToEnableEmlsrOn; /**< IDs of the links on which EMLSR mode has to
+                                                   be enabled */
+    std::size_t m_nEmlsrStations{1};          ///< number of stations to create that activate EMLSR
+    std::size_t m_nNonEmlsrStations{0};       /**< number of stations to create that do not
+                                                activate EMLSR */
+    Time m_transitionTimeout{MicroSeconds(128)}; ///< Transition Timeout advertised by the AP MLD
+    std::vector<Time> m_paddingDelay{
+        {MicroSeconds(32)}}; ///< Padding Delay advertised by the non-AP MLD
+    std::vector<Time> m_transitionDelay{
+        {MicroSeconds(16)}};                ///< Transition Delay advertised by the non-AP MLD
+    bool m_establishBaDl{false};            /**< whether BA needs to be established (for TID 0)
+                                                 with the AP as originator */
+    bool m_establishBaUl{false};            /**< whether BA needs to be established (for TID 0)
+                                                 with the AP as recipient */
+    std::vector<FrameInfo> m_txPsdus;       ///< transmitted PSDUs
+    Ptr<ApWifiMac> m_apMac;                 ///< AP wifi MAC
+    std::vector<Ptr<StaWifiMac>> m_staMacs; ///< MACs of the non-AP MLDs
+    std::vector<PacketSocketAddress> m_dlSockets; ///< packet socket address for DL traffic
+    std::vector<PacketSocketAddress> m_ulSockets; ///< packet socket address for UL traffic
+    uint16_t m_lastAid{0};                        ///< AID of last associated station
+    Time m_duration{0};                           ///< simulation duration
+
+  private:
+    /**
+     * Set the SSID on the next station that needs to start the association procedure.
+     * This method is connected to the ApWifiMac's AssociatedSta trace source.
+     * Start generating traffic (if needed) when all stations are associated.
+     *
+     * \param aid the AID assigned to the previous associated STA
+     */
+    void SetSsid(uint16_t aid, Mac48Address /* addr */);
+
+    /**
+     * Start the generation of traffic (needs to be overridden)
+     */
+    virtual void StartTraffic()
+    {
+    }
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test the exchange of EML Operating Mode Notification frames.
+ *
+ * This test considers an AP MLD and a non-AP MLD with EMLSR activated. Upon association,
+ * the non-AP MLD sends an EML Operating Mode Notification frame, which is however corrupted
+ * by using a post reception error model (installed on the AP MLD). We keep corrupting the
+ * EML Notification frames transmitted by the non-AP MLD until the frame is dropped due to
+ * exceeded max retry limit. It is checked that:
+ *
+ * - the Association Request contains a Multi-Link Element including an EML Capabilities field
+ *   that contains the expected values for Padding Delay and Transition Delay
+ * - the Association Response contains a Multi-Link Element including an EML Capabilities field
+ *   that contains the expected value for Transition Timeout
+ * - all EML Notification frames contain the expected values for EMLSR Mode, EMLMR Mode and
+ *   Link Bitmap fields and are transmitted on the link used for association
+ * - the correct EMLSR link set is stored by the EMLSR Manager, both when the transition
+ *   timeout expires and when an EML Notification response is received from the AP MLD (thus,
+ *   the correct EMLSR link set is stored after whichever of the two events occur first)
+ */
+class EmlOmnExchangeTest : public EmlsrOperationsTestBase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param linksToEnableEmlsrOn IDs of links on which EMLSR mode should be enabled
+     * \param transitionTimeout the Transition Timeout advertised by the AP MLD
+     */
+    EmlOmnExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn, Time transitionTimeout);
+    ~EmlOmnExchangeTest() override = default;
+
+  protected:
+    void DoSetup() override;
+    void DoRun() override;
+    void Transmit(Ptr<WifiMac> mac,
+                  uint8_t phyId,
+                  WifiConstPsduMap psduMap,
+                  WifiTxVector txVector,
+                  double txPowerW) override;
+
+    /**
+     * Callback invoked when the non-AP MLD receives the acknowledgment for a transmitted MPDU.
+     *
+     * \param mpdu the acknowledged MPDU
+     */
+    void TxOk(Ptr<const WifiMpdu> mpdu);
+    /**
+     * Callback invoked when the non-AP MLD drops the given MPDU for the given reason.
+     *
+     * \param reason the reason why the MPDU was dropped
+     * \param mpdu the dropped MPDU
+     */
+    void TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu);
+
+    /**
+     * Check the content of the EML Capabilities subfield of the Multi-Link Element included
+     * in the Association Request frame sent by the non-AP MLD.
+     *
+     * \param mpdu the MPDU containing the Association Request frame
+     * \param txVector the TXVECTOR used to transmit the frame
+     * \param linkId the ID of the link on which the frame was transmitted
+     */
+    void CheckEmlCapabilitiesInAssocReq(Ptr<const WifiMpdu> mpdu,
+                                        const WifiTxVector& txVector,
+                                        uint8_t linkId);
+    /**
+     * Check the content of the EML Capabilities subfield of the Multi-Link Element included
+     * in the Association Response frame sent by the AP MLD to the EMLSR client.
+     *
+     * \param mpdu the MPDU containing the Association Response frame
+     * \param txVector the TXVECTOR used to transmit the frame
+     * \param linkId the ID of the link on which the frame was transmitted
+     */
+    void CheckEmlCapabilitiesInAssocResp(Ptr<const WifiMpdu> mpdu,
+                                         const WifiTxVector& txVector,
+                                         uint8_t linkId);
+    /**
+     * Check the content of a received EML Operating Mode Notification frame.
+     *
+     * \param psdu the PSDU containing the EML Operating Mode Notification frame
+     * \param txVector the TXVECTOR used to transmit the frame
+     * \param linkId the ID of the link on which the frame was transmitted
+     */
+    void CheckEmlNotification(Ptr<const WifiPsdu> psdu,
+                              const WifiTxVector& txVector,
+                              uint8_t linkId);
+    /**
+     * Check that the EMLSR mode has been enabled on the expected EMLSR links.
+     */
+    void CheckEmlsrLinks();
+
+  private:
+    std::size_t m_checkEmlsrLinksCount;        /**< counter for the number of times CheckEmlsrLinks
+                                                    is called (should be two: when the transition
+                                                    timeout expires and when the EML Notification
+                                                    response from the AP MLD is received */
+    std::size_t m_emlNotificationDroppedCount; /**< counter for the number of times the EML
+                                                    Notification frame sent by the non-AP MLD
+                                                    has been dropped due to max retry limit */
+    Ptr<ListErrorModel> m_errorModel;          ///< error rate model to corrupt packets at AP MLD
+    std::list<uint64_t> m_uidList;             ///< list of UIDs of packets to corrupt
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test the transmission of DL frames to EMLSR clients.
+ *
+ * This test considers an AP MLD and a configurable number of non-AP MLDs that support EMLSR
+ * and a configurable number of non-AP MLDs that do not support EMLSR. All MLDs have three
+ * setup links, while the set of EMLSR links for the EMLSR clients is configurable.
+ * Block ack agreements (for TID 0, with the AP MLD as originator) are established with all
+ * the non-AP MLDs before that EMLSR clients send the EML Operating Mode Notification frame
+ * to enable the EMLSR mode on their EMLSR links.
+ *
+ * Before enabling EMLSR mode, it is checked that:
+ *
+ * - all EMLSR links (but the link used for ML setup) of the EMLSR clients are considered to
+ *   be in power save mode and are blocked by the AP MLD; all the other links have transitioned
+ *   to active mode and are not blocked
+ * - no MU-RTS Trigger Frame is sent as Initial control frame
+ * - In case of EMLSR clients having no link that is not an EMLSR link and is different than
+ *   the link used for ML setup, the two A-MPDUs used to trigger BA establishment are
+ *   transmitted one after another on the link used for ML setup. Otherwise, the two A-MPDUs
+ *   are sent concurrently on two distinct links
+ *
+ * After enabling EMLSR mode, it is checked that:
+ *
+ * - all EMLSR links of the EMLSR clients are considered to be in active mode and are not
+ *   blocked by the AP MLD
+ * - If all setup links are EMLSR links, the first two frame exchanges are both protected by
+ *   MU-RTS TF and occur one after another. Otherwise, one frame exchange occurs on the
+ *   non-EMLSR link and is not protected by MU-RTS TF; the other frame exchange occurs on an
+ *   EMLSR link and is protected by MU-RTS TF
+ * - the AP MLD blocks transmission on all other EMLSR links when sending an ICF to an EMLSR client
+ * - After completing a frame exchange with an EMLSR client, the AP MLD can start another frame
+ *   exchange with that EMLSR client within the same TXOP (after a SIFS) without sending an ICF
+ * - During the transition delay, all EMLSR links are not used for DL transmissions
+ * - The padding added to Initial Control frames is the largest among all the EMLSR clients
+ *   solicited by the ICF
+ *
+ * After disabling EMLSR mode, it is checked that:
+ *
+ * - all EMLSR links (but the link used to exchange EML Notification frames) of the EMLSR clients
+ *   are considered to be in power save mode and are blocked by the AP MLD
+ * - an MU-RTS Trigger Frame is sent by the AP MLD as ICF for sending the EML Notification
+ *   response, unless the link used to exchange EML Notification frames is a non-EMLSR link
+ * - no MU-RTS Trigger Frame is used as ICF for QoS data frames
+ * - In case of EMLSR clients having no link that is not an EMLSR link and is different than
+ *   the link used to exchange EML Notification frames, the two A-MPDUs are transmitted one
+ *   after another on the link used to exchange EML Notification frames. Otherwise, the two
+ *   A-MPDUs are sent concurrently on two distinct links
+ */
+class EmlsrDlTxopTest : public EmlsrOperationsTestBase
+{
+  public:
+    /**
+     * Parameters for the EMLSR DL TXOP test
+     */
+    struct Params
+    {
+        std::size_t nEmlsrStations;    //!< number of non-AP MLDs that support EMLSR
+        std::size_t nNonEmlsrStations; //!< number of non-AP MLDs that do not support EMLSR
+        std::set<uint8_t>
+            linksToEnableEmlsrOn;       //!< IDs of links on which EMLSR mode should be enabled
+        std::vector<Time> paddingDelay; //!< vector (whose size equals <i>nEmlsrStations</i>) of the
+                                        //!< padding delay values advertised by non-AP MLDs
+        std::vector<Time>
+            transitionDelay;    //!< vector (whose size equals <i>nEmlsrStations</i>) of
+                                //!< transition the delay values advertised by non-AP MLDs
+        Time transitionTimeout; //!< the Transition Timeout advertised by the AP MLD
+    };
+
+    /**
+     * Constructor
+     *
+     * \param params parameters for the EMLSR DL TXOP test
+     */
+    EmlsrDlTxopTest(const Params& params);
+    ~EmlsrDlTxopTest() override = default;
+
+  protected:
+    void DoSetup() override;
+    void DoRun() override;
+    void Transmit(Ptr<WifiMac> mac,
+                  uint8_t phyId,
+                  WifiConstPsduMap psduMap,
+                  WifiTxVector txVector,
+                  double txPowerW) override;
+
+    /**
+     * Check that the simulation produced the expected results.
+     */
+    void CheckResults();
+
+    /**
+     * Check that the AP MLD considers the correct Power Management mode for the links setup
+     * with the given non-AP MLD. This method is intended to be called shortly after ML setup.
+     *
+     * \param address a link address of the given non-AP MLD
+     */
+    void CheckPmModeAfterAssociation(const Mac48Address& address);
+
+    /**
+     * Check that appropriate actions are taken by the AP MLD transmitting an EML Operating Mode
+     * Notification response frame to an EMLSR client on the given link.
+     *
+     * \param mpdu the MPDU carrying the EML Operating Mode Notification frame
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckEmlNotificationFrame(Ptr<const WifiMpdu> mpdu,
+                                   const WifiTxVector& txVector,
+                                   uint8_t linkId);
+
+    /**
+     * Check that appropriate actions are taken by the AP MLD transmitting an initial
+     * Control frame to an EMLSR client on the given link.
+     *
+     * \param mpdu the MPDU carrying the MU-RTS TF
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckInitialControlFrame(Ptr<const WifiMpdu> mpdu,
+                                  const WifiTxVector& txVector,
+                                  uint8_t linkId);
+
+    /**
+     * Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing
+     * QoS data frames to EMLSR clients on the given link.
+     *
+     * \param psduMap the PSDU(s) carrying QoS data frames
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckQosFrames(const WifiConstPsduMap& psduMap,
+                        const WifiTxVector& txVector,
+                        uint8_t linkId);
+
+    /**
+     * Check that appropriate actions are taken by the AP MLD receiving a PPDU containing
+     * BlockAck frames from EMLSR clients on the given link.
+     *
+     * \param psduMap the PSDU carrying BlockAck frames
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param phyId the ID of the PHY transmitting the PSDU(s)
+     */
+    void CheckBlockAck(const WifiConstPsduMap& psduMap,
+                       const WifiTxVector& txVector,
+                       uint8_t phyId);
+
+  private:
+    void StartTraffic() override;
+
+    /**
+     * Enable EMLSR mode on the next EMLSR client
+     */
+    void EnableEmlsrMode();
+
+    std::set<uint8_t> m_emlsrLinks; /**< IDs of the links on which EMLSR mode has to be enabled */
+    Time m_emlsrEnabledTime;        //!< when EMLSR mode has been enabled on all EMLSR clients
+    const Time m_fe2to3delay;       /**< time interval between 2nd and 3rd frame exchange sequences
+                                         after the enablement of EMLSR mode */
+    std::size_t m_countQoSframes;   //!< counter for QoS frames (transition delay test)
+    std::size_t m_countBlockAck;    //!< counter for BlockAck frames (transition delay test)
+    Ptr<ListErrorModel> m_errorModel; ///< error rate model to corrupt BlockAck at AP MLD
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test the transmission of UL frames from EMLSR clients.
+ *
+ * This test considers an AP MLD and a non-AP MLD that support EMLSR. The non-AP MLD setups three
+ * links, while the set of EMLSR links is configurable. Block ack agreements (for TID 0) for both
+ * DL and UL directions are established after that the EML Operating Mode Notification frames are
+ * exchanged to enable the EMLSR mode on the EMLSR links. Aux PHYs on the EMLSR client do not
+ * switch link, hence the main PHY will switch link (if needed) when terminating a TXOP.
+ *
+ * It is checked that:
+ *
+ * - Initially, aux PHYs are configured so that they are unable to transmit frames. Before
+ *   generating the packets for the first UL data frame, transmissions on the link where the
+ *   main PHY is operating and on the non-EMLSR link (if any) are blocked. Thus, the first UL data
+ *   frame is held until transmissions on the link where the main PHY is operating are unblocked.
+ *   The first UL data frame is sent by the main PHY without RTS protection. When the data frame
+ *   exchange terminates, the MediumSyncDelay timer is started on the other EMLSR links and the
+ *   CCA ED threshold is set as expected
+ * - If there is a non-EMLSR link, another data frame can be sent concurrently (without protection)
+ *   on the non-EMLSR link
+ * - When the first UL data frame is transmitted, we make the aux PHYs on the EMLSR client capable
+ *   of transmitting, we block transmissions on the link where the main PHY is operating and
+ *   generate new UL packets, which will then be transmitted on a link where an aux PHY is
+ *   operating. Thus, the aux PHY transmits an RTS frame and the main PHY will take over and
+ *   transmit the second UL data frame. We check that, while the link on which the main PHY was
+ *   operating is blocked because another EMLSR link is being used, new backoff values for that
+ *   link are generated if and only if the QosTxop::GenerateBackoffIfTxopWithoutTx attribute is
+ *   true; otherwise, a new backoff value is generated when the link is unblocked.
+ * - When the exchange of the second UL data frame terminates, we make the aux PHY unable to
+ *   transmit, block transmissions on the non-EMLSR link (if any) and generate some more UL
+ *   packets, which will then be transmitted by the main PHY. However, a MediumSyncDelay timer
+ *   is now running on the link where the main PHY is operating, hence transmissions are protected
+ *   by an RTS frame. We install a post reception error model on the AP MLD so that all RTS frames
+ *   sent by the EMLSR client are not received by the AP MLD. We check that the EMLSR client makes
+ *   at most the configured max number of transmission attempts and that another UL data frame is
+ *   sent once the MediumSyncDelay timer is expired. We also check that the TX width of the RTS
+ *   frames and the UL data frame equal the channel width used by the main PHY.
+ * - We check that no issue arises in case an aux PHY sends an RTS frame but the CTS response is
+ *   not transmitted successfully. Specifically, we check that the main PHY is completing the
+ *   channel switch when the (unsuccessful) reception of the CTS ends and that a new RTS/CTS
+ *   exchange is carried out to protect the transmission of the last data frame.
+ */
+class EmlsrUlTxopTest : public EmlsrOperationsTestBase
+{
+  public:
+    /**
+     * Parameters for the EMLSR UL TXOP test
+     */
+    struct Params
+    {
+        std::set<uint8_t>
+            linksToEnableEmlsrOn;       //!< IDs of links on which EMLSR mode should be enabled
+        uint16_t channelWidth;          //!< width (MHz) of the channels used by MLDs
+        uint16_t auxPhyChannelWidth;    //!< max width (MHz) supported by aux PHYs
+        Time mediumSyncDuration;        //!< duration of the MediumSyncDelay timer
+        uint8_t msdMaxNTxops;           //!< Max number of TXOPs that an EMLSR client is allowed
+                                        //!< to attempt to initiate while the MediumSyncDelay
+                                        //!< timer is running (zero indicates no limit)
+        bool genBackoffIfTxopWithoutTx; //!< whether the backoff should be invoked when the AC
+                                        //!< gains the right to start a TXOP but it does not
+                                        //!< transmit any frame
+    };
+
+    /**
+     * Constructor
+     *
+     * \param params parameters for the EMLSR UL TXOP test
+     */
+    EmlsrUlTxopTest(const Params& params);
+    ~EmlsrUlTxopTest() override = default;
+
+  protected:
+    void DoSetup() override;
+    void DoRun() override;
+    void Transmit(Ptr<WifiMac> mac,
+                  uint8_t phyId,
+                  WifiConstPsduMap psduMap,
+                  WifiTxVector txVector,
+                  double txPowerW) override;
+
+    /**
+     * Check that the simulation produced the expected results.
+     */
+    void CheckResults();
+
+    /**
+     * Check that appropriate actions are taken by the EMLSR client when transmitting an RTS
+     * frame on the given link.
+     *
+     * \param mpdu the MPDU carrying the RTS frame
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckRtsFrames(Ptr<const WifiMpdu> mpdu, const WifiTxVector& txVector, uint8_t linkId);
+
+    /**
+     * Check that appropriate actions are taken by the EMLSR client when receiving a CTS
+     * frame on the given link.
+     *
+     * \param mpdu the MPDU carrying the CTS frame
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckCtsFrames(Ptr<const WifiMpdu> mpdu, const WifiTxVector& txVector, uint8_t linkId);
+
+    /**
+     * Check that appropriate actions are taken when an MLD transmits a PPDU containing
+     * QoS data frames on the given link.
+     *
+     * \param psduMap the PSDU(s) carrying QoS data frames
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckQosFrames(const WifiConstPsduMap& psduMap,
+                        const WifiTxVector& txVector,
+                        uint8_t linkId);
+
+    /**
+     * Check that appropriate actions are taken when an MLD transmits a PPDU containing
+     * BlockAck frames on the given link.
+     *
+     * \param psduMap the PSDU carrying BlockAck frames
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckBlockAck(const WifiConstPsduMap& psduMap,
+                       const WifiTxVector& txVector,
+                       uint8_t linkId);
+
+  private:
+    void StartTraffic() override;
+
+    /**
+     * Callback invoked when a new backoff value is generated by the EMLSR client.
+     *
+     * \param backoff the generated backoff value
+     * \param linkId the ID of the link for which the backoff value has been generated
+     */
+    void BackoffGenerated(uint32_t backoff, uint8_t linkId);
+
+    std::set<uint8_t> m_emlsrLinks; /**< IDs of the links on which EMLSR mode has to be enabled */
+    uint16_t m_channelWidth;        //!< width (MHz) of the channels used by MLDs
+    uint16_t m_auxPhyChannelWidth;  //!< max width (MHz) supported by aux PHYs
+    Time m_mediumSyncDuration;      //!< duration of the MediumSyncDelay timer
+    uint8_t m_msdMaxNTxops;         //!< Max number of TXOPs that an EMLSR client is allowed
+                                    //!< to attempt to initiate while the MediumSyncDelay
+                                    //!< timer is running (zero indicates no limit)
+    std::optional<uint8_t> m_nonEmlsrLink; //!< ID of the non-EMLSR link (if any)
+    Time m_emlsrEnabledTime;              //!< when EMLSR mode has been enabled on all EMLSR clients
+    Time m_firstUlPktsGenTime;            //!< generation time of the first two UL packets
+    const Time m_unblockMainPhyLinkDelay; //!< delay between the time the first two UL packets are
+                                          //!< generated and the time transmissions are unblocked
+                                          //!< on the link where the main PHY is operating on
+    Time m_lastMsdExpiryTime;             //!< expiry time of the last MediumSyncDelay timer
+    bool m_checkBackoffStarted;           //!< whether we are checking the generated backoff values
+    std::optional<Time> m_backoffEndTime; //!< expected backoff end time on main PHY link
+    Ptr<ListErrorModel> m_errorModel;     ///< error rate model to corrupt packets
+    std::size_t m_countQoSframes;         //!< counter for QoS frames
+    std::size_t m_countBlockAck;          //!< counter for BlockAck frames
+    std::size_t m_countRtsframes;         //!< counter for RTS frames
+    bool m_genBackoffIfTxopWithoutTx;     //!< whether the backoff should be invoked when the AC
+                                          //!< gains the right to start a TXOP but it does not
+                                          //!< transmit any frame
+    std::optional<bool> m_corruptCts;     //!< whether the transmitted CTS must be corrupted
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test the switching of PHYs on EMLSR clients.
+ *
+ * An AP MLD and an EMLSR client setup 3 links, on which EMLSR mode is enabled. The AP MLD
+ * transmits 4 QoS data frames (one after another, each protected by ICF):
+ *
+ * - the first one on the link used for ML setup, hence no PHY switch occurs
+ * - the second one on another link, thus causing the main PHY to switch link
+ * - the third one on the remaining link, thus causing the main PHY to switch link again
+ * - the fourth one on the link used for ML setup; if the aux PHYs switches link, there is
+ *   one aux PHY listening on such a link and the main PHY switches to this link, otherwise
+ *   no PHY is listening on such a link and there is no response to the ICF sent by the AP MLD
+ */
+class EmlsrLinkSwitchTest : public EmlsrOperationsTestBase
+{
+  public:
+    /**
+     * Parameters for the EMLSR link switching test
+     */
+    struct Params
+    {
+        bool
+            switchAuxPhy; //!< whether AUX PHY should switch channel to operate on the link on which
+                          //!<  the Main PHY was operating before moving to the link of the Aux PHY
+        bool resetCamState; //!< whether to reset the state of the ChannelAccessManager associated
+                            //!< with the link on which the main PHY has just switched to
+        uint16_t auxPhyMaxChWidth; //!< max channel width (MHz) supported by aux PHYs
+    };
+
+    /**
+     * Constructor
+     *
+     * \param params parameters for the EMLSR link switching test
+     */
+    EmlsrLinkSwitchTest(const Params& params);
+
+    ~EmlsrLinkSwitchTest() override = default;
+
+  protected:
+    void DoSetup() override;
+    void DoRun() override;
+    void Transmit(Ptr<WifiMac> mac,
+                  uint8_t phyId,
+                  WifiConstPsduMap psduMap,
+                  WifiTxVector txVector,
+                  double txPowerW) override;
+
+    /**
+     * Check that the simulation produced the expected results.
+     */
+    void CheckResults();
+
+    /**
+     * Check that the Main PHY (and possibly the Aux PHY) correctly switches channel when the
+     * reception of an ICF ends.
+     *
+     * \param psduMap the PSDU carrying the MU-RTS TF
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckInitialControlFrame(const WifiConstPsduMap& psduMap,
+                                  const WifiTxVector& txVector,
+                                  uint8_t linkId);
+
+    /**
+     * Check that appropriate actions are taken by the AP MLD transmitting a PPDU containing
+     * QoS data frames to the EMLSR client on the given link.
+     *
+     * \param psduMap the PSDU(s) carrying QoS data frames
+     * \param txVector the TXVECTOR used to send the PPDU
+     * \param linkId the ID of the given link
+     */
+    void CheckQosFrames(const WifiConstPsduMap& psduMap,
+                        const WifiTxVector& txVector,
+                        uint8_t linkId);
+
+  private:
+    bool m_switchAuxPhy;  /**< whether AUX PHY should switch channel to operate on the link on which
+                               the Main PHY was operating before moving to the link of Aux PHY */
+    bool m_resetCamState; /**< whether to reset the state of the ChannelAccessManager associated
+                               with the link on which the main PHY has just switched to */
+    uint16_t m_auxPhyMaxChWidth;  //!< max channel width (MHz) supported by aux PHYs
+    std::size_t m_countQoSframes; //!< counter for QoS data frames
+    std::size_t m_txPsdusPos;     //!< a position in the vector of TX PSDUs
+};
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief wifi EMLSR Test Suite
+ */
+class WifiEmlsrTestSuite : public TestSuite
+{
+  public:
+    WifiEmlsrTestSuite();
+};
+
+#endif /* WIFI_EMLSR_TEST_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-mac-ofdma-test.cc ns-3.41/src/wifi/test/wifi-mac-ofdma-test.cc
--- ns-3.40/src/wifi/test/wifi-mac-ofdma-test.cc	2023-09-27 13:30:37.922839900 -0700
+++ ns-3.41/src/wifi/test/wifi-mac-ofdma-test.cc	2024-02-09 14:44:08.613795300 -0800
@@ -100,11 +100,12 @@
             .SetParent<MultiUserScheduler>()
             .SetGroupName("Wifi")
             .AddConstructor<TestMultiUserScheduler>()
-            .AddAttribute("ModulationClass",
-                          "Modulation class for DL MU PPDUs and TB PPDUs.",
-                          EnumValue(WIFI_MOD_CLASS_HE),
-                          MakeEnumAccessor(&TestMultiUserScheduler::m_modClass),
-                          MakeEnumChecker(WIFI_MOD_CLASS_HE, "HE", WIFI_MOD_CLASS_EHT, "EHT"));
+            .AddAttribute(
+                "ModulationClass",
+                "Modulation class for DL MU PPDUs and TB PPDUs.",
+                EnumValue(WIFI_MOD_CLASS_HE),
+                MakeEnumAccessor<WifiModulationClass>(&TestMultiUserScheduler::m_modClass),
+                MakeEnumChecker(WIFI_MOD_CLASS_HE, "HE", WIFI_MOD_CLASS_EHT, "EHT"));
     return tid;
 }
 
@@ -186,7 +187,8 @@
         m_txParams.Clear();
         // set the TXVECTOR used to send the Trigger Frame
         m_txParams.m_txVector =
-            m_apMac->GetWifiRemoteStationManager()->GetRtsTxVector(m_triggerHdr.GetAddr1());
+            m_apMac->GetWifiRemoteStationManager()->GetRtsTxVector(m_triggerHdr.GetAddr1(),
+                                                                   m_allowedWidth);
 
         if (!GetHeFem(SINGLE_LINK_OP_ID)->TryAddMpdu(item, m_txParams, m_availableTime) ||
             (m_availableTime != Time::Min() &&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-mlo-test.cc ns-3.41/src/wifi/test/wifi-mlo-test.cc
--- ns-3.40/src/wifi/test/wifi-mlo-test.cc	2023-09-27 13:30:37.926839800 -0700
+++ ns-3.41/src/wifi/test/wifi-mlo-test.cc	2024-02-09 14:44:08.613795300 -0800
@@ -613,7 +613,7 @@
 MultiLinkOperationsTestBase::DoSetup()
 {
     RngSeedManager::SetSeed(1);
-    RngSeedManager::SetRun(2);
+    RngSeedManager::SetRun(3);
     int64_t streamNumber = 30;
 
     NodeContainer wifiApNode;
@@ -932,12 +932,14 @@
     MultiLinkOperationsTestBase::DoSetup();
 
     m_staMacs[0]->SetAttribute("ActiveProbing", BooleanValue(m_scanType == WifiScanType::ACTIVE));
-    m_apMac->GetEhtConfiguration()->SetAttribute("TidToLinkMappingNegSupport",
-                                                 EnumValue(m_apNegSupport));
+    m_apMac->GetEhtConfiguration()->SetAttribute(
+        "TidToLinkMappingNegSupport",
+        EnumValue(static_cast<WifiTidToLinkMappingNegSupport>(m_apNegSupport)));
     // For non-AP MLD, it does not make sense to set the negotiation type to 0 (unless the AP MLD
     // also advertises 0) or 1 (the AP MLD is discarded if it advertises a support of 3)
     auto staEhtConfig = m_staMacs[0]->GetEhtConfiguration();
-    staEhtConfig->SetAttribute("TidToLinkMappingNegSupport", EnumValue(3));
+    staEhtConfig->SetAttribute("TidToLinkMappingNegSupport",
+                               EnumValue(WIFI_TID_TO_LINK_MAPPING_ANY_LINK_SET));
     staEhtConfig->SetAttribute("TidToLinkMappingDl", StringValue(m_dlTidLinkMappingStr));
     staEhtConfig->SetAttribute("TidToLinkMappingUl", StringValue(m_ulTidLinkMappingStr));
 
@@ -2422,6 +2424,7 @@
     std::vector<PacketSocketAddress> m_sockets; ///< packet socket addresses for STAs
     std::size_t m_nPackets;                     ///< number of application packets to generate
     std::size_t m_blockAckCount{0};             ///< transmitted BlockAck counter
+    std::size_t m_tfCount{0};                   ///< transmitted Trigger Frame counter
     // std::size_t m_blockAckReqCount{0};     ///< transmitted BlockAckReq counter
     std::map<AddrSeqNoPair, std::size_t> m_inflightCount; ///< max number of simultaneous
                                                           ///< transmissions of each data frame
@@ -2570,6 +2573,13 @@
                         GetApplication(m_sockets[i], m_nPackets, 450, txDuration, i * 4));
                 }
             }
+            if (++m_tfCount == m_staMacs[0]->GetSetupLinkIds().size())
+            {
+                // a TF has been sent on all the setup links, we can now disable UL OFDMA
+                auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
+                NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "Expected an aggregated MU scheduler");
+                muScheduler->SetAttribute("EnableUlOfdma", BooleanValue(false));
+            }
             break;
         default:;
         }
@@ -2778,13 +2788,12 @@
     }
 
     // aggregate MU scheduler
-    auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>(
-        "EnableUlOfdma",
-        BooleanValue(m_muTrafficPattern == WifiMuTrafficPattern::UL_MU),
-        "EnableBsrp",
-        BooleanValue(false),
-        "UlPsduSize",
-        UintegerValue(2000));
+    auto muScheduler = CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma",
+                                                                        BooleanValue(false),
+                                                                        "EnableBsrp",
+                                                                        BooleanValue(false),
+                                                                        "UlPsduSize",
+                                                                        UintegerValue(2000));
     m_apMac->AggregateObject(muScheduler);
 
     // install post reception error model on all devices
@@ -2861,6 +2870,7 @@
         Simulator::Schedule(m_nStations * MilliSeconds(50), [this]() {
             auto muScheduler = m_apMac->GetObject<MultiUserScheduler>();
             NS_TEST_ASSERT_MSG_NE(muScheduler, nullptr, "Expected an aggregated MU scheduler");
+            muScheduler->SetAttribute("EnableUlOfdma", BooleanValue(true));
             muScheduler->SetAccessReqInterval(MilliSeconds(3));
             // channel access is requested only once
             muScheduler->SetAccessReqInterval(Seconds(0));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-phy-cca-test.cc ns-3.41/src/wifi/test/wifi-phy-cca-test.cc
--- ns-3.40/src/wifi/test/wifi-phy-cca-test.cc	2023-09-27 13:30:37.926839800 -0700
+++ ns-3.41/src/wifi/test/wifi-phy-cca-test.cc	2024-02-09 14:44:08.613795300 -0800
@@ -812,7 +812,7 @@
     std::size_t
         m_numSignalGenerators; ///< The number of non-wifi signals generators needed for the test
 
-    std::unique_ptr<CcaTestPhyListener>
+    std::shared_ptr<CcaTestPhyListener>
         m_rxPhyStateListener; ///< Listener for PHY state transitions
 
     uint16_t m_frequency;    ///< Operating frequency in MHz
@@ -1018,7 +1018,7 @@
     rxDev->SetVhtConfiguration(vhtConfiguration);
     m_rxPhy = CreateObject<SpectrumWifiPhy>();
     m_rxPhyStateListener = std::make_unique<CcaTestPhyListener>();
-    m_rxPhy->RegisterListener(m_rxPhyStateListener.get());
+    m_rxPhy->RegisterListener(m_rxPhyStateListener);
     Ptr<InterferenceHelper> rxInterferenceHelper = CreateObject<InterferenceHelper>();
     m_rxPhy->SetInterferenceHelper(rxInterferenceHelper);
     Ptr<ErrorRateModel> rxErrorModel = CreateObject<NistErrorRateModel>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-phy-ofdma-test.cc ns-3.41/src/wifi/test/wifi-phy-ofdma-test.cc
--- ns-3.40/src/wifi/test/wifi-phy-ofdma-test.cc	2023-09-27 13:30:37.926839800 -0700
+++ ns-3.41/src/wifi/test/wifi-phy-ofdma-test.cc	2024-02-09 14:44:08.617795200 -0800
@@ -822,13 +822,13 @@
                                                                       WIFI_PHY_BAND_5GHZ));
 
     m_phyAp->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta1->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta2->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta3->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
 
     Simulator::Schedule(Seconds(0.5), &TestDlOfdmaPhyTransmission::ResetResults, this);
 
@@ -1922,7 +1922,7 @@
                                                                       WIFI_STANDARD_80211ax,
                                                                       WIFI_PHY_BAND_5GHZ));
     m_phyAp->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, WIFI_PHY_BAND_5GHZ, 0});
     m_phyAp->SetDevice(apDev);
     m_phyAp->TraceConnectWithoutContext("TxPpduUid",
                                         MakeCallback(&TestUlOfdmaPpduUid::TxPpduAp, this));
@@ -1944,7 +1944,7 @@
     m_phySta1->AddChannel(spectrumChannel);
     m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta1->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta1->SetDevice(sta1Dev);
     m_phySta1->TraceConnectWithoutContext("TxPpduUid",
                                           MakeCallback(&TestUlOfdmaPpduUid::TxPpduSta1, this));
@@ -1964,7 +1964,7 @@
     m_phySta2->AddChannel(spectrumChannel);
     m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta2->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta2->SetDevice(sta2Dev);
     m_phySta2->TraceConnectWithoutContext("TxPpduUid",
                                           MakeCallback(&TestUlOfdmaPpduUid::TxPpduSta2, this));
@@ -3180,7 +3180,7 @@
     Ptr<OfdmaSpectrumWifiPhy> m_phySta2; ///< PHY of STA 2
     Ptr<OfdmaSpectrumWifiPhy> m_phySta3; ///< PHY of STA 3
 
-    std::unique_ptr<OfdmaTestPhyListener>
+    std::shared_ptr<OfdmaTestPhyListener>
         m_apPhyStateListener; ///< listener for AP PHY state transitions
 
     Ptr<WaveformGenerator> m_phyInterferer; ///< PHY of interferer
@@ -3735,7 +3735,7 @@
     Ptr<ConstantPositionMobilityModel> apMobility = CreateObject<ConstantPositionMobilityModel>();
     m_phyAp->SetMobility(apMobility);
     m_apPhyStateListener = std::make_unique<OfdmaTestPhyListener>();
-    m_phyAp->RegisterListener(m_apPhyStateListener.get());
+    m_phyAp->RegisterListener(m_apPhyStateListener);
     apDev->SetPhy(m_phyAp);
     apMac->SetWifiPhys({m_phyAp});
     apNode->AggregateObject(apMobility);
@@ -4124,13 +4124,13 @@
                                                                       WIFI_PHY_BAND_5GHZ));
 
     m_phyAp->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta1->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta2->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
     m_phySta3->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, m_channelWidth, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, m_channelWidth, WIFI_PHY_BAND_5GHZ, 0});
 
     Time delay = Seconds(0.0);
     Simulator::Schedule(delay, &TestUlOfdmaPhyTransmission::Reset, this);
@@ -5095,7 +5095,7 @@
                                                                       WIFI_PHY_BAND_5GHZ));
 
     m_phyAp->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, WIFI_PHY_BAND_5GHZ, 0});
     m_phyAp->SetReceiveOkCallback(MakeCallback(&TestPhyPaddingExclusion::RxSuccess, this));
     m_phyAp->SetReceiveErrorCallback(MakeCallback(&TestPhyPaddingExclusion::RxFailure, this));
     Ptr<ConstantPositionMobilityModel> apMobility = CreateObject<ConstantPositionMobilityModel>();
@@ -5119,7 +5119,7 @@
     m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta1->AssignStreams(streamNumber);
     m_phySta1->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, WIFI_PHY_BAND_5GHZ, 0});
     Ptr<ConstantPositionMobilityModel> sta1Mobility = CreateObject<ConstantPositionMobilityModel>();
     m_phySta1->SetMobility(sta1Mobility);
     sta1Dev->SetPhy(m_phySta1);
@@ -5140,7 +5140,7 @@
     m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta2->AssignStreams(streamNumber);
     m_phySta2->SetOperatingChannel(
-        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, (int)(WIFI_PHY_BAND_5GHZ), 0});
+        WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, WIFI_PHY_BAND_5GHZ, 0});
     Ptr<ConstantPositionMobilityModel> sta2Mobility = CreateObject<ConstantPositionMobilityModel>();
     m_phySta2->SetMobility(sta2Mobility);
     sta2Dev->SetPhy(m_phySta2);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-primary-channels-test.cc ns-3.41/src/wifi/test/wifi-primary-channels-test.cc
--- ns-3.40/src/wifi/test/wifi-primary-channels-test.cc	2023-09-27 13:30:37.926839800 -0700
+++ ns-3.41/src/wifi/test/wifi-primary-channels-test.cc	2024-02-09 14:44:08.617795200 -0800
@@ -390,7 +390,7 @@
                 "WaitBeaconTimeout",
                 TimeValue(MicroSeconds(102400))); // same as BeaconInterval
 
-    TupleValue<UintegerValue, UintegerValue, EnumValue, UintegerValue> channelValue;
+    TupleValue<UintegerValue, UintegerValue, EnumValue<WifiPhyBand>, UintegerValue> channelValue;
 
     // Each BSS uses a distinct primary20 channel
     for (uint8_t bss = 0; bss < m_nBss; bss++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wifi/test/wifi-test.cc ns-3.41/src/wifi/test/wifi-test.cc
--- ns-3.40/src/wifi/test/wifi-test.cc	2023-09-27 13:30:37.926839800 -0700
+++ ns-3.41/src/wifi/test/wifi-test.cc	2024-02-09 14:44:08.617795200 -0800
@@ -328,7 +328,7 @@
 InterferenceHelperSequenceTest::SwitchCh(Ptr<WifiNetDevice> dev)
 {
     Ptr<WifiPhy> p = dev->GetPhy();
-    p->SetOperatingChannel(WifiPhy::ChannelTuple{40, 0, (int)(WIFI_PHY_BAND_5GHZ), 0});
+    p->SetOperatingChannel(WifiPhy::ChannelTuple{40, 0, WIFI_PHY_BAND_5GHZ, 0});
 }
 
 Ptr<Node>
@@ -1772,8 +1772,8 @@
 void
 Bug2831TestCase::ChangeSupportedChannelWidth()
 {
-    m_apPhy->SetOperatingChannel(WifiPhy::ChannelTuple{38, 40, (int)(WIFI_PHY_BAND_5GHZ), 0});
-    m_staPhy->SetOperatingChannel(WifiPhy::ChannelTuple{38, 40, (int)(WIFI_PHY_BAND_5GHZ), 0});
+    m_apPhy->SetOperatingChannel(WifiPhy::ChannelTuple{38, 40, WIFI_PHY_BAND_5GHZ, 0});
+    m_staPhy->SetOperatingChannel(WifiPhy::ChannelTuple{38, 40, WIFI_PHY_BAND_5GHZ, 0});
 }
 
 void
@@ -1843,7 +1843,7 @@
     m_apPhy->SetMobility(apMobility);
     m_apPhy->SetDevice(apDev);
     m_apPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    m_apPhy->SetOperatingChannel(WifiPhy::ChannelTuple{36, 20, (int)(WIFI_PHY_BAND_5GHZ), 0});
+    m_apPhy->SetOperatingChannel(WifiPhy::ChannelTuple{36, 20, WIFI_PHY_BAND_5GHZ, 0});
 
     ObjectFactory mac;
     mac.SetTypeId("ns3::ApWifiMac");
@@ -1884,7 +1884,7 @@
     m_staPhy->SetMobility(staMobility);
     m_staPhy->SetDevice(apDev);
     m_staPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    m_staPhy->SetOperatingChannel(WifiPhy::ChannelTuple{36, 20, (int)(WIFI_PHY_BAND_5GHZ), 0});
+    m_staPhy->SetOperatingChannel(WifiPhy::ChannelTuple{36, 20, WIFI_PHY_BAND_5GHZ, 0});
 
     mac.SetTypeId("ns3::StaWifiMac");
     auto staMac = mac.Create<WifiMac>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/CMakeLists.txt ns-3.41/src/wimax/CMakeLists.txt
--- ns-3.40/src/wimax/CMakeLists.txt	2023-09-27 13:30:37.926839800 -0700
+++ ns-3.41/src/wimax/CMakeLists.txt	2024-02-09 14:44:08.617795200 -0800
@@ -100,11 +100,8 @@
   LIBNAME wimax
   SOURCE_FILES ${source_files}
   HEADER_FILES ${header_files}
-  LIBRARIES_TO_LINK
-    ${libnetwork}
-    ${libinternet}
-    ${libpropagation}
-    ${libmobility}
+  LIBRARIES_TO_LINK ${libinternet}
+                    ${libpropagation}
   TEST_SOURCES
     test/mac-messages-test.cc
     test/phy-test.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/bs-net-device.h ns-3.41/src/wimax/model/bs-net-device.h
--- ns-3.40/src/wimax/model/bs-net-device.h	2023-09-27 13:30:37.926839800 -0700
+++ ns-3.41/src/wimax/model/bs-net-device.h	2024-02-09 14:44:08.621795200 -0800
@@ -492,7 +492,7 @@
      * \see class CallBackTraceSource
      */
     TracedCallback<Ptr<const Packet>>
-        m_bsPromiscRxTrace; ///< base station promiscious receive trace callback
+        m_bsPromiscRxTrace; ///< base station promiscuous receive trace callback
 
     /**
      * The trace source fired for packets successfully received by the device
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/ipcs-classifier-record.cc ns-3.41/src/wimax/model/ipcs-classifier-record.cc
--- ns-3.40/src/wimax/model/ipcs-classifier-record.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/ipcs-classifier-record.cc	2024-02-09 14:44:08.621795200 -0800
@@ -332,7 +332,7 @@
     ProtocolTlvValue protoVal;
     for (auto iter = m_protocol.begin(); iter != m_protocol.end(); ++iter)
     {
-        protoVal.Add((*iter));
+        protoVal.Add(*iter);
     }
 
     PortRangeTlvValue portValueSrc;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/service-flow-manager.cc ns-3.41/src/wimax/model/service-flow-manager.cc
--- ns-3.40/src/wimax/model/service-flow-manager.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/service-flow-manager.cc	2024-02-09 14:44:08.621795200 -0800
@@ -96,7 +96,7 @@
         {
             if ((*iter)->CheckClassifierMatch(srcAddress, dstAddress, srcPort, dstPort, proto))
             {
-                return (*iter);
+                return *iter;
             }
         }
     }
@@ -110,7 +110,7 @@
     {
         if ((*iter)->GetSfid() == sfid)
         {
-            return (*iter);
+            return *iter;
         }
     }
 
@@ -125,7 +125,7 @@
     {
         if ((*iter)->GetCid() == cid.GetIdentifier())
         {
-            return (*iter);
+            return *iter;
         }
     }
 
@@ -142,7 +142,7 @@
         if (((*iter)->GetSchedulingType() == schedulingType) ||
             (schedulingType == ServiceFlow::SF_TYPE_ALL))
         {
-            tmpServiceFlows.push_back((*iter));
+            tmpServiceFlows.push_back(*iter);
         }
     }
     return tmpServiceFlows;
@@ -180,7 +180,7 @@
     {
         if (!(*iter)->GetIsEnabled())
         {
-            return (*iter);
+            return *iter;
         }
     }
     return nullptr;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/ss-net-device.cc ns-3.41/src/wimax/model/ss-net-device.cc
--- ns-3.40/src/wimax/model/ss-net-device.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/ss-net-device.cc	2024-02-09 14:44:08.625795400 -0800
@@ -54,7 +54,7 @@
 Time
 SubscriberStationNetDevice::GetDefaultLostDlMapInterval()
 {
-    return (MicroSeconds(500000));
+    return MicroSeconds(500000);
 }
 
 TypeId
@@ -745,7 +745,7 @@
                           "Error while enqueuing  packet: incorrect header type");
 
             GrantManagementSubheader grantMgmntSubhdr;
-            grantMgmntSubhdr.SetPm(true);
+            grantMgmntSubhdr.SetPm(1);
             packet->AddHeader(grantMgmntSubhdr);
         }
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/ss-record.cc ns-3.41/src/wimax/model/ss-record.cc
--- ns-3.40/src/wimax/model/ss-record.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/ss-record.cc	2024-02-09 14:44:08.625795400 -0800
@@ -238,7 +238,7 @@
         if (((*iter)->GetSchedulingType() == schedulingType) ||
             (schedulingType == ServiceFlow::SF_TYPE_ALL))
         {
-            tmpServiceFlows.push_back((*iter));
+            tmpServiceFlows.push_back(*iter);
         }
     }
     return tmpServiceFlows;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/wimax-connection.cc ns-3.41/src/wimax/model/wimax-connection.cc
--- ns-3.40/src/wimax/model/wimax-connection.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/wimax-connection.cc	2024-02-09 14:44:08.625795400 -0800
@@ -43,7 +43,7 @@
                             .AddAttribute("Type",
                                           "Connection type",
                                           EnumValue(Cid::INITIAL_RANGING),
-                                          MakeEnumAccessor(&WimaxConnection::GetType),
+                                          MakeEnumAccessor<Cid::Type>(&WimaxConnection::GetType),
                                           MakeEnumChecker(Cid::BROADCAST,
                                                           "Broadcast",
                                                           Cid::INITIAL_RANGING,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/wimax-mac-header.cc ns-3.41/src/wimax/model/wimax-mac-header.cc
--- ns-3.40/src/wimax/model/wimax-mac-header.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/wimax-mac-header.cc	2024-02-09 14:44:08.625795400 -0800
@@ -325,7 +325,7 @@
 bool
 GenericMacHeader::check_hcs() const
 {
-    return (m_hcs == c_hcs);
+    return m_hcs == c_hcs;
 }
 
 // ----------------------------------------------------------------------------------------------------------
@@ -527,7 +527,7 @@
 bool
 BandwidthRequestHeader::check_hcs() const
 {
-    return (m_hcs == c_hcs);
+    return m_hcs == c_hcs;
 }
 
 // ----------------------------------------------------------------------------------------------------------
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/wimax-net-device.h ns-3.41/src/wimax/model/wimax-net-device.h
--- ns-3.40/src/wimax/model/wimax-net-device.h	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/wimax-net-device.h	2024-02-09 14:44:08.625795400 -0800
@@ -211,7 +211,7 @@
 
     /**
      * Set the connection manager of the device
-     * \param  connectionManager the commection manager to be installed in the device
+     * \param  connectionManager the connection manager to be installed in the device
      */
     virtual void SetConnectionManager(Ptr<ConnectionManager> connectionManager);
 
@@ -421,13 +421,13 @@
                   const Address& dest,
                   uint16_t protocolNumber) override;
     /**
-     * Set promiscious receive callback function
-     * \param cb the promiscious mode callback
+     * Set promiscuous receive callback function
+     * \param cb the promiscuous mode callback
      */
     void SetPromiscReceiveCallback(PromiscReceiveCallback cb) override;
     /**
-     * Get promiscious receive callback function
-     * \returns the promiscious mode callback
+     * Get promiscuous receive callback function
+     * \returns the promiscuous mode callback
      */
     NetDevice::PromiscReceiveCallback GetPromiscReceiveCallback();
     /**
@@ -470,12 +470,12 @@
     bool IsBridge() const override;
 
     /**
-     * Check if device is promiscious
-     * \returns true if promiscious
+     * Check if device is promiscuous
+     * \returns true if promiscuous
      */
     bool IsPromisc();
     /**
-     * Notify promiscious trace of a packet arrival
+     * Notify promiscuous trace of a packet arrival
      * \param p the packet
      */
     void NotifyPromiscTrace(Ptr<Packet> p);
@@ -527,7 +527,7 @@
     Ptr<Node> m_node;                              ///< the node
     Ptr<WimaxPhy> m_phy;                           ///< the phy
     NetDevice::ReceiveCallback m_forwardUp;        ///< forward up callback function
-    NetDevice::PromiscReceiveCallback m_promiscRx; ///< promiscious receive fcallback function
+    NetDevice::PromiscReceiveCallback m_promiscRx; ///< promiscuous receive callback function
 
     uint32_t m_ifIndex;          ///< IF index
     std::string m_name;          ///< service name
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/model/wimax-tlv.cc ns-3.41/src/wimax/model/wimax-tlv.cc
--- ns-3.40/src/wimax/model/wimax-tlv.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/model/wimax-tlv.cc	2024-02-09 14:44:08.625795400 -0800
@@ -966,7 +966,7 @@
 {
     for (auto iter = m_protocol->begin(); iter != m_protocol->end(); ++iter)
     {
-        i.WriteU8((*iter));
+        i.WriteU8(*iter);
     }
 }
 
@@ -1006,7 +1006,7 @@
     auto tmp = new ProtocolTlvValue();
     for (auto iter = m_protocol->begin(); iter != m_protocol->end(); ++iter)
     {
-        tmp->Add((*iter));
+        tmp->Add(*iter);
     }
     return tmp;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/src/wimax/test/phy-test.cc ns-3.41/src/wimax/test/phy-test.cc
--- ns-3.40/src/wimax/test/phy-test.cc	2023-09-27 13:30:37.930839800 -0700
+++ ns-3.41/src/wimax/test/phy-test.cc	2024-02-09 14:44:08.625795400 -0800
@@ -103,7 +103,7 @@
         }
     }
     Simulator::Destroy();
-    return (false); // Test was ok, all the SSs are registered
+    return false; // Test was ok, all the SSs are registered
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/test.py ns-3.41/test.py
--- ns-3.40/test.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/test.py	2024-02-09 14:44:08.629795300 -0800
@@ -18,6 +18,7 @@
 import argparse
 import fnmatch
 import os
+import queue
 import shutil
 import signal
 import subprocess
@@ -26,29 +27,44 @@
 import time
 import xml.etree.ElementTree as ET
 
-
 from utils import get_list_from_file
 
 # Global variable
 args = None
 
 # imported from waflib Logs
-colors_lst={'USE':True,'BOLD':'\x1b[01;1m','RED':'\x1b[01;31m','GREEN':'\x1b[32m','YELLOW':'\x1b[33m','PINK':'\x1b[35m','BLUE':'\x1b[01;34m','CYAN':'\x1b[36m','GREY':'\x1b[37m','NORMAL':'\x1b[0m','cursor_on':'\x1b[?25h','cursor_off':'\x1b[?25l',}
+colors_lst = {
+    "USE": True,
+    "BOLD": "\x1b[01;1m",
+    "RED": "\x1b[01;31m",
+    "GREEN": "\x1b[32m",
+    "YELLOW": "\x1b[33m",
+    "PINK": "\x1b[35m",
+    "BLUE": "\x1b[01;34m",
+    "CYAN": "\x1b[36m",
+    "GREY": "\x1b[37m",
+    "NORMAL": "\x1b[0m",
+    "cursor_on": "\x1b[?25h",
+    "cursor_off": "\x1b[?25l",
+}
+
+
 def get_color(cl):
-    if colors_lst['USE']:
-        return colors_lst.get(cl, '')
-    return ''
+    if colors_lst["USE"]:
+        return colors_lst.get(cl, "")
+    return ""
+
+
 class color_dict(object):
     def __getattr__(self, a):
         return get_color(a)
+
     def __call__(self, a):
         return get_color(a)
+
+
 colors = color_dict()
 
-try:
-    import queue
-except ImportError:
-    import Queue as queue
 #
 # XXX This should really be part of a ns3 command to list the configuration
 # items relative to optional ns-3 pieces.
@@ -63,7 +79,6 @@
     "NS3_ENABLED_MODULES",
     "NS3_ENABLED_CONTRIBUTED_MODULES",
     "NS3_MODULE_PATH",
-    "ENABLE_REAL_TIME",
     "ENABLE_EXAMPLES",
     "ENABLE_TESTS",
     "EXAMPLE_DIRECTORIES",
@@ -78,7 +93,6 @@
     "VALGRIND_FOUND",
 ]
 
-ENABLE_REAL_TIME = False
 ENABLE_EXAMPLES = True
 ENABLE_TESTS = True
 NSCLICK = False
@@ -122,6 +136,7 @@
     "lte-pss-ff-mac-scheduler",
 ]
 
+
 #
 # Parse the examples-to-run file if it exists.
 #
@@ -134,97 +149,96 @@
     python_script_dir,
     example_tests,
     example_names_original,
-    python_tests):
-
+    python_tests,
+):
     # Look for the examples-to-run file exists.
-    if os.path.exists(examples_to_run_path):
+    if not os.path.exists(examples_to_run_path):
+        return
 
-        # Each tuple in the C++ list of examples to run contains
-        #
-        #     (example_name, do_run, do_valgrind_run)
-        #
-        # where example_name is the executable to be run, do_run is a
-        # condition under which to run the example, and do_valgrind_run is
-        # a condition under which to run the example under valgrind.  This
-        # is needed because NSC causes illegal instruction crashes with
-        # some tests when they are run under valgrind.
-        #
-        # Note that the two conditions are Python statements that
-        # can depend on ns3 configuration variables.  For example,
-        # when NSC was in the codebase, we could write:
-        #
-        #     ("tcp-nsc-lfn", "NSC_ENABLED == True", "NSC_ENABLED == False"),
-        #
-        cpp_examples = get_list_from_file(examples_to_run_path, "cpp_examples")
-        for example_name, do_run, do_valgrind_run in cpp_examples:
+    # Each tuple in the C++ list of examples to run contains
+    #
+    #     (example_name, do_run, do_valgrind_run)
+    #
+    # where example_name is the executable to be run, do_run is a
+    # condition under which to run the example, and do_valgrind_run is
+    # a condition under which to run the example under valgrind.  This
+    # is needed because NSC causes illegal instruction crashes with
+    # some tests when they are run under valgrind.
+    #
+    # Note that the two conditions are Python statements that
+    # can depend on ns3 configuration variables.  For example,
+    # when NSC was in the codebase, we could write:
+    #
+    #     ("tcp-nsc-lfn", "NSC_ENABLED == True", "NSC_ENABLED == False"),
+    #
+    cpp_examples = get_list_from_file(examples_to_run_path, "cpp_examples")
+    for example_name, do_run, do_valgrind_run in cpp_examples:
+        # Separate the example name from its arguments.
+        example_name_original = example_name
+        example_name_parts = example_name.split(" ", 1)
+        if len(example_name_parts) == 1:
+            example_name = example_name_parts[0]
+            example_arguments = ""
+        else:
+            example_name = example_name_parts[0]
+            example_arguments = example_name_parts[1]
 
-            # Separate the example name from its arguments.
-            example_name_original = example_name
-            example_name_parts = example_name.split(' ', 1)
-            if len(example_name_parts) == 1:
-                example_name      = example_name_parts[0]
-                example_arguments = ""
-            else:
-                example_name      = example_name_parts[0]
-                example_arguments = example_name_parts[1]
+        # Add the proper prefix and suffix to the example name to
+        # match what is done in the CMakeLists.txt file.
+        example_path = "%s%s-%s%s" % (APPNAME, VERSION, example_name, BUILD_PROFILE_SUFFIX)
 
-            # Add the proper prefix and suffix to the example name to
-            # match what is done in the CMakeLists.txt file.
-            example_path = "%s%s-%s%s" % (APPNAME, VERSION, example_name, BUILD_PROFILE_SUFFIX)
-
-            # Set the full path for the example.
-            example_path = os.path.join(cpp_executable_dir, example_path)
-            example_path += '.exe' if sys.platform == 'win32' else ''
-            example_name = os.path.join(
-                os.path.relpath(cpp_executable_dir, NS3_BUILDDIR),
-                example_name)
-            # Add all of the C++ examples that were built, i.e. found
-            # in the directory, to the list of C++ examples to run.
-            if os.path.exists(example_path):
-                # Add any arguments to the path.
-                if len(example_name_parts) != 1:
-                    example_path = "%s %s" % (example_path, example_arguments)
-                    example_name = "%s %s" % (example_name, example_arguments)
-
-                # Add this example.
-                example_tests.append((example_name, example_path, do_run, do_valgrind_run))
-                example_names_original.append(example_name_original)
+        # Set the full path for the example.
+        example_path = os.path.join(cpp_executable_dir, example_path)
+        example_path += ".exe" if sys.platform == "win32" else ""
+        example_name = os.path.join(os.path.relpath(cpp_executable_dir, NS3_BUILDDIR), example_name)
+        # Add all of the C++ examples that were built, i.e. found
+        # in the directory, to the list of C++ examples to run.
+        if os.path.exists(example_path):
+            # Add any arguments to the path.
+            if len(example_name_parts) != 1:
+                example_path = "%s %s" % (example_path, example_arguments)
+                example_name = "%s %s" % (example_name, example_arguments)
+
+            # Add this example.
+            example_tests.append((example_name, example_path, do_run, do_valgrind_run))
+            example_names_original.append(example_name_original)
+
+    # Each tuple in the Python list of examples to run contains
+    #
+    #     (example_name, do_run)
+    #
+    # where example_name is the Python script to be run and
+    # do_run is a condition under which to run the example.
+    #
+    # Note that the condition is a Python statement that can
+    # depend on ns3 configuration variables.  For example,
+    #
+    #     ("brite-generic-example", "ENABLE_BRITE == True", "False"),
+    #
+    python_examples = get_list_from_file(examples_to_run_path, "python_examples")
+    for example_name, do_run in python_examples:
+        # Separate the example name from its arguments.
+        example_name_parts = example_name.split(" ", 1)
+        if len(example_name_parts) == 1:
+            example_name = example_name_parts[0]
+            example_arguments = ""
+        else:
+            example_name = example_name_parts[0]
+            example_arguments = example_name_parts[1]
 
-        # Each tuple in the Python list of examples to run contains
-        #
-        #     (example_name, do_run)
-        #
-        # where example_name is the Python script to be run and
-        # do_run is a condition under which to run the example.
-        #
-        # Note that the condition is a Python statement that can
-        # depend on ns3 configuration variables.  For example,
-        #
-        #     ("realtime-udp-echo.py", "ENABLE_REAL_TIME == True"),
-        #
-        python_examples = get_list_from_file(examples_to_run_path, "python_examples")
-        for example_name, do_run in python_examples:
-            # Separate the example name from its arguments.
-            example_name_parts = example_name.split(' ', 1)
-            if len(example_name_parts) == 1:
-                example_name      = example_name_parts[0]
-                example_arguments = ""
-            else:
-                example_name      = example_name_parts[0]
-                example_arguments = example_name_parts[1]
+        # Set the full path for the example.
+        example_path = os.path.join(python_script_dir, example_name)
 
-            # Set the full path for the example.
-            example_path = os.path.join(python_script_dir, example_name)
+        # Add all of the Python examples that were found to the
+        # list of Python examples to run.
+        if os.path.exists(example_path):
+            # Add any arguments to the path.
+            if len(example_name_parts) != 1:
+                example_path = "%s %s" % (example_path, example_arguments)
 
-            # Add all of the Python examples that were found to the
-            # list of Python examples to run.
-            if os.path.exists(example_path):
-                # Add any arguments to the path.
-                if len(example_name_parts) != 1:
-                    example_path = "%s %s" % (example_path, example_arguments)
+            # Add this example.
+            python_tests.append((example_path, do_run))
 
-                # Add this example.
-                python_tests.append((example_path, do_run))
 
 #
 # The test suites are going to want to output status.  They are running
@@ -240,61 +254,65 @@
 #
 TMP_OUTPUT_DIR = "testpy-output"
 
+
 def read_test(test):
-    result = test.find('Result').text
-    name = test.find('Name').text
-    if not test.find('Reason') is None:
-        reason = test.find('Reason').text
+    result = test.find("Result").text
+    name = test.find("Name").text
+    if not test.find("Reason") is None:
+        reason = test.find("Reason").text
     else:
-        reason = ''
-    if not test.find('Time') is None:
-        time_real = test.find('Time').get('real')
+        reason = ""
+    if not test.find("Time") is None:
+        time_real = test.find("Time").get("real")
     else:
-        time_real = ''
+        time_real = ""
     return (result, name, reason, time_real)
 
+
 #
 # A simple example of writing a text file with a test result summary.  It is
 # expected that this output will be fine for developers looking for problems.
 #
-def node_to_text(test, f, test_type='Suite'):
+def node_to_text(test, f, test_type="Suite"):
     (result, name, reason, time_real) = read_test(test)
     if reason:
         reason = " (%s)" % reason
 
-    output = "%s: Test %s \"%s\" (%s)%s\n" % (result, test_type, name, time_real, reason)
+    output = '%s: Test %s "%s" (%s)%s\n' % (result, test_type, name, time_real, reason)
     f.write(output)
-    for details in test.findall('FailureDetails'):
+    for details in test.findall("FailureDetails"):
         f.write("    Details:\n")
-        f.write("      Message:   %s\n" % details.find('Message').text)
-        f.write("      Condition: %s\n" % details.find('Condition').text)
-        f.write("      Actual:    %s\n" % details.find('Actual').text)
-        f.write("      Limit:     %s\n" % details.find('Limit').text)
-        f.write("      File:      %s\n" % details.find('File').text)
-        f.write("      Line:      %s\n" % details.find('Line').text)
-    for child in test.findall('Test'):
-        node_to_text(child, f, 'Case')
+        f.write("      Message:   %s\n" % details.find("Message").text)
+        f.write("      Condition: %s\n" % details.find("Condition").text)
+        f.write("      Actual:    %s\n" % details.find("Actual").text)
+        f.write("      Limit:     %s\n" % details.find("Limit").text)
+        f.write("      File:      %s\n" % details.find("File").text)
+        f.write("      Line:      %s\n" % details.find("Line").text)
+    for child in test.findall("Test"):
+        node_to_text(child, f, "Case")
+
 
 def translate_to_text(results_file, text_file):
-    text_file += ('.txt' if '.txt' not in text_file else '')
-    print('Writing results to text file \"%s\"...' % text_file, end='')
+    text_file += ".txt" if ".txt" not in text_file else ""
+    print('Writing results to text file "%s"...' % text_file, end="")
     et = ET.parse(results_file)
 
-    with open(text_file, 'w', encoding='utf-8') as f:
-        for test in et.findall('Test'):
+    with open(text_file, "w", encoding="utf-8") as f:
+        for test in et.findall("Test"):
             node_to_text(test, f)
 
-        for example in et.findall('Example'):
-            result = example.find('Result').text
-            name = example.find('Name').text
-            if not example.find('Time') is None:
-                time_real = example.find('Time').get('real')
+        for example in et.findall("Example"):
+            result = example.find("Result").text
+            name = example.find("Name").text
+            if not example.find("Time") is None:
+                time_real = example.find("Time").get("real")
             else:
-                time_real = ''
-            output = "%s: Example \"%s\" (%s)\n" % (result, name, time_real)
+                time_real = ""
+            output = '%s: Example "%s" (%s)\n' % (result, name, time_real)
             f.write(output)
 
-    print('done.')
+    print("done.")
+
 
 #
 # A simple example of writing an HTML file with a test result summary.  It is
@@ -303,10 +321,10 @@
 # since it will probably grow over time.
 #
 def translate_to_html(results_file, html_file):
-    html_file += ('.html' if '.html' not in html_file else '')
-    print('Writing results to html file %s...' % html_file, end='')
+    html_file += ".html" if ".html" not in html_file else ""
+    print("Writing results to html file %s..." % html_file, end="")
 
-    with open(html_file, 'w', encoding='utf-8') as f:
+    with open(html_file, "w", encoding="utf-8") as f:
         f.write("<html>\n")
         f.write("<body>\n")
         f.write("<center><h1>ns-3 Test Results</h1></center>\n")
@@ -320,7 +338,7 @@
         # Iterate through the test suites
         #
         f.write("<h2>Test Suites</h2>\n")
-        for suite in et.findall('Test'):
+        for suite in et.findall("Test"):
             #
             # For each test suite, get its name, result and execution time info
             #
@@ -333,16 +351,19 @@
             # and print in red.
             #
             if result == "PASS":
-                f.write("<h3 style=\"color:green\">%s: %s (%s)</h3>\n" % (result, name, time))
+                f.write('<h3 style="color:green">%s: %s (%s)</h3>\n' % (result, name, time))
             elif result == "SKIP":
-                f.write("<h3 style=\"color:#ff6600\">%s: %s (%s) (%s)</h3>\n" % (result, name, time, reason))
+                f.write(
+                    '<h3 style="color:#ff6600">%s: %s (%s) (%s)</h3>\n'
+                    % (result, name, time, reason)
+                )
             else:
-                f.write("<h3 style=\"color:red\">%s: %s (%s)</h3>\n" % (result, name, time))
+                f.write('<h3 style="color:red">%s: %s (%s)</h3>\n' % (result, name, time))
 
             #
             # The test case information goes in a table.
             #
-            f.write("<table border=\"1\">\n")
+            f.write('<table border="1">\n')
 
             #
             # The first column of the table has the heading Result
@@ -364,9 +385,9 @@
             if result in ["CRASH", "SKIP", "VALGR"]:
                 f.write("<tr>\n")
                 if result == "SKIP":
-                    f.write("<td style=\"color:#ff6600\">%s</td>\n" % result)
+                    f.write('<td style="color:#ff6600">%s</td>\n' % result)
                 else:
-                    f.write("<td style=\"color:red\">%s</td>\n" % result)
+                    f.write('<td style="color:red">%s</td>\n' % result)
                 f.write("</tr>\n")
                 f.write("</table>\n")
                 continue
@@ -396,8 +417,7 @@
             #
             # Now iterate through all the test cases.
             #
-            for case in suite.findall('Test'):
-
+            for case in suite.findall("Test"):
                 #
                 # Get the name, result and timing information from xml to use in
                 # printing table below.
@@ -427,8 +447,7 @@
                     #
 
                     first_row = True
-                    for details in case.findall('FailureDetails'):
-
+                    for details in case.findall("FailureDetails"):
                         #
                         # Start a new row in the table for each possible Failure Detail
                         #
@@ -436,7 +455,7 @@
 
                         if first_row:
                             first_row = False
-                            f.write("<td style=\"color:red\">%s</td>\n" % result)
+                            f.write('<td style="color:red">%s</td>\n' % result)
                             f.write("<td>%s</td>\n" % name)
                             f.write("<td>%s</td>\n" % time)
                         else:
@@ -445,12 +464,12 @@
                             f.write("<td></td>\n")
 
                         f.write("<td>")
-                        f.write("<b>Message: </b>%s, " % details.find('Message').text)
-                        f.write("<b>Condition: </b>%s, " % details.find('Condition').text)
-                        f.write("<b>Actual: </b>%s, " % details.find('Actual').text)
-                        f.write("<b>Limit: </b>%s, " % details.find('Limit').text)
-                        f.write("<b>File: </b>%s, " % details.find('File').text)
-                        f.write("<b>Line: </b>%s" % details.find('Line').text)
+                        f.write("<b>Message: </b>%s, " % details.find("Message").text)
+                        f.write("<b>Condition: </b>%s, " % details.find("Condition").text)
+                        f.write("<b>Actual: </b>%s, " % details.find("Actual").text)
+                        f.write("<b>Limit: </b>%s, " % details.find("Limit").text)
+                        f.write("<b>File: </b>%s, " % details.find("File").text)
+                        f.write("<b>Line: </b>%s" % details.find("Line").text)
                         f.write("</td>\n")
 
                         #
@@ -471,7 +490,7 @@
                     #   +--------+----------------+------+---------+
                     #
                     f.write("<tr>\n")
-                    f.write("<td style=\"color:green\">%s</td>\n" % result)
+                    f.write('<td style="color:green">%s</td>\n' % result)
                     f.write("<td>%s</td>\n" % name)
                     f.write("<td>%s</td>\n" % time)
                     f.write("<td>%s</td>\n" % reason)
@@ -490,7 +509,7 @@
         #
         # Example status is rendered in a table just like the suites.
         #
-        f.write("<table border=\"1\">\n")
+        f.write('<table border="1">\n')
 
         #
         # The table headings look like,
@@ -508,7 +527,6 @@
         # Now iterate through all the examples
         #
         for example in et.findall("Example"):
-
             #
             # Start a new row for each example
             #
@@ -524,11 +542,11 @@
             # in red; otherwise green.  This goes in a <td> ... </td> table data
             #
             if result == "PASS":
-                f.write("<td style=\"color:green\">%s</td>\n" % result)
+                f.write('<td style="color:green">%s</td>\n' % result)
             elif result == "SKIP":
-                f.write("<td style=\"color:#ff6600\">%s</fd>\n" % result)
+                f.write('<td style="color:#ff6600">%s</fd>\n' % result)
             else:
-                f.write("<td style=\"color:red\">%s</td>\n" % result)
+                f.write('<td style="color:red">%s</td>\n' % result)
 
             #
             # Write the example name as a new tag data.
@@ -561,7 +579,8 @@
         f.write("</body>\n")
         f.write("</html>\n")
 
-    print('done.')
+    print("done.")
+
 
 #
 # Python Control-C handling is broken in the presence of multiple threads.
@@ -571,6 +590,7 @@
 #
 thread_exit = False
 
+
 def sigint_hook(signal, frame):
     global thread_exit
     thread_exit = True
@@ -601,14 +621,17 @@
         with open(lock_filename, "rt", encoding="utf-8") as f:
             for line in f:
                 if line.startswith("top_dir ="):
-                    key, val = line.split('=')
+                    key, val = line.split("=")
                     top_dir = eval(val.strip())
                 if line.startswith("out_dir ="):
-                    key, val = line.split('=')
+                    key, val = line.split("=")
                     out_dir = eval(val.strip())
 
     except FileNotFoundError:
-        print('The .lock-ns3 file was not found.  You must configure before running test.py.', file=sys.stderr)
+        print(
+            "The .lock-ns3 file was not found.  You must configure before running test.py.",
+            file=sys.stderr,
+        )
         sys.exit(2)
 
     global NS3_BASEDIR
@@ -616,7 +639,7 @@
     global NS3_BUILDDIR
     NS3_BUILDDIR = out_dir
 
-    with open(lock_filename, encoding='utf-8') as f:
+    with open(lock_filename, encoding="utf-8") as f:
         for line in f.readlines():
             for item in interesting_config_items:
                 if line.startswith(item):
@@ -626,6 +649,7 @@
         for item in interesting_config_items:
             print("%s ==" % item, eval(item))
 
+
 #
 # It seems pointless to fork a process to run ns3 to fork a process to run
 # the test runner, so we just run the test runner directly.  The main thing
@@ -661,7 +685,7 @@
         os.environ["PYTHONPATH"] += ":" + pypath
 
     if args.verbose:
-        print("os.environ[\"PYTHONPATH\"] == %s" % os.environ["PYTHONPATH"])
+        print('os.environ["PYTHONPATH"] == %s' % os.environ["PYTHONPATH"])
 
     if sys.platform == "darwin":
         if not have_DYLD_LIBRARY_PATH:
@@ -669,28 +693,29 @@
         for path in NS3_MODULE_PATH:
             os.environ["DYLD_LIBRARY_PATH"] += ":" + path
         if args.verbose:
-            print("os.environ[\"DYLD_LIBRARY_PATH\"] == %s" % os.environ["DYLD_LIBRARY_PATH"])
+            print('os.environ["DYLD_LIBRARY_PATH"] == %s' % os.environ["DYLD_LIBRARY_PATH"])
     elif sys.platform == "win32":
         if not have_PATH:
             os.environ["PATH"] = ""
         for path in NS3_MODULE_PATH:
-            os.environ["PATH"] += ';' + path
+            os.environ["PATH"] += ";" + path
         if args.verbose:
-            print("os.environ[\"PATH\"] == %s" % os.environ["PATH"])
+            print('os.environ["PATH"] == %s' % os.environ["PATH"])
     elif sys.platform == "cygwin":
         if not have_PATH:
             os.environ["PATH"] = ""
         for path in NS3_MODULE_PATH:
             os.environ["PATH"] += ":" + path
         if args.verbose:
-            print("os.environ[\"PATH\"] == %s" % os.environ["PATH"])
+            print('os.environ["PATH"] == %s' % os.environ["PATH"])
     else:
         if not have_LD_LIBRARY_PATH:
             os.environ["LD_LIBRARY_PATH"] = ""
         for path in NS3_MODULE_PATH:
             os.environ["LD_LIBRARY_PATH"] += ":" + str(path)
         if args.verbose:
-            print("os.environ[\"LD_LIBRARY_PATH\"] == %s" % os.environ["LD_LIBRARY_PATH"])
+            print('os.environ["LD_LIBRARY_PATH"] == %s' % os.environ["LD_LIBRARY_PATH"])
+
 
 #
 # Short note on generating suppressions:
@@ -778,8 +803,8 @@
 # VALGRIND_SUPPRESSIONS_FILE = "testpy.supp"
 VALGRIND_SUPPRESSIONS_FILE = None
 
-def run_job_synchronously(shell_command, directory, valgrind, is_python, build_path=""):
 
+def run_job_synchronously(shell_command, directory, valgrind, is_python, build_path=""):
     if VALGRIND_SUPPRESSIONS_FILE is not None:
         suppressions_path = os.path.join(NS3_BASEDIR, VALGRIND_SUPPRESSIONS_FILE)
 
@@ -793,10 +818,15 @@
 
     if valgrind:
         if VALGRIND_SUPPRESSIONS_FILE:
-            cmd = "valgrind --suppressions=%s --leak-check=full --show-reachable=yes --error-exitcode=2 --errors-for-leak-kinds=all %s" % (suppressions_path,
-                path_cmd)
+            cmd = (
+                "valgrind --suppressions=%s --leak-check=full --show-reachable=yes --error-exitcode=2 --errors-for-leak-kinds=all %s"
+                % (suppressions_path, path_cmd)
+            )
         else:
-            cmd = "valgrind --leak-check=full --show-reachable=yes --error-exitcode=2 --errors-for-leak-kinds=all %s" % (path_cmd)
+            cmd = (
+                "valgrind --leak-check=full --show-reachable=yes --error-exitcode=2 --errors-for-leak-kinds=all %s"
+                % (path_cmd)
+            )
     else:
         cmd = path_cmd
 
@@ -804,7 +834,9 @@
         print("Synchronously execute %s" % cmd)
 
     start_time = time.time()
-    proc = subprocess.Popen(cmd, shell=True, cwd=directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    proc = subprocess.Popen(
+        cmd, shell=True, cwd=directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    )
     stdout_results, stderr_results = proc.communicate()
     elapsed_time = time.time() - start_time
 
@@ -814,6 +846,7 @@
         try:
             stream_results = stream_results.decode()
         except UnicodeDecodeError:
+
             def decode(byte_array: bytes):
                 try:
                     byte_array.decode()
@@ -823,7 +856,9 @@
             # Find lines where the decoding error happened
             non_utf8_lines = list(map(lambda line: decode(line), stream_results.splitlines()))
             non_utf8_lines = list(filter(lambda line: line is not None, non_utf8_lines))
-            print(f"Non-decodable characters found in {stream_name} output of {cmd}: {non_utf8_lines}")
+            print(
+                f"Non-decodable characters found in {stream_name} output of {cmd}: {non_utf8_lines}"
+            )
 
             # Continue decoding on errors
             stream_results = stream_results.decode(errors="backslashreplace")
@@ -838,6 +873,7 @@
 
     return (retval, stdout_results, stderr_results, elapsed_time)
 
+
 #
 # This class defines a unit of testing work.  It will typically refer to
 # a test suite to run using the test-runner, or an example to run directly.
@@ -973,6 +1009,7 @@
     def set_elapsed_time(self, elapsed_time):
         self.elapsed_time = elapsed_time
 
+
 #
 # The worker thread class that handles the actual running of a given test.
 # Once spawned, it receives requests for work through its input_queue and
@@ -1026,8 +1063,14 @@
                     # know.  It will be something like "examples/udp/udp-echo" or
                     # "examples/wireless/mixed-wireless.py"
                     #
-                    (job.returncode, job.standard_out, job.standard_err, et) = run_job_synchronously(job.shell_command,
-                        job.cwd, args.valgrind, job.is_pyexample, job.build_path)
+                    (
+                        job.returncode,
+                        job.standard_out,
+                        job.standard_err,
+                        et,
+                    ) = run_job_synchronously(
+                        job.shell_command, job.cwd, args.valgrind, job.is_pyexample, job.build_path
+                    )
                 else:
                     #
                     # If we're a test suite, we need to provide a little more info
@@ -1035,12 +1078,22 @@
                     # file name
                     #
                     if args.update_data:
-                        update_data = '--update-data'
+                        update_data = "--update-data"
                     else:
-                        update_data = ''
-                    (job.returncode, job.standard_out, job.standard_err, et) = run_job_synchronously(job.shell_command +
-                        " --xml --tempdir=%s --out=%s %s" % (job.tempdir, job.tmp_file_name, update_data),
-                        job.cwd, args.valgrind, False)
+                        update_data = ""
+                    (
+                        job.returncode,
+                        job.standard_out,
+                        job.standard_err,
+                        et,
+                    ) = run_job_synchronously(
+                        job.shell_command
+                        + " --xml --tempdir=%s --out=%s %s"
+                        % (job.tempdir, job.tmp_file_name, update_data),
+                        job.cwd,
+                        args.valgrind,
+                        False,
+                    )
 
                 job.set_elapsed_time(et)
 
@@ -1054,17 +1107,21 @@
 
                 self.output_queue.put(job)
 
+
 #
 # This function loads the list of previously successful or skipped examples and test suites.
 #
 def load_previously_successful_tests():
     import glob
+
     previously_run_tests_to_skip = {"test": [], "example": []}
     previous_results = glob.glob(f"{TMP_OUTPUT_DIR}/*-results.xml")
     if not previous_results:
         print("No previous runs to rerun")
         exit(-1)
-    latest_result_file = list(sorted(previous_results, key=lambda x: os.path.basename(x), reverse=True))[0]
+    latest_result_file = list(
+        sorted(previous_results, key=lambda x: os.path.basename(x), reverse=True)
+    )[0]
 
     try:
         previous_run_results = ET.parse(latest_result_file)
@@ -1074,15 +1131,18 @@
 
     for test_type in ["Test", "Example"]:
         if previous_run_results.find(test_type):
-            temp = list(map(lambda x: (x.find('Name').text, x.find('Result').text),
-                            previous_run_results.findall(test_type)
-                            )
-                        )
-            temp = list(filter(lambda x: x[1] in ['PASS', 'SKIP'], temp))
+            temp = list(
+                map(
+                    lambda x: (x.find("Name").text, x.find("Result").text),
+                    previous_run_results.findall(test_type),
+                )
+            )
+            temp = list(filter(lambda x: x[1] in ["PASS", "SKIP"], temp))
             temp = [x[0] for x in temp]
             previously_run_tests_to_skip[test_type.lower()] = temp
     return previously_run_tests_to_skip
 
+
 #
 # This is the main function that does the work of interacting with the
 # test-runner itself.
@@ -1100,7 +1160,7 @@
     # Set the proper suffix.
     #
     global BUILD_PROFILE_SUFFIX
-    if BUILD_PROFILE == 'release':
+    if BUILD_PROFILE == "release":
         BUILD_PROFILE_SUFFIX = ""
     else:
         BUILD_PROFILE_SUFFIX = "-" + BUILD_PROFILE
@@ -1110,7 +1170,7 @@
     # match what is done in the CMakeLists.txt file.
     #
     test_runner_name = "%s%s-%s%s" % (APPNAME, VERSION, "test-runner", BUILD_PROFILE_SUFFIX)
-    test_runner_name += '.exe' if sys.platform == 'win32' else ''
+    test_runner_name += ".exe" if sys.platform == "win32" else ""
 
     #
     # Run ns3 to make sure that everything is built, configured and ready to go
@@ -1119,7 +1179,6 @@
     # we allow users that know what they're doing to not invoke ns3 at all.
     #
     if not args.no_build:
-
         # If the user only wants to run a single example, then we can just build
         # that example.
         #
@@ -1142,7 +1201,6 @@
             print("ns3 died. Not running tests", file=sys.stderr)
             return proc.returncode
 
-
     #
     # Dynamically set up paths.
     #
@@ -1157,7 +1215,10 @@
         ns3_runnable_scripts = get_list_from_file(lock_filename, "ns3_runnable_scripts")
         ns3_runnable_scripts = [os.path.basename(script) for script in ns3_runnable_scripts]
     else:
-        print('The build status file was not found.  You must configure before running test.py.', file=sys.stderr)
+        print(
+            "The build status file was not found.  You must configure before running test.py.",
+            file=sys.stderr,
+        )
         sys.exit(2)
 
     #
@@ -1177,10 +1238,10 @@
     python_tests = []
     for directory in EXAMPLE_DIRECTORIES:
         # Set the directories and paths for this example.
-        example_directory   = os.path.join("examples", directory)
+        example_directory = os.path.join("examples", directory)
         examples_to_run_path = os.path.join(example_directory, "examples-to-run.py")
-        cpp_executable_dir   = os.path.join(NS3_BUILDDIR, example_directory)
-        python_script_dir    = os.path.join(example_directory)
+        cpp_executable_dir = os.path.join(NS3_BUILDDIR, example_directory)
+        python_script_dir = os.path.join(example_directory)
 
         # Parse this example directory's file.
         parse_examples_to_run_file(
@@ -1189,18 +1250,19 @@
             python_script_dir,
             example_tests,
             example_names_original,
-            python_tests)
+            python_tests,
+        )
 
     for module in NS3_ENABLED_MODULES:
         # Remove the "ns3-" from the module name.
-        module = module[len("ns3-"):]
+        module = module[len("ns3-") :]
 
         # Set the directories and paths for this example.
-        module_directory     = os.path.join("src", module)
-        example_directory    = os.path.join(module_directory, "examples")
+        module_directory = os.path.join("src", module)
+        example_directory = os.path.join(module_directory, "examples")
         examples_to_run_path = os.path.join(module_directory, "test", "examples-to-run.py")
-        cpp_executable_dir   = os.path.join(NS3_BUILDDIR, example_directory)
-        python_script_dir    = os.path.join(example_directory)
+        cpp_executable_dir = os.path.join(NS3_BUILDDIR, example_directory)
+        python_script_dir = os.path.join(example_directory)
 
         # Parse this module's file.
         parse_examples_to_run_file(
@@ -1209,18 +1271,19 @@
             python_script_dir,
             example_tests,
             example_names_original,
-            python_tests)
+            python_tests,
+        )
 
     for module in NS3_ENABLED_CONTRIBUTED_MODULES:
         # Remove the "ns3-" from the module name.
-        module = module[len("ns3-"):]
+        module = module[len("ns3-") :]
 
         # Set the directories and paths for this example.
-        module_directory     = os.path.join("contrib", module)
-        example_directory    = os.path.join(module_directory, "examples")
+        module_directory = os.path.join("contrib", module)
+        example_directory = os.path.join(module_directory, "examples")
         examples_to_run_path = os.path.join(module_directory, "test", "examples-to-run.py")
-        cpp_executable_dir   = os.path.join(NS3_BUILDDIR, example_directory)
-        python_script_dir    = os.path.join(example_directory)
+        cpp_executable_dir = os.path.join(NS3_BUILDDIR, example_directory)
+        python_script_dir = os.path.join(example_directory)
 
         # Parse this module's file.
         parse_examples_to_run_file(
@@ -1229,7 +1292,8 @@
             python_script_dir,
             example_tests,
             example_names_original,
-            python_tests)
+            python_tests,
+        )
 
     #
     # If lots of logging is enabled, we can crash Python when it tries to
@@ -1246,25 +1310,41 @@
     #
     if args.kinds:
         path_cmd = os.path.join("utils", test_runner_name + " --print-test-type-list")
-        (rc, standard_out, standard_err, et) = run_job_synchronously(path_cmd, os.getcwd(), False, False)
+        (rc, standard_out, standard_err, et) = run_job_synchronously(
+            path_cmd, os.getcwd(), False, False
+        )
         print(standard_out)
 
     if args.list:
         list_items = []
         if ENABLE_TESTS:
             if len(args.constrain):
-                path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list --print-test-types --test-type=%s" % args.constrain)
+                path_cmd = os.path.join(
+                    "utils",
+                    test_runner_name
+                    + " --print-test-name-list --print-test-types --test-type=%s" % args.constrain,
+                )
             else:
-                path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list --print-test-types")
-            (rc, standard_out, standard_err, et) = run_job_synchronously(path_cmd, os.getcwd(), False, False)
+                path_cmd = os.path.join(
+                    "utils", test_runner_name + " --print-test-name-list --print-test-types"
+                )
+            (rc, standard_out, standard_err, et) = run_job_synchronously(
+                path_cmd, os.getcwd(), False, False
+            )
             if rc != 0:
                 # This is usually a sign that ns-3 crashed or exited uncleanly
-                print(('test.py error:  test-runner return code returned {}'.format(rc)))
-                print(('To debug, try running {}\n'.format('\'./ns3 run \"test-runner --print-test-name-list\"\'')))
+                print(("test.py error:  test-runner return code returned {}".format(rc)))
+                print(
+                    (
+                        "To debug, try running {}\n".format(
+                            "'./ns3 run \"test-runner --print-test-name-list\"'"
+                        )
+                    )
+                )
                 return
             if isinstance(standard_out, bytes):
                 standard_out = standard_out.decode()
-            list_items = standard_out.split('\n')
+            list_items = standard_out.split("\n")
             list_items.sort()
         print("Test Type    Test Name")
         print("---------    ---------")
@@ -1277,8 +1357,8 @@
             examples_sorted.sort()
         if ENABLE_PYTHON_BINDINGS:
             python_examples_sorted = []
-            for (x, y) in python_tests:
-                if y == 'True':
+            for x, y in python_tests:
+                if y == "True":
                     python_examples_sorted.append(x)
             python_examples_sorted.sort()
             examples_sorted.extend(python_examples_sorted)
@@ -1334,9 +1414,9 @@
     # The file is created outside the directory that gets automatically deleted.
     #
     xml_results_file = os.path.join(TMP_OUTPUT_DIR, f"{date_and_time}-results.xml")
-    with open(xml_results_file, 'w', encoding='utf-8') as f:
+    with open(xml_results_file, "w", encoding="utf-8") as f:
         f.write('<?xml version="1.0"?>\n')
-        f.write('<Results>\n')
+        f.write("<Results>\n")
 
     #
     # We need to figure out what test suites to execute.  We are either given one
@@ -1375,20 +1455,30 @@
         suites_found = fnmatch.filter(suites.split("\n"), args.suite)
 
         if not suites_found:
-            print('The test suite was not run because an unknown test suite name was requested.', file=sys.stderr)
+            print(
+                "The test suite was not run because an unknown test suite name was requested.",
+                file=sys.stderr,
+            )
             sys.exit(2)
         elif len(suites_found) == 1:
             single_suite = True
 
-        suites = '\n'.join(suites_found)
+        suites = "\n".join(suites_found)
 
     elif ENABLE_TESTS and len(args.example) == 0 and len(args.pyexample) == 0:
         if len(args.constrain):
-            path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list --test-type=%s" % args.constrain)
-            (rc, suites, standard_err, et) = run_job_synchronously(path_cmd, os.getcwd(), False, False)
+            path_cmd = os.path.join(
+                "utils",
+                test_runner_name + " --print-test-name-list --test-type=%s" % args.constrain,
+            )
+            (rc, suites, standard_err, et) = run_job_synchronously(
+                path_cmd, os.getcwd(), False, False
+            )
         else:
             path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list")
-            (rc, suites, standard_err, et) = run_job_synchronously(path_cmd, os.getcwd(), False, False)
+            (rc, suites, standard_err, et) = run_job_synchronously(
+                path_cmd, os.getcwd(), False, False
+            )
     else:
         suites = ""
 
@@ -1402,21 +1492,24 @@
     #
     if isinstance(suites, bytes):
         suites = suites.decode()
-    suite_list = suites.split('\n')
+    suite_list = suites.split("\n")
 
     #
     # Performance tests should only be run when they are requested,
     # i.e. they are not run by default in test.py.
     # If a specific suite was requested we run it, even if
     # it is a performance test.
-    if not single_suite and args.constrain != 'performance':
-
+    if not single_suite and args.constrain != "performance":
         # Get a list of all of the performance tests.
-        path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list --test-type=%s" % "performance")
-        (rc, performance_tests, standard_err, et) = run_job_synchronously(path_cmd, os.getcwd(), False, False)
+        path_cmd = os.path.join(
+            "utils", test_runner_name + " --print-test-name-list --test-type=%s" % "performance"
+        )
+        (rc, performance_tests, standard_err, et) = run_job_synchronously(
+            path_cmd, os.getcwd(), False, False
+        )
         if isinstance(performance_tests, bytes):
             performance_tests = performance_tests.decode()
-        performance_test_list = performance_tests.split('\n')
+        performance_test_list = performance_tests.split("\n")
 
         # Remove any performance tests from the suites list.
         for performance_test in performance_test_list:
@@ -1440,10 +1533,12 @@
     processors = 1
 
     if sys.platform != "win32":
-        if 'SC_NPROCESSORS_ONLN' in os.sysconf_names:
-            processors = os.sysconf('SC_NPROCESSORS_ONLN')
+        if "SC_NPROCESSORS_ONLN" in os.sysconf_names:
+            processors = os.sysconf("SC_NPROCESSORS_ONLN")
         else:
-            proc = subprocess.Popen("sysctl -n hw.ncpu", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            proc = subprocess.Popen(
+                "sysctl -n hw.ncpu", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+            )
             stdout_results, stderr_results = proc.communicate()
             stdout_results = stdout_results.decode()
             stderr_results = stderr_results.decode()
@@ -1454,10 +1549,10 @@
 
     if args.process_limit:
         if processors < args.process_limit:
-            print('Using all %s processors' % processors)
+            print("Using all %s processors" % processors)
         else:
             processors = args.process_limit
-            print('Limiting to %s worker processes' % processors)
+            print("Limiting to %s worker processes" % processors)
 
     #
     # Now, spin up one thread per processor which will eventually mean one test
@@ -1506,7 +1601,9 @@
             else:
                 fullness = " --fullness=QUICK"
 
-            path_cmd = os.path.join("utils", test_runner_name + " --test-name=%s%s%s" % (test, multiple, fullness))
+            path_cmd = os.path.join(
+                "utils", test_runner_name + " --test-name=%s%s%s" % (test, multiple, fullness)
+            )
 
             job.set_shell_command(path_cmd)
 
@@ -1562,9 +1659,9 @@
             if ENABLE_EXAMPLES:
                 for name, test, do_run, do_valgrind_run in example_tests:
                     # Remove any arguments and directory names from test.
-                    test_name = test.split(' ', 1)[0]
+                    test_name = test.split(" ", 1)[0]
                     test_name = os.path.basename(test_name)
-                    test_name = test_name[:-4] if sys.platform == 'win32' else test_name
+                    test_name = test_name[:-4] if sys.platform == "win32" else test_name
 
                     # Don't try to run this example if it isn't runnable.
                     if test_name in ns3_runnable_programs_dictionary:
@@ -1584,7 +1681,10 @@
                                 job.set_is_skip(True)
                                 job.set_skip_reason("skip in valgrind runs")
 
-                            if args.rerun_failed and name in previously_run_tests_to_skip["example"]:
+                            if (
+                                args.rerun_failed
+                                and name in previously_run_tests_to_skip["example"]
+                            ):
                                 job.is_skip = True
                                 job.set_skip_reason("didn't fail in the previous run")
 
@@ -1657,7 +1757,7 @@
         if len(args.constrain) == 0 or args.constrain == "pyexample":
             for test, do_run in python_tests:
                 # Remove any arguments and directory names from test.
-                test_name = test.split(' ', 1)[0]
+                test_name = test.split(" ", 1)[0]
                 test_name = os.path.basename(test_name)
 
                 # Don't try to run this example if it isn't runnable.
@@ -1705,6 +1805,7 @@
         # Find the full relative path to file if only a partial path has been given.
         if not os.path.exists(args.pyexample):
             import glob
+
             files = glob.glob("./**/%s" % args.pyexample, recursive=True)
             if files:
                 args.pyexample = files[0]
@@ -1802,7 +1903,7 @@
                 status = "CRASH"
                 status_print = colors.PINK + status + colors.NORMAL
 
-        print("[%d/%d]" % (i, total_tests), end=' ')
+        print("[%d/%d]" % (i, total_tests), end=" ")
         if args.duration or args.constrain == "performance":
             print("%s (%.3f): %s %s" % (status_print, job.elapsed_time, kind, job.display_name))
         else:
@@ -1818,24 +1919,24 @@
             # XXX We could add some timing information to the examples, i.e. run
             # them through time and print the results here.
             #
-            with open(xml_results_file, 'a', encoding='utf-8') as f:
-                f.write('<Example>\n')
+            with open(xml_results_file, "a", encoding="utf-8") as f:
+                f.write("<Example>\n")
                 example_name = "  <Name>%s</Name>\n" % job.display_name
                 f.write(example_name)
 
                 if status == "PASS":
-                    f.write('  <Result>PASS</Result>\n')
+                    f.write("  <Result>PASS</Result>\n")
                 elif status == "FAIL":
-                    f.write('  <Result>FAIL</Result>\n')
+                    f.write("  <Result>FAIL</Result>\n")
                 elif status == "VALGR":
-                    f.write('  <Result>VALGR</Result>\n')
+                    f.write("  <Result>VALGR</Result>\n")
                 elif status == "SKIP":
-                    f.write('  <Result>SKIP</Result>\n')
+                    f.write("  <Result>SKIP</Result>\n")
                 else:
-                    f.write('  <Result>CRASH</Result>\n')
+                    f.write("  <Result>CRASH</Result>\n")
 
                 f.write('  <Time real="%.3f"/>\n' % job.elapsed_time)
-                f.write('</Example>\n')
+                f.write("</Example>\n")
 
         else:
             #
@@ -1883,16 +1984,18 @@
             # followed by a VALGR failing test suite of the same name.
             #
             if job.is_skip:
-                with open(xml_results_file, 'a', encoding='utf-8') as f:
+                with open(xml_results_file, "a", encoding="utf-8") as f:
                     f.write("<Test>\n")
                     f.write("  <Name>%s</Name>\n" % job.display_name)
-                    f.write('  <Result>SKIP</Result>\n')
+                    f.write("  <Result>SKIP</Result>\n")
                     f.write("  <Reason>%s</Reason>\n" % job.skip_reason)
                     f.write("</Test>\n")
             else:
                 failed_jobs.append(job)
                 if job.returncode == 0 or job.returncode == 1 or job.returncode == 2:
-                    with open(xml_results_file, 'a', encoding='utf-8') as f_to, open(job.tmp_file_name, encoding='utf-8') as f_from:
+                    with open(xml_results_file, "a", encoding="utf-8") as f_to, open(
+                        job.tmp_file_name, encoding="utf-8"
+                    ) as f_from:
                         contents = f_from.read()
                         if status == "VALGR":
                             pre = contents.find("<Result>") + len("<Result>")
@@ -1903,10 +2006,10 @@
                         if et.find("Result").text in ["PASS", "SKIP"]:
                             failed_jobs.pop()
                 else:
-                    with open(xml_results_file, 'a', encoding='utf-8') as f:
+                    with open(xml_results_file, "a", encoding="utf-8") as f:
                         f.write("<Test>\n")
                         f.write("  <Name>%s</Name>\n" % job.display_name)
-                        f.write('  <Result>CRASH</Result>\n')
+                        f.write("  <Result>CRASH</Result>\n")
                         f.write("</Test>\n")
 
     #
@@ -1923,39 +2026,55 @@
     # individual pieces.  So, we need to finish off and close out the XML
     # document
     #
-    with open(xml_results_file, 'a', encoding='utf-8') as f:
-        f.write('</Results>\n')
+    with open(xml_results_file, "a", encoding="utf-8") as f:
+        f.write("</Results>\n")
 
     #
     # Print a quick summary of events
     #
-    print("%d of %d tests passed (%d passed, %d skipped, %d failed, %d crashed, %d valgrind errors)" % (passed_tests,
-        total_tests, passed_tests, skipped_tests, failed_tests, crashed_tests, valgrind_errors))
+    print(
+        "%d of %d tests passed (%d passed, %d skipped, %d failed, %d crashed, %d valgrind errors)"
+        % (
+            passed_tests,
+            total_tests,
+            passed_tests,
+            skipped_tests,
+            failed_tests,
+            crashed_tests,
+            valgrind_errors,
+        )
+    )
     #
     # Repeat summary of skipped, failed, crashed, valgrind events
     #
     if skipped_testnames:
         skipped_testnames.sort()
-        print('List of SKIPped tests:\n    %s' % '\n    '.join(map(str, skipped_testnames)))
+        print("List of SKIPped tests:\n    %s" % "\n    ".join(map(str, skipped_testnames)))
     if failed_testnames:
         failed_testnames.sort()
-        print('List of FAILed tests:\n    %s' % '\n    '.join(map(str, failed_testnames)))
+        print("List of FAILed tests:\n    %s" % "\n    ".join(map(str, failed_testnames)))
     if crashed_testnames:
         crashed_testnames.sort()
-        print('List of CRASHed tests:\n    %s' % '\n    '.join(map(str, crashed_testnames)))
+        print("List of CRASHed tests:\n    %s" % "\n    ".join(map(str, crashed_testnames)))
     if valgrind_testnames:
         valgrind_testnames.sort()
-        print('List of VALGR failures:\n    %s' % '\n    '.join(map(str, valgrind_testnames)))
+        print("List of VALGR failures:\n    %s" % "\n    ".join(map(str, valgrind_testnames)))
 
     if failed_jobs and args.verbose_failed:
         for job in failed_jobs:
             if job.standard_out or job.standard_err:
-                job_type = 'example' if (job.is_example or job.is_pyexample) else 'test suite'
-                print(f"===================== Begin of {job_type} '{job.display_name}' stdout =====================")
+                job_type = "example" if (job.is_example or job.is_pyexample) else "test suite"
+                print(
+                    f"===================== Begin of {job_type} '{job.display_name}' stdout ====================="
+                )
                 print(job.standard_out)
-                print(f"===================== Begin of {job_type} '{job.display_name}' stderr =====================")
+                print(
+                    f"===================== Begin of {job_type} '{job.display_name}' stderr ====================="
+                )
                 print(job.standard_err)
-                print(f"===================== End of {job_type} '{job.display_name}' ==============================")
+                print(
+                    f"===================== End of {job_type} '{job.display_name}' =============================="
+                )
     #
     # The last things to do are to translate the XML results file to "human-
     # readable form" if the user asked for it (or make an XML file somewhere)
@@ -1970,10 +2089,10 @@
         translate_to_text(xml_results_file, args.text)
 
     if len(args.xml):
-        xml_file = args.xml + ('.xml' if ".xml" not in args.xml else '')
-        print('Writing results to xml file %s...' % xml_file, end='')
+        xml_file = args.xml + (".xml" if ".xml" not in args.xml else "")
+        print("Writing results to xml file %s..." % xml_file, end="")
         shutil.copyfile(xml_results_file, xml_file)
-        print('done.')
+        print("done.")
 
     #
     # Let the user know if they need to turn on tests or examples.
@@ -1981,11 +2100,11 @@
     if not ENABLE_TESTS or not ENABLE_EXAMPLES:
         print()
         if not ENABLE_TESTS:
-            print('***  Note: ns-3 tests are currently disabled. Enable them by adding')
+            print("***  Note: ns-3 tests are currently disabled. Enable them by adding")
             print('***  "--enable-tests" to ./ns3 configure or modifying your .ns3rc file.')
             print()
         if not ENABLE_EXAMPLES:
-            print('***  Note: ns-3 examples are currently disabled. Enable them by adding')
+            print("***  Note: ns-3 examples are currently disabled. Enable them by adding")
             print('***  "--enable-examples" to ./ns3 configure or modifying your .ns3rc file.')
             print()
 
@@ -1995,8 +2114,8 @@
     #
     if args.valgrind and not VALGRIND_FOUND:
         print()
-        print('***  Note: you are trying to use valgrind, but valgrind could not be found')
-        print('***  on your machine.  All tests and examples will crash or be skipped.')
+        print("***  Note: you are trying to use valgrind, but valgrind could not be found")
+        print("***  on your machine.  All tests and examples will crash or be skipped.")
         print()
 
     #
@@ -2009,93 +2128,212 @@
         shutil.rmtree(testpy_output_dir)
 
     if passed_tests + skipped_tests == total_tests:
-        return 0 # success
+        return 0  # success
     else:
-        return 1 # catchall for general errors
+        return 1  # catchall for general errors
+
 
 def main(argv):
     parser = argparse.ArgumentParser()
-    parser.add_argument("-b", "--buildpath", action="store", type=str, default="",
-                        help="specify the path where ns-3 was built (defaults to the build directory for the current variant)")
-
-    parser.add_argument("-c", "--constrain", action="store", type=str, default="",
-                        help="constrain the test-runner by kind of test")
-
-    parser.add_argument("-d", "--duration", action="store_true", default=False,
-                        help="print the duration of each test suite and example")
-
-    parser.add_argument("-e", "--example", action="store", type=str, default="",
-                        help="specify a single example to run (no relative path is needed)")
-
-    parser.add_argument("-u", "--update-data", action="store_true", default=False,
-                        help="If examples use reference data files, get them to re-generate them")
-
-    parser.add_argument("-f", "--fullness", action="store", type=str, default="QUICK",
-                        choices=["QUICK", "EXTENSIVE", "TAKES_FOREVER"],
-                        help="choose the duration of tests to run: QUICK, EXTENSIVE, or TAKES_FOREVER, where EXTENSIVE includes QUICK and TAKES_FOREVER includes QUICK and EXTENSIVE (only QUICK tests are run by default)")
-
-    parser.add_argument("-g", "--grind", action="store_true", dest="valgrind", default=False,
-                        help="run the test suites and examples using valgrind")
-
-    parser.add_argument("-k", "--kinds", action="store_true", default=False,
-                        help="print the kinds of tests available")
-
-    parser.add_argument("-l", "--list", action="store_true", default=False,
-                        help="print the list of known tests")
-
-    parser.add_argument("-m", "--multiple", action="store_true", default=False,
-                        help="report multiple failures from test suites and test cases")
-
-    parser.add_argument("-n", "--no-build", action="store_true", default=False,
-                        help="do not build before starting testing")
-
-    parser.add_argument("-p", "--pyexample", action="store", type=str, default="",
-                        help="specify a single python example to run (with relative path)")
-
-    parser.add_argument("-r", "--retain", action="store_true", default=False,
-                        help="retain all temporary files (which are normally deleted)")
-
-    parser.add_argument("-s", "--suite", action="store", type=str, default="",
-                        help="specify a single test suite to run")
-
-    parser.add_argument("-t", "--text", action="store", type=str, default="",
-                        metavar="TEXT-FILE",
-                        help="write detailed test results into TEXT-FILE.txt")
-
-    parser.add_argument("-v", "--verbose", action="store_true", default=False,
-                        help="print progress and informational messages")
-
-    parser.add_argument("--verbose-failed", action="store_true", default=False,
-                        help="print progress and informational messages for failed jobs")
-
-    parser.add_argument("-w", "--web", "--html", action="store", type=str, dest="html", default="",
-                        metavar="HTML-FILE",
-                        help="write detailed test results into HTML-FILE.html")
-
-    parser.add_argument("-x", "--xml", action="store", type=str, default="",
-                        metavar="XML-FILE",
-                        help="write detailed test results into XML-FILE.xml")
-
-    parser.add_argument("--nocolor", action="store_true", default=False,
-                        help="do not use colors in the standard output")
-
-    parser.add_argument("--jobs", action="store", type=int, dest="process_limit", default=0,
-                        help="limit number of worker threads")
-
-    parser.add_argument("--rerun-failed", action="store_true", dest="rerun_failed", default=False,
-                        help="rerun failed tests")
+    parser.add_argument(
+        "-b",
+        "--buildpath",
+        action="store",
+        type=str,
+        default="",
+        help="specify the path where ns-3 was built (defaults to the build directory for the current variant)",
+    )
+
+    parser.add_argument(
+        "-c",
+        "--constrain",
+        action="store",
+        type=str,
+        default="",
+        help="constrain the test-runner by kind of test",
+    )
+
+    parser.add_argument(
+        "-d",
+        "--duration",
+        action="store_true",
+        default=False,
+        help="print the duration of each test suite and example",
+    )
+
+    parser.add_argument(
+        "-e",
+        "--example",
+        action="store",
+        type=str,
+        default="",
+        help="specify a single example to run (no relative path is needed)",
+    )
+
+    parser.add_argument(
+        "-u",
+        "--update-data",
+        action="store_true",
+        default=False,
+        help="If examples use reference data files, get them to re-generate them",
+    )
+
+    parser.add_argument(
+        "-f",
+        "--fullness",
+        action="store",
+        type=str,
+        default="QUICK",
+        choices=["QUICK", "EXTENSIVE", "TAKES_FOREVER"],
+        help="choose the duration of tests to run: QUICK, EXTENSIVE, or TAKES_FOREVER, where EXTENSIVE includes QUICK and TAKES_FOREVER includes QUICK and EXTENSIVE (only QUICK tests are run by default)",
+    )
+
+    parser.add_argument(
+        "-g",
+        "--grind",
+        action="store_true",
+        dest="valgrind",
+        default=False,
+        help="run the test suites and examples using valgrind",
+    )
+
+    parser.add_argument(
+        "-k",
+        "--kinds",
+        action="store_true",
+        default=False,
+        help="print the kinds of tests available",
+    )
+
+    parser.add_argument(
+        "-l", "--list", action="store_true", default=False, help="print the list of known tests"
+    )
+
+    parser.add_argument(
+        "-m",
+        "--multiple",
+        action="store_true",
+        default=False,
+        help="report multiple failures from test suites and test cases",
+    )
+
+    parser.add_argument(
+        "-n",
+        "--no-build",
+        action="store_true",
+        default=False,
+        help="do not build before starting testing",
+    )
+
+    parser.add_argument(
+        "-p",
+        "--pyexample",
+        action="store",
+        type=str,
+        default="",
+        help="specify a single python example to run (with relative path)",
+    )
+
+    parser.add_argument(
+        "-r",
+        "--retain",
+        action="store_true",
+        default=False,
+        help="retain all temporary files (which are normally deleted)",
+    )
+
+    parser.add_argument(
+        "-s",
+        "--suite",
+        action="store",
+        type=str,
+        default="",
+        help="specify a single test suite to run",
+    )
+
+    parser.add_argument(
+        "-t",
+        "--text",
+        action="store",
+        type=str,
+        default="",
+        metavar="TEXT-FILE",
+        help="write detailed test results into TEXT-FILE.txt",
+    )
+
+    parser.add_argument(
+        "-v",
+        "--verbose",
+        action="store_true",
+        default=False,
+        help="print progress and informational messages",
+    )
+
+    parser.add_argument(
+        "--verbose-failed",
+        action="store_true",
+        default=False,
+        help="print progress and informational messages for failed jobs",
+    )
+
+    parser.add_argument(
+        "-w",
+        "--web",
+        "--html",
+        action="store",
+        type=str,
+        dest="html",
+        default="",
+        metavar="HTML-FILE",
+        help="write detailed test results into HTML-FILE.html",
+    )
+
+    parser.add_argument(
+        "-x",
+        "--xml",
+        action="store",
+        type=str,
+        default="",
+        metavar="XML-FILE",
+        help="write detailed test results into XML-FILE.xml",
+    )
+
+    parser.add_argument(
+        "--nocolor",
+        action="store_true",
+        default=False,
+        help="do not use colors in the standard output",
+    )
+
+    parser.add_argument(
+        "--jobs",
+        action="store",
+        type=int,
+        dest="process_limit",
+        default=0,
+        help="limit number of worker threads",
+    )
+
+    parser.add_argument(
+        "--rerun-failed",
+        action="store_true",
+        dest="rerun_failed",
+        default=False,
+        help="rerun failed tests",
+    )
 
     global args
     args = parser.parse_args()
     signal.signal(signal.SIGINT, sigint_hook)
 
     # From waf/waflib/Options.py
-    envcolor = os.environ.get('NOCOLOR', '') and 'no' or 'auto' or 'yes'
+    envcolor = os.environ.get("NOCOLOR", "") and "no" or "auto" or "yes"
 
-    if args.nocolor or envcolor == 'no':
-        colors_lst['USE'] = False
+    if args.nocolor or envcolor == "no":
+        colors_lst["USE"] = False
 
     return run_tests()
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     sys.exit(main(sys.argv))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/bench-scheduler.cc ns-3.41/utils/bench-scheduler.cc
--- ns-3.40/utils/bench-scheduler.cc	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/bench-scheduler.cc	2024-02-09 14:44:08.629795300 -0800
@@ -479,10 +479,10 @@
               "\n"
               "If no scheduler is specified the MapScheduler will be run.");
     cmd.AddValue("all", "use all schedulers", allSched);
-    cmd.AddValue("cal", "use CalendarSheduler", schedCal);
+    cmd.AddValue("cal", "use CalendarScheduler", schedCal);
     cmd.AddValue("calrev", "reverse ordering in the CalendarScheduler", calRev);
     cmd.AddValue("heap", "use HeapScheduler", schedHeap);
-    cmd.AddValue("list", "use ListSheduler", schedList);
+    cmd.AddValue("list", "use ListScheduler", schedList);
     cmd.AddValue("map", "use MapScheduler (default)", schedMap);
     cmd.AddValue("pri", "use PriorityQueue", schedPQ);
     cmd.AddValue("debug", "enable debugging output", g_debug);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/check-style-clang-format.py ns-3.41/utils/check-style-clang-format.py
--- ns-3.40/utils/check-style-clang-format.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/check-style-clang-format.py	2024-02-09 14:44:08.629795300 -0800
@@ -18,7 +18,7 @@
 # Author: Eduardo Nuno Almeida <enmsa@outlook.pt> [INESC TEC and FEUP, Portugal]
 
 """
-Check and apply the ns-3 coding style to all files in the PATH argument.
+Check and apply the ns-3 coding style recursively to all files in the PATH arguments.
 
 The coding style is defined with the clang-format tool, whose definitions are in
 the ".clang-format" file. This script performs the following checks / fixes:
@@ -30,8 +30,8 @@
 This script can be applied to all text files in a given path or to individual files.
 
 NOTE: The formatting check requires clang-format (version >= 14) to be found on the path.
-Trimming of trailing whitespace and conversion of tabs to spaces (via the "--no-formatting"
-option) do not depend on clang-format.
+The remaining checks do not depend on clang-format and can be executed by disabling clang-format
+checking with the "--no-formatting" option.
 """
 
 import argparse
@@ -42,88 +42,88 @@
 import shutil
 import subprocess
 import sys
-
 from typing import Callable, Dict, List, Tuple
 
 ###########################################################
 # PARAMETERS
 ###########################################################
 CLANG_FORMAT_VERSIONS = [
+    17,
     16,
     15,
     14,
 ]
 
-CLANG_FORMAT_GUARD_ON = '// clang-format on'
-CLANG_FORMAT_GUARD_OFF = '// clang-format off'
+CLANG_FORMAT_GUARD_ON = "// clang-format on"
+CLANG_FORMAT_GUARD_OFF = "// clang-format off"
 
 DIRECTORIES_TO_SKIP = [
-    '__pycache__',
-    '.vscode',
-    'bindings',
-    'build',
-    'cmake-cache',
-    'testpy-output',
+    "__pycache__",
+    ".git",
+    "bindings",
+    "build",
+    "cmake-cache",
+    "testpy-output",
 ]
 
 # List of files entirely copied from elsewhere that should not be checked,
 # in order to optimize the performance of this script
 FILES_TO_SKIP = [
-    'valgrind.h',
+    "valgrind.h",
 ]
 
 FILE_EXTENSIONS_TO_CHECK_FORMATTING = [
-    '.c',
-    '.cc',
-    '.h',
+    ".c",
+    ".cc",
+    ".h",
 ]
 
 FILE_EXTENSIONS_TO_CHECK_INCLUDE_PREFIXES = FILE_EXTENSIONS_TO_CHECK_FORMATTING
 
 FILE_EXTENSIONS_TO_CHECK_WHITESPACE = [
-    '.c',
-    '.cc',
-    '.click',
-    '.cmake',
-    '.conf',
-    '.css',
-    '.dot',
-    '.gnuplot',
-    '.gp',
-    '.h',
-    '.html',
-    '.js',
-    '.json',
-    '.m',
-    '.md',
-    '.mob',
-    '.ns_params',
-    '.ns_movements',
-    '.params',
-    '.pl',
-    '.plt',
-    '.py',
-    '.rst',
-    '.seqdiag',
-    '.sh',
-    '.txt',
-    '.yml',
+    ".c",
+    ".cc",
+    ".click",
+    ".cmake",
+    ".conf",
+    ".css",
+    ".dot",
+    ".gnuplot",
+    ".gp",
+    ".h",
+    ".html",
+    ".js",
+    ".json",
+    ".m",
+    ".md",
+    ".mob",
+    ".ns_params",
+    ".ns_movements",
+    ".params",
+    ".pl",
+    ".plt",
+    ".py",
+    ".rst",
+    ".seqdiag",
+    ".sh",
+    ".txt",
+    ".yml",
 ]
 
 FILES_TO_CHECK_WHITESPACE = [
-    'Makefile',
-    'ns3',
+    "Makefile",
+    "ns3",
 ]
 
 FILE_EXTENSIONS_TO_CHECK_TABS = [
-    '.c',
-    '.cc',
-    '.h',
-    '.md',
-    '.py',
-    '.rst',
-    '.sh',
-    '.yml',
+    ".c",
+    ".cc",
+    ".h",
+    ".md",
+    ".py",
+    ".rst",
+    ".sh",
+    ".yml",
 ]
 TAB_SIZE = 4
 
@@ -141,14 +141,16 @@
 
     _, directory = os.path.split(dirpath)
 
-    return not (directory in DIRECTORIES_TO_SKIP or
-                (directory.startswith('.') and directory != '.'))
+    return not (
+        directory in DIRECTORIES_TO_SKIP or (directory.startswith(".") and directory != ".")
+    )
 
 
-def should_analyze_file(path: str,
-                        files_to_check: List[str],
-                        file_extensions_to_check: List[str],
-                        ) -> bool:
+def should_analyze_file(
+    path: str,
+    files_to_check: List[str],
+    file_extensions_to_check: List[str],
+) -> bool:
     """
     Check whether a file should be analyzed.
 
@@ -165,15 +167,16 @@
 
     basename, extension = os.path.splitext(filename)
 
-    return (basename in files_to_check or
-            extension in file_extensions_to_check)
+    return basename in files_to_check or extension in file_extensions_to_check
 
 
-def find_files_to_check_style(path: str) -> Tuple[List[str], List[str], List[str], List[str]]:
+def find_files_to_check_style(
+    paths: List[str],
+) -> Tuple[List[str], List[str], List[str], List[str]]:
     """
-    Find all files to be checked in a given path.
+    Find all files to be checked in a given list of paths.
 
-    @param path Path to check.
+    @param paths List of paths to the files to check.
     @return Tuple [List of files to check include prefixes,
                    List of files to check formatting,
                    List of files to check trailing whitespace,
@@ -181,22 +184,24 @@
     """
 
     files_to_check: List[str] = []
-    abs_path = os.path.abspath(os.path.expanduser(path))
 
-    if os.path.isfile(abs_path):
-        files_to_check = [path]
+    for path in paths:
+        abs_path = os.path.abspath(os.path.expanduser(path))
 
-    elif os.path.isdir(abs_path):
-        for dirpath, dirnames, filenames in os.walk(path, topdown=True):
-            if not should_analyze_directory(dirpath):
-                # Remove directory and its subdirectories
-                dirnames[:] = []
-                continue
+        if os.path.isfile(abs_path):
+            files_to_check.append(path)
 
-            files_to_check.extend([os.path.join(dirpath, f) for f in filenames])
+        elif os.path.isdir(abs_path):
+            for dirpath, dirnames, filenames in os.walk(path, topdown=True):
+                if not should_analyze_directory(dirpath):
+                    # Remove directory and its subdirectories
+                    dirnames[:] = []
+                    continue
 
-    else:
-        raise ValueError(f'Error: {path} is not a file nor a directory')
+                files_to_check.extend([os.path.join(dirpath, f) for f in filenames])
+
+        else:
+            raise ValueError(f"Error: {path} is not a file nor a directory")
 
     files_to_check.sort()
 
@@ -236,51 +241,52 @@
 
     # Find exact version
     for version in CLANG_FORMAT_VERSIONS:
-        clang_format_path = shutil.which(f'clang-format-{version}')
+        clang_format_path = shutil.which(f"clang-format-{version}")
 
         if clang_format_path:
             return clang_format_path
 
     # Find default version and check if it is supported
-    clang_format_path = shutil.which('clang-format')
+    clang_format_path = shutil.which("clang-format")
 
     if clang_format_path:
         process = subprocess.run(
-            [clang_format_path, '--version'],
+            [clang_format_path, "--version"],
             capture_output=True,
             text=True,
             check=True,
         )
 
-        version = process.stdout.strip().split(' ')[-1]
-        major_version = int(version.split('.')[0])
+        version = process.stdout.strip().split(" ")[-1]
+        major_version = int(version.split(".")[0])
 
         if major_version in CLANG_FORMAT_VERSIONS:
             return clang_format_path
 
     # No supported version of clang-format found
     raise RuntimeError(
-        f'Could not find any supported version of clang-format installed on this system. '
-        f'List of supported versions: {CLANG_FORMAT_VERSIONS}.'
+        f"Could not find any supported version of clang-format installed on this system. "
+        f"List of supported versions: {CLANG_FORMAT_VERSIONS}."
     )
 
 
 ###########################################################
 # CHECK STYLE MAIN FUNCTIONS
 ###########################################################
-def check_style_clang_format(path: str,
-                             enable_check_include_prefixes: bool,
-                             enable_check_formatting: bool,
-                             enable_check_whitespace: bool,
-                             enable_check_tabs: bool,
-                             fix: bool,
-                             verbose: bool,
-                             n_jobs: int = 1,
-                             ) -> bool:
+def check_style_clang_format(
+    paths: List[str],
+    enable_check_include_prefixes: bool,
+    enable_check_formatting: bool,
+    enable_check_whitespace: bool,
+    enable_check_tabs: bool,
+    fix: bool,
+    verbose: bool,
+    n_jobs: int = 1,
+) -> bool:
     """
     Check / fix the coding style of a list of files.
 
-    @param path Path to the files.
+    @param paths List of paths to the files to check.
     @param enable_check_include_prefixes Whether to enable checking #include headers from the same module with the "ns3/" prefix.
     @param enable_check_formatting Whether to enable checking code formatting.
     @param enable_check_whitespace Whether to enable checking trailing whitespace.
@@ -291,10 +297,12 @@
     @return Whether all files are compliant with all enabled style checks.
     """
 
-    (files_to_check_include_prefixes,
-     files_to_check_formatting,
-     files_to_check_whitespace,
-     files_to_check_tabs) = find_files_to_check_style(path)
+    (
+        files_to_check_include_prefixes,
+        files_to_check_formatting,
+        files_to_check_whitespace,
+        files_to_check_tabs,
+    ) = find_files_to_check_style(paths)
 
     check_include_prefixes_successful = True
     check_formatting_successful = True
@@ -302,74 +310,83 @@
     check_tabs_successful = True
 
     if enable_check_include_prefixes:
-        check_include_prefixes_successful = check_style_file(
-            files_to_check_include_prefixes,
-            check_include_prefixes_file,
+        check_include_prefixes_successful = check_style_files(
             '#include headers from the same module with the "ns3/" prefix',
+            check_manually_file,
+            files_to_check_include_prefixes,
             fix,
             verbose,
             n_jobs,
+            respect_clang_format_guards=True,
+            check_style_line_function=check_include_prefixes_line,
         )
 
-        print('')
+        print("")
 
     if enable_check_formatting:
-        check_formatting_successful = check_style_file(
-            files_to_check_formatting,
+        check_formatting_successful = check_style_files(
+            "bad code formatting",
             check_formatting_file,
-            'bad code formatting',
+            files_to_check_formatting,
             fix,
             verbose,
             n_jobs,
             clang_format_path=find_clang_format_path(),
         )
 
-        print('')
+        print("")
 
     if enable_check_whitespace:
-        check_whitespace_successful = check_style_file(
+        check_whitespace_successful = check_style_files(
+            "trailing whitespace",
+            check_manually_file,
             files_to_check_whitespace,
-            check_trailing_whitespace_file,
-            'trailing whitespace',
             fix,
             verbose,
             n_jobs,
+            respect_clang_format_guards=False,
+            check_style_line_function=check_whitespace_line,
         )
 
-        print('')
+        print("")
 
     if enable_check_tabs:
-        check_tabs_successful = check_style_file(
+        check_tabs_successful = check_style_files(
+            "tabs",
+            check_manually_file,
             files_to_check_tabs,
-            check_tabs_file,
-            'tabs',
             fix,
             verbose,
             n_jobs,
+            respect_clang_format_guards=True,
+            check_style_line_function=check_tabs_line,
         )
 
-    return all([
-        check_include_prefixes_successful,
-        check_formatting_successful,
-        check_whitespace_successful,
-        check_tabs_successful,
-    ])
-
-
-def check_style_file(filenames: List[str],
-                     check_style_file_function: Callable,
-                     style_check_str: str,
-                     fix: bool,
-                     verbose: bool,
-                     n_jobs: int,
-                     **kwargs,
-                     ) -> bool:
+    return all(
+        [
+            check_include_prefixes_successful,
+            check_formatting_successful,
+            check_whitespace_successful,
+            check_tabs_successful,
+        ]
+    )
+
+
+def check_style_files(
+    style_check_str: str,
+    check_style_file_function: Callable[..., Tuple[str, bool, List[str]]],
+    filenames: List[str],
+    fix: bool,
+    verbose: bool,
+    n_jobs: int,
+    **kwargs,
+) -> bool:
     """
     Check / fix style of a list of files.
 
-    @param filename Name of the file to be checked.
-    @param check_style_file_function Function used to check the file.
     @param style_check_str Description of the check to be performed.
+    @param check_style_file_function Function used to check the file.
+    @param filename Name of the file to be checked.
     @param fix Whether to fix (True) or just check (False) the file (True).
     @param verbose Show the lines that are not compliant with the style.
     @param n_jobs Number of parallel jobs.
@@ -390,7 +407,7 @@
             *[arg if isinstance(arg, list) else itertools.repeat(arg) for arg in kwargs.values()],
         )
 
-        for (filename, is_file_compliant, verbose_infos) in non_compliant_files_results:
+        for filename, is_file_compliant, verbose_infos in non_compliant_files_results:
             if not is_file_compliant:
                 non_compliant_files.append(filename)
 
@@ -399,22 +416,22 @@
 
     # Output results
     if not non_compliant_files:
-        print(f'- No files detected with {style_check_str}')
+        print(f"- No files detected with {style_check_str}")
         return True
 
     else:
         n_non_compliant_files = len(non_compliant_files)
 
         if fix:
-            print(f'- Fixed {style_check_str} in the files ({n_non_compliant_files}):')
+            print(f"- Fixed {style_check_str} in the files ({n_non_compliant_files}):")
         else:
-            print(f'- Detected {style_check_str} in the files ({n_non_compliant_files}):')
+            print(f"- Detected {style_check_str} in the files ({n_non_compliant_files}):")
 
         for f in non_compliant_files:
             if verbose:
-                print(*[f'    {l}' for l in files_verbose_infos[f]], sep='\n')
+                print(*[f"    {l}" for l in files_verbose_infos[f]], sep="\n")
             else:
-                print(f'    - {f}')
+                print(f"    - {f}")
 
         # If all files were fixed, there are no more non-compliant files
         return fix
@@ -423,86 +440,12 @@
 ###########################################################
 # CHECK STYLE FUNCTIONS
 ###########################################################
-def check_include_prefixes_file(filename: str,
-                                fix: bool,
-                                verbose: bool,
-                                ) -> Tuple[str, bool, List[str]]:
-    """
-    Check / fix #include headers from the same module with the "ns3/" prefix in a file.
-
-    @param filename Name of the file to be checked.
-    @param fix Whether to fix (True) or just check (False) the style of the file (True).
-    @param verbose Show the lines that are not compliant with the style.
-    @return Tuple [Filename,
-                   Whether the file is compliant with the style (before the check),
-                   Verbose information].
-    """
-
-    is_file_compliant = True
-    clang_format_enabled = True
-
-    verbose_infos: List[str] = []
-
-    with open(filename, 'r', encoding='utf-8') as f:
-        file_lines = f.readlines()
-
-    for (i, line) in enumerate(file_lines):
-
-        # Check clang-format guards
-        line_stripped = line.strip()
-
-        if line_stripped == CLANG_FORMAT_GUARD_ON:
-            clang_format_enabled = True
-        elif line_stripped == CLANG_FORMAT_GUARD_OFF:
-            clang_format_enabled = False
-
-        if (not clang_format_enabled and
-                line_stripped not in (CLANG_FORMAT_GUARD_ON, CLANG_FORMAT_GUARD_OFF)):
-            continue
-
-        # Check if the line is an #include and extract its header file
-        header_file = re.findall(r'^#include ["<]ns3/(.*\.h)[">]', line_stripped)
-
-        if not header_file:
-            continue
-
-        # Check if the header file belongs to the same module and remove the "ns3/" prefix
-        header_file = header_file[0]
-        parent_path = os.path.split(filename)[0]
-
-        if not os.path.exists(os.path.join(parent_path, header_file)):
-            continue
-
-        is_file_compliant = False
-        file_lines[i] = line_stripped.replace(
-            f'ns3/{header_file}', header_file).replace('<', '"').replace('>', '"') + '\n'
-
-        if verbose:
-            header_index = len('#include "')
-
-            verbose_infos.extend([
-                f'{filename}:{i + 1}:{header_index + 1}: error: #include headers from the same module with the "ns3/" prefix detected',
-                f'    {line_stripped}',
-                f'    {"":{header_index}}^',
-            ])
-
-        # Optimization: If running in non-verbose check mode, only one error is needed to check that the file is not compliant
-        if not fix and not verbose:
-            break
-
-    # Update file with the fixed lines
-    if fix and not is_file_compliant:
-        with open(filename, 'w', encoding='utf-8') as f:
-            f.writelines(file_lines)
-
-    return (filename, is_file_compliant, verbose_infos)
-
-
-def check_formatting_file(filename: str,
-                          fix: bool,
-                          verbose: bool,
-                          clang_format_path: str,
-                          ) -> Tuple[str, bool, List[str]]:
+def check_formatting_file(
+    filename: str,
+    fix: bool,
+    verbose: bool,
+    clang_format_path: str,
+) -> Tuple[str, bool, List[str]]:
     """
     Check / fix the coding style of a file with clang-format.
 
@@ -522,18 +465,18 @@
         [
             clang_format_path,
             filename,
-            '-style=file',
-            '--dry-run',
-            '--Werror',
+            "-style=file",
+            "--dry-run",
+            "--Werror",
             # Optimization: In non-verbose mode, only one error is needed to check that the file is not compliant
-            f'--ferror-limit={0 if verbose else 1}',
+            f"--ferror-limit={0 if verbose else 1}",
         ],
         check=False,
         capture_output=True,
         text=True,
     )
 
-    is_file_compliant = (process.returncode == 0)
+    is_file_compliant = process.returncode == 0
 
     if verbose:
         verbose_infos = process.stderr.splitlines()
@@ -544,8 +487,8 @@
             [
                 clang_format_path,
                 filename,
-                '-style=file',
-                '-i',
+                "-style=file",
+                "-i",
             ],
             check=False,
             stdout=subprocess.DEVNULL,
@@ -555,16 +498,21 @@
     return (filename, is_file_compliant, verbose_infos)
 
 
-def check_trailing_whitespace_file(filename: str,
-                                   fix: bool,
-                                   verbose: bool,
-                                   ) -> Tuple[str, bool, List[str]]:
+def check_manually_file(
+    filename: str,
+    fix: bool,
+    verbose: bool,
+    respect_clang_format_guards: bool,
+    check_style_line_function: Callable[[str, str, int], Tuple[bool, str, List[str]]],
+) -> Tuple[str, bool, List[str]]:
     """
-    Check / fix trailing whitespace in a file.
+    Check / fix a file manually using a function to check / fix each line.
 
     @param filename Name of the file to be checked.
     @param fix Whether to fix (True) or just check (False) the style of the file (True).
     @param verbose Show the lines that are not compliant with the style.
+    @param respect_clang_format_guards Whether to respect clang-format guards.
+    @param check_style_line_function Function used to check each line.
     @return Tuple [Filename,
                    Whether the file is compliant with the style (before the check),
                    Verbose information].
@@ -572,151 +520,242 @@
 
     is_file_compliant = True
     verbose_infos: List[str] = []
+    clang_format_enabled = True
 
-    with open(filename, 'r', encoding='utf-8') as f:
+    with open(filename, "r", encoding="utf-8") as f:
         file_lines = f.readlines()
 
-    # Check if there are trailing whitespace and fix them
-    for (i, line) in enumerate(file_lines):
-        line_fixed = line.rstrip() + '\n'
-
-        if line_fixed == line:
-            continue
-
-        is_file_compliant = False
-        file_lines[i] = line_fixed
-
-        if verbose:
-            line_fixed_stripped_expanded = line_fixed.rstrip().expandtabs(TAB_SIZE)
-
-            verbose_infos.extend([
-                f'{filename}:{i + 1}:{len(line_fixed_stripped_expanded) + 1}: error: Trailing whitespace detected',
-                f'    {line_fixed_stripped_expanded}',
-                f'    {"":{len(line_fixed_stripped_expanded)}}^',
-            ])
-
-        # Optimization: If running in non-verbose check mode, only one error is needed to check that the file is not compliant
-        if not fix and not verbose:
-            break
+    for i, line in enumerate(file_lines):
+        # Check clang-format guards
+        if respect_clang_format_guards:
+            line_stripped = line.strip()
+
+            if line_stripped == CLANG_FORMAT_GUARD_ON:
+                clang_format_enabled = True
+            elif line_stripped == CLANG_FORMAT_GUARD_OFF:
+                clang_format_enabled = False
+
+            if not clang_format_enabled and line_stripped not in (
+                CLANG_FORMAT_GUARD_ON,
+                CLANG_FORMAT_GUARD_OFF,
+            ):
+                continue
+
+        # Check if the line is compliant with the style and fix it
+        (is_line_compliant, line_fixed, line_verbose_infos) = check_style_line_function(
+            line, filename, i
+        )
+
+        if not is_line_compliant:
+            is_file_compliant = False
+            file_lines[i] = line_fixed
+            verbose_infos.extend(line_verbose_infos)
+
+            # Optimization: If running in non-verbose check mode, only one error is needed to check that the file is not compliant
+            if not fix and not verbose:
+                break
 
     # Update file with the fixed lines
     if fix and not is_file_compliant:
-        with open(filename, 'w', encoding='utf-8') as f:
+        with open(filename, "w", encoding="utf-8") as f:
             f.writelines(file_lines)
 
     return (filename, is_file_compliant, verbose_infos)
 
 
-def check_tabs_file(filename: str,
-                    fix: bool,
-                    verbose: bool,
-                    ) -> Tuple[str, bool, List[str]]:
+def check_include_prefixes_line(
+    line: str,
+    filename: str,
+    line_number: int,
+) -> Tuple[bool, str, List[str]]:
     """
-    Check / fix tabs in a file.
+    Check / fix #include headers from the same module with the "ns3/" prefix in a line.
 
+    @param line The line to check.
     @param filename Name of the file to be checked.
-    @param fix Whether to fix (True) or just check (False) the style of the file (True).
-    @param verbose Show the lines that are not compliant with the style.
-    @return Tuple [Filename,
-                   Whether the file is compliant with the style (before the check),
+    @param line_number The number of the line checked.
+    @return Tuple [Whether the line is compliant with the style (before the check),
+                   Fixed line,
                    Verbose information].
     """
 
-    is_file_compliant = True
-    clang_format_enabled = True
+    is_line_compliant = True
+    line_fixed = line
+    verbose_infos: List[str] = []
+
+    # Check if the line is an #include and extract its header file
+    line_stripped = line.strip()
+    header_file = re.findall(r'^#include ["<]ns3/(.*\.h)[">]', line_stripped)
+
+    if header_file:
+        # Check if the header file belongs to the same module and remove the "ns3/" prefix
+        header_file = header_file[0]
+        parent_path = os.path.split(filename)[0]
+
+        if os.path.exists(os.path.join(parent_path, header_file)):
+            is_line_compliant = False
+            line_fixed = (
+                line_stripped.replace(f"ns3/{header_file}", header_file)
+                .replace("<", '"')
+                .replace(">", '"')
+                + "\n"
+            )
+
+            header_index = len('#include "')
 
+            verbose_infos.extend(
+                [
+                    f'{filename}:{line_number + 1}:{header_index + 1}: error: #include headers from the same module with the "ns3/" prefix detected',
+                    f"    {line_stripped}",
+                    f'    {"":{header_index}}^',
+                ]
+            )
+
+    return (is_line_compliant, line_fixed, verbose_infos)
+
+
+def check_whitespace_line(
+    line: str,
+    filename: str,
+    line_number: int,
+) -> Tuple[bool, str, List[str]]:
+    """
+    Check / fix whitespace in a line.
+
+    @param line The line to check.
+    @param filename Name of the file to be checked.
+    @param line_number The number of the line checked.
+    @return Tuple [Whether the line is compliant with the style (before the check),
+                   Fixed line,
+                   Verbose information].
+    """
+
+    is_line_compliant = True
+    line_fixed = line.rstrip() + "\n"
     verbose_infos: List[str] = []
 
-    with open(filename, 'r', encoding='utf-8') as f:
-        file_lines = f.readlines()
+    if line_fixed != line:
+        is_line_compliant = False
+        line_fixed_stripped_expanded = line_fixed.rstrip().expandtabs(TAB_SIZE)
 
-    for (i, line) in enumerate(file_lines):
+        verbose_infos = [
+            f"{filename}:{line_number + 1}:{len(line_fixed_stripped_expanded) + 1}: error: Trailing whitespace detected",
+            f"    {line_fixed_stripped_expanded}",
+            f'    {"":{len(line_fixed_stripped_expanded)}}^',
+        ]
 
-        # Check clang-format guards
-        line_stripped = line.strip()
+    return (is_line_compliant, line_fixed, verbose_infos)
 
-        if line_stripped == CLANG_FORMAT_GUARD_ON:
-            clang_format_enabled = True
-        elif line_stripped == CLANG_FORMAT_GUARD_OFF:
-            clang_format_enabled = False
-
-        if (not clang_format_enabled and
-                line_stripped not in (CLANG_FORMAT_GUARD_ON, CLANG_FORMAT_GUARD_OFF)):
-            continue
-
-        # Check if there are tabs and fix them
-        tab_index = line.find('\t')
-
-        if tab_index == -1:
-            continue
-
-        is_file_compliant = False
-        file_lines[i] = line.expandtabs(TAB_SIZE)
-
-        if verbose:
-            verbose_infos.extend([
-                f'{filename}:{i + 1}:{tab_index + 1}: error: Tab detected',
-                f'    {line.rstrip()}',
-                f'    {"":{tab_index}}^',
-            ])
-
-        # Optimization: If running in non-verbose check mode, only one error is needed to check that the file is not compliant
-        if not fix and not verbose:
-            break
 
-    # Update file with the fixed lines
-    if fix and not is_file_compliant:
-        with open(filename, 'w', encoding='utf-8') as f:
-            f.writelines(file_lines)
+def check_tabs_line(
+    line: str,
+    filename: str,
+    line_number: int,
+) -> Tuple[bool, str, List[str]]:
+    """
+    Check / fix tabs in a line.
 
-    return (filename, is_file_compliant, verbose_infos)
+    @param line The line to check.
+    @param filename Name of the file to be checked.
+    @param line_number The number of the line checked.
+    @return Tuple [Whether the line is compliant with the style (before the check),
+                   Fixed line,
+                   Verbose information].
+    """
+
+    is_line_compliant = True
+    line_fixed = line
+    verbose_infos: List[str] = []
+
+    tab_index = line.find("\t")
+
+    if tab_index != -1:
+        is_line_compliant = False
+        line_fixed = line.expandtabs(TAB_SIZE)
+
+        verbose_infos = [
+            f"{filename}:{line_number + 1}:{tab_index + 1}: error: Tab detected",
+            f"    {line.rstrip()}",
+            f'    {"":{tab_index}}^',
+        ]
+
+    return (is_line_compliant, line_fixed, verbose_infos)
 
 
 ###########################################################
 # MAIN
 ###########################################################
-if __name__ == '__main__':
-
+if __name__ == "__main__":
     parser = argparse.ArgumentParser(
-        description='Check and apply the ns-3 coding style to all files in a given PATH. '
-        'The script checks the formatting of the file with clang-format. '
+        description="Check and apply the ns-3 coding style recursively to all files in the given PATHs. "
+        "The script checks the formatting of the file with clang-format. "
         'Additionally, it checks #include headers from the same module with the "ns3/" prefix, '
-        'the presence of trailing whitespace and tabs. '
+        "the presence of trailing whitespace and tabs. "
         'Formatting, local #include "ns3/" prefixes and tabs checks respect clang-format guards. '
         'When used in "check mode" (default), the script checks if all files are well '
-        'formatted and do not have trailing whitespace nor tabs. '
-        'If it detects non-formatted files, they will be printed and this process exits with a '
-        'non-zero code. When used in "fix mode", this script automatically fixes the files.')
+        "formatted and do not have trailing whitespace nor tabs. "
+        "If it detects non-formatted files, they will be printed and this process exits with a "
+        'non-zero code. When used in "fix mode", this script automatically fixes the files.'
+    )
 
-    parser.add_argument('path', action='store', type=str,
-                        help='Path to the files to check')
+    parser.add_argument(
+        "paths",
+        action="store",
+        type=str,
+        nargs="+",
+        help="List of paths to the files to check",
+    )
 
-    parser.add_argument('--no-include-prefixes', action='store_true',
-                        help='Do not check / fix #include headers from the same module with the "ns3/" prefix')
+    parser.add_argument(
+        "--no-include-prefixes",
+        action="store_true",
+        help='Do not check / fix #include headers from the same module with the "ns3/" prefix',
+    )
 
-    parser.add_argument('--no-formatting', action='store_true',
-                        help='Do not check / fix code formatting')
+    parser.add_argument(
+        "--no-formatting",
+        action="store_true",
+        help="Do not check / fix code formatting",
+    )
 
-    parser.add_argument('--no-whitespace', action='store_true',
-                        help='Do not check / fix trailing whitespace')
+    parser.add_argument(
+        "--no-whitespace",
+        action="store_true",
+        help="Do not check / fix trailing whitespace",
+    )
 
-    parser.add_argument('--no-tabs', action='store_true',
-                        help='Do not check / fix tabs')
+    parser.add_argument(
+        "--no-tabs",
+        action="store_true",
+        help="Do not check / fix tabs",
+    )
 
-    parser.add_argument('--fix', action='store_true',
-                        help='Fix coding style issues detected in the files')
+    parser.add_argument(
+        "--fix",
+        action="store_true",
+        help="Fix coding style issues detected in the files",
+    )
 
-    parser.add_argument('-v', '--verbose', action='store_true',
-                        help='Show the lines that are not well-formatted')
+    parser.add_argument(
+        "-v",
+        "--verbose",
+        action="store_true",
+        help="Show the lines that are not well-formatted",
+    )
 
-    parser.add_argument('-j', '--jobs', type=int, default=max(1, os.cpu_count() - 1),
-                        help='Number of parallel jobs')
+    parser.add_argument(
+        "-j",
+        "--jobs",
+        type=int,
+        default=max(1, os.cpu_count() - 1),
+        help="Number of parallel jobs",
+    )
 
     args = parser.parse_args()
 
     try:
         all_checks_successful = check_style_clang_format(
-            path=args.path,
+            paths=args.paths,
             enable_check_include_prefixes=(not args.no_include_prefixes),
             enable_check_formatting=(not args.no_formatting),
             enable_check_whitespace=(not args.no_whitespace),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/CMakeLists.txt ns-3.41/utils/CMakeLists.txt
--- ns-3.40/utils/CMakeLists.txt	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/CMakeLists.txt	2024-02-09 14:44:08.629795300 -0800
@@ -33,13 +33,11 @@
 
     if(${NS3_MONOLIB})
       target_link_libraries(
-        test-runner ${LIB_AS_NEEDED_PRE} ${ns3-libs-tests} ${LIB_AS_NEEDED_POST}
-        ${lib-ns3-monolib} ${ns3-contrib-libs}
+        test-runner ${LIB_AS_NEEDED_PRE} ${ns3-libs-tests} ${lib-ns3-monolib} ${ns3-contrib-libs} ${LIB_AS_NEEDED_POST}
       )
     else()
       target_link_libraries(
-        test-runner ${LIB_AS_NEEDED_PRE} ${ns3-libs-tests} ${LIB_AS_NEEDED_POST}
-        ${ns3-libs} ${ns3-contrib-libs}
+        test-runner ${LIB_AS_NEEDED_PRE} ${ns3-libs-tests} ${ns3-libs} ${ns3-contrib-libs} ${LIB_AS_NEEDED_POST}
       )
     endif()
   endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/codespell-ignored-lines ns-3.41/utils/codespell-ignored-lines
--- ns-3.40/utils/codespell-ignored-lines	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/codespell-ignored-lines	2024-02-09 14:44:08.629795300 -0800
@@ -33,8 +33,8 @@
     echo "$filterin"
 
 #./src/visualizer/visualizer/hud.py:93-94
-            tbe = math.log10(dx_over_ndiv)#;   /* looking for approx. 'ndiv' divisions in a length 'dx' */
-            div = pow(10, rint(tbe))#;         /* div: power of 10 closest to dx/ndiv */
+            tbe = math.log10(dx_over_ndiv)
+            div = pow(10, rint(tbe))
 
 #./utils/tests/test-ns3.py:2944
             'convert {image} -colorspace HSI -channel b -separate +channel -scale 1x1 -format "%[fx:100*u]" info:'
@@ -49,6 +49,42 @@
                 arp_h->ar_tha);                                       // Target hardware address.
             arp_h->ar_hln = sizeof arp_h->ar_tha;                     // Hardware address length.
 
+# Names and emails
+ * Author: Manuel Requena <manuel.requena@cttc.es>
+ *   Manuel Requena <manuel.requena@cttc.es>
+ *   Dave Täht <d@taht.net>
+.. [Pal19] J. Palmei, S. Gupta, P. Imputato, J. Morton, M. Tahiliani, S. Avallone, and D. Taht, Design and Evaluation of COBALT Queue Discipline, 2019 IEEE International Symposium on Local and Metropolitan Area Networks (LANMAN), Paris, France, 2019.
+.. [Hoe18] T. Hoiland-Jørgensen, D. Taht and J. Morton, "Piece of CAKE: A Comprehensive Queue Management Solution for Home Gateways," 2018 IEEE International Symposium on Local and Metropolitan Area Networks (LANMAN), Washington, DC, USA, 2018.
+.. [Hoe16] T. Hoeiland-Joergensen, P. McKenney, D. Taht, J. Gettys and E. Dumazet, The FlowQueue-CoDel Packet Scheduler and Active Queue Management Algorithm, IETF draft.  Available online at `<https://tools.ietf.org/html/draft-ietf-aqm-fq-codel>`_
+   Morton,  Mohit P. Tahiliani, Stefano Avallone and Dave Taht (2019).
+ * Author: Jaume Nin <jnin@cttc.cat>
+ *   Jaume Nin <jnin@cttc.cat>
+ * Author: Jaume Nin <jaume.nin@cttc.cat>
+ *   Jaume Nin <jaume.nin@cttc.cat>
+ * Author: Jaume Nin <jaume.nin@cttc.es>
+ * Author: Jaume Nin <jnin@cttc.es>
+ *   Jaume Nin <jnin@cttc.es>
+ * Copyright (c) 2014 Universita' degli Studi di Napoli "Federico II"
+ *               2014 Universita' degli Studi di Napoli "Federico II"
+ * Copyright (c) 2015 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2015 Universita' degli Studi di Napoli "Federico II"
+ *               2015 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2016 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
+ *               2017 Università' degli Studi di Napoli Federico II
+ *               2017 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2018 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2019 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2021 2020 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2021 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
+ * Copyright (c) 2022 Universita' degli Studi di Napoli "Federico II"
+ * Copyright (c) 2023 Universita' degli Studi di Napoli Federico II
+
+./src/core/examples/hash-example.cc
+6ed21de2  brand-newness       peripherial
+
 ./src/dsr/model/dsr-routing.cc:243, 246, 3360, 3363
             .AddAttribute("GratReplyHoldoff",
                           MakeTimeAccessor(&DsrRouting::m_gratReplyHoldoff),
@@ -83,6 +119,9 @@
 ./src/lte/model/lte-mi-error-model.h:23:
  *      for mobIlE Video AppLications) project, which is a
 
+./src/traffic-control/doc/tbf.rst:84:
+.. [Ref2] J. Vehent; Journey to the Center of the Linux Kernel: Traffic Control, Shaping and QoS; Available online at `<https://web.archive.org/web/20190930031243/http://wiki.linuxwall.info/doku.php/en:ressources:dossiers:networking:traffic_control#tbf_-_token_bucket_filter>`_.
+
 ./src/traffic-control/model/codel-queue-disc.h:17:
  * Codel, the COntrolled DELay Queueing discipline
 
@@ -117,5 +156,8 @@
   * (wifi) Enumerated value `MDAOP_ADVERTISMENTS` from `mgmt-headers.h` was renamed `MDAOP_ADVERTISEMENTS`.
   * (wifi) Define `IE_BEAMLINK_MAINENANCE` from `wifi-information-element.h` was renamed `IE_BEAMLINK_MAINTENANCE`.
   * (wimax) Attribute `m_nrRecivedFecBlocks` from `simple-ofdm-wimax-phy.h` was renamed `m_nrReceivedFecBlocks`.
+  * (lte) Struct member `fdbetsFlowPerf_t::lastTtiBytesTrasmitted` in file `fdbet-ff-mac-scheduler.h` was renamed `fdbetsFlowPerf_t::lastTtiBytesTransmitted`.
+  * (lte) Struct member `tdbetsFlowPerf_t::lastTtiBytesTrasmitted` in file `tdbet-ff-mac-scheduler.h` was renamed `fdbetsFlowPerf_t::lastTtiBytesTransmitted`.
+  * (lte) Struct member `pfsFlowPerf_t::lastTtiBytesTrasmitted` in file `pf-ff-mac-scheduler.h` was renamed `fdbetsFlowPerf_t::lastTtiBytesTransmitted`.
 * The spelling of the attribute `IntialCellVoltage` from `LiIonEnergySource` was corrected to `InitialCellVoltage`; this will affect existing users who were using the attribute with the misspelling.
 * The attribute `Recievers` in class `YansWifiPhy` was misspelled, so this has been corrected to `Receivers`.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/codespell-ignored-words ns-3.41/utils/codespell-ignored-words
--- ns-3.40/utils/codespell-ignored-words	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/codespell-ignored-words	2024-02-09 14:44:08.629795300 -0800
@@ -2,81 +2,40 @@
 te
 
 #Core
-anid
-anumber
 bu
 mone
-peripherial
-studi
-thre
 
 #Network
 ot
 nd
 
 #Internet
-devicec
 ect
 iif
-nce
-inflight
-upto
-
-#Internet Apps
-clen
-
-#Buildings
-nin
 
 #DSR
-cleanp
 grat
 
 #LTE
 commun
-couting
 ue
 ues
 
-#Netanim
-writen
-
-#Spectrum
-inh
-
-#Wave
-aci
-ois
-
-#Wifi
-fo
-
 #Traffic Control
-taht
 interm
 
+#Wi-Fi
+aci
+
 #Wimax
 aas
 opps
 
-#Propagation
-alos
-clos
-
-#Examples
-clienta
-
 #Brite
 brite
 
-#Mesh
-inport
-
 #Visualizer
 inout
 
 #Utils
 filetest
-
-#test-ns3.py
-dependant
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/create-module.py ns-3.41/utils/create-module.py
--- ns-3.40/utils/create-module.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/create-module.py	2024-02-09 14:44:08.629795300 -0800
@@ -1,13 +1,12 @@
 #! /usr/bin/env python3
-import sys
 import argparse
 import os
 import re
 import shutil
-
+import sys
 from pathlib import Path
 
-CMAKELISTS_TEMPLATE = '''\
+CMAKELISTS_TEMPLATE = """\
 check_include_file_cxx(stdint.h HAVE_STDINT_H)
 if(HAVE_STDINT_H)
     add_definitions(-DHAVE_STDINT_H)
@@ -30,11 +29,10 @@
     TEST_SOURCES test/{MODULE}-test-suite.cc
                  ${{examples_as_tests_sources}}
 )
-
-'''
+"""
 
 
-MODEL_CC_TEMPLATE = '''\
+MODEL_CC_TEMPLATE = """\
 #include "{MODULE}.h"
 
 namespace ns3
@@ -43,10 +41,10 @@
 /* ... */
 
 }}
-'''
+"""
 
 
-MODEL_H_TEMPLATE = '''\
+MODEL_H_TEMPLATE = """\
 #ifndef {INCLUDE_GUARD}
 #define {INCLUDE_GUARD}
 
@@ -67,10 +65,10 @@
 }}
 
 #endif /* {INCLUDE_GUARD} */
-'''
+"""
 
 
-HELPER_CC_TEMPLATE = '''\
+HELPER_CC_TEMPLATE = """\
 #include "{MODULE}-helper.h"
 
 namespace ns3
@@ -79,10 +77,10 @@
 /* ... */
 
 }}
-'''
+"""
 
 
-HELPER_H_TEMPLATE = '''\
+HELPER_H_TEMPLATE = """\
 #ifndef {INCLUDE_GUARD}
 #define {INCLUDE_GUARD}
 
@@ -99,19 +97,18 @@
 }}
 
 #endif /* {INCLUDE_GUARD} */
-'''
+"""
 
 
-EXAMPLES_CMAKELISTS_TEMPLATE = '''\
+EXAMPLES_CMAKELISTS_TEMPLATE = """\
 build_lib_example(
     NAME {MODULE}-example
     SOURCE_FILES {MODULE}-example.cc
     LIBRARIES_TO_LINK ${{lib{MODULE}}}
 )
+"""
 
-'''
-
-EXAMPLE_CC_TEMPLATE = '''\
+EXAMPLE_CC_TEMPLATE = """\
 #include "ns3/core-module.h"
 #include "ns3/{MODULE}-helper.h"
 
@@ -139,10 +136,10 @@
     Simulator::Destroy();
     return 0;
 }}
-'''
+"""
 
 
-TEST_CC_TEMPLATE = '''\
+TEST_CC_TEMPLATE = """\
 
 // Include a header file from your module to test.
 #include "ns3/{MODULE}.h"
@@ -229,10 +226,10 @@
  * Static variable for test initialization
  */
 static {CAPITALIZED}TestSuite s{COMPOUND}TestSuite;
-'''
+"""
 
 
-DOC_RST_TEMPLATE = '''Example Module Documentation
+DOC_RST_TEMPLATE = """Example Module Documentation
 ----------------------------
 
 .. include:: replace.txt
@@ -330,18 +327,19 @@
 Describe how the model has been tested/validated.  What tests run in the
 test suite?  How much API and code is covered by the tests?  Again,
 references to outside published work may help here.
-'''
+"""
+
 
 def create_file(path, template, **kwargs):
     artifact_path = Path(path)
 
-    #open file for (w)rite and in (t)ext mode
+    # open file for (w)rite and in (t)ext mode
     with artifact_path.open("wt", encoding="utf-8") as f:
         f.write(template.format(**kwargs))
 
 
 def make_cmakelists(moduledir, modname):
-    path = Path(moduledir, 'CMakeLists.txt')
+    path = Path(moduledir, "CMakeLists.txt")
     macro = "build_lib"
     create_file(path, CMAKELISTS_TEMPLATE, MODULE=modname)
 
@@ -352,14 +350,12 @@
     modelpath = Path(moduledir, "model")
     modelpath.mkdir(parents=True)
 
-    srcfile_path = modelpath.joinpath(modname).with_suffix('.cc')
+    srcfile_path = modelpath.joinpath(modname).with_suffix(".cc")
     create_file(srcfile_path, MODEL_CC_TEMPLATE, MODULE=modname)
 
-    hfile_path = modelpath.joinpath(modname).with_suffix('.h')
-    guard = "{}_H".format(modname.replace('-', '_').upper())
-    create_file(hfile_path, MODEL_H_TEMPLATE,
-                MODULE=modname,
-                INCLUDE_GUARD=guard)
+    hfile_path = modelpath.joinpath(modname).with_suffix(".h")
+    guard = "{}_H".format(modname.replace("-", "_").upper())
+    create_file(hfile_path, MODEL_H_TEMPLATE, MODULE=modname, INCLUDE_GUARD=guard)
 
     return True
 
@@ -368,11 +364,17 @@
     testpath = Path(moduledir, "test")
     testpath.mkdir(parents=True)
 
-    file_path = testpath.joinpath(modname+'-test-suite').with_suffix('.cc')
-    name_parts = modname.split('-')
-    create_file(file_path, TEST_CC_TEMPLATE, MODULE=modname,
-                CAPITALIZED=''.join([word.capitalize() for word in name_parts]),
-                COMPOUND=''.join([word.capitalize() if index > 0 else word for index, word in enumerate(name_parts)]))
+    file_path = testpath.joinpath(modname + "-test-suite").with_suffix(".cc")
+    name_parts = modname.split("-")
+    create_file(
+        file_path,
+        TEST_CC_TEMPLATE,
+        MODULE=modname,
+        CAPITALIZED="".join([word.capitalize() for word in name_parts]),
+        COMPOUND="".join(
+            [word.capitalize() if index > 0 else word for index, word in enumerate(name_parts)]
+        ),
+    )
 
     return True
 
@@ -381,11 +383,11 @@
     helperpath = Path(moduledir, "helper")
     helperpath.mkdir(parents=True)
 
-    srcfile_path = helperpath.joinpath(modname+'-helper').with_suffix('.cc')
+    srcfile_path = helperpath.joinpath(modname + "-helper").with_suffix(".cc")
     create_file(srcfile_path, HELPER_CC_TEMPLATE, MODULE=modname)
 
-    h_file_path = helperpath.joinpath(modname+'-helper').with_suffix('.h')
-    guard = "{}_HELPER_H".format(modname.replace('-', '_').upper())
+    h_file_path = helperpath.joinpath(modname + "-helper").with_suffix(".h")
+    guard = "{}_HELPER_H".format(modname.replace("-", "_").upper())
     create_file(h_file_path, HELPER_H_TEMPLATE, MODULE=modname, INCLUDE_GUARD=guard)
 
     return True
@@ -395,10 +397,10 @@
     examplespath = Path(moduledir, "examples")
     examplespath.mkdir(parents=True)
 
-    cmakelistspath = Path(examplespath, 'CMakeLists.txt')
+    cmakelistspath = Path(examplespath, "CMakeLists.txt")
     create_file(cmakelistspath, EXAMPLES_CMAKELISTS_TEMPLATE, MODULE=modname)
 
-    examplesfile_path = examplespath.joinpath(modname+'-example').with_suffix('.cc')
+    examplesfile_path = examplespath.joinpath(modname + "-example").with_suffix(".cc")
     create_file(examplesfile_path, EXAMPLE_CC_TEMPLATE, MODULE=modname)
 
     return True
@@ -408,11 +410,11 @@
     docpath = Path(moduledir, "doc")
     docpath.mkdir(parents=True)
 
-    #the module_dir template parameter must be a relative path
-    #instead of an absolute path
+    # the module_dir template parameter must be a relative path
+    # instead of an absolute path
     mod_relpath = os.path.relpath(str(moduledir))
 
-    file_name = '{}.rst'.format(modname)
+    file_name = "{}.rst".format(modname)
     file_path = Path(docpath, file_name)
     create_file(file_path, DOC_RST_TEMPLATE, MODULE=modname, MODULE_DIR=mod_relpath)
 
@@ -428,8 +430,7 @@
 
     print("Creating module {}".format(modulepath))
 
-    functions = (make_cmakelists, make_model, make_test,
-                 make_helper, make_examples, make_doc)
+    functions = (make_cmakelists, make_model, make_test, make_helper, make_examples, make_doc)
 
     try:
         modulepath.mkdir(parents=True)
@@ -449,6 +450,7 @@
 
     return True
 
+
 def create_argument_parser():
     description = """Generate scaffolding for ns-3 modules
 
@@ -527,25 +529,36 @@
 
     formatter = argparse.RawDescriptionHelpFormatter
 
-    parser = argparse.ArgumentParser(description=description,
-                                     epilog=epilog,
-                                     formatter_class=formatter)
-
-    parser.add_argument('--project', default='',
-                        help=("Specify a relative path under the contrib directory "
-                            "where the new modules will be generated. The path "
-                            "will be created if it does not exist."))
-
-    parser.add_argument('modnames', nargs='+',
-                        help=("One or more modules to generate.  Module names "
-                            "are limited to the following: letters, numbers, -, "
-                            "_. Modules are generated under the contrib directory "
-                            "except when the module name starts with src/. Modules "
-                            "that start with src/ are generated under the src "
-                            "directory."))
+    parser = argparse.ArgumentParser(
+        description=description, epilog=epilog, formatter_class=formatter
+    )
+
+    parser.add_argument(
+        "--project",
+        default="",
+        help=(
+            "Specify a relative path under the contrib directory "
+            "where the new modules will be generated. The path "
+            "will be created if it does not exist."
+        ),
+    )
+
+    parser.add_argument(
+        "modnames",
+        nargs="+",
+        help=(
+            "One or more modules to generate.  Module names "
+            "are limited to the following: letters, numbers, -, "
+            "_. Modules are generated under the contrib directory "
+            "except when the module name starts with src/. Modules "
+            "that start with src/ are generated under the src "
+            "directory."
+        ),
+    )
 
     return parser
 
+
 def main(argv):
     parser = create_argument_parser()
 
@@ -556,46 +569,47 @@
 
     base_path = Path.cwd()
 
-    src_path = base_path.joinpath('src')
-    contrib_path = base_path.joinpath('contrib')
+    src_path = base_path.joinpath("src")
+    contrib_path = base_path.joinpath("contrib")
 
     for p in (src_path, contrib_path):
         if not p.is_dir():
-            parser.error("Cannot find the directory '{}'.\nPlease run this "
-                        "script from the top level of the ns3 directory".format(
-                            p))
+            parser.error(
+                "Cannot find the directory '{}'.\nPlease run this "
+                "script from the top level of the ns3 directory".format(p)
+            )
 
     #
     # Error check the arguments
     #
 
     # Alphanumeric and '-' only
-    allowedRE = re.compile('^(\w|-)+$')
+    allowedRE = re.compile("^(\w|-)+$")
 
     project_path = None
 
     if project:
-        #project may be a path in the form a/b/c
-        #remove any leading or trailing path separators
+        # project may be a path in the form a/b/c
+        # remove any leading or trailing path separators
         project_path = Path(project)
 
         if project_path.is_absolute():
-            #remove leading separator
+            # remove leading separator
             project_path = project_path.relative_to(os.sep)
 
         if not all(allowedRE.match(part) for part in project_path.parts):
-            parser.error('Project path may only contain the characters [a-zA-Z0-9_-].')
+            parser.error("Project path may only contain the characters [a-zA-Z0-9_-].")
     #
     # Create each module, if it doesn't exist
     #
     modules = []
     for name in modnames:
         if name:
-            #remove any leading or trailing directory separators
+            # remove any leading or trailing directory separators
             name = name.strip(os.sep)
 
         if not name:
-            #skip empty modules
+            # skip empty modules
             continue
 
         name_path = Path(name)
@@ -604,33 +618,41 @@
             print("Skipping {}: module name can not be a path".format(name))
             continue
 
-        #default target directory is contrib
+        # default target directory is contrib
         modpath = contrib_path
 
-        if name_path.parts[0] == 'src':
+        if name_path.parts[0] == "src":
             if project:
-                parser.error("{}: Cannot specify src/ in a module name when --project option is used".format(name))
+                parser.error(
+                    "{}: Cannot specify src/ in a module name when --project option is used".format(
+                        name
+                    )
+                )
 
             modpath = src_path
 
-            #create a new path without the src part
-            name_path = name_path.relative_to('src')
+            # create a new path without the src part
+            name_path = name_path.relative_to("src")
 
-        elif name_path.parts[0] == 'contrib':
+        elif name_path.parts[0] == "contrib":
             modpath = contrib_path
 
-            #create a new path without the contrib part
-            name_path = name_path.relative_to('contrib')
+            # create a new path without the contrib part
+            name_path = name_path.relative_to("contrib")
 
         if project_path:
-            #if a project path was specified, that overrides other paths
-            #project paths are always relative to the contrib path
+            # if a project path was specified, that overrides other paths
+            # project paths are always relative to the contrib path
             modpath = contrib_path.joinpath(project_path)
 
         modname = name_path.parts[0]
 
         if not allowedRE.match(modname):
-            print("Skipping {}: module name may only contain the characters [a-zA-Z0-9_-]".format(modname))
+            print(
+                "Skipping {}: module name may only contain the characters [a-zA-Z0-9_-]".format(
+                    modname
+                )
+            )
             continue
 
         modules.append((modpath, modname))
@@ -642,7 +664,8 @@
 
     return 0
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     return_value = 0
     try:
         return_value = main(sys.argv)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/grid.py ns-3.41/utils/grid.py
--- ns-3.40/utils/grid.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/grid.py	2024-02-09 14:44:08.629795300 -0800
@@ -1,8 +1,9 @@
 #!/usr/bin/env python3
 
-import cairo
-import sys
 import re
+import sys
+
+import cairo
 import gtk
 
 
@@ -14,7 +15,7 @@
     #  end
     ## @var value
     #  value
-    def __init__(self, start=0, end=0, value=''):
+    def __init__(self, start=0, end=0, value=""):
         """! Initializer
         @param self this object
         @param start start
@@ -24,13 +25,15 @@
         self.start = start
         self.end = end
         self.value = value
+
+
 ## EventString class
 class EventString:
     ## @var at
     #  at
     ## @var value
     #  value
-    def __init__(self, at=0, value=''):
+    def __init__(self, at=0, value=""):
         """! Initializer
         @param self this object
         @param at you
@@ -38,6 +41,8 @@
         """
         self.at = at
         self.value = value
+
+
 ## EventFloat class
 class EventFloat:
     ## @var at
@@ -52,6 +57,8 @@
         """
         self.at = at
         self.value = value
+
+
 ## EventInt class
 class EventInt:
     ## @var at
@@ -66,6 +73,8 @@
         """
         self.at = at
         self.value = value
+
+
 def ranges_cmp(a, b):
     diff = a.start - b.start
     if diff < 0:
@@ -74,6 +83,8 @@
         return +1
     else:
         return 0
+
+
 def events_cmp(a, b):
     diff = a.at - b.at
     if diff < 0:
@@ -82,13 +93,15 @@
         return +1
     else:
         return 0
+
+
 ## TimelineDataRange
 class TimelineDataRange:
     ## @var name
     #  name
     ## @var ranges
     #  ranges
-    def __init__(self, name=''):
+    def __init__(self, name=""):
         """! Initializer
         @param self this object
         @param name name
@@ -96,6 +109,7 @@
         self.name = name
         self.ranges = []
         return
+
     def __search(self, key):
         """! Search
         @param self this object
@@ -103,7 +117,7 @@
         @return index if found or -1 if not found
         """
         l = 0
-        u = len(self.ranges)-1
+        u = len(self.ranges) - 1
         while l <= u:
             i = int((l + u) / 2)
             if key >= self.ranges[i].start and key <= self.ranges[i].end:
@@ -113,7 +127,8 @@
             else:
                 # key > self.ranges[i].end
                 l = i + 1
-        return - 1
+        return -1
+
     def add_range(self, range):
         """! Add range
         @param self this object
@@ -121,12 +136,14 @@
         @return none
         """
         self.ranges.append(range)
+
     def get_all(self):
         """! Get all ranges
         @param self this object
         @return the ranges
         """
         return self.ranges
+
     def get_ranges(self, start, end):
         """! Get selected ranges
         @param self this object
@@ -139,11 +156,12 @@
         if s == -1 and e == -1:
             return []
         elif s == -1:
-            return self.ranges[0:e + 1]
+            return self.ranges[0 : e + 1]
         elif e == -1:
-            return self.ranges[s:len(self.ranges)]
+            return self.ranges[s : len(self.ranges)]
         else:
-            return self.ranges[s:e + 1]
+            return self.ranges[s : e + 1]
+
     def get_ranges_bounds(self, start, end):
         """! Get ranges bounds
         @param self this object
@@ -161,12 +179,14 @@
             return (s, len(self.ranges))
         else:
             return (s, e + 1)
+
     def sort(self):
         """! Sort ranges
         @param self this object
         @return none
         """
         self.ranges.sort(ranges_cmp)
+
     def get_bounds(self):
         """! Get bounds
         @param self this object
@@ -174,23 +194,26 @@
         """
         if len(self.ranges) > 0:
             lo = self.ranges[0].start
-            hi = self.ranges[len(self.ranges)-1].end
+            hi = self.ranges[len(self.ranges) - 1].end
             return (lo, hi)
         else:
             return (0, 0)
+
+
 ## TimelineEvent class
 class TimelineEvent:
     ## @var name
     #  name
     ## @var events
     #  events
-    def __init__(self, name=''):
+    def __init__(self, name=""):
         """! Get ranges bounds
         @param self this object
         @param name name
         """
         self.name = name
         self.events = []
+
     def __search(self, key):
         """! Search function
         @param self this object
@@ -198,7 +221,7 @@
         @return event index
         """
         l = 0
-        u = len(self.events)-1
+        u = len(self.events) - 1
         while l <= u:
             i = int((l + u) / 2)
             if key == self.events[i].at:
@@ -209,6 +232,7 @@
                 # key > self.events[i].at
                 l = i + 1
         return l
+
     def add_event(self, event):
         """! Add Event
         @param self this object
@@ -216,6 +240,7 @@
         @return none
         """
         self.events.append(event)
+
     def get_events(self, start, end):
         """! Get Events
         @param self this object
@@ -225,7 +250,8 @@
         """
         s = self.__search(start)
         e = self.__search(end)
-        return self.events[s:e + 1]
+        return self.events[s : e + 1]
+
     def get_events_bounds(self, start, end):
         """! Get Events Bounds
         @param self this object
@@ -236,12 +262,14 @@
         s = self.__search(start)
         e = self.__search(end)
         return (s, e + 1)
+
     def sort(self):
         """! Sort function
         @param self this object
         @return none
         """
         self.events.sort(events_cmp)
+
     def get_bounds(self):
         """! Get Bounds
         @param self this object
@@ -254,6 +282,7 @@
         else:
             return (0, 0)
 
+
 ## Timeline class
 class Timeline:
     ## @var name
@@ -264,7 +293,7 @@
     #  event string
     ## @var event_int
     #  event int
-    def __init__(self, name=''):
+    def __init__(self, name=""):
         """! Initializer
         @param self this object
         @param name name
@@ -273,6 +302,7 @@
         self.event_str = []
         self.event_int = []
         self.name = name
+
     def get_range(self, name):
         """! Get range
         @param self this object
@@ -285,6 +315,7 @@
         timeline = TimelineDataRange(name)
         self.ranges.append(timeline)
         return timeline
+
     def get_event_str(self, name):
         """! Get Event String
         @param self this object
@@ -297,6 +328,7 @@
         timeline = TimelineEvent(name)
         self.event_str.append(timeline)
         return timeline
+
     def get_event_int(self, name):
         """! Get Event Int
         @param self this object
@@ -309,24 +341,28 @@
         timeline = TimelineEvent(name)
         self.event_int.append(timeline)
         return timeline
+
     def get_ranges(self):
         """! Get Ranges
         @param self this object
         @return the ranges
         """
         return self.ranges
+
     def get_events_str(self):
         """! Get Events string
         @param self this object
         @return event string
         """
         return self.event_str
+
     def get_events_int(self):
         """! Get Events int
         @param self this object
         @return evrnt int
         """
         return self.event_int
+
     def sort(self):
         """! Sort the ranges and events
         @param self this object
@@ -338,6 +374,7 @@
             event.sort()
         for event in self.event_str:
             event.sort()
+
     def get_bounds(self):
         """! Get Bounds
         @param self this object
@@ -365,15 +402,17 @@
                 hi = ev_hi
         return (lo, hi)
 
+
 ## Timelines class
 class Timelines:
     ## @var timelines
     #  timelines
     def __init__(self):
-        """ Initializer
+        """Initializer
         @param self: this object
         """
         self.timelines = []
+
     def get(self, name):
         """! Get Timeline
         @param self this object
@@ -386,12 +425,14 @@
         timeline = Timeline(name)
         self.timelines.append(timeline)
         return timeline
+
     def get_all(self):
         """! Get All Timeline
         @param self this object
         @return all timelines
         """
         return self.timelines
+
     def sort(self):
         """! Sort the timelines
         @param self this object
@@ -399,6 +440,7 @@
         """
         for timeline in self.timelines:
             timeline.sort()
+
     def get_bounds(self):
         """! Get Bounds
         @param self this object
@@ -413,6 +455,7 @@
             if t_hi > hi:
                 hi = t_hi
         return (lo, hi)
+
     def get_all_range_values(self):
         """! Get All Ranges
         @param self this object
@@ -425,6 +468,7 @@
                     range_values[ran.value] = 1
         return range_values.keys()
 
+
 ## Color class
 class Color:
     ## @var r
@@ -443,6 +487,7 @@
         self.r = r
         self.g = g
         self.b = b
+
     def set(self, r, g, b):
         """! Set color
         @param self: this object
@@ -455,6 +500,7 @@
         self.g = g
         self.b = b
 
+
 ## Colors class
 class Colors:
     ## @var __colors
@@ -462,12 +508,21 @@
     ## @var default_colors
     #  default colors
     ## XXX add more
-    default_colors = [Color(1, 0, 0), Color(0, 1, 0), Color(0, 0, 1), Color(1, 1, 0), Color(1, 0, 1), Color(0, 1, 1)]
+    default_colors = [
+        Color(1, 0, 0),
+        Color(0, 1, 0),
+        Color(0, 0, 1),
+        Color(1, 1, 0),
+        Color(1, 0, 1),
+        Color(0, 1, 1),
+    ]
+
     def __init__(self):
         """! Initializer
         @param self this object
         """
         self.__colors = {}
+
     def add(self, name, color):
         """! Add
         @param self this object
@@ -476,6 +531,7 @@
         @return none
         """
         self.__colors[name] = color
+
     def lookup(self, name):
         """! Lookup name
         @param self this object
@@ -486,6 +542,7 @@
             self.add(name, self.default_colors.pop())
         return self.__colors.get(name)
 
+
 ## TopLegendRenderer class
 class TopLegendRenderer:
     ## @var __padding
@@ -503,6 +560,7 @@
         @param self this object
         """
         self.__padding = 10
+
     def set_padding(self, padding):
         """! Set padding
         @param self this object
@@ -510,6 +568,7 @@
         @return none
         """
         self.__padding = padding
+
     def set_legends(self, legends, colors):
         """! Set padding
         @param self this object
@@ -519,6 +578,7 @@
         """
         self.__legends = legends
         self.__colors = colors
+
     def layout(self, width):
         """! Set padding
         @param self this object
@@ -552,6 +612,7 @@
         @return height
         """
         return self.__height
+
     def draw(self, ctx):
         """! Set padding
         @param self this object
@@ -578,15 +639,14 @@
             ctx.set_source_rgb(0, 0, 0)
             ctx.set_line_width(2)
             ctx.stroke_preserve()
-            ctx.set_source_rgb(self.__colors[i].r,
-                               self.__colors[i].g,
-                               self.__colors[i].b)
+            ctx.set_source_rgb(self.__colors[i].r, self.__colors[i].g, self.__colors[i].b)
             ctx.fill()
-            ctx.move_to(x + self.__padding*2, total_height + t_height)
+            ctx.move_to(x + self.__padding * 2, total_height + t_height)
             ctx.set_source_rgb(0, 0, 0)
             ctx.show_text(legend)
             i += 1
 
+
 ## TimelinesRenderer class
 class TimelinesRenderer:
     ## @var padding
@@ -617,12 +677,14 @@
         """
         self.padding = 10
         return
+
     def get_height(self):
         """! Get Height
         @param self this object
         @return height
         """
         return self.height
+
     def set_timelines(self, timelines, colors):
         """! Set Timelines
         @param self this object
@@ -632,6 +694,7 @@
         """
         self.timelines = timelines
         self.colors = colors
+
     def set_render_range(self, start, end):
         """! Set Render Range
         @param self this object
@@ -641,12 +704,16 @@
         """
         self.start = start
         self.end = end
+
     def get_data_x_start(self):
         """! Get Data X Start
         @param self: this object
         @return X start
         """
-        return self.padding / 2 + self.left_width + self.padding + self.right_width + self.padding / 2
+        return (
+            self.padding / 2 + self.left_width + self.padding + self.right_width + self.padding / 2
+        )
+
     def layout(self, width):
         """! Get Data X Start
         @param self this object
@@ -655,7 +722,9 @@
         """
         surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1)
         ctx = cairo.Context(surface)
-        max_text_height = ctx.text_extents("ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789")[3]
+        max_text_height = ctx.text_extents(
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789"
+        )[3]
 
         left_width = 0
         right_width = 0
@@ -694,6 +763,7 @@
         self.max_text_height = max_text_height
         self.width = width
         self.height = height + self.padding
+
     def draw_line(self, ctx, x, y, width, height):
         """! Draw Line
         @param self this object
@@ -711,6 +781,7 @@
         ctx.set_line_width(1.0)
         ctx.set_source_rgb(0, 0, 0)
         ctx.stroke()
+
     def draw_events(self, ctx, events, x, y, width, height):
         """! Draw Event
         @param self this object
@@ -723,8 +794,7 @@
         @return none
         """
         if (self.grey_background % 2) == 0:
-            ctx.rectangle(x, y - self.padding / 2,
-                          width, height + self.padding)
+            ctx.rectangle(x, y - self.padding / 2, width, height + self.padding)
             ctx.set_source_rgb(0.9, 0.9, 0.9)
             ctx.fill()
         last_x_drawn = int(x)
@@ -740,6 +810,7 @@
                 ctx.show_text(str(event.value))
                 last_x_drawn = real_x
         self.grey_background += 1
+
     def draw_ranges(self, ctx, ranges, x, y, width, height):
         """! Draw Ranges
         @param self this object
@@ -752,8 +823,7 @@
         @return none
         """
         if (self.grey_background % 2) == 0:
-            ctx.rectangle(x, y - self.padding / 2,
-                          width, height + self.padding)
+            ctx.rectangle(x, y - self.padding / 2, width, height + self.padding)
             ctx.set_source_rgb(0.9, 0.9, 0.9)
             ctx.fill()
         last_x_drawn = int(x - 1)
@@ -800,34 +870,51 @@
                 (y_bearing, t_width, t_height) = ctx.text_extents(events_int.name)[1:4]
                 ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing))
                 ctx.show_text(events_int.name)
-                self.draw_events(ctx, events_int, data_x_start, cur_y, data_width, self.max_text_height + 5)
+                self.draw_events(
+                    ctx, events_int, data_x_start, cur_y, data_width, self.max_text_height + 5
+                )
                 cur_y += self.max_text_height + 5 + self.padding
-                self.draw_line(ctx, right_x_start - self.padding / 2, cur_y - self.padding / 2,
-                               self.right_width + self.padding, 0)
+                self.draw_line(
+                    ctx,
+                    right_x_start - self.padding / 2,
+                    cur_y - self.padding / 2,
+                    self.right_width + self.padding,
+                    0,
+                )
 
             for events_str in timeline.get_events_str():
                 (y_bearing, t_width, t_height) = ctx.text_extents(events_str.name)[1:4]
                 ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing))
                 ctx.show_text(events_str.name)
-                self.draw_events(ctx, events_str, data_x_start, cur_y, data_width, self.max_text_height + 5)
+                self.draw_events(
+                    ctx, events_str, data_x_start, cur_y, data_width, self.max_text_height + 5
+                )
                 cur_y += self.max_text_height + 5 + self.padding
-                self.draw_line(ctx, right_x_start - self.padding / 2, cur_y - self.padding / 2,
-                               self.right_width + self.padding, 0)
+                self.draw_line(
+                    ctx,
+                    right_x_start - self.padding / 2,
+                    cur_y - self.padding / 2,
+                    self.right_width + self.padding,
+                    0,
+                )
             for ranges in timeline.get_ranges():
                 (y_bearing, t_width, t_height) = ctx.text_extents(ranges.name)[1:4]
                 ctx.move_to(right_x_start, cur_y + self.max_text_height - (t_height + y_bearing))
                 ctx.show_text(ranges.name)
                 self.draw_ranges(ctx, ranges, data_x_start, cur_y, data_width, 10)
                 cur_y += self.max_text_height + self.padding
-                self.draw_line(ctx, right_x_start - self.padding / 2, cur_y - self.padding / 2,
-                               self.right_width + self.padding, 0)
-            self.draw_line(ctx, 0, cur_y - self.padding / 2,
-                           self.width, 0)
+                self.draw_line(
+                    ctx,
+                    right_x_start - self.padding / 2,
+                    cur_y - self.padding / 2,
+                    self.right_width + self.padding,
+                    0,
+                )
+            self.draw_line(ctx, 0, cur_y - self.padding / 2, self.width, 0)
         bot_y = cur_y - self.padding / 2
-        self.draw_line(ctx, left_x_end + self.padding / 2, 0,
-                       0, bot_y)
-        self.draw_line(ctx, right_x_end + self.padding / 2, 0,
-                       0, bot_y)
+        self.draw_line(ctx, left_x_end + self.padding / 2, 0, 0, bot_y)
+        self.draw_line(ctx, right_x_end + self.padding / 2, 0, 0, bot_y)
+
 
 ## ScaleRenderer class
 class ScaleRenderer:
@@ -851,6 +938,7 @@
         """
         self.__top = 0
         return
+
     def set_bounds(self, lo, hi):
         """! Set Bounds
         @param self this object
@@ -860,6 +948,7 @@
         """
         self.__lo = lo
         self.__hi = hi
+
     def get_position(self, x):
         """! Get Position
         @param self this object
@@ -868,18 +957,21 @@
         """
         real_x = (x - self.__lo) * self.__width / (self.__hi - self.__lo)
         return real_x
+
     def set_top(self):
         """! Set Top
         @param self this object
         @return none
         """
         self.__top = 1
+
     def set_bot(self):
         """! Set Bottom
         @param self this object
         @return none
         """
         self.__top = 0
+
     def layout(self, width):
         """! Layout
         @param self this object
@@ -892,7 +984,7 @@
         # calculate scale delta
         data_delta = self.__hi - self.__lo
         closest = 1
-        while (closest*10) < data_delta:
+        while (closest * 10) < data_delta:
             closest *= 10
         if (data_delta / closest) == 0:
             delta = closest
@@ -907,7 +999,9 @@
         self.__width = width
 
         # calculate text height
-        max_text_height = ctx.text_extents("ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789")[3]
+        max_text_height = ctx.text_extents(
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcedefghijklmnopqrstuvwxyz0123456789"
+        )[3]
         self.max_text_height = max_text_height
         height = max_text_height + 10
         self.__height = height
@@ -918,6 +1012,7 @@
         @return height
         """
         return self.__height
+
     def draw(self, ctx):
         """! Draw
         @param self this object
@@ -939,7 +1034,7 @@
         for x in ticks:
             real_x = (x - self.__lo) * self.__width / (self.__hi - self.__lo)
             ctx.move_to(real_x, 0)
-            ctx.line_to(real_x, 5*s)
+            ctx.line_to(real_x, 5 * s)
             ctx.close_path()
             ctx.stroke()
             (t_y_bearing, t_width, t_height) = ctx.text_extents(str(x))[1:4]
@@ -947,7 +1042,7 @@
                 text_delta = t_height + t_y_bearing
             else:
                 text_delta = -t_y_bearing
-            ctx.move_to(real_x - t_width / 2, (5 + 5 + text_delta)*s)
+            ctx.move_to(real_x - t_width / 2, (5 + 5 + text_delta) * s)
             ctx.show_text(str(x))
         # draw subticks
         delta /= 10
@@ -957,7 +1052,7 @@
             for x in range(int(start), int(end + delta), int(delta)):
                 real_x = (x - self.__lo) * self.__width / (self.__hi - self.__lo)
                 ctx.move_to(real_x, 0)
-                ctx.line_to(real_x, 3*s)
+                ctx.line_to(real_x, 3 * s)
                 ctx.close_path()
                 ctx.stroke()
 
@@ -1001,18 +1096,21 @@
         self.__bot_scale.set_bot()
         self.__width = 1
         self.__height = 1
+
     def get_width(self):
         """! Get Width
         @param self: this object
         @return width
         """
         return self.__width
+
     def get_height(self):
         """! Get Height
         @param self this object
         @return height
         """
         return self.__height
+
     # return x, y, width, height
     def get_data_rectangle(self):
         """! Get Data Rectangle
@@ -1022,6 +1120,7 @@
         y_start = self.__top_legend.get_height()
         x_start = self.__data.get_data_x_start()
         return (x_start, y_start, self.__width - x_start, self.__data.get_height())
+
     def scale_data(self, x):
         """! Get Data Rectangle
         @param self this object
@@ -1031,17 +1130,24 @@
         x_start = self.__data.get_data_x_start()
         x_scaled = x / (self.__width - x_start) * (self.__r_end - self.__r_start)
         return x_scaled
+
     # return x, y, width, height
     def get_selection_rectangle(self):
         """! Get Selection Rectangle
         @param self this object
         @return rectangle
         """
-        y_start = self.__top_legend.get_height() + self.__data.get_height() + self.__mid_scale.get_height() + 20
+        y_start = (
+            self.__top_legend.get_height()
+            + self.__data.get_height()
+            + self.__mid_scale.get_height()
+            + 20
+        )
         y_height = self.__bot_scale.get_height() + 20
         x_start = self.__bot_scale.get_position(self.__r_start)
         x_end = self.__bot_scale.get_position(self.__r_end)
         return (x_start, y_start, x_end - x_start, y_height)
+
     def scale_selection(self, x):
         """! Scale Selection
         @param self this object
@@ -1050,6 +1156,7 @@
         """
         x_scaled = x / self.__width * (self.__end - self.__start)
         return x_scaled
+
     def set_range(self, start, end):
         """! Set Range
         @param self this object
@@ -1066,12 +1173,14 @@
         self.__data.set_render_range(start, end)
         self.__mid_scale.set_bounds(start, end)
         self.layout(self.__width, self.__height)
+
     def get_range(self):
         """! Get Range
         @param self this object
         @return range
         """
         return (self.__r_start, self.__r_end)
+
     def set_data(self, data):
         """! Set Date
         @param self this object
@@ -1079,6 +1188,7 @@
         @return none
         """
         self.__data = data
+
     def set_top_legend(self, top_legend):
         """! Set Top Legend
         @param self this object
@@ -1086,6 +1196,7 @@
         @return none
         """
         self.__top_legend = top_legend
+
     def layout(self, width, height):
         """! Set Layout
         @param self this object
@@ -1101,6 +1212,7 @@
         self.__mid_scale.layout(width - self.__data.get_data_x_start())
         self.__bot_scale.layout(width)
         return
+
     def __x_pixel(self, x, width):
         """! X Pixel
         @param self this object
@@ -1140,15 +1252,16 @@
 
         # data
         ctx.save()
-        ctx.translate(0,
-                      top_legend_height)
+        ctx.translate(0, top_legend_height)
         self.__data.draw(ctx)
         ctx.restore()
 
         # scale below data
         ctx.save()
-        ctx.translate(self.__data.get_data_x_start(),
-                      top_legend_height + self.__data.get_height() + self.__mid_scale.get_height())
+        ctx.translate(
+            self.__data.get_data_x_start(),
+            top_legend_height + self.__data.get_height() + self.__mid_scale.get_height(),
+        )
         self.__mid_scale.draw(ctx)
         ctx.restore()
 
@@ -1202,13 +1315,8 @@
         unused_start = self.__bot_scale.get_position(self.__r_start)
         unused_end = self.__bot_scale.get_position(self.__r_end)
         unused_height = self.__bot_scale.get_height() + 20
-        ctx.rectangle(0, height_used,
-                      unused_start,
-                      unused_height)
-        ctx.rectangle(unused_end,
-                      height_used,
-                      self.__width - unused_end,
-                      unused_height)
+        ctx.rectangle(0, height_used, unused_start, unused_height)
+        ctx.rectangle(unused_end, height_used, self.__width - unused_end, unused_height)
         ctx.set_source_rgb(0.9, 0.9, 0.9)
         ctx.fill()
 
@@ -1248,6 +1356,7 @@
         self.__bot_scale.draw(ctx)
         ctx.restore()
 
+
 ## GtkGraphicRenderer class
 class GtkGraphicRenderer(gtk.DrawingArea):
     ## @var __data
@@ -1279,7 +1388,17 @@
     ## @var __height
     #  height
     ## @var __buffer_surface
-    #  buffer surface
+    # __buffer_surface
+    ## @var expose
+    #  expose function
+    ## @var size_allocate
+    #  size_allocate function
+    ## @var motion_notify
+    #  motion_notify function
+    ## @var button_press
+    #  button_press function
+    ## @var button_release
+    #  button_release function
     def __init__(self, data):
         """! Initializer
         @param self this object
@@ -1296,19 +1415,21 @@
         self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
         self.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
         self.connect("expose_event", self.expose)
-        self.connect('size-allocate', self.size_allocate)
-        self.connect('motion-notify-event', self.motion_notify)
-        self.connect('button-press-event', self.button_press)
-        self.connect('button-release-event', self.button_release)
+        self.connect("size-allocate", self.size_allocate)
+        self.connect("motion-notify-event", self.motion_notify)
+        self.connect("button-press-event", self.button_press)
+        self.connect("button-release-event", self.button_release)
+
     def set_smaller_zoom(self):
         """! Set Smaller Zoom
         @param self this object
         @return none
         """
         (start, end) = self.__data.get_range()
-        self.__data.set_range(start, start + (end - start)*2)
+        self.__data.set_range(start, start + (end - start) * 2)
         self.__force_full_redraw = True
         self.queue_draw()
+
     def set_bigger_zoom(self):
         """! Set Bigger Zoom
         @param self this object
@@ -1318,18 +1439,20 @@
         self.__data.set_range(start, start + (end - start) / 2)
         self.__force_full_redraw = True
         self.queue_draw()
+
     def output_png(self, filename):
         """! Output PNG
         @param self this object
         @param filename file name
         @return none
         """
-        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
-                                     self.__data.get_width(),
-                                     self.__data.get_height())
+        surface = cairo.ImageSurface(
+            cairo.FORMAT_ARGB32, self.__data.get_width(), self.__data.get_height()
+        )
         ctx = cairo.Context(self.__buffer_surface)
         self.__data.draw(ctx)
         surface.write_to_png(filename)
+
     def button_press(self, widget, event):
         """! Button Press
         @param self this object
@@ -1358,6 +1481,7 @@
                 self.__moving_top_cur = event.x
                 return True
         return False
+
     def button_release(self, widget, event):
         """! Button Release
         @param self this object
@@ -1392,6 +1516,7 @@
         if self.__moving_top:
             self.__moving_top = False
         return False
+
     def motion_notify(self, widget, event):
         """! Motion Notify
         @param self this object
@@ -1420,7 +1545,7 @@
             return True
         if self.__moving_both:
             cur_e = self.__width - (x + width - self.__moving_both_start)
-            cur_s = (self.__moving_both_start - x)
+            cur_s = self.__moving_both_start - x
             if event.x < cur_s:
                 self.__moving_both_cur = cur_s
             elif event.x > cur_e:
@@ -1452,6 +1577,7 @@
                 return True
         widget.window.set_cursor(None)
         return False
+
     def size_allocate(self, widget, allocation):
         """! Size Allocate
         @param self this object
@@ -1464,6 +1590,7 @@
         self.__data.layout(allocation.width, allocation.height)
         self.__force_full_redraw = True
         self.queue_draw()
+
     def expose(self, widget, event):
         """! Expose
         @param self this object
@@ -1472,15 +1599,14 @@
         @return false
         """
         if self.__force_full_redraw:
-            self.__buffer_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
-                                                       self.__data.get_width(),
-                                                       self.__data.get_height())
+            self.__buffer_surface = cairo.ImageSurface(
+                cairo.FORMAT_ARGB32, self.__data.get_width(), self.__data.get_height()
+            )
             ctx = cairo.Context(self.__buffer_surface)
             self.__data.draw(ctx)
             self.__force_full_redraw = False
         ctx = widget.window.cairo_create()
-        ctx.rectangle(event.area.x, event.area.y,
-                      event.area.width, event.area.height)
+        ctx.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
         ctx.clip()
         ctx.set_source_surface(self.__buffer_surface)
         ctx.paint()
@@ -1513,6 +1639,7 @@
             ctx.stroke()
         return False
 
+
 ## MainWindow class
 class MainWindow:
     ## @var __window
@@ -1521,11 +1648,20 @@
     #  render
     ## @var __dialog
     #  dialog
+    ## @var __set_smaller_cb
+    #  __set_smaller_cb function
+    ## @var __set_bigger_cb
+    #  __set_bigger_cb function
+    ## @var __output_png_cb
+    #  __output_png_cb function
+    ## @var __dialog_response_cb
+    #  __dialog_response_cb function
     def __init__(self):
         """! Initializer
         @param self this object
         """
         return
+
     def run(self, graphic):
         """! Run function
         @param self this object
@@ -1551,10 +1687,11 @@
         output_png = gtk.Button("Output Png")
         output_png.connect("clicked", self.__output_png_cb)
         hbox.pack_start(output_png)
-        window.connect('destroy', gtk.main_quit)
+        window.connect("destroy", gtk.main_quit)
         window.show_all()
-        #gtk.bindings_activate(gtk.main_quit, 'q', 0)
+        # gtk.bindings_activate(gtk.main_quit, 'q', 0)
         gtk.main()
+
     def __set_smaller_cb(self, widget):
         """! Set Smaller Callback
         @param self this object
@@ -1562,6 +1699,7 @@
         @return none
         """
         self.__render.set_smaller_zoom()
+
     def __set_bigger_cb(self, widget):
         """! Set Bigger Callback
         @param self this object
@@ -1569,18 +1707,21 @@
         @return none
         """
         self.__render.set_bigger_zoom()
+
     def __output_png_cb(self, widget):
         """! Output PNG Callback
         @param self this object
         @param widget widget
         @return none
         """
-        dialog = gtk.FileChooserDialog("Output Png", self.__window,
-                                       gtk.FILE_CHOOSER_ACTION_SAVE, ("Save", 1))
+        dialog = gtk.FileChooserDialog(
+            "Output Png", self.__window, gtk.FILE_CHOOSER_ACTION_SAVE, ("Save", 1)
+        )
         self.__dialog = dialog
         dialog.set_default_response(1)
         dialog.connect("response", self.__dialog_response_cb)
         dialog.show()
+
     def __dialog_response_cb(self, widget, response):
         """! Dialog Response Callback
         @param self this object
@@ -1598,12 +1739,12 @@
 def read_data(filename):
     timelines = Timelines()
     colors = Colors()
-    m1 = re.compile('range ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)')
-    m2 = re.compile('event-str ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+)')
-    m3 = re.compile('event-int ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)')
-    m4 = re.compile('color ([^ ]+) #([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})')
+    m1 = re.compile("range ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)")
+    m2 = re.compile("event-str ([^ ]+) ([^ ]+) ([^ ]+) ([0-9]+)")
+    m3 = re.compile("event-int ([^ ]+) ([^ ]+) ([0-9]+) ([0-9]+)")
+    m4 = re.compile("color ([^ ]+) #([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})([a-fA-F0-9]{2,2})")
 
-    with open(filename, encoding='utf-8') as fh:
+    with open(filename, encoding="utf-8") as fh:
         for line in fh.readlines():
             m = m1.match(line)
             if m:
@@ -1658,8 +1799,7 @@
     range_colors = []
     for range_value in range_values:
         range_colors.append(colors.lookup(range_value))
-    top_legend.set_legends(range_values,
-                           range_colors)
+    top_legend.set_legends(range_values, range_colors)
     graphic.set_top_legend(top_legend)
     data = TimelinesRenderer()
     data.set_timelines(timelines, colors)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/python-unit-tests.py ns-3.41/utils/python-unit-tests.py
--- ns-3.40/utils/python-unit-tests.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/python-unit-tests.py	2024-02-09 14:44:08.629795300 -0800
@@ -19,7 +19,15 @@
 # Author: Gustavo J. A. M. Carneiro <gjc@inescporto.pt>
 
 import unittest
-from ns import ns
+
+try:
+    from ns import ns
+except ModuleNotFoundError:
+    raise SystemExit(
+        "Error: ns3 Python module not found;"
+        " Python bindings may not be enabled"
+        " or your PYTHONPATH might not be properly configured"
+    )
 import sys
 
 UINT32_MAX = 0xFFFFFFFF
@@ -53,12 +61,14 @@
         ns.Simulator.Destroy()
         self._args_received = None
         self._cb_time = None
-        ns.cppyy.cppdef("""
+        ns.cppyy.cppdef(
+            """
             EventImpl* pythonMakeEvent(void (*f)(std::vector<std::string>), std::vector<std::string> l)
             {
                 return MakeEvent(f, l);
             }
-        """)
+        """
+        )
         event = ns.cppyy.gbl.pythonMakeEvent(callback, sys.argv)
         ns.Simulator.ScheduleNow(event)
         ns.Simulator.Run()
@@ -82,12 +92,14 @@
         ns.Simulator.Destroy()
         self._args_received = None
         self._cb_time = None
-        ns.cppyy.cppdef("""
+        ns.cppyy.cppdef(
+            """
             EventImpl* pythonMakeEvent2(void (*f)(std::vector<std::string>), std::vector<std::string> l)
             {
                 return MakeEvent(f, l);
             }
-        """)
+        """
+        )
         event = ns.cppyy.gbl.pythonMakeEvent2(callback, sys.argv)
         ns.Simulator.Schedule(ns.Seconds(123), event)
         ns.Simulator.Run()
@@ -113,12 +125,14 @@
         self._cb_time = None
         ns.cppyy.cppdef("void null(){ return; }")
         ns.Simulator.Schedule(ns.Seconds(123), ns.cppyy.gbl.null)
-        ns.cppyy.cppdef("""
+        ns.cppyy.cppdef(
+            """
             EventImpl* pythonMakeEvent3(void (*f)(std::vector<std::string>), std::vector<std::string> l)
             {
                 return MakeEvent(f, l);
             }
-        """)
+        """
+        )
         event = ns.cppyy.gbl.pythonMakeEvent3(callback, sys.argv)
         ns.Simulator.ScheduleDestroy(event)
         ns.Simulator.Run()
@@ -146,12 +160,14 @@
         self._args_received = None
         self._cb_time = None
         self._context_received = None
-        ns.cppyy.cppdef("""
+        ns.cppyy.cppdef(
+            """
             EventImpl* pythonMakeEvent4(void (*f)(uint32_t, std::vector<std::string>), uint32_t context, std::vector<std::string> l)
             {
                 return MakeEvent(f, context, l);
             }
-        """)
+        """
+        )
         event = ns.cppyy.gbl.pythonMakeEvent4(callback, 54321, sys.argv)
         ns.Simulator.ScheduleWithContext(54321, ns.Seconds(123), event)
         ns.Simulator.Run()
@@ -203,20 +219,29 @@
         def python_rx_callback(socket) -> None:
             self._received_packet = socket.Recv(maxSize=UINT32_MAX, flags=0)
 
-        ns.cppyy.cppdef("""
+        ns.cppyy.cppdef(
+            """
             Callback<void,ns3::Ptr<ns3::Socket> > make_rx_callback_test_socket(void(*func)(Ptr<Socket>))
             {
                 return MakeCallback(func);
             }
-        """)
+        """
+        )
 
-        sink = ns.network.Socket.CreateSocket(node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory"))
+        sink = ns.network.Socket.CreateSocket(
+            node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory")
+        )
         sink.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), 80).ConvertTo())
         sink.SetRecvCallback(ns.cppyy.gbl.make_rx_callback_test_socket(python_rx_callback))
 
-        source = ns.network.Socket.CreateSocket(node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory"))
-        source.SendTo(ns.network.Packet(19), 0,
-                      ns.network.InetSocketAddress(ns.network.Ipv4Address("127.0.0.1"), 80).ConvertTo())
+        source = ns.network.Socket.CreateSocket(
+            node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory")
+        )
+        source.SendTo(
+            ns.network.Packet(19),
+            0,
+            ns.network.InetSocketAddress(ns.network.Ipv4Address("127.0.0.1"), 80).ConvertTo(),
+        )
 
         ns.Simulator.Run()
         self.assertTrue(self._received_packet is not None)
@@ -290,7 +315,7 @@
         @param self this object
         @return None
         """
-        from ctypes import c_bool, c_int, c_double, c_char_p, create_string_buffer
+        from ctypes import c_bool, c_char_p, c_double, c_int, create_string_buffer
 
         test1 = c_bool(True)
         test2 = c_int(42)
@@ -355,12 +380,12 @@
         stack.Install(nodes)
 
         address = ns.internet.Ipv4AddressHelper()
-        address.SetBase(ns.network.Ipv4Address("10.1.1.0"),
-                        ns.network.Ipv4Mask("255.255.255.0"))
+        address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
 
         interfaces = address.Assign(devices)
 
-        ns.cppyy.cppdef("""
+        ns.cppyy.cppdef(
+            """
             namespace ns3
             {
                 Callback<void,Ptr<Socket> > make_rx_callback(void(*func)(Ptr<Socket>))
@@ -372,7 +397,8 @@
                     return MakeEvent(f, socket, packet, address);
                 }
             }
-        """)
+        """
+        )
 
         ## EchoServer application class
         class EchoServer(ns.applications.Application):
@@ -388,13 +414,19 @@
                 return None
                 """
                 super().__init__()
+                ## __python_owns__ flag indicates that Cppyy should not manage the lifetime of this variable
                 self.__python_owns__ = False  # Let C++ destroy this on Simulator::Destroy
                 ## Listen port for the server
                 self.port = port
                 ## Socket used by the server to listen to port
-                self.m_socket = ns.network.Socket.CreateSocket(node,
-                                                               ns.core.TypeId.LookupByName("ns3::UdpSocketFactory"))
-                self.m_socket.Bind(ns.network.InetSocketAddress(ns.network.Ipv4Address.GetAny(), self.port).ConvertTo())
+                self.m_socket = ns.network.Socket.CreateSocket(
+                    node, ns.core.TypeId.LookupByName("ns3::UdpSocketFactory")
+                )
+                self.m_socket.Bind(
+                    ns.network.InetSocketAddress(
+                        ns.network.Ipv4Address.GetAny(), self.port
+                    ).ConvertTo()
+                )
                 self.m_socket.SetRecvCallback(ns.make_rx_callback(EchoServer._Receive))
                 EchoServer.socketToInstanceDict[self.m_socket] = self
 
@@ -415,13 +447,16 @@
                 self.m_socket.SendTo(packet, 0, address)
                 if EchoServer.LOGGING:
                     inetAddress = ns.InetSocketAddress.ConvertFrom(address)
-                    print("At time +{s}s server sent {b} bytes from {ip} port {port}"
-                          .format(s=ns.Simulator.Now().GetSeconds(),
-                                  b=packet.__deref__().GetSize(),
-                                  ip=inetAddress.GetIpv4(),
-                                  port=inetAddress.GetPort()),
-                          file=sys.stderr,
-                          flush=True)
+                    print(
+                        "At time +{s}s server sent {b} bytes from {ip} port {port}".format(
+                            s=ns.Simulator.Now().GetSeconds(),
+                            b=packet.__deref__().GetSize(),
+                            ip=inetAddress.GetIpv4(),
+                            port=inetAddress.GetPort(),
+                        ),
+                        file=sys.stderr,
+                        flush=True,
+                    )
 
             def Receive(self):
                 """! Function to receive a packet from an address
@@ -432,13 +467,16 @@
                 packet = self.m_socket.RecvFrom(address)
                 if EchoServer.LOGGING:
                     inetAddress = ns.InetSocketAddress.ConvertFrom(address)
-                    print("At time +{s}s server received {b} bytes from {ip} port {port}"
-                          .format(s=ns.Simulator.Now().GetSeconds(),
-                                  b=packet.__deref__().GetSize(),
-                                  ip=inetAddress.GetIpv4(),
-                                  port=inetAddress.GetPort()),
-                          file=sys.stderr,
-                          flush=True)
+                    print(
+                        "At time +{s}s server received {b} bytes from {ip} port {port}".format(
+                            s=ns.Simulator.Now().GetSeconds(),
+                            b=packet.__deref__().GetSize(),
+                            ip=inetAddress.GetIpv4(),
+                            port=inetAddress.GetPort(),
+                        ),
+                        file=sys.stderr,
+                        flush=True,
+                    )
                 event = ns.pythonMakeEventSend(EchoServer._Send, self.m_socket, packet, address)
                 ns.Simulator.Schedule(ns.Seconds(1), event)
 
@@ -486,5 +524,5 @@
         ns.Simulator.Destroy()
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main(verbosity=1, failfast=True)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-alpine.yml ns-3.41/utils/tests/gitlab-ci-alpine.yml
--- ns-3.40/utils/tests/gitlab-ci-alpine.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-alpine.yml	2024-02-09 14:44:08.629795300 -0800
@@ -20,6 +20,8 @@
       python3
       bash
       iproute2 linux-headers
+      openmpi openmpi-dev
+      openssh
   variables:
     COMPILER: g++
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-clang.yml ns-3.41/utils/tests/gitlab-ci-clang.yml
--- ns-3.40/utils/tests/gitlab-ci-clang.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-clang.yml	2024-02-09 14:44:08.629795300 -0800
@@ -20,7 +20,6 @@
     - DEBIAN_FRONTEND=noninteractive apt install -y
       clang-$CLANG cmake ninja-build ccache build-essential
       libboost-all-dev
-      libeigen3-dev
       libgtk-3-0 libgtk-3-dev
       libgsl-dev $LIBGSL gsl-bin
       libsqlite3-dev sqlite3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-code-linting.yml ns-3.41/utils/tests/gitlab-ci-code-linting.yml
--- ns-3.40/utils/tests/gitlab-ci-code-linting.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-code-linting.yml	2024-02-09 14:44:08.629795300 -0800
@@ -15,6 +15,11 @@
     - python3 utils/check-style-clang-format.py --verbose .
   timeout: 1h
 
+check-style-clang-format-17:
+  extends: .check-style-clang-format
+  variables:
+    CLANG_FORMAT_VERSION: 17
+
 check-style-clang-format-16:
   extends: .check-style-clang-format
   variables:
@@ -31,7 +36,7 @@
     CLANG_FORMAT_VERSION: 14
 
 # Clang-tidy
-clang-tidy-16:
+clang-tidy-17:
   stage: code-linting
   image: ubuntu:rolling
   variables:
@@ -42,16 +47,14 @@
     - apt update
     - DEBIAN_FRONTEND=noninteractive apt install -y
       clang cmake
-      clang-tidy clang-tidy-16
-      python3 python3-pip
+      clang-tidy clang-tidy-17
       libboost-all-dev libeigen3-dev libgtk-3-dev libopenmpi-dev libsqlite3-dev
       gsl-bin libgsl-dev libgsl27
       git ssh
-    - pip3 install --break-system-packages cppyy
     - ./ns3 configure -d debug
       --enable-clang-tidy
       --enable-examples --enable-tests --enable-asserts
-      --enable-mpi --enable-python-bindings
+      --enable-mpi
   script:
     - if (git remote | grep -qw upstream) ; then
       git remote remove upstream ;
@@ -59,27 +62,25 @@
     - git remote add -t $CI_DEFAULT_BRANCH --no-tags -f upstream https://gitlab.com/nsnam/ns-3-dev.git
     - git diff --name-only upstream/$CI_DEFAULT_BRANCH > $FILES_CHANGED
 
-    # Run clang-tidy-diff in the opposite cases of the if-else below.
+    # Run full clang-tidy in the following cases: 1) default branch, 2) ".clang-tidy" file changed
+    # Run clang-tidy-diff in the opposite cases.
     # File paths generated by git diff are relative to the working tree. Therefore, iregex should only contain paths relative to the working tree.
-    - if !([[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]] || (grep -q ".clang-tidy" $FILES_CHANGED)) ; then
-      echo "Running clang-tidy-diff" ;
-      git diff -U0 upstream/$CI_DEFAULT_BRANCH |
-      clang-tidy-diff-16.py -path cmake-cache/ -p1 -quiet -use-color
-      -iregex "(contrib|examples|src|scratch|utils)\/.+\.(cpp|cc|cxx|c|h|hpp)"
+    - if [[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]] || (grep -q ".clang-tidy" $FILES_CHANGED) ; then
+      echo "Running full clang-tidy" ;
+      run-clang-tidy-17 -p cmake-cache/ -quiet
       1> $CLANG_TIDY_OUTPUT
       2> /dev/null ;
-      fi
-
-    # Run full clang-tidy in the following cases: 1) default branch, 2) ".clang-tidy" file changed, 3) clang-tidy-diff had an error
-    - if [[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]] || (grep -q ".clang-tidy" $FILES_CHANGED) || (egrep -q -A 3 "file not found \[clang-diagnostic-error\]" $CLANG_TIDY_OUTPUT) ; then
-      echo "Running full clang-tidy" ;
-      run-clang-tidy-16 -p cmake-cache/ -quiet
+      else
+      echo "Running clang-tidy-diff" ;
+      git diff -U0 upstream/$CI_DEFAULT_BRANCH |
+      clang-tidy-diff-17.py -path cmake-cache/ -p1 -quiet -use-color
+      -iregex "(contrib|examples|src|scratch|utils)\/.+\.(cpp|cc|cxx|h|hpp)"
       1> $CLANG_TIDY_OUTPUT
       2> /dev/null ;
       fi
 
     # Check job results
-    - (! egrep -A 3 "error:|warning:|note:" $CLANG_TIDY_OUTPUT)
+    - (! egrep -v "file not found \[clang-diagnostic-error\]" $CLANG_TIDY_OUTPUT | egrep -A3 "error:|warning:|note:")
     - echo "No clang-tidy errors found"
   dependencies: []
   artifacts:
@@ -120,3 +121,32 @@
     # Check source code and commit messages
     - codespell -f -C0 ./
   timeout: 1h
+
+# Check cmake format
+cmake-format:
+  stage: .pre
+  image: python:latest
+  before_script:
+    - pip install pyyaml cmake cmake-format ninja
+  script:
+    - ./ns3 configure --enable-modules=core
+    - ./ns3 build cmake-format-check
+  timeout: 1h
+
+# Check Python format
+.python-format:
+  stage: .pre
+  before_script:
+    - pip install black isort
+  script:
+    - black --check .
+    - isort --check .
+  timeout: 1h
+
+python-format-latest:
+  extends: .python-format
+  image: python:latest
+
+python-format-3.6:
+  extends: .python-format
+  image: python:3.6
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-cppyy.yml ns-3.41/utils/tests/gitlab-ci-cppyy.yml
--- ns-3.40/utils/tests/gitlab-ci-cppyy.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-cppyy.yml	2024-02-09 14:44:08.629795300 -0800
@@ -2,10 +2,7 @@
   stage: test
   image: ubuntu:22.04 # python 3.10
   rules:
-    - if: $RELEASE == "weekly"
-    - if: $RELEASE == "daily"
     - if: $RELEASE == "manual"
-    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
   dependencies: []
   before_script:
     - apt update
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-doc.yml ns-3.41/utils/tests/gitlab-ci-doc.yml
--- ns-3.40/utils/tests/gitlab-ci-doc.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-doc.yml	2024-02-09 14:44:08.629795300 -0800
@@ -46,9 +46,10 @@
     # - doc/doxygen.warnings.report.sh -f "$CHANGED_FILES"
     - doc/doxygen.warnings.report.sh
     # Just need to check doc/introspected-command-line.h and doc/introspected-doxygen.h
-    - python3 utils/check-style-clang-format.py --no-formatting --no-tabs doc/introspected-doxygen.h
-    - python3 utils/check-style-clang-format.py --no-formatting --no-tabs doc/introspected-command-line.h
+    - python3 utils/check-style-clang-format.py --no-formatting --no-tabs doc/introspected-doxygen.h doc/introspected-command-line.h
     - ./ns3 clean
+    # print the doxygen version - just to double check what has been used.
+    - doxygen -v
     # - doxygen doc/doxygen.conf
     # - mv doc/html/ public/doxygen
   cache:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-gcc.yml ns-3.41/utils/tests/gitlab-ci-gcc.yml
--- ns-3.40/utils/tests/gitlab-ci-gcc.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-gcc.yml	2024-02-09 14:44:08.629795300 -0800
@@ -37,8 +37,6 @@
       libeigen3-dev
       qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
       ssh
-  variables:
-    ENABLE_MPI: --enable-mpi
 
 weekly-gcc:
   rules:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-per-commit.yml ns-3.41/utils/tests/gitlab-ci-per-commit.yml
--- ns-3.40/utils/tests/gitlab-ci-per-commit.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-per-commit.yml	2024-02-09 14:44:08.629795300 -0800
@@ -17,8 +17,6 @@
       python
       boost gsl gtk3 openmpi
       openssh
-  variables:
-    ENABLE_MPI: --enable-mpi
 
 # Build stage
 per-commit-clang-debug:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-release.yml ns-3.41/utils/tests/gitlab-ci-release.yml
--- ns-3.40/utils/tests/gitlab-ci-release.yml	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.41/utils/tests/gitlab-ci-release.yml	2024-02-09 14:44:08.629795300 -0800
@@ -0,0 +1,25 @@
+# ns-3 CI/CD script GitLab Releases
+#
+# Contains jobs to generate automatic GitLab releases of stable ns-3 versions.
+
+gitlab-release:
+  stage: release
+  image: registry.gitlab.com/gitlab-org/release-cli:latest
+  rules:
+    - if: $CI_COMMIT_TAG
+
+  script:
+    - echo "Creating a new release for tag $CI_COMMIT_TAG"
+
+  release:
+    name: $CI_COMMIT_TAG
+    description: "$CI_COMMIT_TAG release"
+    tag_name: $CI_COMMIT_TAG
+    assets:
+      links:
+        - name: Official Releases (nsnam.org)
+          url: https://www.nsnam.org/releases/
+        - name: RELEASE_NOTES.md
+          url: $CI_PROJECT_URL/-/blob/$CI_DEFAULT_BRANCH/RELEASE_NOTES.md
+        - name: CHANGES.md
+          url: $CI_PROJECT_URL/-/blob/$CI_DEFAULT_BRANCH/CHANGES.md
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci-scheduled.yml ns-3.41/utils/tests/gitlab-ci-scheduled.yml
--- ns-3.40/utils/tests/gitlab-ci-scheduled.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci-scheduled.yml	2024-02-09 14:44:08.629795300 -0800
@@ -14,14 +14,16 @@
     - pacman -Syu --noconfirm
       base-devel cmake ninja ccache valgrind
       python
-      boost gsl gtk3
+      boost gsl gtk3 openmpi
       glibc-debug
+      openssh
   script:
     - mkdir -p $CCACHE_BASEDIR_VALUE
     - export CCACHE_BASEDIR=${PWD}
     - export CCACHE_DIR=${PWD}/$CCACHE_BASEDIR_VALUE
+    - export MPI_CI=1
     # missing the flag --disable-python
-    - ./ns3 configure -d $MODE -GNinja --enable-examples --enable-tests --disable-gtk
+    - ./ns3 configure -d $MODE -GNinja --enable-examples --enable-tests --enable-mpi --disable-gtk
     - if [[ "$CI_JOB_STAGE" == "build" ]];
       then ./ns3 build;
       fi
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/gitlab-ci.yml ns-3.41/utils/tests/gitlab-ci.yml
--- ns-3.40/utils/tests/gitlab-ci.yml	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/gitlab-ci.yml	2024-02-09 14:44:08.629795300 -0800
@@ -23,6 +23,7 @@
   - test
   - code-linting
   - documentation
+  - release
 
 workflow:
   rules:
@@ -43,7 +44,7 @@
     - export CCACHE_BASEDIR=${PWD}
     - export CCACHE_DIR=${PWD}/$CCACHE_BASEDIR_VALUE
     - export MPI_CI=1
-    - CXX=$COMPILER ./ns3 configure -d $MODE -GNinja --enable-examples --enable-tests --enable-asserts --enable-werror $ENABLE_MPI
+    - CXX=$COMPILER ./ns3 configure -d $MODE -GNinja --enable-examples --enable-tests --enable-asserts --enable-werror --enable-mpi
       $EXTRA_OPTIONS
     - ccache -z
     - if [[ "$CI_JOB_STAGE" == "build" ]];
@@ -93,3 +94,4 @@
   - "utils/tests/gitlab-ci-code-linting.yml"
   - "utils/tests/gitlab-ci-doc.yml"
   - "utils/tests/gitlab-ci-cppyy.yml"
+  - "utils/tests/gitlab-ci-release.yml"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/README.md ns-3.41/utils/tests/README.md
--- ns-3.40/utils/tests/README.md	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/README.md	2024-02-09 14:44:08.629795300 -0800
@@ -1,7 +1,6 @@
-./utils/tests directory
-=======================
+# ./utils/tests directory
 
-## Table of Contents:
+## Table of Contents
 
 1) [What is](#what-is)
 2) [Gitlab CI infrastructure](#gitlab-ci-infrastructure)
@@ -13,7 +12,7 @@
 
 ## Gitlab CI infrastructure
 
-To know more about the Gitlab CI feature, please refer to https://about.gitlab.com/product/continuous-integration/. We use the services offered by Gitlab to test our software in multiple ways. If you are interested how to run these tests on your fork, please read below.
+To know more about the Gitlab CI feature, please refer to <https://about.gitlab.com/product/continuous-integration/>. We use the services offered by Gitlab to test our software in multiple ways. If you are interested how to run these tests on your fork, please read below.
 
 Note: each user does have a limited amount of "minutes" (time used to run CI jobs). Minutes are limited, and if you run out of minutes you will not be able to run jobs until the next month - or until you pay for more minutes.
 
@@ -27,7 +26,7 @@
 - `gitlab-ci-scheduled.yml`: daily and weekly ordinary and slow jobs.
 - `gitlab-ci-gcc.yml`: jobs to test various GCC versions.
 
-If you did fork ns-3, the CI should already point to the right configureation file. Otherwise, you will have to select the `gitlab-ci.yml` in your fork configuration. Note that Gitlab requires a user verification (usually it requires a valid credit card) to run CI jobs. If you did skip the verification process the CI will always fail.
+If you did fork ns-3, the CI should already point to the right configuration file. Otherwise, you will have to select the `gitlab-ci.yml` in your fork configuration. Note that Gitlab requires a user verification (usually it requires a valid credit card) to run CI jobs. If you did skip the verification process the CI will always fail.
 
 ### CI pipelines
 
@@ -79,7 +78,6 @@
 
 **Note**: The job-level timeout can exceed the [project-level](https://docs.gitlab.com/ee/ci/pipelines/settings.html#set-a-limit-for-how-long-jobs-can-run) timeout (default: 60 min), but can not exceed the Runner-specific timeout.
 
-
 To summarize, if you are unsure about the commit you're about to
 merge into master, you can push it to a branch of your fork, then go to
 the Gitlab interface, click CI/CD, click Schedules, create a New
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/TestBase.py ns-3.41/utils/tests/TestBase.py
--- ns-3.40/utils/tests/TestBase.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/TestBase.py	2024-02-09 14:44:08.629795300 -0800
@@ -17,42 +17,52 @@
 #
 
 from __future__ import print_function
-import sys
-import subprocess
+
 import argparse
 import os
+import subprocess
+import sys
+
 
 def print_case_in_file(case_string, out):
     for i in range(100):
-        print("-", end='', file=out)
+        print("-", end="", file=out)
     print(file=out)
-    print("running test case " + case_string, end='\n\n', file=out)
+    print("running test case " + case_string, end="\n\n", file=out)
     out.flush()
 
+
 def print_failed_cases(failed_cases):
     print("\nFailed Cases:")
     for case in failed_cases:
         print(case)
 
+
 def print_cmds(cmds):
-    print('Commands to be executed:')
+    print("Commands to be executed:")
     for cmd in cmds:
-        print(cmd.replace(sys.executable, ''))
+        print(cmd.replace(sys.executable, ""))
+
 
 def set_workdir():
-    dir_files = [f for f in os.listdir('.') if os.path.exists(f)]
-    if not 'VERSION' in dir_files and not 'ns3' in dir_files:
-        if os.path.split(os.path.abspath('.'))[1] == 'tests' and os.path.split(os.path.abspath(os.pardir))[1] == 'utils':
-            os.chdir('../../')
+    dir_files = [f for f in os.listdir(".") if os.path.exists(f)]
+    if not "VERSION" in dir_files and not "ns3" in dir_files:
+        if (
+            os.path.split(os.path.abspath("."))[1] == "tests"
+            and os.path.split(os.path.abspath(os.pardir))[1] == "utils"
+        ):
+            os.chdir("../../")
         else:
-            print('Error: Invalid working directory')
+            print("Error: Invalid working directory")
             sys.exit(1)
 
+
 ## TestBaseClass class
 class TestBaseClass:
     """
-        Generic class for testing tools based on provided commands and test cases.
+    Generic class for testing tools based on provided commands and test cases.
     """
+
     ## @var my_env
     #  os environment
     ## @var mode
@@ -72,9 +82,9 @@
         """
         self.my_env = os.environ
         set_workdir()
-        self.my_env['LD_LIBRARY_PATH'] = os.getcwd() + "/build"
+        self.my_env["LD_LIBRARY_PATH"] = os.getcwd() + "/build"
         self.mode = mode
-        self.outfile = 'test-port-'+self.mode+'.out'
+        self.outfile = "test-port-" + self.mode + ".out"
         self.options = self.parseargs(argv, desc)
 
     def parseargs(self, argv, desc):
@@ -86,15 +96,39 @@
         @return command line arguments
         """
         parser = argparse.ArgumentParser(description=desc)
-        parser.add_argument('-f', '--file', action='store', dest='out_file', default=self.outfile,
-                            metavar="FILE",
-                            help='File to be used for storing the command specific output (Default: '+self.outfile+')')
-        parser.add_argument('-c', action='store_true', dest='cmds', default=False,
-                            help='List out all the commands being tested')
-        parser.add_argument('-m', action='store_true', dest='mute', default=False,
-                            help='Sends only stderr output to FILE')
-        parser.add_argument('-x', '--customcmd', action='store', dest='custcmd', default=None,
-                            help='Enter a comma-separated list of commands to override the existing ones. NOT APPLICABLE FOR TEST-PY SUITE.')
+        parser.add_argument(
+            "-f",
+            "--file",
+            action="store",
+            dest="out_file",
+            default=self.outfile,
+            metavar="FILE",
+            help="File to be used for storing the command specific output (Default: "
+            + self.outfile
+            + ")",
+        )
+        parser.add_argument(
+            "-c",
+            action="store_true",
+            dest="cmds",
+            default=False,
+            help="List out all the commands being tested",
+        )
+        parser.add_argument(
+            "-m",
+            action="store_true",
+            dest="mute",
+            default=False,
+            help="Sends only stderr output to FILE",
+        )
+        parser.add_argument(
+            "-x",
+            "--customcmd",
+            action="store",
+            dest="custcmd",
+            default=None,
+            help="Enter a comma-separated list of commands to override the existing ones. NOT APPLICABLE FOR TEST-PY SUITE.",
+        )
         return parser.parse_args(argv)
 
     def override_cmds(self):
@@ -115,38 +149,39 @@
         if self.options.cmds:
             print_cmds(cmds)
             return
-        base_dir = os.sep.join(os.path.abspath(__file__).replace(os.path.pathsep, '/').split('/')[:-3])
+        base_dir = os.sep.join(
+            os.path.abspath(__file__).replace(os.path.pathsep, "/").split("/")[:-3]
+        )
         final_return = 0
         total_tests = len(cmds)
         passed = 0
         progress = 0.0
         failed_cases = []
-        with open(self.options.out_file, 'w', encoding='utf-8') as out:
+        with open(self.options.out_file, "w", encoding="utf-8") as out:
             outstream = out
-            with open(os.devnull, 'w', encoding='utf-8') as sink:
+            with open(os.devnull, "w", encoding="utf-8") as sink:
                 if self.options.mute:
                     outstream = sink
                 for cmd in cmds:
-                    case_string = cmd.replace(sys.executable, '')
+                    case_string = cmd.replace(sys.executable, "")
                     print("running test case: " + case_string)
                     print_case_in_file(case_string, out)
                     progress += 1
-                    ret = subprocess.call(cmd,
-                                          shell=True,
-                                          env=self.my_env,
-                                          stdout=outstream,
-                                          stderr=out,
-                                          cwd=base_dir
-                                          )
+                    ret = subprocess.call(
+                        cmd, shell=True, env=self.my_env, stdout=outstream, stderr=out, cwd=base_dir
+                    )
                     if not ret:
                         passed += 1
                     else:
                         final_return = 1
                         failed_cases.append(case_string)
-                    print("[ %s out of %s ] test cases passed; Progress = %.2f%% \n" % (passed, total_tests, progress*100/total_tests))
+                    print(
+                        "[ %s out of %s ] test cases passed; Progress = %.2f%% \n"
+                        % (passed, total_tests, progress * 100 / total_tests)
+                    )
         if final_return != 0:
             print_failed_cases(failed_cases)
         else:
             print("\nAll cases passed")
-        print("Detailed output available in " + self.options.out_file, end='\n\n')
+        print("Detailed output available in " + self.options.out_file, end="\n\n")
         return final_return
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/test-ns3.py ns-3.41/utils/tests/test-ns3.py
--- ns-3.40/utils/tests/test-ns3.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/test-ns3.py	2024-02-09 14:44:08.633795300 -0800
@@ -43,10 +43,14 @@
 
 # Cmake commands
 num_threads = max(1, os.cpu_count() - 1)
-cmake_build_project_command = "cmake --build . -j".format(ns3_path=ns3_path)
-cmake_build_target_command = partial("cmake --build . -j {jobs} --target {target}".format,
-                                     jobs=num_threads
-                                     )
+cmake_build_project_command = "cmake --build {cmake_cache} -j".format(
+    ns3_path=ns3_path, cmake_cache=os.path.abspath(os.path.join(ns3_path, "cmake-cache"))
+)
+cmake_build_target_command = partial(
+    "cmake --build {cmake_cache} -j {jobs} --target {target}".format,
+    jobs=num_threads,
+    cmake_cache=os.path.abspath(os.path.join(ns3_path, "cmake-cache")),
+)
 win32 = sys.platform == "win32"
 platform_makefiles = "MinGW Makefiles" if win32 else "Unix Makefiles"
 ext = ".exe" if win32 else ""
@@ -95,10 +99,10 @@
         arguments = [program]
 
     if args != "":
-        arguments.extend(re.findall("(?:\".*?\"|\S)+", args))  # noqa
+        arguments.extend(re.findall('(?:".*?"|\S)+', args))  # noqa
 
     for i in range(len(arguments)):
-        arguments[i] = arguments[i].replace("\"", "")
+        arguments[i] = arguments[i].replace('"', "")
 
     # Forward environment variables used by the ns3 script
     current_env = os.environ.copy()
@@ -114,10 +118,14 @@
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
         cwd=cwd,  # run process from the ns-3-dev path
-        env=current_env
+        env=current_env,
     )
     # Return (error code, stdout and stderr)
-    return ret.returncode, ret.stdout.decode(sys.stdout.encoding), ret.stderr.decode(sys.stderr.encoding)
+    return (
+        ret.returncode,
+        ret.stdout.decode(sys.stdout.encoding),
+        ret.stderr.decode(sys.stderr.encoding),
+    )
 
 
 def get_programs_list():
@@ -143,7 +151,7 @@
     @param lib_outdir: path containing libraries
     @return list of built libraries.
     """
-    libraries = glob.glob(lib_outdir + '/*', recursive=True)
+    libraries = glob.glob(lib_outdir + "/*", recursive=True)
     return list(filter(lambda x: "scratch-nested-subdir-lib" not in x, libraries))
 
 
@@ -153,7 +161,7 @@
     @param outdir: path containing headers
     @return list of headers.
     """
-    return glob.glob(outdir + '/**/*.h', recursive=True)
+    return glob.glob(outdir + "/**/*.h", recursive=True)
 
 
 def read_lock_entry(entry):
@@ -215,11 +223,13 @@
 
         # Create Docker client instance and start it
         ## The Python-on-whales container instance
-        self.container = docker.run(containerName,
-                                    interactive=True, detach=True,
-                                    tty=False,
-                                    volumes=[(ns3_path, "/ns-3-dev")]
-                                    )
+        self.container = docker.run(
+            containerName,
+            interactive=True,
+            detach=True,
+            tty=False,
+            volumes=[(ns3_path, "/ns-3-dev")],
+        )
 
         # Redefine the execute command of the container
         def split_exec(docker_container, cmd):
@@ -285,7 +295,9 @@
                 continue
 
             # Open the examples CMakeLists.txt and read it
-            with open(os.path.join(example_directory, "CMakeLists.txt"), "r", encoding="utf-8") as f:
+            with open(
+                os.path.join(example_directory, "CMakeLists.txt"), "r", encoding="utf-8"
+            ) as f:
                 cmake_contents = f.read()
 
             # For each file, check if it is in the CMake contents
@@ -328,8 +340,9 @@
                     unused_sources.add(file)
 
         # Remove temporary exceptions
-        exceptions = ["win32-system-wall-clock-ms.cc",  # Should be removed with MR784
-                      ]
+        exceptions = [
+            "win32-system-wall-clock-ms.cc",  # Should be removed with MR784
+        ]
         for exception in exceptions:
             for unused_source in unused_sources:
                 if os.path.basename(unused_source) == exception:
@@ -394,20 +407,25 @@
 
             module_name = os.path.relpath(path, ns3_path)
             module_name_nodir = module_name.replace("src/", "").replace("contrib/", "")
-            modules[module_name_nodir] = {"sources": set(),
-                                          "headers": set(),
-                                          "libraries": set(),
-                                          "included_headers": set(),
-                                          "included_libraries": set(),
-                                          }
+            modules[module_name_nodir] = {
+                "sources": set(),
+                "headers": set(),
+                "libraries": set(),
+                "included_headers": set(),
+                "included_libraries": set(),
+            }
 
             # Separate list of source files and header files
             for line in cmake_contents:
-                base_name = os.path.basename(line[:-1])
-                if not os.path.exists(os.path.join(path, line.strip())):
+                source_file_path = re.findall(r"\b(?:[^\s]+\.[ch]{1,2})\b", line.strip())
+                if not source_file_path:
+                    continue
+                source_file_path = source_file_path[0]
+                base_name = os.path.basename(source_file_path)
+                if not os.path.exists(os.path.join(path, source_file_path)):
                     continue
 
-                if ".h" in line:
+                if ".h" in source_file_path:
                     # Register all module headers as module headers and sources
                     modules[module_name_nodir]["headers"].add(base_name)
                     modules[module_name_nodir]["sources"].add(base_name)
@@ -415,24 +433,34 @@
                     # Register the header as part of the current module
                     headers_to_modules[base_name] = module_name_nodir
 
-                if ".cc" in line:
+                if ".cc" in source_file_path:
                     # Register the source file as part of the current module
                     modules[module_name_nodir]["sources"].add(base_name)
 
-                if ".cc" in line or ".h" in line:
+                if ".cc" in source_file_path or ".h" in source_file_path:
                     # Extract includes from headers and source files and then add to a list of included headers
-                    source_file = os.path.join(ns3_path, module_name, line.strip())
+                    source_file = os.path.join(ns3_path, module_name, source_file_path)
                     with open(source_file, "r", encoding="utf-8") as f:
                         source_contents = f.read()
-                    modules[module_name_nodir]["included_headers"].update(map(lambda x: x.replace("ns3/", ""),
-                                                                              re.findall("#include.*[\"|<](.*)[\"|>]",
-                                                                                         source_contents)
-                                                                              )
-                                                                          )
+                    modules[module_name_nodir]["included_headers"].update(
+                        map(
+                            lambda x: x.replace("ns3/", ""),
+                            re.findall('#include.*["|<](.*)["|>]', source_contents),
+                        )
+                    )
                     continue
 
             # Extract libraries linked to the module
-            modules[module_name_nodir]["libraries"].update(re.findall("\\${lib(.*)}", "".join(cmake_contents)))
+            modules[module_name_nodir]["libraries"].update(
+                re.findall("\${lib(.*?)}", "".join(cmake_contents))
+            )
+            modules[module_name_nodir]["libraries"] = list(
+                filter(
+                    lambda x: x
+                    not in ["raries_to_link", module_name_nodir, module_name_nodir + "-obj"],
+                    modules[module_name_nodir]["libraries"],
+                )
+            )
 
         # Now that we have all the information we need, check if we have all the included libraries linked
         all_project_headers = set(headers_to_modules.keys())
@@ -441,20 +469,70 @@
         print(file=sys.stderr)
         for module in sorted(modules):
             external_headers = modules[module]["included_headers"].difference(all_project_headers)
-            project_headers_included = modules[module]["included_headers"].difference(external_headers)
+            project_headers_included = modules[module]["included_headers"].difference(
+                external_headers
+            )
             modules[module]["included_libraries"] = set(
-                [headers_to_modules[x] for x in project_headers_included]).difference(
-                {module})
+                [headers_to_modules[x] for x in project_headers_included]
+            ).difference({module})
 
             diff = modules[module]["included_libraries"].difference(modules[module]["libraries"])
-            if len(diff) > 0:
-                print("Module %s includes modules that are not linked: %s" % (module, ", ".join(list(diff))),
-                      file=sys.stderr)
-                sys.stderr.flush()
-            # Uncomment this to turn into a real test
-            # self.assertEqual(len(diff), 0,
-            #                 msg="Module %s includes modules that are not linked: %s" % (module, ", ".join(list(diff)))
-            #                 )
+
+        # Find graph with least amount of edges based on included_libraries
+        def recursive_check_dependencies(checked_module):
+            # Remove direct explicit dependencies
+            for module_to_link in modules[checked_module]["included_libraries"]:
+                modules[checked_module]["included_libraries"] = set(
+                    modules[checked_module]["included_libraries"]
+                ) - set(modules[module_to_link]["included_libraries"])
+
+            for module_to_link in modules[checked_module]["included_libraries"]:
+                recursive_check_dependencies(module_to_link)
+
+            # Remove unnecessary implicit dependencies
+            def is_implicitly_linked(searched_module, current_module):
+                if len(modules[current_module]["included_libraries"]) == 0:
+                    return False
+                if searched_module in modules[current_module]["included_libraries"]:
+                    return True
+                for module in modules[current_module]["included_libraries"]:
+                    if is_implicitly_linked(searched_module, module):
+                        return True
+                return False
+
+            from itertools import combinations
+
+            implicitly_linked = set()
+            for dep1, dep2 in combinations(modules[checked_module]["included_libraries"], 2):
+                if is_implicitly_linked(dep1, dep2):
+                    implicitly_linked.add(dep1)
+                if is_implicitly_linked(dep2, dep1):
+                    implicitly_linked.add(dep2)
+
+            modules[checked_module]["included_libraries"] = (
+                set(modules[checked_module]["included_libraries"]) - implicitly_linked
+            )
+
+        for module in modules:
+            recursive_check_dependencies(module)
+
+        # Print findings
+        for module in sorted(modules):
+            if module == "test":
+                continue
+            minimal_linking_set = ", ".join(modules[module]["included_libraries"])
+            unnecessarily_linked = ", ".join(
+                set(modules[module]["libraries"]) - set(modules[module]["included_libraries"])
+            )
+            missing_linked = ", ".join(
+                set(modules[module]["included_libraries"]) - set(modules[module]["libraries"])
+            )
+            if unnecessarily_linked:
+                print(f"Module '{module}' unnecessarily linked: {unnecessarily_linked}.")
+            if missing_linked:
+                print(f"Module '{module}' missing linked: {missing_linked}.")
+            if unnecessarily_linked or missing_linked:
+                print(f"Module '{module}' minimal linking set: {minimal_linking_set}.")
         self.assertTrue(True)
 
 
@@ -475,13 +553,12 @@
         @return None
         """
         if not NS3StyleTestCase.starting_diff:
-
             if shutil.which("git") is None:
                 self.skipTest("Git is not available")
 
             try:
-                from git import Repo  # noqa
                 import git.exc  # noqa
+                from git import Repo  # noqa
             except ImportError:
                 self.skipTest("GitPython is not available")
 
@@ -604,7 +681,9 @@
         Test the debug build
         @return None
         """
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d debug --enable-verbose")
+        return_code, stdout, stderr = run_ns3(
+            'configure -G "{generator}" -d debug --enable-verbose'
+        )
         self.assertEqual(return_code, 0)
         self.assertIn("Build profile                 : debug", stdout)
         self.assertIn("Build files have been written to", stdout)
@@ -623,7 +702,7 @@
         Test the release build
         @return None
         """
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d release")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" -d release')
         self.assertEqual(return_code, 0)
         self.assertIn("Build profile                 : release", stdout)
         self.assertIn("Build files have been written to", stdout)
@@ -633,7 +712,9 @@
         Test the optimized build
         @return None
         """
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d optimized --enable-verbose")
+        return_code, stdout, stderr = run_ns3(
+            'configure -G "{generator}" -d optimized --enable-verbose'
+        )
         self.assertEqual(return_code, 0)
         self.assertIn("Build profile                 : optimized", stdout)
         self.assertIn("Build files have been written to", stdout)
@@ -652,7 +733,7 @@
         Test a build type with a typo
         @return None
         """
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d Optimized")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" -d Optimized')
         self.assertEqual(return_code, 2)
         self.assertIn("invalid choice: 'Optimized'", stderr)
 
@@ -661,7 +742,7 @@
         Test a build type with another typo
         @return None
         """
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d OPTIMIZED")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" -d OPTIMIZED')
         self.assertEqual(return_code, 2)
         self.assertIn("invalid choice: 'OPTIMIZED'", stderr)
 
@@ -673,31 +754,37 @@
         return_code, _, _ = run_ns3("clean")
         self.assertEqual(return_code, 0)
 
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --dry-run -d debug")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --dry-run -d debug')
         self.assertEqual(return_code, 0)
         self.assertIn(
             "-DCMAKE_BUILD_TYPE=debug -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON -DNS3_NATIVE_OPTIMIZATIONS=OFF",
-            stdout)
+            stdout,
+        )
 
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --dry-run -d debug --disable-asserts --disable-logs --disable-werror")
+            'configure -G "{generator}" --dry-run -d debug --disable-asserts --disable-logs --disable-werror'
+        )
         self.assertEqual(return_code, 0)
         self.assertIn(
             "-DCMAKE_BUILD_TYPE=debug -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_ASSERT=OFF -DNS3_LOG=OFF -DNS3_WARNINGS_AS_ERRORS=OFF",
-            stdout)
+            stdout,
+        )
 
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --dry-run")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --dry-run')
         self.assertEqual(return_code, 0)
         self.assertIn(
             "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=OFF -DNS3_NATIVE_OPTIMIZATIONS=OFF",
-            stdout)
+            stdout,
+        )
 
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --dry-run --enable-asserts --enable-logs --enable-werror")
+            'configure -G "{generator}" --dry-run --enable-asserts --enable-logs --enable-werror'
+        )
         self.assertEqual(return_code, 0)
         self.assertIn(
             "-DCMAKE_BUILD_TYPE=default -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_NATIVE_OPTIMIZATIONS=OFF -DNS3_ASSERT=ON -DNS3_LOG=ON -DNS3_WARNINGS_AS_ERRORS=ON",
-            stdout)
+            stdout,
+        )
 
 
 class NS3BaseTestCase(unittest.TestCase):
@@ -705,16 +792,18 @@
     Generic test case with basic function inherited by more complex tests.
     """
 
-    def config_ok(self, return_code, stdout):
+    def config_ok(self, return_code, stdout, stderr):
         """!
         Check if configuration for release mode worked normally
         @param return_code: return code from CMake
         @param stdout: output from CMake.
+        @param stderr: error from CMake.
         @return None
         """
         self.assertEqual(return_code, 0)
         self.assertIn("Build profile                 : release", stdout)
         self.assertIn("Build files have been written to", stdout)
+        self.assertNotIn("uninitialized variable", stderr)
 
     def setUp(self):
         """!
@@ -730,8 +819,10 @@
 
         # Reconfigure from scratch before each test
         run_ns3("clean")
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" -d release --enable-verbose")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            'configure -G "{generator}" -d release --enable-verbose'
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # Check if .lock-ns3 exists, then read to get list of executables.
         self.assertTrue(os.path.exists(ns3_lock_filename))
@@ -761,19 +852,20 @@
         Test enabling and disabling examples
         @return None
         """
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-examples")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
 
         # This just tests if we didn't break anything, not that we actually have enabled anything.
-        self.config_ok(return_code, stdout)
+        self.config_ok(return_code, stdout, stderr)
 
         # If nothing went wrong, we should have more executables in the list after enabling the examples.
+        ## ns3_executables
         self.assertGreater(len(get_programs_list()), len(self.ns3_executables))
 
         # Now we disabled them back.
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-examples")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --disable-examples')
 
         # This just tests if we didn't break anything, not that we actually have enabled anything.
-        self.config_ok(return_code, stdout)
+        self.config_ok(return_code, stdout, stderr)
 
         # Then check if they went back to the original list.
         self.assertEqual(len(get_programs_list()), len(self.ns3_executables))
@@ -784,8 +876,8 @@
         @return None
         """
         # Try enabling tests
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-tests")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-tests')
+        self.config_ok(return_code, stdout, stderr)
 
         # Then try building the libcore test
         return_code, stdout, stderr = run_ns3("build core-test")
@@ -795,8 +887,8 @@
         self.assertIn("Built target libcore-test", stdout)
 
         # Now we disabled the tests
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-tests")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --disable-tests')
+        self.config_ok(return_code, stdout, stderr)
 
         # Now building the library test should fail
         return_code, stdout, stderr = run_ns3("build core-test")
@@ -811,23 +903,28 @@
         @return None
         """
         # Try filtering enabled modules to network+Wi-Fi and their dependencies
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-modules='network;wifi'")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            "configure -G \"{generator}\" --enable-modules='network;wifi'"
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have fewer modules
         enabled_modules = get_enabled_modules()
+        ## ns3_modules
         self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
         self.assertIn("ns3-network", enabled_modules)
         self.assertIn("ns3-wifi", enabled_modules)
 
         # Try enabling only core
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-modules='core'")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            "configure -G \"{generator}\" --enable-modules='core'"
+        )
+        self.config_ok(return_code, stdout, stderr)
         self.assertIn("ns3-core", get_enabled_modules())
 
         # Try cleaning the list of enabled modules to reset to the normal configuration.
         return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-modules=''")
-        self.config_ok(return_code, stdout)
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have the same amount of modules that we had when we started.
         self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
@@ -838,8 +935,10 @@
         @return None
         """
         # Try filtering disabled modules to disable lte and modules that depend on it.
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-modules='lte;wimax'")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            "configure -G \"{generator}\" --disable-modules='lte;wimax'"
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have fewer modules.
         enabled_modules = get_enabled_modules()
@@ -849,7 +948,7 @@
 
         # Try cleaning the list of enabled modules to reset to the normal configuration.
         return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-modules=''")
-        self.config_ok(return_code, stdout)
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have the same amount of modules that we had when we started.
         self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
@@ -860,8 +959,10 @@
         @return None
         """
         # Try filtering enabled modules to network+Wi-Fi and their dependencies.
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-modules='network,wifi'")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            "configure -G \"{generator}\" --enable-modules='network,wifi'"
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have fewer modules.
         enabled_modules = get_enabled_modules()
@@ -871,7 +972,7 @@
 
         # Try cleaning the list of enabled modules to reset to the normal configuration.
         return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-modules=''")
-        self.config_ok(return_code, stdout)
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have the same amount of modules that we had when we started.
         self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
@@ -882,8 +983,10 @@
         @return None
         """
         # Try filtering disabled modules to disable lte and modules that depend on it.
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-modules='lte,mpi'")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            "configure -G \"{generator}\" --disable-modules='lte,mpi'"
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have fewer modules.
         enabled_modules = get_enabled_modules()
@@ -893,7 +996,7 @@
 
         # Try cleaning the list of enabled modules to reset to the normal configuration.
         return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-modules=''")
-        self.config_ok(return_code, stdout)
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have the same amount of modules that we had when we started.
         self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
@@ -928,18 +1031,18 @@
                                     "
 
             ## map ns3rc templates to types # noqa
-            ns3rc_templates = {
-                "python": ns3rc_python_template,
-                "cmake": ns3rc_cmake_template
-            }
+            ns3rc_templates = {"python": ns3rc_python_template, "cmake": ns3rc_cmake_template}
 
             def __init__(self, type_ns3rc):
+                ## type contains the ns3rc variant type (deprecated python-based or current cmake-based)
                 self.type = type_ns3rc
 
             def format(self, **args):
                 # Convert arguments from python-based ns3rc format to CMake
                 if self.type == "cmake":
-                    args["modules"] = args["modules"].replace("'", "").replace("\"", "").replace(",", " ")
+                    args["modules"] = (
+                        args["modules"].replace("'", "").replace('"', "").replace(",", " ")
+                    )
                     args["examples"] = "ON" if args["examples"] == "True" else "OFF"
                     args["tests"] = "ON" if args["tests"] == "True" else "OFF"
 
@@ -961,8 +1064,8 @@
                 f.write(ns3rc_template.format(modules="'lte'", examples="False", tests="True"))
 
             # Reconfigure.
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
-            self.config_ok(return_code, stdout)
+            return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
+            self.config_ok(return_code, stdout, stderr)
 
             # Check.
             enabled_modules = get_enabled_modules()
@@ -976,8 +1079,8 @@
                 f.write(ns3rc_template.format(modules="'wifi'", examples="True", tests="False"))
 
             # Reconfigure
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
-            self.config_ok(return_code, stdout)
+            return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
+            self.config_ok(return_code, stdout, stderr)
 
             # Check
             enabled_modules = get_enabled_modules()
@@ -988,11 +1091,15 @@
 
             # Replace the ns3rc file with multiple modules
             with open(ns3rc_script, "w", encoding="utf-8") as f:
-                f.write(ns3rc_template.format(modules="'core','network'", examples="True", tests="False"))
+                f.write(
+                    ns3rc_template.format(
+                        modules="'core','network'", examples="True", tests="False"
+                    )
+                )
 
             # Reconfigure
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
-            self.config_ok(return_code, stdout)
+            return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
+            self.config_ok(return_code, stdout, stderr)
 
             # Check
             enabled_modules = get_enabled_modules()
@@ -1006,19 +1113,28 @@
             # in various different ways and with comments
             with open(ns3rc_script, "w", encoding="utf-8") as f:
                 if ns3rc_type == "python":
-                    f.write(ns3rc_template.format(modules="""'core', #comment
+                    f.write(
+                        ns3rc_template.format(
+                            modules="""'core', #comment
                     'lte',
                     #comment2,
                     #comment3
-                    'network', 'internet','wimax'""", examples="True", tests="True"))
+                    'network', 'internet','wimax'""",
+                            examples="True",
+                            tests="True",
+                        )
+                    )
                 else:
-                    f.write(ns3rc_template.format(modules="'core', 'lte', 'network', 'internet', 'wimax'",
-                                                  examples="True",
-                                                  tests="True")
-                            )
+                    f.write(
+                        ns3rc_template.format(
+                            modules="'core', 'lte', 'network', 'internet', 'wimax'",
+                            examples="True",
+                            tests="True",
+                        )
+                    )
             # Reconfigure
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
-            self.config_ok(return_code, stdout)
+            return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
+            self.config_ok(return_code, stdout, stderr)
 
             # Check
             enabled_modules = get_enabled_modules()
@@ -1034,8 +1150,8 @@
             os.remove(ns3rc_script)
 
             # Reconfigure
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
-            self.config_ok(return_code, stdout)
+            return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
+            self.config_ok(return_code, stdout, stderr)
 
             # Check
             self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
@@ -1061,7 +1177,7 @@
         run_ns3("clean")
 
         # Build target before using below
-        run_ns3("configure -G \"{generator}\" -d release --enable-verbose")
+        run_ns3('configure -G "{generator}" -d release --enable-verbose')
         run_ns3("build scratch-simulator")
 
         # Run all cases and then check outputs
@@ -1107,7 +1223,7 @@
         return_code, _, _ = run_ns3("clean")
         self.assertEqual(return_code, 0)
 
-        return_code, _, _ = run_ns3("configure -G \"{generator}\" --enable-examples --enable-tests")
+        return_code, _, _ = run_ns3('configure -G "{generator}" --enable-examples --enable-tests')
         self.assertEqual(return_code, 0)
 
         # Build necessary executables
@@ -1115,25 +1231,30 @@
         self.assertEqual(return_code, 0)
 
         # Now some tests will succeed normally
-        return_code, stdout, stderr = run_ns3("run \"test-runner --test-name=command-line\" --no-build")
+        return_code, stdout, stderr = run_ns3(
+            'run "test-runner --test-name=command-line" --no-build'
+        )
         self.assertEqual(return_code, 0)
 
         # Now some tests will fail during NS_COMMANDLINE_INTROSPECTION
-        return_code, stdout, stderr = run_ns3("run \"test-runner --test-name=command-line\" --no-build",
-                                              env={"NS_COMMANDLINE_INTROSPECTION": ".."}
-                                              )
+        return_code, stdout, stderr = run_ns3(
+            'run "test-runner --test-name=command-line" --no-build',
+            env={"NS_COMMANDLINE_INTROSPECTION": ".."},
+        )
         self.assertNotEqual(return_code, 0)
 
         # Cause a sigsegv
         sigsegv_example = os.path.join(ns3_path, "scratch", "sigsegv.cc")
         with open(sigsegv_example, "w", encoding="utf-8") as f:
-            f.write("""
+            f.write(
+                """
                     int main (int argc, char *argv[])
                     {
                       char *s = "hello world"; *s = 'H';
                       return 0;
                     }
-                    """)
+                    """
+            )
         return_code, stdout, stderr = run_ns3("run sigsegv")
         if win32:
             self.assertEqual(return_code, 4294967295)  # unsigned -1
@@ -1145,7 +1266,8 @@
         # Cause an abort
         abort_example = os.path.join(ns3_path, "scratch", "abort.cc")
         with open(abort_example, "w", encoding="utf-8") as f:
-            f.write("""
+            f.write(
+                """
                     #include "ns3/core-module.h"
 
                     using namespace ns3;
@@ -1154,7 +1276,8 @@
                       NS_ABORT_IF(true);
                       return 0;
                     }
-                    """)
+                    """
+            )
         return_code, stdout, stderr = run_ns3("run abort")
         if win32:
             self.assertEqual(return_code, 3)
@@ -1192,7 +1315,7 @@
         if shutil.which("git") is None:
             self.skipTest("git is not available")
 
-        return_code, _, _ = run_ns3("configure -G \"{generator}\" --enable-build-version")
+        return_code, _, _ = run_ns3('configure -G "{generator}" --enable-build-version')
         self.assertEqual(return_code, 0)
 
         return_code, stdout, stderr = run_ns3("show version")
@@ -1206,11 +1329,13 @@
         @return None
         """
 
-        test_files = ["scratch/main.cc",
-                      "scratch/empty.cc",
-                      "scratch/subdir1/main.cc",
-                      "scratch/subdir2/main.cc",
-                      "scratch/main.test.dots.in.name.cc"]
+        test_files = [
+            "scratch/main.cc",
+            "scratch/empty.cc",
+            "scratch/subdir1/main.cc",
+            "scratch/subdir2/main.cc",
+            "scratch/main.test.dots.in.name.cc",
+        ]
         backup_files = ["scratch/.main.cc"]  # hidden files should be ignored
 
         # Create test scratch files
@@ -1227,15 +1352,17 @@
                     f.write("")
 
         # Reload the cmake cache to pick them up
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
         self.assertEqual(return_code, 0)
 
         # Try to build them with ns3 and cmake
         for path in test_files + backup_files:
             path = path.replace(".cc", "")
-            return_code1, stdout1, stderr1 = run_program("cmake", "--build . --target %s -j %d"
-                                                         % (path.replace("/", "_"), num_threads),
-                                                         cwd=os.path.join(ns3_path, "cmake-cache"))
+            return_code1, stdout1, stderr1 = run_program(
+                "cmake",
+                "--build . --target %s -j %d" % (path.replace("/", "_"), num_threads),
+                cwd=os.path.join(ns3_path, "cmake-cache"),
+            )
             return_code2, stdout2, stderr2 = run_ns3("build %s" % path)
             if "main" in path and ".main" not in path:
                 self.assertEqual(return_code1, 0)
@@ -1253,14 +1380,14 @@
             else:
                 self.assertEqual(return_code, 1)
 
-        # Now test them in CMake 3.10
         run_ns3("clean")
-        with DockerContainerManager(self, "ubuntu:18.04") as container:
+        with DockerContainerManager(self, "ubuntu:20.04") as container:
             container.execute("apt-get update")
-            container.execute("apt-get install -y python3 cmake g++-8 ninja-build")
+            container.execute("apt-get install -y python3 cmake g++ ninja-build")
             try:
                 container.execute(
-                    "./ns3 configure --enable-modules=core,network,internet -- -DCMAKE_CXX_COMPILER=/usr/bin/g++-8")
+                    "./ns3 configure --enable-modules=core,network,internet -- -DCMAKE_CXX_COMPILER=/usr/bin/g++"
+                )
             except DockerException as e:
                 self.fail()
             for path in test_files:
@@ -1280,16 +1407,16 @@
                 continue
             filename = os.path.basename(path).replace(".cc", "")
             executable_absolute_path = os.path.dirname(os.path.join(ns3_path, "build", path))
-            executable_name = list(filter(lambda x: filename in x,
-                                          os.listdir(executable_absolute_path)
-                                          )
-                                   )[0]
+            if os.path.exists(executable_absolute_path):
+                executable_name = list(
+                    filter(lambda x: filename in x, os.listdir(executable_absolute_path))
+                )[0]
 
-            os.remove(os.path.join(executable_absolute_path, executable_name))
+                os.remove(os.path.join(executable_absolute_path, executable_name))
             if not os.listdir(os.path.dirname(path)):
                 os.rmdir(os.path.dirname(source_absolute_path))
 
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
         self.assertEqual(return_code, 0)
 
     def test_14_MpiCommandTemplate(self):
@@ -1301,7 +1428,7 @@
         if shutil.which("mpiexec") is None or win32:
             self.skipTest("Mpi is not available")
 
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-examples")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
         self.assertEqual(return_code, 0)
         executables = get_programs_list()
 
@@ -1312,8 +1439,8 @@
         # Get executable path
         sample_simulator_path = list(filter(lambda x: "sample-simulator" in x, executables))[0]
 
-        mpi_command = "--dry-run run sample-simulator --command-template=\"mpiexec -np 2 %s\""
-        non_mpi_command = "--dry-run run sample-simulator --command-template=\"echo %s\""
+        mpi_command = '--dry-run run sample-simulator --command-template="mpiexec -np 2 %s"'
+        non_mpi_command = '--dry-run run sample-simulator --command-template="echo %s"'
 
         # Get the commands to run sample-simulator in two processes with mpi
         return_code, stdout, stderr = run_ns3(mpi_command)
@@ -1325,9 +1452,14 @@
         self.assertEqual(return_code, 0)
         if os.getenv("USER", "") == "root":
             if shutil.which("ompi_info"):
-                self.assertIn("mpiexec --allow-run-as-root --oversubscribe -np 2 %s" % sample_simulator_path, stdout)
+                self.assertIn(
+                    "mpiexec --allow-run-as-root --oversubscribe -np 2 %s" % sample_simulator_path,
+                    stdout,
+                )
             else:
-                self.assertIn("mpiexec --allow-run-as-root -np 2 %s" % sample_simulator_path, stdout)
+                self.assertIn(
+                    "mpiexec --allow-run-as-root -np 2 %s" % sample_simulator_path, stdout
+                )
         else:
             self.assertIn("mpiexec -np 2 %s" % sample_simulator_path, stdout)
 
@@ -1341,7 +1473,7 @@
         self.assertEqual(return_code, 0)
         self.assertIn("echo %s" % sample_simulator_path, stdout)
 
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --disable-examples")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --disable-examples')
         self.assertEqual(return_code, 0)
 
     def test_15_InvalidLibrariesToLink(self):
@@ -1362,15 +1494,18 @@
             f.write("")
         for invalid_or_nonexistent_library in ["", "gsd", "lib", "libfi", "calibre"]:
             with open("contrib/borked/CMakeLists.txt", "w", encoding="utf-8") as f:
-                f.write("""
+                f.write(
+                    """
                         build_lib(
                             LIBNAME borked
                             SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc
                             LIBRARIES_TO_LINK ${libcore} %s
                         )
-                        """ % invalid_or_nonexistent_library)
+                        """
+                    % invalid_or_nonexistent_library
+                )
 
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-examples")
+            return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
             if invalid_or_nonexistent_library in ["", "gsd", "libfi", "calibre"]:
                 self.assertEqual(return_code, 0)
             elif invalid_or_nonexistent_library in ["lib"]:
@@ -1388,7 +1523,9 @@
             elif invalid_or_nonexistent_library in ["gsd", "libfi", "calibre"]:
                 self.assertEqual(return_code, 2)  # should fail due to missing library
                 if "lld" in stdout + stderr:
-                    self.assertIn("unable to find library -l%s" % invalid_or_nonexistent_library, stderr)
+                    self.assertIn(
+                        "unable to find library -l%s" % invalid_or_nonexistent_library, stderr
+                    )
                 elif "mold" in stdout + stderr:
                     self.assertIn("library not found: %s" % invalid_or_nonexistent_library, stderr)
                 else:
@@ -1401,24 +1538,29 @@
         # - invalid library names (should fail to configure)
         # - valid library names but nonexistent libraries (should not create a target)
         with open("contrib/borked/CMakeLists.txt", "w", encoding="utf-8") as f:
-            f.write("""
+            f.write(
+                """
                     build_lib(
                         LIBNAME borked
                         SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc
                         LIBRARIES_TO_LINK ${libcore}
                     )
-                    """)
+                    """
+            )
         for invalid_or_nonexistent_library in ["", "gsd", "lib", "libfi", "calibre"]:
             with open("contrib/borked/examples/CMakeLists.txt", "w", encoding="utf-8") as f:
-                f.write("""
+                f.write(
+                    """
                         build_lib_example(
                             NAME borked-example
                             SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty-main.cc
                             LIBRARIES_TO_LINK ${libborked} %s
                         )
-                        """ % invalid_or_nonexistent_library)
+                        """
+                    % invalid_or_nonexistent_library
+                )
 
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
+            return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
             if invalid_or_nonexistent_library in ["", "gsd", "libfi", "calibre"]:
                 self.assertEqual(return_code, 0)  # should be able to configure
             elif invalid_or_nonexistent_library in ["lib"]:
@@ -1455,15 +1597,17 @@
         with open("contrib/calibre/examples/CMakeLists.txt", "w", encoding="utf-8") as f:
             f.write("")
         with open("contrib/calibre/CMakeLists.txt", "w", encoding="utf-8") as f:
-            f.write("""
+            f.write(
+                """
                 build_lib(
                     LIBNAME calibre
                     SOURCE_FILES ${PROJECT_SOURCE_DIR}/build-support/empty.cc
                     LIBRARIES_TO_LINK ${libcore}
                 )
-                """)
+                """
+            )
 
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
 
         # This only checks if configuration passes
         self.assertEqual(return_code, 0)
@@ -1474,7 +1618,9 @@
         # This checks not only if "lib" from "calibre" was incorrectly removed,
         # but also if the pkgconfig file was generated with the correct name
         self.assertNotIn("care", stdout)
-        self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "pkgconfig", "ns3-calibre.pc")))
+        self.assertTrue(
+            os.path.exists(os.path.join(ns3_path, "cmake-cache", "pkgconfig", "ns3-calibre.pc"))
+        )
 
         # Check if we can build this library
         return_code, stdout, stderr = run_ns3("build calibre")
@@ -1489,13 +1635,20 @@
         cmake_performance_trace.log file
         @return None
         """
+        cmake_performance_trace_log = os.path.join(ns3_path, "cmake_performance_trace.log")
+        if os.path.exists(cmake_performance_trace_log):
+            os.remove(cmake_performance_trace_log)
+
         return_code, stdout, stderr = run_ns3("configure --trace-performance")
         self.assertEqual(return_code, 0)
         if win32:
             self.assertIn("--profiling-format=google-trace --profiling-output=", stdout)
         else:
-            self.assertIn("--profiling-format=google-trace --profiling-output=../cmake_performance_trace.log", stdout)
-        self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake_performance_trace.log")))
+            self.assertIn(
+                "--profiling-format=google-trace --profiling-output=./cmake_performance_trace.log",
+                stdout,
+            )
+        self.assertTrue(os.path.exists(cmake_performance_trace_log))
 
     def test_18_CheckBuildVersionAndVersionCache(self):
         """!
@@ -1528,17 +1681,18 @@
             self.assertFalse(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
 
             # Second case: try with a version cache file but without Git (it should succeed)
-            version_cache_contents = ("CLOSEST_TAG = '\"ns-3.0.0\"'\n"
-                                      "VERSION_COMMIT_HASH = '\"0000000000\"'\n"
-                                      "VERSION_DIRTY_FLAG = '0'\n"
-                                      "VERSION_MAJOR = '3'\n"
-                                      "VERSION_MINOR = '0'\n"
-                                      "VERSION_PATCH = '0'\n"
-                                      "VERSION_RELEASE_CANDIDATE = '\"\"'\n"
-                                      "VERSION_TAG = '\"ns-3.0.0\"'\n"
-                                      "VERSION_TAG_DISTANCE = '0'\n"
-                                      "VERSION_BUILD_PROFILE = 'debug'\n"
-                                      )
+            version_cache_contents = (
+                "CLOSEST_TAG = '\"ns-3.0.0\"'\n"
+                "VERSION_COMMIT_HASH = '\"0000000000\"'\n"
+                "VERSION_DIRTY_FLAG = '0'\n"
+                "VERSION_MAJOR = '3'\n"
+                "VERSION_MINOR = '0'\n"
+                "VERSION_PATCH = '0'\n"
+                "VERSION_RELEASE_CANDIDATE = '\"\"'\n"
+                "VERSION_TAG = '\"ns-3.0.0\"'\n"
+                "VERSION_TAG_DISTANCE = '0'\n"
+                "VERSION_BUILD_PROFILE = 'debug'\n"
+            )
             with open(version_cache_file, "w", encoding="utf-8") as version:
                 version.write(version_cache_contents)
 
@@ -1584,15 +1738,18 @@
         @return None
         """
         # Try filtering enabled modules to core+network and their dependencies
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-examples --enable-tests")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            'configure -G "{generator}" --enable-examples --enable-tests'
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         modules_before_filtering = get_enabled_modules()
         programs_before_filtering = get_programs_list()
 
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --filter-module-examples-and-tests='core;network'")
-        self.config_ok(return_code, stdout)
+            "configure -G \"{generator}\" --filter-module-examples-and-tests='core;network'"
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         modules_after_filtering = get_enabled_modules()
         programs_after_filtering = get_programs_list()
@@ -1604,8 +1761,9 @@
 
         # Try filtering in only core
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --filter-module-examples-and-tests='core'")
-        self.config_ok(return_code, stdout)
+            "configure -G \"{generator}\" --filter-module-examples-and-tests='core'"
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have the same number of modules
         self.assertEqual(len(get_enabled_modules()), len(modules_after_filtering))
@@ -1614,8 +1772,9 @@
 
         # Try cleaning the list of enabled modules to reset to the normal configuration.
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --disable-examples --disable-tests --filter-module-examples-and-tests=''")
-        self.config_ok(return_code, stdout)
+            "configure -G \"{generator}\" --disable-examples --disable-tests --filter-module-examples-and-tests=''"
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # At this point we should have the same amount of modules that we had when we started.
         self.assertEqual(len(get_enabled_modules()), len(self.ns3_modules))
@@ -1638,7 +1797,9 @@
 
             # Check if configuration properly detected lld
             self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
-            with open(os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8") as f:
+            with open(
+                os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8"
+            ) as f:
                 self.assertIn("-fuse-ld=lld", f.read())
 
             # Try to build using the lld linker
@@ -1650,8 +1811,11 @@
             # Now add mold to the PATH
             if not os.path.exists("./mold-1.4.2-x86_64-linux.tar.gz"):
                 container.execute(
-                    "wget https://github.com/rui314/mold/releases/download/v1.4.2/mold-1.4.2-x86_64-linux.tar.gz")
-            container.execute("tar xzfC mold-1.4.2-x86_64-linux.tar.gz /usr/local --strip-components=1")
+                    "wget https://github.com/rui314/mold/releases/download/v1.4.2/mold-1.4.2-x86_64-linux.tar.gz"
+                )
+            container.execute(
+                "tar xzfC mold-1.4.2-x86_64-linux.tar.gz /usr/local --strip-components=1"
+            )
 
             # Configure should detect and use mold
             run_ns3("clean")
@@ -1659,7 +1823,9 @@
 
             # Check if configuration properly detected mold
             self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
-            with open(os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8") as f:
+            with open(
+                os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8"
+            ) as f:
                 self.assertIn("-fuse-ld=mold", f.read())
 
             # Try to build using the lld linker
@@ -1676,7 +1842,9 @@
 
             # Check if configuration properly disabled lld/mold usage
             self.assertTrue(os.path.exists(os.path.join(ns3_path, "cmake-cache", "build.ninja")))
-            with open(os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8") as f:
+            with open(
+                os.path.join(ns3_path, "cmake-cache", "build.ninja"), "r", encoding="utf-8"
+            ) as f:
                 self.assertNotIn("-fuse-ld=mold", f.read())
 
     def test_21_ClangTimeTrace(self):
@@ -1694,7 +1862,8 @@
             # Enable ClangTimeTrace without git (it should fail)
             try:
                 container.execute(
-                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON")
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON"
+                )
             except DockerException as e:
                 self.assertIn("could not find git for clone of ClangBuildAnalyzer", e.stderr)
 
@@ -1703,7 +1872,8 @@
             # Enable ClangTimeTrace without git (it should succeed)
             try:
                 container.execute(
-                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON")
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON"
+                )
             except DockerException as e:
                 self.assertIn("could not find git for clone of ClangBuildAnalyzer", e.stderr)
 
@@ -1728,8 +1898,11 @@
 
             try:
                 container.execute(
-                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DNS3_CLANG_TIMETRACE=ON")
-                self.assertTrue(False, "ClangTimeTrace requires Clang, but GCC just passed the checks too")
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-examples --enable-tests -- -DNS3_CLANG_TIMETRACE=ON"
+                )
+                self.assertTrue(
+                    False, "ClangTimeTrace requires Clang, but GCC just passed the checks too"
+                )
             except DockerException as e:
                 self.assertIn("TimeTrace is a Clang feature", e.stderr)
 
@@ -1749,7 +1922,8 @@
             # Enable Ninja tracing without using the Ninja generator
             try:
                 container.execute(
-                    "./ns3 configure --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
+                    "./ns3 configure --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10"
+                )
             except DockerException as e:
                 self.assertIn("Ninjatracing requires the Ninja generator", e.stderr)
 
@@ -1760,7 +1934,8 @@
             # Enable Ninjatracing support without git (should fail)
             try:
                 container.execute(
-                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10"
+                )
             except DockerException as e:
                 self.assertIn("could not find git for clone of NinjaTracing", e.stderr)
 
@@ -1768,7 +1943,8 @@
             # Enable Ninjatracing support with git (it should succeed)
             try:
                 container.execute(
-                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10"
+                )
             except DockerException as e:
                 self.assertTrue(False, "Failed to configure with Ninjatracing")
 
@@ -1796,7 +1972,8 @@
             # Enable Clang TimeTrace feature for more detailed traces
             try:
                 container.execute(
-                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON")
+                    "./ns3 configure -G Ninja --enable-modules=core --enable-ninja-tracing -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10 -DNS3_CLANG_TIMETRACE=ON"
+                )
             except DockerException as e:
                 self.assertTrue(False, "Failed to configure Ninjatracing with Clang's TimeTrace")
 
@@ -1823,17 +2000,6 @@
         """
 
         run_ns3("clean")
-        # Ubuntu 18.04 ships with:
-        # - cmake 3.10: does not support PCH
-        # - ccache 3.4: incompatible with pch
-        with DockerContainerManager(self, "ubuntu:18.04") as container:
-            container.execute("apt-get update")
-            container.execute("apt-get install -y python3 cmake ccache clang-10")
-            try:
-                container.execute("./ns3 configure -- -DCMAKE_CXX_COMPILER=/usr/bin/clang++-10")
-            except DockerException as e:
-                self.assertIn("does not support precompiled headers", e.stderr)
-        run_ns3("clean")
 
         # Ubuntu 20.04 ships with:
         # - cmake 3.16: does support PCH
@@ -1863,16 +2029,37 @@
         Check for regressions in test object build.
         @return None
         """
-        return_code, stdout, stderr = run_ns3('configure')
+        return_code, stdout, stderr = run_ns3("configure")
         self.assertEqual(return_code, 0)
 
         test_module_cache = os.path.join(ns3_path, "cmake-cache", "src", "test")
         self.assertFalse(os.path.exists(test_module_cache))
 
-        return_code, stdout, stderr = run_ns3('configure --enable-tests')
+        return_code, stdout, stderr = run_ns3("configure --enable-tests")
         self.assertEqual(return_code, 0)
         self.assertTrue(os.path.exists(test_module_cache))
 
+    def test_25_CheckBareConfig(self):
+        """!
+        Check for regressions in a bare ns-3 configuration.
+        @return None
+        """
+
+        run_ns3("clean")
+
+        with DockerContainerManager(self, "ubuntu:20.04") as container:
+            container.execute("apt-get update")
+            container.execute("apt-get install -y python3 cmake g++")
+            return_code = 0
+            stdout = ""
+            try:
+                stdout = container.execute("./ns3 configure -d release")
+            except DockerException as e:
+                return_code = 1
+            self.config_ok(return_code, stdout, stdout)
+
+        run_ns3("clean")
+
 
 class NS3BuildBaseTestCase(NS3BaseTestCase):
     """!
@@ -1962,8 +2149,8 @@
             f.write("3-00\n")
 
         # Reconfigure.
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\"")
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}"')
+        self.config_ok(return_code, stdout, stderr)
 
         # Build.
         return_code, stdout, stderr = run_ns3("build")
@@ -2020,9 +2207,13 @@
         absolute_path = os.sep.join([ns3_path, "build", "release"])
         relative_path = os.sep.join(["build", "release"])
         for different_out_dir in [absolute_path, relative_path]:
-            return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --out=%s" % different_out_dir)
-            self.config_ok(return_code, stdout)
-            self.assertIn("Build directory               : %s" % absolute_path.replace(os.sep, '/'), stdout)
+            return_code, stdout, stderr = run_ns3(
+                'configure -G "{generator}" --out=%s' % different_out_dir
+            )
+            self.config_ok(return_code, stdout, stderr)
+            self.assertIn(
+                "Build directory               : %s" % absolute_path.replace(os.sep, "/"), stdout
+            )
 
             # Build
             run_ns3("build")
@@ -2045,8 +2236,10 @@
 
         # Restore original output directory.
         return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --out=''")
-        self.config_ok(return_code, stdout)
-        self.assertIn("Build directory               : %s" % usual_outdir.replace(os.sep, '/'), stdout)
+        self.config_ok(return_code, stdout, stderr)
+        self.assertIn(
+            "Build directory               : %s" % usual_outdir.replace(os.sep, "/"), stdout
+        )
 
         # Try re-building.
         run_ns3("build")
@@ -2082,8 +2275,10 @@
 
         # Reconfigure setting the installation folder to ns-3-dev/build/install.
         install_prefix = os.sep.join([ns3_path, "build", "install"])
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --prefix=%s" % install_prefix)
-        self.config_ok(return_code, stdout)
+        return_code, stdout, stderr = run_ns3(
+            'configure -G "{generator}" --prefix=%s' % install_prefix
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # Build.
         run_ns3("build")
@@ -2106,15 +2301,17 @@
 
         # Make sure all headers were installed.
         installed_headers = get_headers_list(install_prefix)
-        missing_headers = list(set([os.path.basename(x) for x in headers])
-                               - (set([os.path.basename(x) for x in installed_headers]))
-                               )
+        missing_headers = list(
+            set([os.path.basename(x) for x in headers])
+            - (set([os.path.basename(x) for x in installed_headers]))
+        )
         self.assertEqual(len(missing_headers), 0)
 
         # Now create a test CMake project and try to find_package ns-3.
         test_main_file = os.sep.join([install_prefix, "main.cpp"])
         with open(test_main_file, "w", encoding="utf-8") as f:
-            f.write("""
+            f.write(
+                """
             #include <ns3/core-module.h>
             using namespace ns3;
             int main ()
@@ -2124,7 +2321,8 @@
                 Simulator::Destroy ();
                 return 0;
             }
-            """)
+            """
+            )
 
         # We try to use this library without specifying a version,
         # specifying ns3-01 (text version with 'dev' is not supported)
@@ -2137,7 +2335,9 @@
                                   list(APPEND CMAKE_PREFIX_PATH ./{lib}/cmake/ns3)
                                   find_package(ns3 {version} COMPONENTS libcore)
                                   target_link_libraries(test PRIVATE ns3::libcore)
-                                  """.format(lib=("lib64" if lib64 else "lib"), version=version)
+                                  """.format(
+                lib=("lib64" if lib64 else "lib"), version=version
+            )
             ns3_import_methods.append(cmake_find_package_import)
 
             # Import ns-3 as pkg-config libraries
@@ -2146,21 +2346,24 @@
                                include(FindPkgConfig)
                                pkg_check_modules(ns3 REQUIRED IMPORTED_TARGET ns3-core{version})
                                target_link_libraries(test PUBLIC PkgConfig::ns3)
-                               """.format(lib=("lib64" if lib64 else "lib"),
-                                          version="=" + version if version else ""
-                                          )
+                               """.format(
+                lib=("lib64" if lib64 else "lib"), version="=" + version if version else ""
+            )
             if shutil.which("pkg-config"):
                 ns3_import_methods.append(pkgconfig_import)
 
             # Test the multiple ways of importing ns-3 libraries
             for import_method in ns3_import_methods:
-                test_cmake_project = """
-                                     cmake_minimum_required(VERSION 3.10..3.10)
+                test_cmake_project = (
+                    """
+                                     cmake_minimum_required(VERSION 3.12..3.12)
                                      project(ns3_consumer CXX)
-                                     set(CMAKE_CXX_STANDARD 17)
+                                     set(CMAKE_CXX_STANDARD 20)
                                      set(CMAKE_CXX_STANDARD_REQUIRED ON)
                                      add_executable(test main.cpp)
-                                     """ + import_method
+                                     """
+                    + import_method
+                )
 
                 test_cmake_project_file = os.sep.join([install_prefix, "CMakeLists.txt"])
                 with open(test_cmake_project_file, "w", encoding="utf-8") as f:
@@ -2170,18 +2373,21 @@
                 cmake = shutil.which("cmake")
                 return_code, stdout, stderr = run_program(
                     cmake,
-                    "-DCMAKE_BUILD_TYPE=debug -G\"{generator}\" .".format(generator=platform_makefiles),
-                    cwd=install_prefix
+                    '-DCMAKE_BUILD_TYPE=debug -G"{generator}" .'.format(
+                        generator=platform_makefiles
+                    ),
+                    cwd=install_prefix,
                 )
 
                 if version == "3.00":
                     self.assertEqual(return_code, 1)
                     if import_method == cmake_find_package_import:
-                        self.assertIn('Could not find a configuration file for package "ns3" that is compatible',
-                                      stderr.replace("\n", ""))
+                        self.assertIn(
+                            'Could not find a configuration file for package "ns3" that is compatible',
+                            stderr.replace("\n", ""),
+                        )
                     elif import_method == pkgconfig_import:
-                        self.assertIn('A required package was not found',
-                                      stderr.replace("\n", ""))
+                        self.assertIn("A required package was not found", stderr.replace("\n", ""))
                     else:
                         raise Exception("Unknown import type")
                 else:
@@ -2202,11 +2408,17 @@
                     if win32:
                         test_program = os.path.join(install_prefix, "test.exe")
                         env_sep = ";" if ";" in os.environ["PATH"] else ":"
-                        env = {"PATH": env_sep.join([os.environ["PATH"], os.path.join(install_prefix, "lib")])}
+                        env = {
+                            "PATH": env_sep.join(
+                                [os.environ["PATH"], os.path.join(install_prefix, "lib")]
+                            )
+                        }
                     else:
                         test_program = "./test"
                         env = None
-                    return_code, stdout, stderr = run_program(test_program, "", cwd=install_prefix, env=env)
+                    return_code, stdout, stderr = run_program(
+                        test_program, "", cwd=install_prefix, env=env
+                    )
                     self.assertEqual(return_code, 0)
 
         # Uninstall
@@ -2224,14 +2436,15 @@
         @return None
         """
         # Build.
-        targets = {"scratch/scratch-simulator": "scratch-simulator",
-                   "scratch/scratch-simulator.cc": "scratch-simulator",
-                   "scratch-simulator": "scratch-simulator",
-                   "scratch/subdir/scratch-subdir": "subdir_scratch-subdir",
-                   "subdir/scratch-subdir": "subdir_scratch-subdir",
-                   "scratch-subdir": "subdir_scratch-subdir",
-                   }
-        for (target_to_run, target_cmake) in targets.items():
+        targets = {
+            "scratch/scratch-simulator": "scratch-simulator",
+            "scratch/scratch-simulator.cc": "scratch-simulator",
+            "scratch-simulator": "scratch-simulator",
+            "scratch/subdir/scratch-subdir": "subdir_scratch-subdir",
+            "subdir/scratch-subdir": "subdir_scratch-subdir",
+            "scratch-subdir": "subdir_scratch-subdir",
+        }
+        for target_to_run, target_cmake in targets.items():
             # Test if build is working.
             build_line = "target scratch_%s" % target_cmake
             return_code, stdout, stderr = run_ns3("build %s" % target_to_run)
@@ -2252,14 +2465,14 @@
         """
 
         # First enable examples
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-examples")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
         self.assertEqual(return_code, 0)
 
         # Copy second.cc from the tutorial examples to the scratch folder
         shutil.copy("./examples/tutorial/second.cc", "./scratch/second.cc")
 
         # Reconfigure to re-scan the scratches
-        return_code, stdout, stderr = run_ns3("configure -G \"{generator}\" --enable-examples")
+        return_code, stdout, stderr = run_ns3('configure -G "{generator}" --enable-examples')
         self.assertEqual(return_code, 0)
 
         # Try to run second and collide
@@ -2267,7 +2480,7 @@
         self.assertEqual(return_code, 1)
         self.assertIn(
             'Build target "second" is ambiguous. Try one of these: "scratch/second", "examples/tutorial/second"',
-            stdout.replace(os.sep, '/')
+            stdout.replace(os.sep, "/"),
         )
 
         # Try to run scratch/second and succeed
@@ -2285,7 +2498,7 @@
         self.assertEqual(return_code, 1)
         self.assertIn(
             'Run target "second" is ambiguous. Try one of these: "scratch/second", "examples/tutorial/second"',
-            stdout.replace(os.sep, '/')
+            stdout.replace(os.sep, "/"),
         )
 
         # Try to run scratch/second and succeed
@@ -2307,7 +2520,8 @@
 
         # First enable examples and static build
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --enable-examples --disable-gtk --enable-static")
+            'configure -G "{generator}" --enable-examples --disable-gtk --enable-static'
+        )
 
         if win32:
             # Configuration should fail explaining Windows
@@ -2319,7 +2533,7 @@
             self.assertEqual(return_code, 0)
 
             # Then try to build one example
-            return_code, stdout, stderr = run_ns3('build sample-simulator')
+            return_code, stdout, stderr = run_ns3("build sample-simulator")
             self.assertEqual(return_code, 0)
             self.assertIn("Built target", stdout)
 
@@ -2337,7 +2551,8 @@
 
         # First enable examples and static build
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --enable-examples --enable-python-bindings")
+            'configure -G "{generator}" --enable-examples --enable-python-bindings'
+        )
 
         # If configuration passes, we are half way done
         self.assertEqual(return_code, 0)
@@ -2351,7 +2566,9 @@
         self.assertEqual(return_code, 0)
 
         # Then try to run a specific test with the full relative path
-        return_code, stdout, stderr = run_program("test.py", "-p ./examples/wireless/mixed-wired-wireless", python=True)
+        return_code, stdout, stderr = run_program(
+            "test.py", "-p ./examples/wireless/mixed-wired-wireless", python=True
+        )
         self.assertEqual(return_code, 0)
 
     def test_13_FetchOptionalComponents(self):
@@ -2381,7 +2598,7 @@
             self.skipTest("Missing git")
 
         destination_contrib = os.path.join(ns3_path, "contrib/test-contrib-dependency")
-        destination_src = os.path.join(ns3_path, "src/test-src-dependant-on-contrib")
+        destination_src = os.path.join(ns3_path, "src/test-src-dependent-on-contrib")
         # Remove pre-existing directories
         if os.path.exists(destination_contrib):
             shutil.rmtree(destination_contrib)
@@ -2389,10 +2606,14 @@
             shutil.rmtree(destination_src)
 
         # Always use a fresh copy
-        shutil.copytree(os.path.join(ns3_path, "build-support/test-files/test-contrib-dependency"),
-                        destination_contrib)
-        shutil.copytree(os.path.join(ns3_path, "build-support/test-files/test-src-dependant-on-contrib"),
-                        destination_src)
+        shutil.copytree(
+            os.path.join(ns3_path, "build-support/test-files/test-contrib-dependency"),
+            destination_contrib,
+        )
+        shutil.copytree(
+            os.path.join(ns3_path, "build-support/test-files/test-src-dependent-on-contrib"),
+            destination_src,
+        )
 
         # Then configure
         return_code, stdout, stderr = run_ns3("configure --enable-examples")
@@ -2423,8 +2644,9 @@
 
         # On top of the release build configured by NS3ConfigureTestCase, also enable examples, tests and docs.
         return_code, stdout, stderr = run_ns3(
-            "configure -d release -G \"{generator}\" --enable-examples --enable-tests")
-        self.config_ok(return_code, stdout)
+            'configure -d release -G "{generator}" --enable-examples --enable-tests'
+        )
+        self.config_ok(return_code, stdout, stderr)
 
         # Check if .lock-ns3 exists, then read to get list of executables.
         self.assertTrue(os.path.exists(ns3_lock_filename))
@@ -2486,7 +2708,7 @@
         Try to run test-runner without building
         @return None
         """
-        return_code, stdout, stderr = run_ns3('build test-runner')
+        return_code, stdout, stderr = run_ns3("build test-runner")
         self.assertEqual(return_code, 0)
 
         return_code, stdout, stderr = run_ns3('run "test-runner --list" --no-build --verbose')
@@ -2523,7 +2745,9 @@
         return_code, stdout, stderr = run_ns3("build scratch-simulator")
         self.assertEqual(return_code, 0)
 
-        return_code, stdout, stderr = run_ns3("run scratch-simulator --gdb --verbose --no-build", env={"gdb_eval": "1"})
+        return_code, stdout, stderr = run_ns3(
+            "run scratch-simulator --gdb --verbose --no-build", env={"gdb_eval": "1"}
+        )
         self.assertEqual(return_code, 0)
         self.assertIn("scratch-simulator", stdout)
         if win32:
@@ -2542,7 +2766,9 @@
         return_code, stdout, stderr = run_ns3("build scratch-simulator")
         self.assertEqual(return_code, 0)
 
-        return_code, stdout, stderr = run_ns3("run scratch-simulator --valgrind --verbose --no-build")
+        return_code, stdout, stderr = run_ns3(
+            "run scratch-simulator --valgrind --verbose --no-build"
+        )
         self.assertEqual(return_code, 0)
         self.assertIn("scratch-simulator", stderr)
         self.assertIn("Memcheck", stderr)
@@ -2679,39 +2905,43 @@
         self.assertFalse(enable_sudo is True)
 
         # First we run to ensure the program was built
-        return_code, stdout, stderr = run_ns3('run scratch-simulator')
+        return_code, stdout, stderr = run_ns3("run scratch-simulator")
         self.assertEqual(return_code, 0)
         self.assertIn("Built target scratch_scratch-simulator", stdout)
         self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout)
-        scratch_simulator_path = list(filter(lambda x: x if "scratch-simulator" in x else None,
-                                             self.ns3_executables
-                                             )
-                                      )[-1]
+        scratch_simulator_path = list(
+            filter(lambda x: x if "scratch-simulator" in x else None, self.ns3_executables)
+        )[-1]
         prev_fstat = os.stat(scratch_simulator_path)  # we get the permissions before enabling sudo
 
         # Now try setting the sudo bits from the run subparser
-        return_code, stdout, stderr = run_ns3('run scratch-simulator --enable-sudo',
-                                              env={"SUDO_PASSWORD": sudo_password})
+        return_code, stdout, stderr = run_ns3(
+            "run scratch-simulator --enable-sudo", env={"SUDO_PASSWORD": sudo_password}
+        )
         self.assertEqual(return_code, 0)
         self.assertIn("Built target scratch_scratch-simulator", stdout)
         self.assertIn(cmake_build_target_command(target="scratch_scratch-simulator"), stdout)
         fstat = os.stat(scratch_simulator_path)
 
         import stat
+
         # If we are on Windows, these permissions mean absolutely nothing,
         # and on Fuse builds they might not make any sense, so we need to skip before failing
-        likely_fuse_mount = ((prev_fstat.st_mode & stat.S_ISUID) == (fstat.st_mode & stat.S_ISUID)) and \
-                            prev_fstat.st_uid == 0  # noqa
+        likely_fuse_mount = (
+            (prev_fstat.st_mode & stat.S_ISUID) == (fstat.st_mode & stat.S_ISUID)
+        ) and prev_fstat.st_uid == 0  # noqa
 
         if win32 or likely_fuse_mount:
             self.skipTest("Windows or likely a FUSE mount")
 
         # If this is a valid platform, we can continue
         self.assertEqual(fstat.st_uid, 0)  # check the file was correctly chown'ed by root
-        self.assertEqual(fstat.st_mode & stat.S_ISUID, stat.S_ISUID)  # check if normal users can run as sudo
+        self.assertEqual(
+            fstat.st_mode & stat.S_ISUID, stat.S_ISUID
+        )  # check if normal users can run as sudo
 
         # Now try setting the sudo bits as a post-build step (as set by configure subparser)
-        return_code, stdout, stderr = run_ns3('configure --enable-sudo')
+        return_code, stdout, stderr = run_ns3("configure --enable-sudo")
         self.assertEqual(return_code, 0)
 
         # Check if it was properly set in the lock file
@@ -2724,7 +2954,7 @@
                 os.remove(executable)
 
         # Try to build and then set sudo bits as a post-build step
-        return_code, stdout, stderr = run_ns3('build', env={"SUDO_PASSWORD": sudo_password})
+        return_code, stdout, stderr = run_ns3("build", env={"SUDO_PASSWORD": sudo_password})
         self.assertEqual(return_code, 0)
 
         # Check if commands are being printed for every target
@@ -2736,7 +2966,9 @@
         # Check scratch simulator yet again
         fstat = os.stat(scratch_simulator_path)
         self.assertEqual(fstat.st_uid, 0)  # check the file was correctly chown'ed by root
-        self.assertEqual(fstat.st_mode & stat.S_ISUID, stat.S_ISUID)  # check if normal users can run as sudo
+        self.assertEqual(
+            fstat.st_mode & stat.S_ISUID, stat.S_ISUID
+        )  # check if normal users can run as sudo
 
     def test_15_CommandTemplate(self):
         """!
@@ -2745,7 +2977,7 @@
         """
 
         # Command templates that are empty or do not have a '%s' should fail
-        return_code0, stdout0, stderr0 = run_ns3('run sample-simulator --command-template')
+        return_code0, stdout0, stderr0 = run_ns3("run sample-simulator --command-template")
         self.assertEqual(return_code0, 2)
         self.assertIn("argument --command-template: expected one argument", stderr0)
 
@@ -2757,8 +2989,12 @@
             self.assertIn("not all arguments converted during string formatting", stderr)
 
         # Command templates with %s should at least continue and try to run the target
-        return_code4, stdout4, _ = run_ns3('run sample-simulator --command-template "%s --PrintVersion" --verbose')
-        return_code5, stdout5, _ = run_ns3('run sample-simulator --command-template="%s --PrintVersion" --verbose')
+        return_code4, stdout4, _ = run_ns3(
+            'run sample-simulator --command-template "%s --PrintVersion" --verbose'
+        )
+        return_code5, stdout5, _ = run_ns3(
+            'run sample-simulator --command-template="%s --PrintVersion" --verbose'
+        )
         self.assertEqual((return_code4, return_code5), (0, 0))
 
         self.assertIn("sample-simulator{ext} --PrintVersion".format(ext=ext), stdout4)
@@ -2773,8 +3009,10 @@
 
         # Test if all argument passing flavors are working
         return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --help" --verbose')
-        return_code1, stdout1, stderr1 = run_ns3('run sample-simulator --command-template="%s --help" --verbose')
-        return_code2, stdout2, stderr2 = run_ns3('run sample-simulator --verbose -- --help')
+        return_code1, stdout1, stderr1 = run_ns3(
+            'run sample-simulator --command-template="%s --help" --verbose'
+        )
+        return_code2, stdout2, stderr2 = run_ns3("run sample-simulator --verbose -- --help")
 
         self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
         self.assertIn("sample-simulator{ext} --help".format(ext=ext), stdout0)
@@ -2783,8 +3021,10 @@
 
         # Test if the same thing happens with an additional run argument (e.g. --no-build)
         return_code0, stdout0, stderr0 = run_ns3('run "sample-simulator --help" --no-build')
-        return_code1, stdout1, stderr1 = run_ns3('run sample-simulator --command-template="%s --help" --no-build')
-        return_code2, stdout2, stderr2 = run_ns3('run sample-simulator --no-build -- --help')
+        return_code1, stdout1, stderr1 = run_ns3(
+            'run sample-simulator --command-template="%s --help" --no-build'
+        )
+        return_code2, stdout2, stderr2 = run_ns3("run sample-simulator --no-build -- --help")
         self.assertEqual((return_code0, return_code1, return_code2), (0, 0, 0))
         self.assertEqual(stdout0, stdout1)
         self.assertEqual(stdout1, stdout2)
@@ -2809,11 +3049,14 @@
         # The order of the arguments is command template,
         # arguments passed with the target itself
         # and forwarded arguments after the -- separator
-        self.assertIn("sample-simulator{ext} --PrintGroups --PrintGlobals --PrintTypeIds".format(ext=ext), stdout)
+        self.assertIn(
+            "sample-simulator{ext} --PrintGroups --PrintGlobals --PrintTypeIds".format(ext=ext),
+            stdout,
+        )
 
         # Check if it complains about the missing -- separator
         cmd0 = 'run sample-simulator --command-template="%s " --PrintTypeIds'
-        cmd1 = 'run sample-simulator --PrintTypeIds'
+        cmd1 = "run sample-simulator --PrintTypeIds"
 
         return_code0, stdout0, stderr0 = run_ns3(cmd0)
         return_code1, stdout1, stderr1 = run_ns3(cmd1)
@@ -2829,6 +3072,9 @@
         if shutil.which("lldb") is None:
             self.skipTest("Missing lldb")
 
+        return_code, stdout, stderr = run_ns3("build scratch-simulator")
+        self.assertEqual(return_code, 0)
+
         return_code, stdout, stderr = run_ns3("run scratch-simulator --lldb --verbose --no-build")
         self.assertEqual(return_code, 0)
         self.assertIn("scratch-simulator", stdout)
@@ -2854,8 +3100,10 @@
             shutil.rmtree(destination_src)
 
         # Always use a fresh copy
-        shutil.copytree(os.path.join(ns3_path, "build-support/test-files/test-package-managers"),
-                        destination_src)
+        shutil.copytree(
+            os.path.join(ns3_path, "build-support/test-files/test-package-managers"),
+            destination_src,
+        )
 
         with DockerContainerManager(self, "ubuntu:22.04") as container:
             # Install toolchain
@@ -2865,8 +3113,7 @@
             # Verify that Armadillo is not available and that we did not
             # add any new unnecessary dependency when features are not used
             try:
-                container.execute(
-                    "./ns3 configure -- -DTEST_PACKAGE_MANAGER:STRING=ON")
+                container.execute("./ns3 configure -- -DTEST_PACKAGE_MANAGER:STRING=ON")
                 self.skipTest("Armadillo is already installed")
             except DockerException as e:
                 pass
@@ -2881,14 +3128,14 @@
             # Install Armadillo with CPM
             try:
                 container.execute(
-                    "./ns3 configure -- -DNS3_CPM=ON -DTEST_PACKAGE_MANAGER:STRING=CPM")
+                    "./ns3 configure -- -DNS3_CPM=ON -DTEST_PACKAGE_MANAGER:STRING=CPM"
+                )
             except DockerException as e:
                 self.fail()
 
             # Try to build module using CPM's Armadillo
             try:
-                container.execute(
-                    "./ns3 build test-package-managers")
+                container.execute("./ns3 build test-package-managers")
             except DockerException as e:
                 self.fail()
 
@@ -2904,8 +3151,7 @@
 
             # Install VcPkg
             try:
-                container.execute(
-                    "./ns3 configure -- -DNS3_VCPKG=ON")
+                container.execute("./ns3 configure -- -DNS3_VCPKG=ON")
             except DockerException as e:
                 self.fail()
 
@@ -2917,8 +3163,7 @@
 
             # Try to build module using VcPkg's Armadillo
             try:
-                container.execute(
-                    "./ns3 build test-package-managers")
+                container.execute("./ns3 build test-package-managers")
             except DockerException as e:
                 self.fail()
 
@@ -2950,34 +3195,42 @@
         try:
             import requests
             import urllib3
+
             urllib3.disable_warnings()
         except ImportError:
             requests = None  # noqa
             self.skipTest("Requests library is not available")
 
-        regex = re.compile(r'((http|https)://[^\ \n\)\"\'\}\>\<\]\;\`\\]*)')  # noqa
+        regex = re.compile(r"((http|https)://[^\ \n\)\"\'\}\>\<\]\;\`\\]*)")  # noqa
         skipped_files = []
 
-        whitelisted_urls = {"https://gitlab.com/your-user-name/ns-3-dev",
-                            "https://www.nsnam.org/release/ns-allinone-3.31.rc1.tar.bz2",
-                            "https://www.nsnam.org/release/ns-allinone-3.X.rcX.tar.bz2",
-                            "https://www.nsnam.org/releases/ns-3-x",
-                            "https://www.nsnam.org/releases/ns-allinone-3.(x-1",
-                            "https://www.nsnam.org/releases/ns-allinone-3.x.tar.bz2",
-                            # split due to command-line formatting
-                            "https://cmake.org/cmake/help/latest/manual/cmake-",
-                            "http://www.ieeeghn.org/wiki/index.php/First-Hand:Digital_Television:_The_",
-                            # Dia placeholder xmlns address
-                            "http://www.lysator.liu.se/~alla/dia/",
-                            # Fails due to bad regex
-                            "http://www.ieeeghn.org/wiki/index.php/First-Hand:Digital_Television:_The_Digital_Terrestrial_Television_Broadcasting_(DTTB",
-                            "http://en.wikipedia.org/wiki/Namespace_(computer_science",
-                            "http://en.wikipedia.org/wiki/Bonobo_(component_model",
-                            "http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85",
-                            # historical links
-                            "http://www.research.att.com/info/kpv/",
-                            "http://www.research.att.com/~gsf/",
-                            }
+        whitelisted_urls = {
+            "https://gitlab.com/your-user-name/ns-3-dev",
+            "https://www.nsnam.org/release/ns-allinone-3.31.rc1.tar.bz2",
+            "https://www.nsnam.org/release/ns-allinone-3.X.rcX.tar.bz2",
+            "https://www.nsnam.org/releases/ns-3-x",
+            "https://www.nsnam.org/releases/ns-allinone-3.(x-1",
+            "https://www.nsnam.org/releases/ns-allinone-3.x.tar.bz2",
+            "https://ns-buildmaster.ee.washington.edu:8010/",
+            # split due to command-line formatting
+            "https://cmake.org/cmake/help/latest/manual/cmake-",
+            "http://www.ieeeghn.org/wiki/index.php/First-Hand:Digital_Television:_The_",
+            # Dia placeholder xmlns address
+            "http://www.lysator.liu.se/~alla/dia/",
+            # Fails due to bad regex
+            "http://www.ieeeghn.org/wiki/index.php/First-Hand:Digital_Television:_The_Digital_Terrestrial_Television_Broadcasting_(DTTB",
+            "http://en.wikipedia.org/wiki/Namespace_(computer_science",
+            "http://en.wikipedia.org/wiki/Bonobo_(component_model",
+            "http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85",
+            # historical links
+            "http://www.research.att.com/info/kpv/",
+            "http://www.research.att.com/~gsf/",
+            "http://nsnam.isi.edu/nsnam/index.php/Contributed_Code",
+            "http://scan5.coverity.com/cgi-bin/upload.py",
+            # terminal output
+            "https://github.com/Kitware/CMake/releases/download/v3.27.1/cmake-3.27.1-linux-x86_64.tar.gz-",
+            "http://mirrors.kernel.org/fedora/releases/11/Everything/i386/os/Packages/",
+        }
 
         # Scan for all URLs in all files we can parse
         files_and_urls = set()
@@ -2985,7 +3238,7 @@
         for topdir in ["bindings", "doc", "examples", "src", "utils"]:
             for root, dirs, files in os.walk(topdir):
                 # do not parse files in build directories
-                if "build" in root or "_static" in root or "source-temp" in root or 'html' in root:
+                if "build" in root or "_static" in root or "source-temp" in root or "html" in root:
                     continue
                 for file in files:
                     filepath = os.path.join(root, file)
@@ -3005,7 +3258,9 @@
                             # Get first group for each match (containing the URL)
                             # and strip final punctuation or commas in matched links
                             # commonly found in the docs
-                            urls = list(map(lambda x: x[0][:-1] if x[0][-1] in ".," else x[0], matches))
+                            urls = list(
+                                map(lambda x: x[0][:-1] if x[0][-1] in ".," else x[0], matches)
+                            )
                     except UnicodeDecodeError:
                         skipped_files.append(filepath)
                         continue
@@ -3016,13 +3271,14 @@
                         files_and_urls.add((filepath, url))
 
         # Instantiate the Django URL validator
-        from django.core.validators import URLValidator  # noqa
         from django.core.exceptions import ValidationError  # noqa
+        from django.core.validators import URLValidator  # noqa
+
         validate_url = URLValidator()
 
         # User agent string to make ACM and Elsevier let us check if links to papers are working
         headers = {
-            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'
+            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
             # noqa
         }
 
@@ -3057,17 +3313,22 @@
                     # People use the wrong code, but the reason
                     # can still be correct
                     if response.status_code in [302, 308, 500, 503]:
-                        if response.reason.lower() in ['found',
-                                                       'moved temporarily',
-                                                       'permanent redirect',
-                                                       'ok',
-                                                       'service temporarily unavailable'
-                                                       ]:
+                        if response.reason.lower() in [
+                            "found",
+                            "moved temporarily",
+                            "permanent redirect",
+                            "ok",
+                            "service temporarily unavailable",
+                        ]:
                             dead_link_msg = None
                             break
                     # In case it didn't pass in any of the previous tests,
                     # set dead_link_msg with the most recent error and try again
-                    dead_link_msg = "%s: URL %s: returned code %d" % (test_filepath, test_url, response.status_code)
+                    dead_link_msg = "%s: URL %s: returned code %d" % (
+                        test_filepath,
+                        test_url,
+                        response.status_code,
+                    )
                 except requests.exceptions.InvalidURL:
                     dead_link_msg = "%s: URL %s: invalid URL" % (test_filepath, test_url)
                 except requests.exceptions.SSLError:
@@ -3079,12 +3340,17 @@
                         error_msg = e.args[0].reason.__str__()
                     except AttributeError:
                         error_msg = e.args[0]
-                    dead_link_msg = "%s: URL %s: failed with exception: %s" % (test_filepath, test_url, error_msg)
+                    dead_link_msg = "%s: URL %s: failed with exception: %s" % (
+                        test_filepath,
+                        test_url,
+                        error_msg,
+                    )
                 tries -= 1
             return dead_link_msg
 
         # Dispatch threads to test multiple URLs concurrently
         from concurrent.futures import ThreadPoolExecutor
+
         with ThreadPoolExecutor(max_workers=100) as executor:
             dead_links = list(executor.map(test_file_url, list(files_and_urls)))
 
@@ -3098,7 +3364,8 @@
         @return None
         """
         return_code, stdout, stderr = run_ns3(
-            "configure --enable-tests --enable-examples --enable-sanitizers -d optimized")
+            "configure --enable-tests --enable-examples --enable-sanitizers -d optimized"
+        )
         self.assertEqual(return_code, 0)
 
         test_return_code, stdout, stderr = run_program("test.py", "", python=True)
@@ -3123,18 +3390,22 @@
             images += list(Path("./doc").glob("**/figures/**/*.{ext}".format(ext=extension)))
 
         # Get the brightness of an image on a scale of 0-100%
-        imagemagick_get_image_brightness = \
-            'convert {image} -colorspace HSI -channel b -separate +channel -scale 1x1 -format "%[fx:100*u]" info:'
+        imagemagick_get_image_brightness = 'convert {image} -colorspace HSI -channel b -separate +channel -scale 1x1 -format "%[fx:100*u]" info:'
 
         # We could invert colors of target image to increase its brightness
         # convert source.png -channel RGB -negate target.png
         brightness_threshold = 50
         for image in images:
-            brightness = subprocess.check_output(imagemagick_get_image_brightness.format(image=image).split())
+            brightness = subprocess.check_output(
+                imagemagick_get_image_brightness.format(image=image).split()
+            )
             brightness = float(brightness.decode().strip("'\""))
-            self.assertGreater(brightness, brightness_threshold,
-                               "Image darker than threshold (%d < %d): %s" % (brightness, brightness_threshold, image)
-                               )
+            self.assertGreater(
+                brightness,
+                brightness_threshold,
+                "Image darker than threshold (%d < %d): %s"
+                % (brightness, brightness_threshold, image),
+            )
 
 
 def main():
@@ -3144,45 +3415,41 @@
     """
 
     test_completeness = {
-        "style": [NS3UnusedSourcesTestCase,
-                  NS3StyleTestCase,
-                  ],
-        "build": [NS3CommonSettingsTestCase,
-                  NS3ConfigureBuildProfileTestCase,
-                  NS3ConfigureTestCase,
-                  NS3BuildBaseTestCase,
-                  NS3ExpectedUseTestCase,
-                  ],
-        "complete": [NS3UnusedSourcesTestCase,
-                     NS3StyleTestCase,
-                     NS3CommonSettingsTestCase,
-                     NS3ConfigureBuildProfileTestCase,
-                     NS3ConfigureTestCase,
-                     NS3BuildBaseTestCase,
-                     NS3ExpectedUseTestCase,
-                     NS3QualityControlTestCase,
-                     ],
-        "extras": [NS3DependenciesTestCase,
-                   ]
+        "style": [
+            NS3UnusedSourcesTestCase,
+            NS3StyleTestCase,
+        ],
+        "build": [
+            NS3CommonSettingsTestCase,
+            NS3ConfigureBuildProfileTestCase,
+            NS3ConfigureTestCase,
+            NS3BuildBaseTestCase,
+            NS3ExpectedUseTestCase,
+        ],
+        "complete": [
+            NS3UnusedSourcesTestCase,
+            NS3StyleTestCase,
+            NS3CommonSettingsTestCase,
+            NS3ConfigureBuildProfileTestCase,
+            NS3ConfigureTestCase,
+            NS3BuildBaseTestCase,
+            NS3ExpectedUseTestCase,
+            NS3QualityControlTestCase,
+        ],
+        "extras": [
+            NS3DependenciesTestCase,
+        ],
     }
 
     import argparse
 
     parser = argparse.ArgumentParser("Test suite for the ns-3 buildsystem")
-    parser.add_argument("-c", "--completeness",
-                        choices=test_completeness.keys(),
-                        default="complete")
-    parser.add_argument("-tn", "--test-name",
-                        action="store",
-                        default=None,
-                        type=str)
-    parser.add_argument("-rtn", "--resume-from-test-name",
-                        action="store",
-                        default=None,
-                        type=str)
-    parser.add_argument("-q", "--quiet",
-                        action="store_true",
-                        default=False)
+    parser.add_argument(
+        "-c", "--completeness", choices=test_completeness.keys(), default="complete"
+    )
+    parser.add_argument("-tn", "--test-name", action="store", default=None, type=str)
+    parser.add_argument("-rtn", "--resume-from-test-name", action="store", default=None, type=str)
+    parser.add_argument("-q", "--quiet", action="store_true", default=False)
     args = parser.parse_args(sys.argv[1:])
 
     loader = unittest.TestLoader()
@@ -3226,5 +3493,5 @@
         shutil.move(ns3rc_script_bak, ns3rc_script)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils/tests/test-test.py ns-3.41/utils/tests/test-test.py
--- ns-3.40/utils/tests/test-test.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils/tests/test-test.py	2024-02-09 14:44:08.633795300 -0800
@@ -61,61 +61,67 @@
 #                         write detailed test results into XML-FILE.xml
 
 
-
 from __future__ import print_function
-from TestBase import TestBaseClass
+
 import sys
 
+from TestBase import TestBaseClass
+
+
 def main(argv):
     """
-        Prepares test cases and executes
+    Prepares test cases and executes
     """
     test_cases = [
-        '',
-        '-h',
-        '--help',
-        '-b build/',
-        '--buildpath=build/',
-        '-c performance',
-        '--constrain=performance',
-        '-d',
-        '--duration',
-        '-e socket-options-ipv6',
-        '--example=socket-options-ipv6',
-        '-u',
-        '--update-data',
-        '-f EXTENSIVE',
-        '--fullness=EXTENSIVE',
-        '-g',
-        '--grind',
-        '-l',
-        '--list',
-        '-m',
-        '--multiple',
-        '-n',
-        '--no-build',
-        '-p first',
-        '--pyexample=first',
-        '-r',
-        '--retain',
-        '-s ns3-tcp-state',
-        '--suite=ns3-tcp-state',
-        '-t t_opt.txt',
-        '--text=t_opt.txt && rm t_opt.txt',
-        '-v',
-        '--verbose',
-        '-w t_opt.html && rm t_opt.html',
-        '--web=t_opt.html && rm t_opt.html',
-        '--html=t_opt.html && rm t_opt.html',
-        '-x t_opt.xml && rm t_opt.xml',
-        '--xml=t_opt.xml && rm t_opt.xml',
+        "",
+        "-h",
+        "--help",
+        "-b build/",
+        "--buildpath=build/",
+        "-c performance",
+        "--constrain=performance",
+        "-d",
+        "--duration",
+        "-e socket-options-ipv6",
+        "--example=socket-options-ipv6",
+        "-u",
+        "--update-data",
+        "-f EXTENSIVE",
+        "--fullness=EXTENSIVE",
+        "-g",
+        "--grind",
+        "-l",
+        "--list",
+        "-m",
+        "--multiple",
+        "-n",
+        "--no-build",
+        "-p first",
+        "--pyexample=first",
+        "-r",
+        "--retain",
+        "-s ns3-tcp-state",
+        "--suite=ns3-tcp-state",
+        "-t t_opt.txt",
+        "--text=t_opt.txt && rm t_opt.txt",
+        "-v",
+        "--verbose",
+        "-w t_opt.html && rm t_opt.html",
+        "--web=t_opt.html && rm t_opt.html",
+        "--html=t_opt.html && rm t_opt.html",
+        "-x t_opt.xml && rm t_opt.xml",
+        "--xml=t_opt.xml && rm t_opt.xml",
     ]
 
-    configure_string = sys.executable + ' ns3 configure --enable-tests --enable-examples'
-    clean_string = sys.executable + ' ns3 clean'
-    cmd_execute_list = ['%s && %s test.py %s && %s' % (configure_string, sys.executable, option, clean_string) for option in test_cases]
-    runner = TestBaseClass(argv[1:], "Test suite for the ns-3 unit test runner", 'test-py')
+    configure_string = sys.executable + " ns3 configure --enable-tests --enable-examples"
+    clean_string = sys.executable + " ns3 clean"
+    cmd_execute_list = [
+        "%s && %s test.py %s && %s" % (configure_string, sys.executable, option, clean_string)
+        for option in test_cases
+    ]
+    runner = TestBaseClass(argv[1:], "Test suite for the ns-3 unit test runner", "test-py")
     return runner.runtests(cmd_execute_list)
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     sys.exit(main(sys.argv))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/utils.py ns-3.41/utils.py
--- ns-3.40/utils.py	2023-09-27 13:30:37.934840000 -0700
+++ ns-3.41/utils.py	2024-02-09 14:44:08.629795300 -0800
@@ -8,79 +8,72 @@
 
 
 def get_list_from_file(file_path, list_name):
-    '''Looks for a Python list called list_name in the file specified
+    """Looks for a Python list called list_name in the file specified
     by file_path and returns it.
 
     If the file or list name aren't found, this function will return
     an empty list.
 
-    '''
-
-    list = []
+    """
 
     # Read in the file if it exists.
-    if os.path.exists(file_path):
-        with open(file_path, "r", encoding="utf-8") as file_in:
+    if not os.path.exists(file_path):
+        return []
 
-            # Look for the list.
-            list_string = ""
-            parsing_multiline_list = False
-            for line in file_in:
-
-                # Remove any comments.
-                if '#' in line:
-                    (line, comment) = line.split('#', 1)
-
-                # Parse the line.
-                if list_name in line or parsing_multiline_list:
-                    list_string += line
-
-                    # Handle multiline lists.
-                    if ']' not in list_string:
-                        parsing_multiline_list = True
-                    else:
-                        # Evaluate the list once its end is reached.
-                        # Make the split function only split it once.
-                        list = eval(list_string.split('=', 1)[1].strip())
-                        break
+    with open(file_path, "r", encoding="utf-8") as file_in:
+        # Look for the list.
+        list_string = ""
+        parsing_multiline_list = False
+        for line in file_in:
+            # Remove any comments.
+            if "#" in line:
+                (line, comment) = line.split("#", 1)
+
+            # Parse the line.
+            if list_name in line or parsing_multiline_list:
+                list_string += line
+
+                # Handle multiline lists.
+                if "]" not in list_string:
+                    parsing_multiline_list = True
+                else:
+                    # Evaluate the list once its end is reached.
+                    # Make the split function only split it once.
+                    return eval(list_string.split("=", 1)[1].strip())
 
-    return list
+    # List name was not found
+    return []
 
 
 def get_bool_from_file(file_path, bool_name, value_if_missing):
-    '''Looks for a Python boolean variable called bool_name in the
+    """Looks for a Python boolean variable called bool_name in the
     file specified by file_path and returns its value.
 
     If the file or boolean variable aren't found, this function will
     return value_if_missing.
 
-    '''
+    """
 
     # Read in the file if it exists.
-    if os.path.exists(file_path):
-        with open(file_path, "r", encoding="utf-8") as file_in:
-
-            # Look for the boolean variable.
-            bool_found = False
-            for line in file_in:
-
-                # Remove any comments.
-                if '#' in line:
-                    (line, comment) = line.split('#', 1)
-
-                # Parse the line.
-                if bool_name in line:
-                    # Evaluate the variable's line once it is found.  Make
-                    # the split function only split it once.
-                    bool = eval(line.split('=', 1)[1].strip())
-                    bool_found = True
-                    break
-
-    if bool_found:
-        return bool
-    else:
+    if not os.path.exists(file_path):
         return value_if_missing
 
+    with open(file_path, "r", encoding="utf-8") as file_in:
+        # Look for the boolean variable.
+        for line in file_in:
+            # Remove any comments.
+            if "#" in line:
+                (line, comment) = line.split("#", 1)
+
+            # Parse the line.
+            if bool_name in line:
+                # Evaluate the variable's line once it is found.  Make
+                # the split function only split it once.
+                return eval(line.split("=", 1)[1].strip())
+
+    # Boolean variable was not found
+    return value_if_missing
+
 
 # Reads the NS-3 configuration file and returns a list of enabled modules.
 #
@@ -89,17 +82,17 @@
 def read_config_file():
     # By default, all modules will be enabled, examples will be disabled,
     # and tests will be disabled.
-    modules_enabled  = ['all_modules']
+    modules_enabled = ["all_modules"]
     examples_enabled = False
-    tests_enabled    = False
+    tests_enabled = False
 
     # See if the ns3 configuration file exists in the current working
     # directory and then look for it in the ~ directory.
     config_file_exists = False
-    dot_ns3rc_name = '.ns3rc'
+    dot_ns3rc_name = ".ns3rc"
     dot_ns3rc_path = dot_ns3rc_name
     if not os.path.exists(dot_ns3rc_path):
-        dot_ns3rc_path = os.path.expanduser('~/') + dot_ns3rc_name
+        dot_ns3rc_path = os.path.expanduser("~/") + dot_ns3rc_name
         if not os.path.exists(dot_ns3rc_path):
             # Return all of the default values if the .ns3rc file can't be found.
             return (config_file_exists, modules_enabled, examples_enabled, tests_enabled)
@@ -107,17 +100,17 @@
     config_file_exists = True
 
     # Read in the enabled modules.
-    modules_enabled = get_list_from_file(dot_ns3rc_path, 'modules_enabled')
+    modules_enabled = get_list_from_file(dot_ns3rc_path, "modules_enabled")
     if not modules_enabled:
         # Enable all modules if the modules_enabled line can't be found.
-        modules_enabled = ['all_modules']
+        modules_enabled = ["all_modules"]
 
     # Read in whether examples should be enabled or not.
     value_if_missing = False
-    examples_enabled = get_bool_from_file(dot_ns3rc_path, 'examples_enabled', value_if_missing)
+    examples_enabled = get_bool_from_file(dot_ns3rc_path, "examples_enabled", value_if_missing)
 
     # Read in whether tests should be enabled or not.
     value_if_missing = False
-    tests_enabled = get_bool_from_file(dot_ns3rc_path, 'tests_enabled', value_if_missing)
+    tests_enabled = get_bool_from_file(dot_ns3rc_path, "tests_enabled", value_if_missing)
 
     return (config_file_exists, modules_enabled, examples_enabled, tests_enabled)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.40/VERSION ns-3.41/VERSION
--- ns-3.40/VERSION	2023-09-27 13:30:55.802883400 -0700
+++ ns-3.41/VERSION	2024-02-09 14:45:45.430484000 -0800
@@ -1 +1 @@
-3.40
+3.41
