diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/AUTHORS ns-3.39/AUTHORS
--- ns-3.38/AUTHORS	2023-03-16 20:54:23.484868000 -0700
+++ ns-3.39/AUTHORS	2023-07-05 14:23:47.577426400 -0700
@@ -7,6 +7,7 @@
 Rohit Agarwal (mindprince@gmail.com)
 Piyush Aggarwal (piyush8311@gmail.com)
 Alessandro Aimi (alleaimi95@gmail.com)
+German Aizek (germanaizek@yandex.ru)
 Zoraze Ali (zoraze.ali@cttc.es)
 Eduardo Almeida (enmsa@outlook.pt)
 Robert Ammon (ammo6818@vandals.uidaho.edu)
@@ -96,6 +97,7 @@
 Tom Goff (tgoff@tgoff.net)
 Mohit Goyal (mohit.bits@gmail.com)
 Juan C. Granda (jcgranda@uniovi.es)
+Giovanni Grieco (giovanni.grieco@poliba.it)
 David Gross (gdavid.devel@gmail.com)
 Maja GrubiÅ¡iÄ‡ (maja.grubisic@live.com)
 RÃ©my GrÃ¼nblatt (remy@grunblatt.org)
@@ -107,6 +109,7 @@
 Bruno Haick (bghaick@hotmail.com)
 Amiraslan Haghrah (amiraslanhaghrah@gmail.com)
 Robert Hartung (hartung@ibr.cs.tu-bs.de)
+Karsten Heimann
 Frank Helbert (frank@ime.usp.br)
 Tom Henderson (tomhend@u.washington.edu)
 Christopher Hepner (hepner@hs-ulm.de)
@@ -135,6 +138,7 @@
 Sascha Alexander Jopen (jopen@informatik.uni-bonn.de)
 Piotr Jurkiewicz (piotr.jerzy.jurkiewicz@gmail.com)
 Evgeny Kalishenko (ydginster@gmail.com)
+Raghuram Kannan (raghuramkannan40@gmail.com)
 Vignesh Kannan (vignesh2496@gmail.com)
 Ouassim Karrakchou (okarr102@uottawa.ca)
 Tanmay Kathpalia (tanmay.kathpalia@landisgyr.com)
@@ -161,6 +165,7 @@
 Harsh Lara (harshapplefan@gmail.com)
 Mattia Lecci (mattia.lecci@gmail.com)
 Robin Lee (xinyashuai1987@163.com)
+Juan Leon (juanvleonr@gmail.com)
 Kristijan LenkoviÄ‡ (k.lenkovic@me.com)
 Daniel Lertpratchya (nikkipui@gmail.com)
 Brett Levasseur (brettl20@gmail.com)
@@ -198,6 +203,7 @@
 Sidharth Nabar (snabar@uw.edu)
 Sharan Naribole (sharan.naribole@gmail.com)
 Hemanth Narra (hemanth@ittc.ku.edu)
+Carlos Natalino (carlos.natalino@chalmers.se)
 Roman Naumann (naumann@informatik.hu-berlin.de)
 Mohtashim Nawaz (nawazmohtashim@gmail.com)
 Sachin Nayak (sachinn@uw.edu)
@@ -250,11 +256,12 @@
 Matias Richart (mrichart@fing.edu.uy)
 George F. Riley (riley@ece.gatech.edu)
 Juergen Rinas (jrinas@gmx.de)
+Robert-ML
 Sebastian Rohde (sebastian.rohde@tu-dortmund.de)
 Karsten Roscher (sfx@rocktale.de)
 Ali Rostami (a.rostami@rutgers.edu)
 Bill Roome (wdr@bell-labs.com)
-David (david.rua@gmail.com)
+David Rua (david.rua@gmail.com)
 Andrea Sacco (andrea.sacco85@gmail.com)
 Richard Sailer (richard_sailer@systemli.org)
 Lynne Salameh (l.salameh@cs.ucl.ac.uk)
@@ -312,6 +319,7 @@
 Tom Wambold (tom5760@gmail.com)
 Danqi Wang (beyondwdq@gmail.com)
 Tinghui Wang (tinghui.wang@wsu.edu)
+Yikun Wang (yikunwang6@gmail.com)
 Mitch Watrous (watrous@u.washington.edu)
 Pierre Wendling (pierre.wendling.4@gmail.com)
 Florian Westphal (fw@strlen.de)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/bindings/python/ns__init__.py ns-3.39/bindings/python/ns__init__.py
--- ns-3.38/bindings/python/ns__init__.py	2023-03-16 20:54:23.492868200 -0700
+++ ns-3.39/bindings/python/ns__init__.py	2023-07-05 14:23:47.585426600 -0700
@@ -1,5 +1,4 @@
 import builtins
-from copy import copy
 from functools import lru_cache
 import glob
 import os.path
@@ -11,7 +10,7 @@
 DEFAULT_LIB_DIR = sysconfig.get_config_var("LIBDIR")
 
 
-def find_ns3_lock():
+def find_ns3_lock() -> str:
     # 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
@@ -29,12 +28,14 @@
     if lock_file in os.listdir(path_to_lock):
         path_to_lock += os.sep + lock_file
     else:
-        path_to_lock = None
+        path_to_lock = ""
     return path_to_lock
 
 
 SYSTEM_LIBRARY_DIRECTORIES = (DEFAULT_LIB_DIR,
-                              os.path.dirname(DEFAULT_LIB_DIR)
+                              os.path.dirname(DEFAULT_LIB_DIR),
+                              "/usr/lib64",
+                              "/usr/lib"
                               )
 DYNAMIC_LIBRARY_EXTENSIONS = {"linux": "so",
                               "win32": "dll",
@@ -81,12 +82,14 @@
     # Search for the core library in the search paths
     libraries = []
     for search_path in library_search_paths:
-        libraries += glob.glob("%s/**/*.%s*" % (search_path, LIBRARY_EXTENSION), recursive=True)
+        if os.path.exists(search_path):
+            libraries += glob.glob("%s/**/*.%s*" % (search_path, LIBRARY_EXTENSION), recursive=True)
 
     # Search system library directories (too slow for recursive search)
     for search_path in SYSTEM_LIBRARY_DIRECTORIES:
-        libraries += glob.glob("%s/**/*.%s*" % (search_path, LIBRARY_EXTENSION), recursive=False)
-        libraries += glob.glob("%s/*.%s*" % (search_path, LIBRARY_EXTENSION), recursive=False)
+        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)
 
     del search_path, library_search_paths
 
@@ -120,8 +123,35 @@
     return matched_libraries
 
 
-def extract_library_include_dirs(library_name: str, prefix: str) -> list:
-    library_path = "%s/lib/%s" % (prefix, library_name)
+LIBRARY_AND_DEFINES = {
+    "libgsl": ["HAVE_GSL"],
+    "libxml2": ["HAVE_LIBXML2"],
+    "libsqlite3": ["HAVE_SQLITE3"],
+    "openflow": ["NS3_OPENFLOW", "ENABLE_OPENFLOW"],
+    "click": ["NS3_CLICK"]
+}
+
+
+def add_library_defines(library_name: str):
+    has_defines = list(filter(lambda x: x in library_name, LIBRARY_AND_DEFINES.keys()))
+    defines = ""
+    if len(has_defines):
+        for define in LIBRARY_AND_DEFINES[has_defines[0]]:
+            defines += (f"""
+                    #ifndef {define}
+                    #define {define} 1
+                    #endif
+                """)
+    return defines
+
+
+def extract_linked_libraries(library_name: str, prefix: str) -> tuple:
+    lib = ""
+    for variant in ["lib", "lib64"]:
+        library_path = f"{prefix}/{variant}/{library_name}"
+        if os.path.exists(library_path):
+            lib = variant
+            break
     linked_libs = []
     # First discover which 3rd-party libraries are used by the current module
     try:
@@ -131,10 +161,17 @@
         print("Failed to extract libraries used by {library} with exception:{exception}"
               .format(library=library_path, exception=e))
         exit(-1)
+    return library_path, lib, list(map(lambda x: x.decode("utf-8"), linked_libs))
+
+
+def extract_library_include_dirs(library_name: str, prefix: str) -> tuple:
+    library_path, lib, linked_libs = extract_linked_libraries(library_name, prefix)
 
     linked_libs_include_dirs = set()
+    defines = add_library_defines(library_name)
+
     # Now find these libraries and add a few include paths for them
-    for linked_library in map(lambda x: x.decode("utf-8"), linked_libs):
+    for linked_library in linked_libs:
         # Skip ns-3 modules
         if "libns3" in linked_library:
             continue
@@ -148,15 +185,18 @@
                 "Failed to find {library}. Make sure its library directory is in LD_LIBRARY_PATH.".format(
                     library=linked_library))
 
-        # Get path with shortest length
+        # Get path with the shortest length
         linked_library_path = sorted(linked_library_path, key=lambda x: len(x))[0]
 
         # If library is part of the ns-3 build, continue without any new includes
         if prefix in linked_library_path:
             continue
 
+        # Add defines based in linked libraries found
+        defines += add_library_defines(linked_library)
+
         # If it is part of the system directories, try to find it
-        system_include_dir = os.path.dirname(linked_library_path).replace("lib", "include")
+        system_include_dir = os.path.dirname(linked_library_path).replace(lib, "include")
         if os.path.exists(system_include_dir):
             linked_libs_include_dirs.add(system_include_dir)
 
@@ -179,134 +219,214 @@
             inc_path = os.path.join(lib_path, "include")
             if os.path.exists(inc_path):
                 linked_libs_include_dirs.add(inc_path)
-    return list(linked_libs_include_dirs)
+    return list(linked_libs_include_dirs), defines
+
+
+def find_ns3_from_lock_file(lock_file: str) -> (str, list, str):
+    # Load NS3_ENABLED_MODULES from the lock file inside the build directory
+    values = {}
+
+    # If we find a lock file, load the ns-3 modules from it
+    # Should be the case when running from the source directory
+    exec(open(lock_file).read(), {}, values)
+    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"))}
+    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:
+        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()))
+        for library in list(libraries_to_filter.keys()):
+            if library not in filtered_in_modules:
+                libraries_to_filter.pop(library)
+        return libraries_to_filter
+
+    libraries = filter_in_matching_ns3_libraries(libraries, modules, version, suffix)
+
+    # 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
+        )
+        if library_name not in libraries:
+            raise Exception("Missing library %s\n" % library_name,
+                            "Build all modules with './ns3 build'"
+                            )
+        libraries_to_load.append(libraries[library_name])
+    return prefix, libraries_to_load, version
+
+
+# Extract version and build suffix (if it exists)
+def filter_module_name(library: str) -> str:
+    library = os.path.splitext(os.path.basename(library))[0]
+    components = library.split("-")
+
+    # Remove version-related prefixes
+    if "libns3" in components[0]:
+        components.pop(0)
+    if "dev" == components[0]:
+        components.pop(0)
+    if "rc" in components[0]:
+        components.pop(0)
+
+    # Drop build profile suffix and test libraries
+    if components[-1] in ["debug", "default", "optimized", "release", "relwithdebinfo", "minsizerel"]:
+        components.pop(-1)
+    return "-".join(components)
+
+
+def extract_version(library: str, module: str) -> str:
+    library = os.path.basename(library)
+    return re.findall(r"libns([\d.|rc|\-dev]+)-", library)[0]
+
+
+def get_newest_version(versions: list) -> str:
+    versions = list(sorted(versions))
+    if "dev" in versions[0]:
+        return versions[0]
+
+    # Check if there is a release of a possible candidate
+    try:
+        pos = versions.index(os.path.splitext(versions[-1])[0])
+    except ValueError:
+        pos = None
+
+    # Remove release candidates
+    if pos is not None:
+        return versions[pos]
+    else:
+        return versions[-1]
+
+
+def find_ns3_from_search() -> (str, list, str):
+    libraries = search_libraries("ns3")
+
+    if not libraries:
+        raise Exception("ns-3 libraries were not found.")
+
+    # If there is a version with a hash by the end, we have a pip-installed library
+    pip_libraries = list(filter(lambda x: "python" in x, libraries))
+    if pip_libraries:
+        # We drop non-pip libraries
+        libraries = pip_libraries
+
+    # The prefix is the directory with the lib directory
+    # libns3-dev-core.so/../../
+    prefix = os.path.dirname(os.path.dirname(libraries[0]))
+
+    # Remove test libraries
+    libraries = list(filter(lambda x: "test" not in x, libraries))
+
+    # Filter out module names
+    modules = set([filter_module_name(library) for library in libraries])
+
+    def filter_in_newest_ns3_libraries(libraries_to_filter: list, modules_to_filter: list) -> tuple:
+        newest_version_found = ""
+        # 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))
+
+            # Extract versions from 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)
+
+            # Check if the version found is the global newest version
+            if not newest_version_found:
+                newest_version_found = newest_version
+            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))
+
+            for conflicting_library in list(conflicting_libraries):
+                if "-".join([newest_version, module]) not in conflicting_library:
+                    libraries.remove(conflicting_library)
+                    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)))
+
+        return libraries_to_filter, newest_version_found
+
+    # Get library base names
+    libraries, version = filter_in_newest_ns3_libraries(libraries, list(modules))
+    return prefix, libraries, version
 
 
 def load_modules():
     lock_file = find_ns3_lock()
     libraries_to_load = []
 
-    if lock_file:
-        # Load NS3_ENABLED_MODULES from the lock file inside the build directory
-        values = {}
-
-        # If we find a lock file, load the ns-3 modules from it
-        # Should be the case when running from the source directory
-        exec(open(lock_file).read(), {}, values)
-        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"))}
-        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:
-            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()))
-            for library in list(libraries_to_filter.keys()):
-                if library not in filtered_in_modules:
-                    libraries_to_filter.pop(library)
-            return libraries_to_filter
+    # Search for prefix to ns-3 build, modules and respective libraries plus version
+    ret = find_ns3_from_search() if not lock_file else find_ns3_from_lock_file(lock_file)
 
-        libraries = filter_in_matching_ns3_libraries(libraries, modules, version, suffix)
-    else:
-        libraries = search_libraries("ns3")
+    # Unpack returned values
+    prefix, libraries, version = ret
+    prefix = os.path.abspath(prefix)
 
-        if not libraries:
-            raise Exception("ns-3 libraries were not found.")
+    # Sort libraries according to their dependencies
+    def sort_to_dependencies(libraries: list, prefix: str) -> list:
+        module_dependencies = {}
+        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(map(lambda x: os.path.basename(x), linked_libraries))
+            module_dependencies[os.path.basename(ns3_library)] = linked_libraries
+
+        def modules_that_can_be_loaded(module_dependencies, pending_modules, current_modules):
+            modules = []
+            for pending_module in pending_modules:
+                can_be_loaded = True
+                for dependency in module_dependencies[pending_module]:
+                    if dependency not in current_modules:
+                        can_be_loaded = False
+                        break
+                if not can_be_loaded:
+                    continue
+                modules.append(pending_module)
+            return modules
+
+        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):
+                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)
+
+        sorted_libraries = []
+        for step in dependency_order(module_dependencies, list(module_dependencies.keys()), [], 0).values():
+            sorted_libraries.extend(step)
+        return sorted_libraries
 
-        # The prefix is the directory with the lib directory
-        # libns3-dev-core.so/../../
-        prefix = os.path.dirname(os.path.dirname(libraries[0]))
-
-        # Remove test libraries
-        libraries = list(filter(lambda x: "test" not in x, libraries))
-
-        # Extract version and build suffix (if it exists)
-        def filter_module_name(library):
-            library = os.path.splitext(os.path.basename(library))[0]
-            components = library.split("-")
-
-            # Remove version-related prefixes
-            if "libns3" in components[0]:
-                components.pop(0)
-            if "dev" == components[0]:
-                components.pop(0)
-            if "rc" in components[0]:
-                components.pop(0)
-
-            # Drop build profile suffix and test libraries
-            if components[-1] in ["debug", "default", "optimized", "release", "relwithdebinfo"]:
-                components.pop(-1)
-            return "-".join(components)
-
-        # Filter out module names
-        modules = set([filter_module_name(library) for library in libraries])
-
-        def extract_version(library: str, module: str) -> str:
-            library = os.path.basename(library)
-            return re.findall(r"libns([\d.|rc|\-dev]+)-", library)[0]
-
-        def get_newest_version(versions: list) -> str:
-            versions = list(sorted(versions))
-            if "dev" in versions[0]:
-                return versions[0]
-
-            # Check if there is a release of a possible candidate
-            try:
-                pos = versions.index(os.path.splitext(versions[-1])[0])
-            except ValueError:
-                pos = None
-
-            # Remove release candidates
-            if pos is not None:
-                return versions[pos]
-            else:
-                return versions[-1]
+    libraries_to_load = sort_to_dependencies(libraries, prefix)
 
-        def filter_in_newest_ns3_libraries(libraries_to_filter: list, modules_to_filter: list) -> list:
-            newest_version_found = None
-            # 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))
-
-                # Extract versions from 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)
-
-                # Check if the version found is the global newest version
-                if not newest_version_found:
-                    newest_version_found = newest_version
-                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))
-
-                for conflicting_library in list(conflicting_libraries):
-                    if "-".join([newest_version, module]) not in conflicting_library:
-                        libraries.remove(conflicting_library)
-                        conflicting_libraries.remove(conflicting_library)
-                        num_libraries -= 1
-
-                if len(conflicting_libraries) > 1:
-                    raise Exception("There are multiple build profiles for module '%s'.\nDelete one to continue: %s"
-                                    % (module, ", ".join(conflicting_libraries)))
-
-            return libraries_to_filter
-
-        # Get library base names
-        libraries = filter_in_newest_ns3_libraries(libraries, modules)
-        libraries_to_load = list(map(lambda x: os.path.basename(x), libraries))
+    # Extract library base names
+    libraries_to_load = [os.path.basename(x) for x in libraries_to_load]
+
+    # Sort modules based on libraries
+    modules = list(map(lambda x: filter_module_name(x), libraries_to_load))
 
     # Try to import Cppyy and warn the user in case it is not found
     try:
@@ -324,35 +444,27 @@
     libcppyy.AddSmartPtrType('Ptr')
 
     # Import ns-3 libraries
-    prefix = os.path.abspath(prefix)
-    cppyy.add_library_path("%s/lib" % prefix)
-    cppyy.add_include_path("%s/include" % prefix)
-
-    if lock_file:
-        # When we have the lock file, we assemble the correct library names
-        for module in modules:
-            library_name = "libns{version}-{module}{suffix}".format(
-                version=version,
-                module=module,
-                suffix=suffix
-            )
-            if library_name not in libraries:
-                raise Exception("Missing library %s\n" % library_name,
-                                "Build all modules with './ns3 build'"
-                                )
-            libraries_to_load.append(libraries[library_name])
+    for variant in ["lib", "lib64"]:
+        path_to_lib = f"{prefix}/{variant}"
+        if not os.path.exists(path_to_lib):
+            continue
+        cppyy.add_library_path(path_to_lib)
+    del variant, path_to_lib
+    cppyy.add_include_path(f"{prefix}/include")
 
     known_include_dirs = set()
     # We then need to include all include directories for dependencies
     for library in libraries_to_load:
-        for linked_lib_include_dir in extract_library_include_dirs(library, prefix):
+        linked_lib_include_dirs, defines = extract_library_include_dirs(library, prefix)
+        cppyy.cppexec(defines)
+        for linked_lib_include_dir in linked_lib_include_dirs:
             if linked_lib_include_dir not in known_include_dirs:
                 known_include_dirs.add(linked_lib_include_dir)
                 if os.path.isdir(linked_lib_include_dir):
                     cppyy.add_include_path(linked_lib_include_dir)
 
     for module in modules:
-        cppyy.include("ns3/%s-module.h" % module)
+        cppyy.include(f"ns3/{module}-module.h")
 
     # After including all headers, we finally load the modules
     for library in libraries_to_load:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/cmake-format-modules.txt ns-3.39/build-support/cmake-format-modules.txt
--- ns-3.38/build-support/cmake-format-modules.txt	2023-03-16 20:54:23.492868200 -0700
+++ ns-3.39/build-support/cmake-format-modules.txt	1969-12-31 16:00:00.000000000 -0800
@@ -1,60 +0,0 @@
-additional_commands:
-    build_exec:
-        flags: [IGNORE_PCH, STANDALONE]
-        kwargs:
-            EXECNAME : '1'
-            EXECNAME_PREFIX : '1'
-            EXECUTABLE_DIRECTORY_PATH : '1'
-            INSTALL_DIRECTORY_PATH : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-            DEFINITIONS : '*'
-
-    build_lib:
-        flags: [IGNORE_PCH]
-        kwargs:
-            LIBNAME : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            PRIVATE_HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-            TEST_SOURCES : '*'
-            DEPRECATED_HEADER_FILES : '*'
-            MODULE_ENABLED_FEATURES : '*'
-
-    build_lib_example:
-        flags: [IGNORE_PCH]
-        kwargs:
-            NAME : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-
-    build_example:
-        flags: [IGNORE_PCH]
-        kwargs:
-            NAME : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-
-    find_external_library:
-        kwargs:
-            DEPENDENCY_NAME : '1'
-            HEADER_NAME : '1'
-            LIBRARY_NAME : '1'
-            HEADER_NAMES : '*'
-            LIBRARY_NAMES : '*'
-            PATH_SUFFIXES : '*'
-            SEARCH_PATHS : '*'
-
-format:
-  tab_size: 2
-  line_width: 80
-  dangle_parens: true
-  autosort: true
-  enable_sort: true
-  max_subgroups_hwrap: 1
-  max_pargs_hwrap: 1
-  max_lines_hwrap: 1
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/cmake-format-modules.yaml ns-3.39/build-support/cmake-format-modules.yaml
--- ns-3.38/build-support/cmake-format-modules.yaml	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/cmake-format-modules.yaml	2023-07-05 14:23:47.585426600 -0700
@@ -0,0 +1,60 @@
+additional_commands:
+    build_exec:
+        flags: [IGNORE_PCH, STANDALONE]
+        kwargs:
+            EXECNAME : '1'
+            EXECNAME_PREFIX : '1'
+            EXECUTABLE_DIRECTORY_PATH : '1'
+            INSTALL_DIRECTORY_PATH : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+            DEFINITIONS : '*'
+
+    build_lib:
+        flags: [IGNORE_PCH]
+        kwargs:
+            LIBNAME : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            PRIVATE_HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+            TEST_SOURCES : '*'
+            DEPRECATED_HEADER_FILES : '*'
+            MODULE_ENABLED_FEATURES : '*'
+
+    build_lib_example:
+        flags: [IGNORE_PCH]
+        kwargs:
+            NAME : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+
+    build_example:
+        flags: [IGNORE_PCH]
+        kwargs:
+            NAME : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+
+    find_external_library:
+        kwargs:
+            DEPENDENCY_NAME : '1'
+            HEADER_NAME : '1'
+            LIBRARY_NAME : '1'
+            HEADER_NAMES : '*'
+            LIBRARY_NAMES : '*'
+            PATH_SUFFIXES : '*'
+            SEARCH_PATHS : '*'
+
+format:
+  tab_size: 2
+  line_width: 80
+  dangle_parens: true
+  autosort: true
+  enable_sort: true
+  max_subgroups_hwrap: 1
+  max_pargs_hwrap: 1
+  max_lines_hwrap: 1
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/cmake-format.txt ns-3.39/build-support/cmake-format.txt
--- ns-3.38/build-support/cmake-format.txt	2023-03-16 20:54:23.492868200 -0700
+++ ns-3.39/build-support/cmake-format.txt	1969-12-31 16:00:00.000000000 -0800
@@ -1,58 +0,0 @@
-additional_commands:
-    build_exec:
-        flags: [IGNORE_PCH, STANDALONE]
-        kwargs:
-            EXECNAME : '1'
-            EXECNAME_PREFIX : '1'
-            EXECUTABLE_DIRECTORY_PATH : '1'
-            INSTALL_DIRECTORY_PATH : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-            DEFINITIONS : '*'
-
-    build_lib:
-        flags: [IGNORE_PCH]
-        kwargs:
-            LIBNAME : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            PRIVATE_HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-            TEST_SOURCES : '*'
-            DEPRECATED_HEADER_FILES : '*'
-            MODULE_ENABLED_FEATURES : '*'
-
-    build_lib_example:
-        flags: [IGNORE_PCH]
-        kwargs:
-            NAME : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-
-    build_example:
-        flags: [IGNORE_PCH]
-        kwargs:
-            NAME : '1'
-            SOURCE_FILES : '*'
-            HEADER_FILES : '*'
-            LIBRARIES_TO_LINK : '*'
-
-    find_external_library:
-        kwargs:
-            DEPENDENCY_NAME : '1'
-            HEADER_NAME : '1'
-            LIBRARY_NAME : '1'
-            HEADER_NAMES : '*'
-            LIBRARY_NAMES : '*'
-            PATH_SUFFIXES : '*'
-            SEARCH_PATHS : '*'
-
-format:
-  tab_size: 2
-  line_width: 80
-  dangle_parens: true
-  autosort: true
-  enable_sort: true
-  max_subgroups_hwrap: 3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/cmake-format.yaml ns-3.39/build-support/cmake-format.yaml
--- ns-3.38/build-support/cmake-format.yaml	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/cmake-format.yaml	2023-07-05 14:23:47.585426600 -0700
@@ -0,0 +1,58 @@
+additional_commands:
+    build_exec:
+        flags: [IGNORE_PCH, STANDALONE]
+        kwargs:
+            EXECNAME : '1'
+            EXECNAME_PREFIX : '1'
+            EXECUTABLE_DIRECTORY_PATH : '1'
+            INSTALL_DIRECTORY_PATH : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+            DEFINITIONS : '*'
+
+    build_lib:
+        flags: [IGNORE_PCH]
+        kwargs:
+            LIBNAME : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            PRIVATE_HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+            TEST_SOURCES : '*'
+            DEPRECATED_HEADER_FILES : '*'
+            MODULE_ENABLED_FEATURES : '*'
+
+    build_lib_example:
+        flags: [IGNORE_PCH]
+        kwargs:
+            NAME : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+
+    build_example:
+        flags: [IGNORE_PCH]
+        kwargs:
+            NAME : '1'
+            SOURCE_FILES : '*'
+            HEADER_FILES : '*'
+            LIBRARIES_TO_LINK : '*'
+
+    find_external_library:
+        kwargs:
+            DEPENDENCY_NAME : '1'
+            HEADER_NAME : '1'
+            LIBRARY_NAME : '1'
+            HEADER_NAMES : '*'
+            LIBRARY_NAMES : '*'
+            PATH_SUFFIXES : '*'
+            SEARCH_PATHS : '*'
+
+format:
+  tab_size: 2
+  line_width: 80
+  dangle_parens: true
+  autosort: true
+  enable_sort: true
+  max_subgroups_hwrap: 3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/custom-modules/ns3-module-macros.cmake ns-3.39/build-support/custom-modules/ns3-module-macros.cmake
--- ns-3.38/build-support/custom-modules/ns3-module-macros.cmake	2023-03-16 20:54:23.492868200 -0700
+++ ns-3.39/build-support/custom-modules/ns3-module-macros.cmake	2023-07-05 14:23:47.589426500 -0700
@@ -104,7 +104,9 @@
       )
     endif()
 
-    if(NOT FILESYSTEM_LIBRARY_IS_LINKED)
+    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.
       list(APPEND BLIB_LIBRARIES_TO_LINK -lstdc++fs)
     endif()
 
@@ -144,6 +146,15 @@
   foreach(library ${BLIB_LIBRARIES_TO_LINK})
     remove_lib_prefix("${library}" module_name)
 
+    # Ignore the case where the library dependency name match the ns-3 module
+    # since it is most likely is due to brite, click and openflow collisions.
+    # All the ns-3 module targets should be prefixed with 'lib' to be
+    # differentiable.
+    if("${library}" STREQUAL "${BLIB_LIBNAME}")
+      list(APPEND non_ns_libraries_to_link ${library})
+      continue()
+    endif()
+
     # Check if the module exists in the ns-3 modules list or if it is a
     # 3rd-party library
     if(${module_name} IN_LIST ns3-all-enabled-modules)
@@ -193,6 +204,20 @@
     ${lib${BLIB_LIBNAME}} ${exported_libraries} ${private_libraries}
   )
 
+  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)
+  endif()
+
   # set output name of library
   set_target_properties(
     ${lib${BLIB_LIBNAME}}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/macros-and-definitions.cmake ns-3.39/build-support/macros-and-definitions.cmake
--- ns-3.38/build-support/macros-and-definitions.cmake	2023-03-16 20:54:23.496868100 -0700
+++ ns-3.39/build-support/macros-and-definitions.cmake	2023-07-05 14:23:47.589426500 -0700
@@ -36,6 +36,9 @@
        "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)
+
 # Replace default CMake messages (logging) with custom colored messages as early
 # as possible
 include(${PROJECT_SOURCE_DIR}/build-support/3rd-party/colored-messages.cmake)
@@ -71,6 +74,11 @@
   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)
@@ -148,6 +156,25 @@
 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()
+
+# 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?
@@ -202,6 +229,7 @@
 endif()
 
 set(GCC FALSE)
+set(GCC8 FALSE)
 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
   if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${GNU_MinVersion})
     message(
@@ -209,6 +237,12 @@
         "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}")
@@ -222,6 +256,7 @@
 set(CXX_UNSUPPORTED_STANDARDS 98 11 14)
 set(CMAKE_CXX_STANDARD_MINIMUM 17)
 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)
@@ -308,7 +343,6 @@
   unset(ns3-headers-to-module-map CACHE)
   unset(ns3-libs CACHE)
   unset(ns3-libs-tests CACHE)
-  unset(ns3-python-bindings-modules CACHE)
   mark_as_advanced(
     build_profile
     build_profile_suffix
@@ -322,7 +356,6 @@
     ns3-headers-to-module-map
     ns3-libs
     ns3-libs-tests
-    ns3-python-bindings-modules
   )
 endmacro()
 
@@ -410,6 +443,8 @@
   if(${NS3_TESTS} OR ${ns3rc_tests_enabled})
     set(ENABLE_TESTS ON)
     enable_testing()
+  else()
+    list(REMOVE_ITEM libs_to_build test)
   endif()
 
   set(profiles_without_suffixes release)
@@ -553,11 +588,11 @@
       cmake-format
       COMMAND
         ${CMAKE_FORMAT_PROGRAM} -c
-        ${PROJECT_SOURCE_DIR}/build-support/cmake-format.txt -i
+        ${PROJECT_SOURCE_DIR}/build-support/cmake-format.yaml -i
         ${INTERNAL_CMAKE_FILES}
       COMMAND
         ${CMAKE_FORMAT_PROGRAM} -c
-        ${PROJECT_SOURCE_DIR}/build-support/cmake-format-modules.txt -i
+        ${PROJECT_SOURCE_DIR}/build-support/cmake-format-modules.yaml -i
         ${MODULES_CMAKE_FILES}
     )
     unset(MODULES_CMAKE_FILES)
@@ -802,7 +837,7 @@
       find_package(Python3 COMPONENTS Interpreter Development)
     else()
       # cmake-format: off
-      set(Python_ADDITIONAL_VERSIONS 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9)
+      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)
@@ -856,10 +891,12 @@
       )
     endif()
   else()
-    message(
-      ${HIGHLIGHTED_STATUS}
-      "Python: an incompatible version of Python was found, python bindings will be disabled"
-    )
+    if(${NS3_PYTHON_BINDINGS})
+      message(
+        ${HIGHLIGHTED_STATUS}
+        "Python: an incompatible version of Python was found, python bindings will be disabled"
+      )
+    endif()
   endif()
 
   set(ENABLE_PYTHON_BINDINGS OFF)
@@ -901,12 +938,16 @@
         message(
           ${HIGHLIGHTED_STATUS}
           "NS3_BINDINGS_INSTALL_DIR was not set. The python bindings won't be installed with ./ns3 install."
+          "This setting is meant for packaging and redistribution."
         )
         message(
           ${HIGHLIGHTED_STATUS}
           "Set NS3_BINDINGS_INSTALL_DIR=\"${SUGGESTED_BINDINGS_INSTALL_DIR}\" to install it to the default location."
         )
       else()
+        if(${NS3_BINDINGS_INSTALL_DIR} STREQUAL "INSTALL_PREFIX")
+          set(NS3_BINDINGS_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
+        endif()
         install(FILES bindings/python/ns__init__.py
                 DESTINATION ${NS3_BINDINGS_INSTALL_DIR}/ns RENAME __init__.py
         )
@@ -1350,8 +1391,6 @@
   set(ns3-contrib-libs)
   set(lib-ns3-static-objs)
   set(ns3-external-libs)
-  set(ns3-python-bindings ns${NS3_VER}-pybindings${build_profile_suffix})
-  set(ns3-python-bindings-modules)
 
   foreach(libname ${scanned_modules})
     # Create libname of output library of module
@@ -1477,7 +1516,7 @@
     include(FetchContent)
     FetchContent_Declare(
       netanim GIT_REPOSITORY https://gitlab.com/nsnam/netanim.git
-      GIT_TAG netanim-3.108
+      GIT_TAG netanim-3.109
     )
     FetchContent_Populate(netanim)
     file(COPY build-support/3rd-party/netanim-cmakelists.cmake
@@ -1577,8 +1616,11 @@
   )
 
   # Resolve nested scratch prefixes without user intervention
-  if("${CMAKE_CURRENT_SOURCE_DIR}" MATCHES "scratch"
-     AND "${BEXEC_EXECNAME_PREFIX}" STREQUAL ""
+  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()
@@ -1926,10 +1968,6 @@
       unset(dependencies)
       unset(contrib_dependencies)
     endforeach()
-
-    if(core IN_LIST ${libs_to_build})
-      list(APPEND ${libs_to_build} test) # include test module
-    endif()
   endif()
 
   if(${NS3_DISABLED_MODULES} OR ${ns3rc_disabled_modules})
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/pip-wheel/auditwheel-exclude-list.py ns-3.39/build-support/pip-wheel/auditwheel-exclude-list.py
--- ns-3.38/build-support/pip-wheel/auditwheel-exclude-list.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/pip-wheel/auditwheel-exclude-list.py	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,11 @@
+import os
+
+ns3_path = os.path.dirname(os.path.abspath(os.sep.join([__file__, "../../"])))
+
+for variant in ["lib", "lib64"]:
+    lib_dir = os.path.abspath(os.path.join(ns3_path, f"build/{variant}"))
+    if not os.path.exists(lib_dir):
+        continue
+    for lib in os.listdir(lib_dir):
+        if "libns3" in lib:
+            print(f"--exclude {lib}", end=' ')
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/pip-wheel/ns/__init__.py ns-3.39/build-support/pip-wheel/ns/__init__.py
--- ns-3.38/build-support/pip-wheel/ns/__init__.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/pip-wheel/ns/__init__.py	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,10 @@
+# This is a stub module that loads the actual ns-3
+# bindings from ns3.ns
+import sys
+
+try:
+    import 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.38/build-support/pip-wheel/visualizer/__init__.py ns-3.39/build-support/pip-wheel/visualizer/__init__.py
--- ns-3.38/build-support/pip-wheel/visualizer/__init__.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/pip-wheel/visualizer/__init__.py	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,11 @@
+# This is a stub module that loads the actual visualizer
+# from ns3.visualizer
+import sys
+
+try:
+    import ns3.visualizer
+except ModuleNotFoundError as e:
+    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
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/pybindings-LP64-to-ILP32.py ns-3.39/build-support/pybindings-LP64-to-ILP32.py
--- ns-3.38/build-support/pybindings-LP64-to-ILP32.py	2023-03-16 20:54:23.496868100 -0700
+++ ns-3.39/build-support/pybindings-LP64-to-ILP32.py	1969-12-31 16:00:00.000000000 -0800
@@ -1,16 +0,0 @@
-#! /usr/bin/env python3
-# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
-
-def lp64_to_ilp32(lp64path, ilp32path):
-    import re
-    lp64bindings = None
-    with open(lp64path, "r") as lp64file:
-        lp64bindings = lp64file.read()
-    with open(ilp32path, "w") as ilp32file:
-        ilp32bindings = re.sub("unsigned long(?!( long))", "unsigned long long", lp64bindings)
-        ilp32file.write(ilp32bindings)
-
-if __name__ == "__main__":
-    import sys
-    print(sys.argv)
-    exit(lp64_to_ilp32(sys.argv[1], sys.argv[2]))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/test-files/test-contrib-dependency/CMakeLists.txt ns-3.39/build-support/test-files/test-contrib-dependency/CMakeLists.txt
--- ns-3.38/build-support/test-files/test-contrib-dependency/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-contrib-dependency/CMakeLists.txt	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,6 @@
+build_lib(
+        LIBNAME test-contrib-dependency
+        SOURCE_FILES contrib-source.cc
+        HEADER_FILES contrib-header.h
+        LIBRARIES_TO_LINK ${libcore}
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/test-files/test-contrib-dependency/contrib-header.h ns-3.39/build-support/test-files/test-contrib-dependency/contrib-header.h
--- ns-3.38/build-support/test-files/test-contrib-dependency/contrib-header.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-contrib-dependency/contrib-header.h	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,6 @@
+#ifndef NS3_CONTRIB_HEADER_H
+#define NS3_CONTRIB_HEADER_H
+
+void testPrint();
+
+#endif // NS3_CONTRIB_HEADER_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/test-files/test-contrib-dependency/contrib-source.cc ns-3.39/build-support/test-files/test-contrib-dependency/contrib-source.cc
--- ns-3.38/build-support/test-files/test-contrib-dependency/contrib-source.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-contrib-dependency/contrib-source.cc	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,9 @@
+#include "contrib-header.h"
+
+#include <ns3/simulator.h>
+
+void
+testPrint()
+{
+    std::cout << ns3::Simulator::Now() << std::endl;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt ns-3.39/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt
--- ns-3.38/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-src-dependant-on-contrib/CMakeLists.txt	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,8 @@
+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.38/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt ns-3.39/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt
--- ns-3.38/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-src-dependant-on-contrib/examples/CMakeLists.txt	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,6 @@
+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.38/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc ns-3.39/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc
--- ns-3.38/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-src-dependant-on-contrib/examples/source-example.cc	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,8 @@
+#include <ns3/src-header.h>
+
+int
+main()
+{
+    testPrint2();
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/build-support/test-files/test-src-dependant-on-contrib/src-header.h ns-3.39/build-support/test-files/test-src-dependant-on-contrib/src-header.h
--- ns-3.38/build-support/test-files/test-src-dependant-on-contrib/src-header.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-src-dependant-on-contrib/src-header.h	2023-07-05 14:23:47.589426500 -0700
@@ -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.38/build-support/test-files/test-src-dependant-on-contrib/src-source.cc ns-3.39/build-support/test-files/test-src-dependant-on-contrib/src-source.cc
--- ns-3.38/build-support/test-files/test-src-dependant-on-contrib/src-source.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/build-support/test-files/test-src-dependant-on-contrib/src-source.cc	2023-07-05 14:23:47.589426500 -0700
@@ -0,0 +1,7 @@
+#include <ns3/contrib-header.h>
+
+void
+testPrint2()
+{
+    testPrint();
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/CHANGES.md ns-3.39/CHANGES.md
--- ns-3.38/CHANGES.md	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/CHANGES.md	2023-07-05 14:23:47.581426600 -0700
@@ -13,6 +13,76 @@
 
 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.38 to ns-3.39
+-------------------------------
+
+### 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`.
+* (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.
+* (lr-wpan) Added `LrwpanMac::MlmeGetRequest` function and the corresponding confirm callbacks as well as `LrwpanMac::SetMlmeGetConfirm` function.
+* (applications) Added `Tx` and `TxWithAddresses` trace sources in `UdpClient`.
+* (spectrum) Added `SpectrumTransmitFilter` class and the ability to add them to `SpectrumChannel` instances.
+* (stats) Added `Histogram::Clear` function to clear the histogram contents.
+* (wifi) Added `WifiBandwidthFilter` class to allow filtering of out-of-band Wi-Fi signals.
+* (flow-monitor) Added `FlowMonitor::ResetAllStats` function to reset the FlowMonitor statistics.
+
+### Changes to existing API
+
+* 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.
+  * (dsr) Class `DsrOptionRerrUnsupportHeader` from `dsr-option-header.h` was renamed `DsrOptionRerrUnsupportedHeader`.
+  * (internet) Enumerated value `IPV6_EXT_AUTHENTIFICATION` from `ipv6-header.h` was renamed `IPV6_EXT_AUTHENTICATION`.
+  * (lr-wpan) Constant `aMaxBeaconPayloadLenght` from `lr-wpan-constants.h` was renamed `aMaxBeaconPayloadLength`.
+  * (lte) Enumeration `ControPduType_t` from `lte-rlc-am-header.h` was renamed `ControlPduType_t`.
+  * (lte) Function `LteUeCphySapProvider::StartInSnycDetection()` from `lte-ue-cphy-sap.h` was renamed `LteUeCphySapProvider::StartInSyncDetection()`.
+  * (lte) Function `MemberLteUeCphySapProvider::StartInSnycDetection()` from `lte-ue-cphy-sap.h` was renamed `MemberLteUeCphySapProvider::StartInSyncDetection()`.
+  * (lte) Function `LteUePhy::StartInSnycDetection()` from `lte-ue-phy.h` was renamed `LteUePhy::StartInSyncDetection()`.
+  * (lte) Function `DoUlInfoListElementHarqFeeback` from `lte-enb-phy-sap.h` and `lte-enb-mac.h` was renamed `DoUlInfoListElementHarqFeedback`.
+  * (lte) Function `DoDlInfoListElementHarqFeeback` from `lte-enb-phy-sap.h` and `lte-enb-mac.h` was renamed `DoDlInfoListElementHarqFeedback`.
+  * (mesh) Function `PeerManagementProtocolMac::SetPeerManagerProtcol` from `peer-management-protocol-mac.h` was renamed `PeerManagementProtocolMac::SetPeerManagerProtocol`.
+  * (network) File `lollipop-comparisions.cc` was renamed `lollipop-comparisons.cc`.
+  * (network) Attribute `currentTrimedFromStart` from `packet-metadata.h` was renamed `currentTrimmedFromStart`.
+  * (network) Attribute `currentTrimedFromEnd` from `packet-metadata.h` was renamed `currentTrimmedFromEnd`.
+  * (sixlowpan) Function `SixLowPanNetDevice::Fragments::GetFraments` from `sixlowpan-net-device.cc` was renamed `SixLowPanNetDevice::Fragments::GetFragments`.
+  * (wave) Function `OcbWifiMac::CancleTx()` from `ocb-wifi-mac.h` was renamed `OcbWifiMac::CancelTx()`.
+  * (wifi) Attribute `m_succesMax1` from `aparf-wifi-manager.h` was renamed `m_successMax1`.
+  * (wifi) Attribute `m_succesMax2` from `aparf-wifi-manager.h` was renamed `m_successMax2`.
+  * (wifi) Enumerated value `MDAOP_ADVERTISMENT_REQUEST` from `mgmt-headers.h` was renamed `MDAOP_ADVERTISEMENT_REQUEST`.
+  * (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`.
+* (lr-wpan) Updated `LrWpanPhy::PlmeSetAttribute` and `LrWpanPhy::PlmeGetAttribute` (Request and Confirm) to use smart pointers.
+* (lr-wpan) Modified `LrWpanPhy::PlmeGetAttributeRequest` to include support for a few attributes (none were supported before the change).
+* (lr-wpan) Added `macShortAddress`, `macExtendedAddress` and `macPanId` to the attributes that can be use with MLME-GET and MLME-SET functions.
+* (wifi) The QosBlockedDestinations class has been removed and its functionality is now provided via a new framework for blocking/unblocking packets that is based on the queue scheduler.
+* (internet) The function signature of `Ipv4RoutingProtocol::RouteInput` and `Ipv6RoutingProtocol::RouteInput` have changed. The `UnicastForwardCallback` (ucb), `MulticastForwardCallback` (mcb), `LocalDeliverCallback` (lcb) and `ErrorCallback` (ecb) should now be passed as const references.
+* (olsr) The defines `OLSR_WILL_*` have been replaced by enum `Willingness`.
+* (olsr) The defines `OLSR_*_LINK` have been replaced by enum `LinkType`.
+* (olsr) The defines `OLSR_*_NEIGH` have been replaced by enum `NeighborType`.
+* (wifi) The `WifiCodeRate` typedef was converted to an enum.
+* (internet) `InternetStackHelper` can be now used on nodes with an `InternetStack` already installed (it will not install IPv[4,6] twice).
+* (lr-wpan) Block the reception of orphan notification commands to devices other than PAN coordinators or coordinators.
+* (lr-wpan) Block the reception of broadcast messages in the same device that issues it. This is done in both cases when the src address is either short or extended address.
+* (lr-wpan) Adds a new variable flag `m_coor` to the MAC to differentiate between coordinators and PAN coordinators.
+* (lte) Add support for DC-GBR. The member `QosBearerType_e` of the structure `LogicalChannelConfigListElement_s` is extended to include DC-GBR resource type. Based on this change, the method **IsGbr** of `EpsBearer`, is renamed to **GetResourceType**. LTE code using this method, is updated according to this change.
+* (lte) The `EpsBearer` is extended to include 3GPP Release 18 5QIs.
+* (lte) Add PDCP discard timer. If enabled using the attribute `EnablePdcpDiscarding`, in case that the buffering time (head-of-line delay) of a packet is greater than the PDB or a value set by the user, it will perform discarding at the moment of passing the PDCP SDU to RLC.
+* (lte) Centralize the constants `MIN_NO_CC` and `MAX_NO_CC`, declared in multiple header files, into the header `lte-common.h`.
+* (wave) The Wave module was removed from the codebase due to lack of maintenance
+
+### Changes to build system
+
+### Changed behavior
+
+* (core) The priority of `DEBUG` level logging has been lowered from just below `WARN` level to just below `LOGIC` level.
+* (buildings) Calculation of the O2I Low/High Building Penetration Losses based on 3GPP 38.901 7.4.3.1 was missing. These losses are now included in the pathloss calculation when buildings are present.
+* (network) The function `Buffer::Allocate` will over-provision `ALLOC_OVER_PROVISION` bytes when allocating buffers for packets. `ALLOC_OVER_PROVISION` is currently set to 100 bytes.
+* (wifi) By default, the `SpectrumWifiHelper` now adds a `WifiBandwidthFilter` to discard out-of-band signals before scheduling them on the receiver.  This should not affect the simulated behavior of Wi-Fi but may speed up the execution of large Wi-Fi simulations.
+* (wifi) Protection mechanisms (e.g., RTS/CTS) are not used if destinations have already received (MU-)RTS in the current TXOP
+* (wifi) Protection mechanisms can be used for management frames as well (if needed)
+
 Changes from ns-3.37 to ns-3.38
 -------------------------------
 
@@ -29,6 +99,7 @@
 * (wifi) Added a new attribute **NMaxInflights** to QosTxop to set the maximum number of links on which an MPDU can be simultaneously in-flight.
 * (wifi) New API has been introduced to support 802.11be Multi-Link Operations (MLO)
 * (wifi) New API has been introduced to support 802.11ax dual NAV, UL MU CS, and MU-RTS/CTS features
+* (wifi) Added a new attribute **TrackSignalsFromInactiveInterfaces** to SpectrumWifiPhy to select whether it should track signals from inactive spectrum PHY interfaces.
 
 ### Changes to existing API
 
@@ -39,6 +110,8 @@
 * (lr-wpan) Added file `src/lr-wpan/model/lr-wpan-constants.h` with common constants of the LR-WPAN module.
 * (lr-wpan) Removed the functions `LrWpanCsmaCa::GetUnitBackoffPeriod()` and `LrWpanCsmaCa::SetUnitBackoffPeriod()`, and moved the constant `m_aUnitBackoffPeriod` to `src/lr-wpan/model/lr-wpan-constants.h`.
 * (lr-wpan) `LrWpanHelper::CreateAssociatedPan` replace `LrWpanHelper::AssociateToPan` and is able to create an associated PAN of the devices with both short addresses (16-bits) and extended addresses (EUI-64 bits).
+* (wifi) `SpectrumWifiPhy::SetChannel` has been renamed to `SpectrumWifiPhy::AddChannel` and has one additional parameter (optional) to indicate the frequency range that is covered by the provided spectrum channel. By default, the whole wifi spectrum channel is considered.
+* The `WifiSpectrumHelper::SetChannel` functions used for MLO do no longer take a link ID parameter, but instead takes the frequency range covered by the spectrum channel and have been renamed to `WifiSpectrumHelper::AddChannel`. The remaining `WifiSpectrumHelper::SetChannel` functions assume the whole wifi spectrum range is used by the spectrum channel.
 
 ### Changes to build system
 
@@ -400,7 +473,7 @@
 * A new attribute `WifiPhy::PostReceptionErrorModel` has been added to force specific packet drops.
 * A new attribute `WifiPhy::PreambleDetectionModel` has been added to decide whether PHY preambles are successfully detected.
 * New attributes `QosTxop::AddBaResponseTimeout` and `QosTxop::FailedAddBaTimeout` have been added to set the timeout to wait for an ADDBA response after the ACK to the ADDBA request is received and to set the timeout after a failed BA agreement, respectively.
-* A new attribute `QosTxop::UseExpliciteBarAfterMissedBlockAck` has been added to specify whether explicit Block Ack Request should be sent upon missed Block Ack Response.
+* A new attribute `QosTxop::UseExplicitBarAfterMissedBlockAck` has been added to specify whether explicit Block Ack Request should be sent upon missed Block Ack Response.
 * Added a new trace source `EndOfHePreamble` in WifiPhy for tracing end of preamble (after training fields) for received 802.11ax packets.
 * Added a new helper method to SpectrumWifiPhyHelper and YansWifiPhyHelper to set the **frame capture model**.
 * Added a new helper method to SpectrumWifiPhyHelper and YansWifiPhyHelper to set the **preamble detection model**.
@@ -587,12 +660,12 @@
 * A new standard value has been added that enables the new 11ax data rates.
 * A new 11ax preamble has been added.
 * A new attribute was added to configure the guard interval duration for High Efficiency (HE) PHY entities. This attribute can be set using the YansWifiPhyHelper.
-* A new information element has been added: HeCapabilities. This information element is added to the MAC frame header if the node is a HE node. This HeCapabilites information element is used to advertise the HE capabilities of the node to other nodes in the network.
+* A new information element has been added: HeCapabilities. This information element is added to the MAC frame header if the node is a HE node. This HeCapabilities information element is used to advertise the HE capabilities of the node to other nodes in the network.
 * A new class were added for the RRPAA WiFi rate control mechanism.
 * Included carrier aggregation feature in LTE module
 
 * LTE model is extended to support carrier aggregation feature according to 3GPP Release 10, for up to 5 component carriers.
-* InstallSingleEnbDevice and InstalSingeUeDevice functions of LteHelper are now constructing LteEnbDevice and LteUeDevice according to CA architecture. Each device, UE and eNodeB contains an instance of component carrier manager, and may have several component carrier instances.
+* InstallSingleEnbDevice and InstallSingleUeDevice functions of LteHelper are now constructing LteEnbDevice and LteUeDevice according to CA architecture. Each device, UE and eNodeB contains an instance of component carrier manager, and may have several component carrier instances.
 * SAP interfaces are extended to include CA message exchange functionality.
 * RRC connection procedure is extended to allow RRC connection reconfiguration for the configuration of the secondary carriers.
 * RRC measurement reporting is extended to allow measurement reporting from the secondary carriers.
@@ -744,7 +817,7 @@
 * A new helper (VhtWifiMacHelper) was added to set up a Very high throughput (VHT) MAC entity.
 * A new standard value has been added that enables the new 11ac data rates.
 * A new 11ac preamble has been added.
-* A new information element has been added: VhtCapabilities. This information element is added to the MAC frame header if the node is a VHT node. This VhtCapabilites information element is used to advertise the VHT capabilities of the node to other nodes in the network.
+* A new information element has been added: VhtCapabilities. This information element is added to the MAC frame header if the node is a VHT node. This VhtCapabilities information element is used to advertise the VHT capabilities of the node to other nodes in the network.
 * The ArpCache API was extended to allow the manual removal of ArpCache entries and the addition of permanent (static) entries for IPv4.
 * The SimpleChannel in the `network` module now allows per-NetDevice blacklists, in order to do hidden terminal testcases.
 
@@ -837,7 +910,7 @@
 
 * New `const double& SpectrumValue::operator[] (size_t index) const`.
 * A new TraceSource has been added to TCP sockets: SlowStartThreshold.
-* New method CommandLine::AddValue (name, attibutePath) to provide a shorthand argument `name` for the Attribute `path`. This also has the effect of including the help string for the Attribute in the Usage message.
+* New method CommandLine::AddValue (name, attributePath) to provide a shorthand argument `name` for the Attribute `path`. This also has the effect of including the help string for the Attribute in the Usage message.
 * The GSoC 2014 project in the LTE module has brought some additional APIs:
   * a new abstract class LteFfrAlgorithm, which every future implementation of frequency reuse algorithm should inherit from
   * a new SAPs: one between MAC Scheduler and FrAlgorithm, one between RRC and FrAlgorithm
@@ -988,7 +1061,7 @@
   * New attributes were added to help the user setup a High Throughput (HT) PHY entity. These attributes can be set using the YansWifiPhyHelper
   * A new standard value has been added that enables the new 11n data rates.
   * New 11n preambles has been added (Mixed format and greenfield). To be able to change Tx duration according to the preamble used, a new class TxVector has been added to carry the transmission parameters (mode, preamble, stbc,..). Several functions have been updated to allow the passage of TxVector instead of WifiMode in MacLow, WifiRemoteStationManager, WifiPhy, YansWifiPhy,..
-  * A new information element has been added: HTCapabilities. This information element is added to the MAC frame header if the node is an HT node. This HTCapabilites information element is used to advertise the HT capabilities of the node to other nodes in the network
+  * A new information element has been added: HTCapabilities. This information element is added to the MAC frame header if the node is an HT node. This HTCapabilities information element is used to advertise the HT capabilities of the node to other nodes in the network
 * InternetStackHelper has two new functions:SetIpv4ArpJitter (bool enable) and SetIpv6NsRsJitter (bool enable) to enable/disable the random jitter on the transmission of IPv4 ARP Request and IPv6 NS/RS.
 * Bounds on valid time inputs for time attributes can now be enabled. See attribute-test-suite.cc for an example.
 * New generic hash function interface provided in the simulation core. Two hash functions are provided: murmur3 (default), and the venerable FNV1a. See the Hash Functions section in the ns-3 manual.
@@ -1057,7 +1130,7 @@
 * New DSR API
   * Added PassiveBuffer class to save maintenance packet entry for passive acknowledgment option
   * Added FindSourceEntry function in RreqTable class to keep track of route request entry received from same source node
-  * Added NotifyDataReciept function in DsrRouting class to notify the data receipt of the next hop from link layer. This is used for the link layer acknowledgment.
+  * Added NotifyDataReceipt function in DsrRouting class to notify the data receipt of the next hop from link layer. This is used for the link layer acknowledgment.
 * New Tag, PacketSocketTag, to carry the destination address of a packet and the packet type
 * New Tag, DeviceNameTag, to carry the ns-3 device name from where a packet is coming
 * New Error Model, BurstError model, to determine which bursts of packets are errored corresponding to an underlying distribution, burst rate, and burst size
@@ -1091,7 +1164,7 @@
     * LteEnbPhy new methods GetLteEnbCphySapProvider, SetLteEnbCphySapUser, GetDlSpectrumPhy, GetUlSpectrumPhy, CreateSrsReport
     * LteEnbPhy methods DoSendMacPdu, DoSetTransmissionMode, DoSetSrsConfigurationIndex, DoGetMacChTtiDelay, DoSendLteControlMessage, AddUePhy, DeleteUePhy made private
     * LteEnbPhySapProvider removed methods SetBandwidth, SetTransmissionMode, SetSrsConfigurationIndex, SetCellId
-    * LteEnbPhySapUser added methods ReceiveRachPreamble, UlInfoListElementHarqFeeback, DlInfoListElementHarqFeeback
+    * LteEnbPhySapUser added methods ReceiveRachPreamble, UlInfoListElementHarqFeedback, DlInfoListElementHarqFeedback
     * LtePdcp added methods (Set/Get)Status
     * LtePdcp DoTransmitRrcPdu renamed DoTransmitPdcpSdu
     * LteUeRrc new enum State. New methods SetLteUeCphySapProvider, GetLteUeCphySapUser, SetLteUeRrcSapUser, GetLteUeRrcSapProvider, GetState, GetDlEarfcn, GetDlBandwidth, GetUlBandwidth, GetCellId, SetUseRlcSm . GetRnti made const.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/.clang-tidy ns-3.39/.clang-tidy
--- ns-3.38/.clang-tidy	2023-03-16 20:54:23.484868000 -0700
+++ ns-3.39/.clang-tidy	2023-07-05 14:23:47.577426400 -0700
@@ -28,23 +28,29 @@
   performance-inefficient-algorithm,
   performance-inefficient-vector-operation,
   performance-trivially-destructible,
+  performance-unnecessary-copy-initialization,
   readability-braces-around-statements,
   readability-container-size-empty,
-  readability-duplicate-include,
   readability-isolate-declaration,
   readability-make-member-function-const,
-  readability-misleading-indentation,
   readability-misplaced-array-index,
   readability-redundant-control-flow,
   readability-redundant-function-ptr-dereference,
   readability-redundant-smartptr-get,
   readability-redundant-string-cstr,
+  readability-simplify-boolean-expr,
   readability-simplify-subscript-expr,
   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
+    value: Ptr
+  - key: readability-simplify-boolean-expr.SimplifyDeMorgan
+    value: false
+
 FormatStyle: "file"
 HeaderFilterRegex: ".*(ns|NS).*/(contrib|examples|src|scratch|utils)/*/.*h"
 UseColor: true
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/CMakeLists.txt ns-3.39/CMakeLists.txt
--- ns-3.38/CMakeLists.txt	2023-03-16 20:54:23.488868200 -0700
+++ ns-3.39/CMakeLists.txt	2023-07-05 14:23:47.581426600 -0700
@@ -25,6 +25,7 @@
 # fd-net-device options
 option(NS3_EMU "Build with emulation support" ON)
 option(NS3_TAP "Build with Tap support" ON)
+option(NS3_DPDK "Enable fd-net-device DPDK features" OFF)
 
 # maintenance and documentation
 option(NS3_CLANG_FORMAT "Enforce cody style with clang-format" OFF)
@@ -92,8 +93,8 @@
 set(NS3_ENABLED_MODULES ""
     CACHE STRING "List of modules to enable (e.g. core;network;internet)"
 )
-set(NS3_DISABLED_MODULES ""
-    CACHE STRING "List of modules to disable (e.g. lte;wimax;wave)"
+set(NS3_DISABLED_MODULES "" CACHE STRING
+                                  "List of modules to disable (e.g. lte;wimax)"
 )
 
 # Filter in the modules from which examples and tests will be built
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/contributing/source/coding-style.rst ns-3.39/doc/contributing/source/coding-style.rst
--- ns-3.38/doc/contributing/source/coding-style.rst	2023-03-16 20:54:23.496868100 -0700
+++ ns-3.39/doc/contributing/source/coding-style.rst	2023-07-05 14:23:47.593426700 -0700
@@ -129,6 +129,9 @@
 All checks are enabled by default. Users can disable specific checks using the corresponding
 flags: ``--no-formatting``, ``--no-whitespace`` and ``--no-tabs``.
 
+Additional information about the formatting issues detected by the script can be enabled
+by adding the ``-v, --verbose`` flag.
+
 In addition to checking the files, the script can automatically fix detected issues in-place.
 This mode is enabled by adding the ``--fix`` flag.
 
@@ -148,16 +151,16 @@
 .. sourcecode:: console
 
   # Entire codebase (using paths relative to the ns-3 main directory)
-  ./utils/check-style-clang-format.py [--fix] [--no-formatting] [--no-whitespace] [--no-tabs] .
+  ./utils/check-style-clang-format.py [--fix] [--verbose] [--no-formatting] [--no-whitespace] [--no-tabs] .
 
   # Entire codebase (using absolute paths)
-  /path/to/utils/check-style-clang-format.py [--fix] [--no-formatting] [--no-whitespace] [--no-tabs] /path/to/ns3
+  /path/to/utils/check-style-clang-format.py [--fix] [--verbose] [--no-formatting] [--no-whitespace] [--no-tabs] /path/to/ns3
 
   # Specific directory
-  /path/to/utils/check-style-clang-format.py [--fix] [--no-formatting] [--no-whitespace] [--no-tabs] absolute_or_relative/path/to/directory
+  /path/to/utils/check-style-clang-format.py [--fix] [--verbose] [--no-formatting] [--no-whitespace] [--no-tabs] absolute_or_relative/path/to/directory
 
   # Individual file
-  /path/to/utils/check-style-clang-format.py [--fix] [--no-formatting] [--no-whitespace] [--no-tabs] absolute_or_relative/path/to/file
+  /path/to/utils/check-style-clang-format.py [--fix] [--verbose] [--no-formatting] [--no-whitespace] [--no-tabs] absolute_or_relative/path/to/file
 
 
 Clang-tidy
@@ -752,8 +755,8 @@
 
     #include <ns3/header.h>
 
-Variables
-=========
+Variables and constants
+=======================
 
 Each variable declaration is on a separate line.
 Variables should be declared at the point in the code where they are needed,
@@ -768,6 +771,34 @@
   int x = 0;
   int y = 0;
 
+Named constants defined in classes should be declared as ``static constexpr`` instead of
+macros, const, or enums. Use of ``static constexpr`` allows a single instance to be
+evaluated at compile-time. Declaring the constant in the class enables it to share the scope
+of the class.
+
+If the constant is only used in one file, consider declaring the constant in the implementation
+file (``*.cc``).
+
+.. sourcecode:: cpp
+
+  // Avoid declaring constants as enum
+  class LteRlcAmHeader : public Header
+  {
+      enum ControlPduType_t
+      {
+          STATUS_PDU = 000,
+      };
+  };
+
+  // Prefer to declare them as static constexpr (in class)
+  class LteRlcAmHeader : public Header
+  {
+      static constexpr uint8_t STATUS_PDU{0};
+  };
+
+  // Or as constexpr (in implementation files)
+  constexpr uint8_t STATUS_PDU{0};
+
 Comments
 ========
 
@@ -1096,6 +1127,53 @@
   n += 3;
   return n;
 
+Boolean Simplifications
+=======================
+
+In order to increase readability and performance, avoid unnecessarily complex boolean
+expressions in if statements and variable declarations.
+
+For instance, the following code:
+
+  .. sourcecode:: cpp
+
+    bool IsPositive(int n)
+    {
+        if (n > 0)
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    void ProcessNumber(int n)
+    {
+        if (IsPositive(n) == true)
+        {
+            ...
+        }
+    }
+
+can be rewritten as:
+
+  .. sourcecode:: cpp
+
+    bool IsPositive(int n)
+    {
+        return n > 0;
+    }
+
+    void ProcessNumber(int n)
+    {
+        if (IsPositive(n))
+        {
+            ...
+        }
+    }
+
 Smart pointer boolean comparisons
 =================================
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/contributing/source/conf.py ns-3.39/doc/contributing/source/conf.py
--- ns-3.38/doc/contributing/source/conf.py	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/doc/contributing/source/conf.py	2023-07-05 14:23:47.593426700 -0700
@@ -48,9 +48,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3.38'
+version = 'ns-3.39'
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3.38'
+release = 'ns-3.39'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/contributing/source/general.rst ns-3.39/doc/contributing/source/general.rst
--- ns-3.38/doc/contributing/source/general.rst	2023-03-16 20:54:23.496868100 -0700
+++ ns-3.39/doc/contributing/source/general.rst	2023-07-05 14:23:47.593426700 -0700
@@ -95,7 +95,6 @@
 An example placement of a copyright statement can be found in the file
 ``src/network/model/packet.h``::
 
-  /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
   /*
    * Copyright (c) 2005,2006 INRIA
    *
@@ -112,7 +111,6 @@
 of code that is covered by the new copyright.  An example is the program
 ``src/lte/model/lte-ue-phy.h``::
 
-  /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
   /*
    * Copyright (c) 2010 TELEMATICS LAB, DEE - Politecnico di Bari
    * Copyright (c) 2018 Fraunhofer ESK : RLF extensions
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/doxygen.conf ns-3.39/doc/doxygen.conf
--- ns-3.38/doc/doxygen.conf	2023-03-16 20:54:23.500868000 -0700
+++ ns-3.39/doc/doxygen.conf	2023-07-05 14:23:47.593426700 -0700
@@ -1881,7 +1881,7 @@
 # If left blank no extra packages will be included.
 # This tag requires that the tag GENERATE_LATEX is set to YES.
 
-EXTRA_PACKAGES         = amsmath
+EXTRA_PACKAGES         = amsmath,amssymb
 
 # The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for
 # the generated LaTeX document. The header should contain everything until the
@@ -2693,5 +2693,5 @@
 DOT_CLEANUP            = YES
 
 # Disable dark mode in the generated HTML in Doxygen 1.9.6 or greater.
-HTML_COLORSTYLE        = LIGHT
+# HTML_COLORSTYLE        = LIGHT
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/installation/source/conf.py ns-3.39/doc/installation/source/conf.py
--- ns-3.38/doc/installation/source/conf.py	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/doc/installation/source/conf.py	2023-07-05 14:23:47.593426700 -0700
@@ -48,9 +48,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = 'ns-3.38'
+version = 'ns-3.39'
 # The full version, including alpha/beta/rc tags.
-release = 'ns-3.38'
+release = 'ns-3.39'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/installation/source/linux.rst ns-3.39/doc/installation/source/linux.rst
--- ns-3.38/doc/installation/source/linux.rst	2023-03-16 20:54:23.500868000 -0700
+++ ns-3.39/doc/installation/source/linux.rst	2023-07-05 14:23:47.593426700 -0700
@@ -21,8 +21,8 @@
 Requirements
 ************
 
-The minimum supported version of Ubuntu is Ubuntu 16.04 LTS (as long as a modern compiler
-version such as g++ version 8 or later is added).
+The minimum supported version of Ubuntu is Ubuntu 18.04 LTS (as long as a modern compiler
+version such as g++ version 9 or later is added).
 
   +--------------------+---------------------------------------------------------------------+
   | **ns-3 Version**   | **apt Packages**                                                    |
@@ -35,11 +35,8 @@
   +--------------------+---------------------------------------------------------------------+
 
 .. note::
-  As of January 2022 (ns-3.36 release and later), the minimum g++ version is g++-8.
-  Older Ubuntu releases (18.04, 16.04) come with an older default g++. On Ubuntu 18.04, this
-  `StackOverflow answer <https://askubuntu.com/a/1028656>`_ can be followed to install and
-  prefer g++-8. On older Ubuntu such as 16.04, to use the most recent |ns3|, you must install
-  g++-8 or g++-9 from the `Ubuntu toolchain <https://launchpad.net/%7Eubuntu-toolchain-r/+archive/ubuntu/test>`_.
+  As of July 2023 (ns-3.39 release and later), the minimum g++ version is g++-9.
+  Older ns-3 releases may work with older versions of g++; check the RELEASE_NOTES.
 
 Recommended
 ***********
@@ -106,9 +103,11 @@
 Python bindings
 ===============
 
+Python requires `Cppyy, <https://cppyy.readthedocs.io/en/latest/installation.html>` and specifically, version 2.4.2 is the latest version known to work with ns-3 at this time.
+
 ns-3.37 and newer::
 
-  python3 -m pip install --user cppyy
+  python3 -m pip install --user cppyy==2.4.2
 
 ns-3.30-3.36 (also requires pybindgen, found in the ``allinone`` directory)::
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/installation/source/overview.rst ns-3.39/doc/installation/source/overview.rst
--- ns-3.38/doc/installation/source/overview.rst	2023-03-16 20:54:23.500868000 -0700
+++ ns-3.39/doc/installation/source/overview.rst	2023-07-05 14:23:47.593426700 -0700
@@ -51,11 +51,9 @@
 |ns3| uses the CMake build system (until release 3.36, the Waf build system was used).  It can be
 built from command-line or via a code editor program.
 
-Most users write C++ ns-3 programs; Python support is less frequently used and is not officially
-maintained as of this writing.  As of *ns-3.37*, |ns3| uses `cppyy` to generate runtime Python
-bindings.  Officially, Python3 support is only lightly maintained, and the `cppyy` support was
-contributed by Gabriel Ferreira as a proof-of-concept (hint: the project is seeking a full-time
-maintainer to develop this further).
+Most users write C++ ns-3 programs; Python support is less frequently used.
+As of *ns-3.37*, |ns3| uses `cppyy` to generate runtime Python
+bindings, and |ns3| is available in the Pip repositories as of ns-3.39 release.
 
 Many users may be familiar with how software is packaged and installed on Linux and other systems
 using package managers.  For example, to install a given Linux development library such as
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/installation/source/quick-start.rst ns-3.39/doc/installation/source/quick-start.rst
--- ns-3.38/doc/installation/source/quick-start.rst	2023-03-16 20:54:23.500868000 -0700
+++ ns-3.39/doc/installation/source/quick-start.rst	2023-07-05 14:23:47.593426700 -0700
@@ -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.38``
+1. Download a release tarball.  This will unpack to a directory such as ``ns-allinone-3.39``
 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.38.tar.bz2
-  $ tar xfj ns-allinone-3.38.tar.bz2
-  $ cd ns-allinone-3.38/ns-3.38
+  $ wget https://www.nsnam.org/releases/ns-allinone-3.39.tar.bz2
+  $ tar xfj ns-allinone-3.39.tar.bz2
+  $ cd ns-allinone-3.39/ns-3.39
 
 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.38-release ns-3.38
+  $ git checkout -b ns-3.39-release ns-3.39
 
 In this quick-start, we are omitting download and build instructions for optional |ns3| modules,
 the ``NetAnim`` animator, Python bindings, and ``NetSimulyzer``.  The
@@ -152,8 +152,8 @@
   propagation               sixlowpan                 spectrum
   stats                     tap-bridge                test (no Python)
   topology-read             traffic-control           uan
-  virtual-net-device        visualizer                wave
-  wifi                      wimax
+  virtual-net-device        visualizer                wifi
+  wimax
 
   Modules not built (see ns-3 tutorial for explanation):
   brite                     click                     openflow
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/installation/source/system.rst ns-3.39/doc/installation/source/system.rst
--- ns-3.38/doc/installation/source/system.rst	2023-03-16 20:54:23.500868000 -0700
+++ ns-3.39/doc/installation/source/system.rst	2023-07-05 14:23:47.593426700 -0700
@@ -140,7 +140,8 @@
 Python bindings (ns-3.37 and newer)
 ===================================
 
-|ns3| Python support now uses `cppyy <https://cppyy.readthedocs.io/en/latest/>`_.
+|ns3| Python support now uses `cppyy <https://cppyy.readthedocs.io/en/latest/>`_.  Version 2.4.2
+is the most recent supported cppyy release; version 3.0.0 is not currently supported.
 
 Using Python bindings (release 3.30 to ns-3.36)
 ===============================================
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/manual/source/conf.py ns-3.39/doc/manual/source/conf.py
--- ns-3.38/doc/manual/source/conf.py	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/doc/manual/source/conf.py	2023-07-05 14:23:47.629426700 -0700
@@ -73,9 +73,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.38'
+version = u'ns-3.39'
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.38'
+release = u'ns-3.39'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/manual/source/events.rst ns-3.39/doc/manual/source/events.rst
--- ns-3.38/doc/manual/source/events.rst	2023-03-16 20:54:23.532868400 -0700
+++ ns-3.39/doc/manual/source/events.rst	2023-07-05 14:23:47.629426700 -0700
@@ -326,20 +326,20 @@
 following table.  See the individual Scheduler API pages for details on the
 complexity of the other API calls.
 
-+-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-|  Scheduler Type                                             | Complexity                                           |
-+-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-|                       |                                     | Time                       | Space                   |
-|  `SchedulerImpl` Type |               Method                +-------------+--------------+----------+--------------+
-|                       |                                     | Insert()    | RemoveNext() | Overhead |  Per Event   |
-+=======================+=====================================+=============+==============+==========+==============+
-| CalendarScheduler     | `<std::list> []`                    | Constant    | Constant     | 24 bytes | 16 bytes     |
-+-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-| HeapScheduler         | Heap on `std::vector`               | Logarithmic | Logarithmic  | 24 bytes | 0            |
-+-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-| ListScheduler         | `std::list`                         | Linear      | Constant     | 24 bytes | 16 bytes     |
-+-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-| MapScheduler          | `st::map`                           | Logarithmic | Constant     | 40 bytes | 32 bytes     |
-+-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
-| PriorityQueueSchduler | `std::priority_queue<,std::vector>` | Logarithimc | Logarithims  | 24 bytes | 0            |
-+-----------------------+-------------------------------------+-------------+--------------+----------+--------------+
++------------------------+-------------------------------------+-------------+--------------+----------+--------------+
+|  Scheduler Type                                              | Complexity                                           |
++------------------------+-------------------------------------+-------------+--------------+----------+--------------+
+|                        |                                     | Time                       | Space                   |
+|  `SchedulerImpl` Type  |               Method                +-------------+--------------+----------+--------------+
+|                        |                                     | Insert()    | RemoveNext() | Overhead |  Per Event   |
++========================+=====================================+=============+==============+==========+==============+
+| CalendarScheduler      | `<std::list> []`                    | Constant    | Constant     | 24 bytes | 16 bytes     |
++------------------------+-------------------------------------+-------------+--------------+----------+--------------+
+| HeapScheduler          | Heap on `std::vector`               | Logarithmic | Logarithmic  | 24 bytes | 0            |
++------------------------+-------------------------------------+-------------+--------------+----------+--------------+
+| ListScheduler          | `std::list`                         | Linear      | Constant     | 24 bytes | 16 bytes     |
++------------------------+-------------------------------------+-------------+--------------+----------+--------------+
+| MapScheduler           | `st::map`                           | Logarithmic | Constant     | 40 bytes | 32 bytes     |
++------------------------+-------------------------------------+-------------+--------------+----------+--------------+
+| PriorityQueueScheduler | `std::priority_queue<,std::vector>` | Logarithimc | Logarithims  | 24 bytes | 0            |
++------------------------+-------------------------------------+-------------+--------------+----------+--------------+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/manual/source/logging-asserts.rst ns-3.39/doc/manual/source/logging-asserts.rst
--- ns-3.38/doc/manual/source/logging-asserts.rst	2023-03-16 20:54:23.532868400 -0700
+++ ns-3.39/doc/manual/source/logging-asserts.rst	2023-07-05 14:23:47.629426700 -0700
@@ -14,8 +14,8 @@
 of simulation programs.  Logging output can be enabled by program statements
 in your ``main()`` program or by the use of the ``NS_LOG`` environment variable.
 
-Logging statements are not compiled into optimized builds of |ns3|.  To use
-logging, one must build the (default) debug build of |ns3|.
+Logging statements are not compiled into ``optimized`` builds of |ns3|.  To use
+logging, one must use the ``default`` or ``debug`` build profiles of |ns3|.
 
 The project makes no guarantee about whether logging output will remain
 the same over time.  Users are cautioned against building simulation output
@@ -29,21 +29,20 @@
 execution events, such as the occurrence of simulation events or the
 use of a particular function.
 
-For example, this code snippet is from ``Ipv4L3Protocol::IsDestinationAddress()``::
+For example, this code snippet is from ``TcpSocketBase::EnterCwr()`` and informs the user that
+the model is reducing the congestion window and changing state::
 
-  if (address == iaddr.GetBroadcast())
-    {
-      NS_LOG_LOGIC("For me (interface broadcast address)");
-      return true;
-     }
+  NS_LOG_INFO("Enter CWR recovery mode; set cwnd to " << m_tcb->m_cWnd << ", ssthresh to "
+                                                      << m_tcb->m_ssThresh << ", recover to "
+                                                      << m_recover);
 
 If logging has been enabled for the ``Ipv4L3Protocol`` component at a severity
-of ``LOGIC`` or above (see below about log severity), the statement
+of ``INFO`` or above (see below about log severity), the statement
 will be printed out; otherwise, it will be suppressed.
 
 The logging implementation is enabled in ``debug`` and ``default``
 builds, but disabled in all other build profiles,
-so has no impact of execution speed.
+so that it does not impact the execution speed of more optimized profiles.
 
 You can try the example program `log-example.cc` in `src/core/example`
 with various values for the `NS_LOG` environment variable to see the
@@ -138,21 +137,21 @@
 
 Individual messages belong to a single "severity class," set by the macro
 creating the message.  In the example above,
-``NS_LOG_LOGIC(..)`` creates the message in the ``LOG_LOGIC`` severity class.
+``NS_LOG_INFO(..)`` creates the message in the ``LOG_INFO`` severity class.
 
 The following severity classes are defined as ``enum`` constants:
 
-================  =====================================
+================  =========================================================
 Severity Class    Meaning
-================  =====================================
+================  =========================================================
 ``LOG_NONE``      The default, no logging
 ``LOG_ERROR``     Serious error messages only
 ``LOG_WARN``      Warning messages
-``LOG_DEBUG``     For use in debugging
-``LOG_INFO``      Informational
+``LOG_INFO``      Info about the model changing state
 ``LOG_FUNCTION``  Function tracing
-``LOG_LOGIC``     Control flow tracing within functions
-================  =====================================
+``LOG_LOGIC``     For tracing key decision points or branches in a function
+``LOG_DEBUG``     For use in debugging
+================  =========================================================
 
 Typically one wants to see messages at a given severity class *and higher*.
 This is done by defining inclusive logging "levels":
@@ -162,10 +161,10 @@
 ======================  ===========================================
 ``LOG_LEVEL_ERROR``     Only ``LOG_ERROR`` severity class messages.
 ``LOG_LEVEL_WARN``      ``LOG_WARN`` and above.
-``LOG_LEVEL_DEBUG``     ``LOG_DEBUG`` and above.
 ``LOG_LEVEL_INFO``      ``LOG_INFO`` and above.
 ``LOG_LEVEL_FUNCTION``  ``LOG_FUNCTION`` and above.
 ``LOG_LEVEL_LOGIC``     ``LOG_LOGIC`` and above.
+``LOG_LEVEL_DEBUG``     ``LOG_DEBUG`` and above.
 ``LOG_LEVEL_ALL``       All severity classes.
 ``LOG_ALL``             Synonym for ``LOG_LEVEL_ALL``
 ======================  ===========================================
@@ -178,10 +177,10 @@
 ============  =================
 ``error``     ``level_error``
 ``warn``      ``level_warn``
-``debug``     ``level_debug``
 ``info``      ``level_info``
 ``function``  ``level_function``
 ``logic``     ``level_logic``
+``debug``     ``level_debug``
 ..            | ``level_all``
               | ``all``
               | ``*``
@@ -193,8 +192,8 @@
 ``debug`` and above.
 
 Severity classes and levels can be combined with the \`|' operator:
-``NS_LOG="*=level_warn|logic"`` will output messages at severity levels
-``error``, ``warn`` and ``logic``.
+``NS_LOG="*=level_warn|debug"`` will output messages at severity levels
+``error``, ``warn`` and ``debug``, but not ``info``, ``function``, or ``logic``.
 
 The ``NS_LOG`` severity level wildcard \`*' and ``all``
 are synonyms for ``level_all``.
@@ -265,10 +264,10 @@
    Scratch Simulator
    [ERROR] error message
    [WARN] warn message
-   [DEBUG] debug message
    [INFO] info message
    [FUNCT] function message
    [LOGIC] logic message
+   [DEBUG] debug message
 
 Time Prefix
 ###########
@@ -415,10 +414,10 @@
   ``LOG_NONE``      (none needed)
   ``LOG_ERROR``     ``NS_LOG_ERROR(...);``
   ``LOG_WARN``      ``NS_LOG_WARN(...);``
-  ``LOG_DEBUG``     ``NS_LOG_DEBUG(...);``
   ``LOG_INFO``      ``NS_LOG_INFO(...);``
   ``LOG_FUNCTION``  ``NS_LOG_FUNCTION(...);``
   ``LOG_LOGIC``     ``NS_LOG_LOGIC(...);``
+  ``LOG_DEBUG``     ``NS_LOG_DEBUG(...);``
   ================  ==========================
 
   The macros function as output streamers, so anything you can send to
@@ -449,15 +448,21 @@
 that logging is only enabled in ``debug``, ``default`` and ``relwithdebinfo``
 builds, so this macro will only produce output in the same builds.
 
+The |ns3| model libraries do not typically use the ``NS_LOG_UNCOND(...)`` macro;
+it is provided for users for assistance with debugging.
+
 Guidelines
 ==========
 
-* Start every class method with ``NS_LOG_FUNCTION(this << args...);``
+* Start every significant class method with ``NS_LOG_FUNCTION(this << args...);``
   This enables easy function call tracing.
 
-  * Except:  don't log operators or explicit copy constructors,
+  * Exception 1:  don't log operators or explicit copy constructors,
     since these will cause infinite recursion and stack overflow.
 
+  * Exception 2:  For simple methods such as getters, avoid function
+    logging because it tends to overload the logging output.
+
   * For methods without arguments use the same form:
     ``NS_LOG_FUNCTION(this);``
 
@@ -467,26 +472,49 @@
     * Without arguments use ``NS_LOG_FUNCTION_NOARGS();``
 
 * Use ``NS_LOG_ERROR`` for serious error conditions that probably
-  invalidate the simulation execution.
-
-* Use ``NS_LOG_WARN`` for unusual conditions that may be correctable.
+  invalidate the simulation execution.  Note that in |ns3|, we typically
+  abort the simulation under such conditions rather than log it as
+  an error (which might go undetected if the user is not using logging).
+  The ``NS_ABORT_MSG_IF/UNLESS(cond,msg)`` macros and variants, as well
+  as the lower-level ``NS_FATAL_ERROR(msg)`` macro, can be used to terminate
+  the simulation with an error message.
+
+* Use ``NS_LOG_WARN`` for unusual conditions that are not considered
+  invalid.  An example might be that some resource has been exhausted
+  (e.g., the DHCP server has run out of addresses to allocate).
   Please give some hints as to the nature of the problem and how
   it might be corrected.
 
-* ``NS_LOG_DEBUG`` is usually used in an *ad hoc* way to understand
-  the execution of a model.
-
-* Use ``NS_LOG_INFO`` for additional information about the execution,
-  such as the size of a data structure when adding/removing from it.
-
-* Use ``NS_LOG_LOGIC`` to trace important logic branches within a function.
+* Use ``NS_LOG_INFO`` for events that cause a state change in the model.
+  Avoid using it for logging periodic events that are not causing a
+  state change (e.g., a Wi-Fi beacon is sent, but all nodes are already
+  associated to the access point).  Try to be efficient in using it;
+  for instance, sending a message is usually an important state change event,
+  but try to capture this event with one single log message at the ``INFO``
+  level rather than multiple.  If multiple log messages are desired to
+  fully capture the event and all of its consequences,  use ``DEBUG`` level
+  for the additional messages.  The intent of this log level is to allow a
+  user to examine the normal operation of a model without becoming overwhelmed
+  by the output.
+
+* ``NS_LOG_LOGIC`` is used to trace important logic branches or decision points
+  within a function, without dumping all details of the variable states,
+  called function return values, individual iterations, etc.  It may be useful
+  to think of it as a less granular level of function logging than ``DEBUG,``
+  and may not be used by all models (some authors may choose to only use
+  ``DEBUG`` level for full logging).
+
+* ``NS_LOG_DEBUG`` is usually used for full voluminous debugging, and contains
+  much more information than ``NS_LOG_INFO``, such as the detailed execution
+  logic of functions and the values that variables take within those functions.
 
 * Test that your logging changes do not break the code.
   Run some example programs with all log components turned on (e.g.
   ``NS_LOG="***"``).
 
-* Use an explicit cast for any variable of type uint8_t or int8_t,
-  e.g., ``NS_LOG_LOGIC("Variable i is " << static_cast<int>(i));``.
+* Use a unary operator (preferred) or an explicit cast for any variable of type uint8_t or int8_t,
+  e.g., ``NS_LOG_DEBUG("Variable i is " << +i);``.
+  e.g., ``NS_LOG_DEBUG("Variable i is " << static_cast<int>(i));`` or
   Without the cast, the integer is interpreted as a char, and the result
   will be most likely not in line with the expectations.
   This is a well documented C++ 'feature'.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/manual/source/python.rst ns-3.39/doc/manual/source/python.rst
--- ns-3.38/doc/manual/source/python.rst	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/manual/source/python.rst	2023-07-05 14:23:47.633426700 -0700
@@ -101,10 +101,25 @@
 Running Python Scripts
 **********************
 
-The main prerequisite is to install `cppyy`.  Depending on how you may manage
+For users that want to change upstream modules in C++ and got a copy of
+ns-3 by Git cloning the ns-3-dev repository, or downloaded the
+ns3-allinone package, or is using bake, continue to the next section.
+
+`Note: models implemented in Python are not available from C++. If you want
+your model to be available for both C++ and Python users, you must implement
+it in C++.`
+
+For users that want to exclusively run simulation scenarios and implement
+simple modules in python, jump to the `Using the pip wheel`_ section.
+
+Using the bindings from the ns-3 source
+=======================================
+
+The main prerequisite is to install `cppyy`, with version no later than 2.4.2.
+Depending on how you may manage
 Python extensions, the installation instructions may vary, but you can first
 check if it installed by seeing if the `cppyy` module can be
-successfully imported:
+successfully imported and the version is no later than 2.4.2:
 
 .. sourcecode:: bash
 
@@ -113,6 +128,8 @@
   [GCC 9.4.0] on linux
   Type "help", "copyright", "credits" or "license" for more information.
   >>> import cppyy
+  >>> print("%s" % cppyy.__version)
+  2.4.2
   >>>
 
 If not, you may try to install via `pip` or whatever other manager you are
@@ -120,7 +137,7 @@
 
 .. sourcecode:: bash
 
-  $ python3 -m pip install --user cppyy
+  $ python3 -m pip install --user cppyy==2.4.2
 
 First, we need to enable the build of Python bindings:
 
@@ -135,13 +152,13 @@
 .. sourcecode:: bash
 
   $ ./ns3 shell
-  $ python3 examples/wireless/mixed-wireless.py
+  $ python3 examples/wireless/mixed-wired-wireless.py
 
 and the other is to use the 'run' option to ns3:
 
 .. sourcecode:: bash
 
-  $ ./ns3 run examples/wireless/mixed-wireless.py
+  $ ./ns3 run examples/wireless/mixed-wired-wireless.py
 
 Use the ``--no-build`` option to run the program without invoking a project rebuild.
 This option may be useful to improve execution time when running the same program
@@ -149,14 +166,14 @@
 
 .. sourcecode:: bash
 
-  $ ./ns3 run --no-build examples/wireless/mixed-wireless.py
+  $ ./ns3 run --no-build examples/wireless/mixed-wired-wireless.py
 
 To run a python script under the C debugger:
 
 .. sourcecode:: bash
 
   $ ./ns3 shell
-  $ gdb --args python3 examples/wireless/mixed-wireless.py
+  $ gdb --args python3 examples/wireless/mixed-wired-wireless.py
 
 To run your own Python script that calls |ns3| and that has this path, ``/path/to/your/example/my-script.py``, do the following:
 
@@ -165,6 +182,133 @@
   $ ./ns3 shell
   $ python3 /path/to/your/example/my-script.py
 
+
+Using the pip wheel
+===================
+
+Starting from ns-3.38, we provide a pip wheel for Python users using Linux.
+
+.. sourcecode:: bash
+
+  $ pip install --user ns3
+
+You can select a specific ns-3 version by specifying the wheel version.
+Specifying a nonexistent version will result in an error message listing the available versions.
+
+.. sourcecode:: bash
+
+  $ pip install --user ns3==3.37
+  Defaulting to user installation because normal site-packages is not writeable
+  ERROR: Could not find a version that satisfies the requirement ns3==3.37 (from versions: 3.37.post415)
+  ERROR: No matching distribution found for ns3==3.37
+
+You can also specify you want at least a specific version (e.g. which shipped a required feature).
+
+.. sourcecode:: bash
+
+  $ pip install --user ns3>=3.37
+  Defaulting to user installation because normal site-packages is not writeable
+  Requirement already satisfied: ns3==3.37.post415 in /home/username/.local/lib/python3.10/site-packages (3.37.post415)
+  Requirement already satisfied: cppyy in /home/username/.local/lib/python3.10/site-packages (from ns3==3.37.post415) (2.4.2)
+  Requirement already satisfied: cppyy-backend==1.14.10 in /home/username/.local/lib/python3.10/site-packages (from cppyy->ns3==3.37.post415) (1.14.10)
+  Requirement already satisfied: CPyCppyy==1.12.12 in /home/username/.local/lib/python3.10/site-packages (from cppyy->ns3==3.37.post415) (1.12.12)
+  Requirement already satisfied: cppyy-cling==6.27.1 in /home/username/.local/lib/python3.10/site-packages (from cppyy->ns3==3.37.post415) (6.27.1)
+
+To check if the pip wheel was installed, use the pip freeze command to list the installed packages,
+then grep ns3 to filter the line of interest.
+
+.. sourcecode:: bash
+
+  $ pip freeze | grep ns3
+  ns3==3.37.post415
+
+.. _ns3 wheel: https://pypi.org/project/ns3/#history
+
+The available versions are also listed on the Pypi page for the `ns3 wheel`_.
+
+After installing it, you can start using ns-3 right away. For example, using the following script.
+
+::
+
+    from ns import ns
+
+    ns.cppyy.cppdef("""
+            using namespace ns3;
+
+            Callback<void,Ptr<const Packet>,const Address&,const Address&>
+            make_sinktrace_callback(void(*func)(Ptr<Packet>,Address,Address))
+            {
+                return MakeCallback(func);
+            }
+        """)
+
+    # Define the trace callback
+    def SinkTracer(packet: ns.Packet, src_address: ns.Address, dst_address: ns.Address) -> None:
+        print(f"At {ns.Simulator.Now().GetSeconds():.0f}s, '{dst_address}' received packet"
+              f" with {packet.__deref__().GetSerializedSize()} bytes from '{src_address}'")
+
+    # Create two nodes
+    csmaNodes = ns.network.NodeContainer()
+    csmaNodes.Create(2)
+
+    # Connect the two nodes
+    csma = ns.csma.CsmaHelper()
+    csma.SetChannelAttribute("DataRate", ns.core.StringValue("100Mbps"))
+    csma.SetChannelAttribute("Delay", ns.core.TimeValue(ns.core.NanoSeconds(6560)))
+    csmaDevices = csma.Install(csmaNodes)
+
+    # Install the internet stack
+    stack = ns.internet.InternetStackHelper()
+    stack.Install(csmaNodes)
+
+    # Assign Ipv4 addresses
+    address = ns.internet.Ipv4AddressHelper()
+    address.SetBase(ns.network.Ipv4Address("10.1.2.0"), ns.network.Ipv4Mask("255.255.255.0"))
+    csmaInterfaces = address.Assign(csmaDevices)
+
+    # Setup applications
+    echoServer = ns.applications.UdpEchoServerHelper(9)
+
+    serverApps = echoServer.Install(csmaNodes.Get(0))
+    serverApps.Start(ns.core.Seconds(1.0))
+    serverApps.Stop(ns.core.Seconds(10.0))
+
+    echoClient = ns.applications.UdpEchoClientHelper(csmaInterfaces.GetAddress(0).ConvertTo(), 9)
+    echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(10))
+    echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds(1.0)))
+    echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
+
+    clientApps = echoClient.Install(csmaNodes.Get(1))
+    clientApps.Start(ns.core.Seconds(2.0))
+    clientApps.Stop(ns.core.Seconds(10.0))
+
+    # Populate routing tables
+    ns.internet.Ipv4GlobalRoutingHelper.PopulateRoutingTables()
+
+    # Setup the trace callback
+    sinkTraceCallback = ns.cppyy.gbl.make_sinktrace_callback(SinkTracer)
+    serverApps.Get(0).__deref__().TraceConnectWithoutContext("RxWithAddresses", sinkTraceCallback);
+
+    # Set the simulation duration to 11 seconds
+    ns.Simulator.Stop(ns.Seconds(11))
+
+    # Run the simulator
+    ns.Simulator.Run()
+    ns.Simulator.Destroy()
+
+Which should print:
+
+.. sourcecode:: bash
+
+  At 2s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+  At 3s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+  At 4s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+  At 5s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+  At 6s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+  At 7s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+  At 8s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+  At 9s, '04-07-00:00:00:00:09:00:00' received packet with 60 bytes from '04-07-0a:01:02:02:01:c0:00'
+
 Caveats
 *******
 
@@ -382,6 +526,240 @@
 like there is Doxygen for the C++ API, but the Doxygen can be consulted
 to understand how the C++ API works.
 
+To inspect what function and classes are available, you can use
+the ``dir`` function. Examples below:
+
+.. sourcecode:: bash
+
+  >>> print(dir(ns.Simulator))
+  ['Cancel', 'Destroy', 'GetContext', 'GetDelayLeft', 'GetEventCount', 'GetImplementation', 'GetMaximumSimulationTime', 'GetSystemId', 'IsExpired', 'IsFinished', 'NO_CONTEXT', 'Now', 'Remove', 'Run', 'Schedule', 'ScheduleDestroy', 'ScheduleNow', 'ScheduleWithContext', 'SetImplementation', 'SetScheduler', 'Stop', '__add__', '__assign__', '__bool__', '__class__', '__delattr__', '__destruct__', '__dict__', '__dir__', '__dispatch__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__invert__', '__le__', '__lt__', '__module__', '__mul__', '__ne__', '__neg__', '__new__', '__pos__', '__python_owns__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__reshape__', '__rmul__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__smartptr__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__weakref__']
+  >>> print(dir(ns.DefaultSimulatorImpl))
+  ['AggregateObject', 'Cancel', 'Destroy', 'Dispose', 'GetAggregateIterator', 'GetAttribute', 'GetAttributeFailSafe', 'GetContext', 'GetDelayLeft', 'GetEventCount', 'GetInstanceTypeId', 'GetMaximumSimulationTime', 'GetObject', 'GetReferenceCount', 'GetSystemId', 'GetTypeId', 'Initialize', 'IsExpired', 'IsFinished', 'IsInitialized', 'Now', 'PreEventHook', 'Ref', 'Remove', 'Run', 'Schedule', 'ScheduleDestroy', 'ScheduleNow', 'ScheduleWithContext', 'SetAttribute', 'SetAttributeFailSafe', 'SetScheduler', 'Stop', 'TraceConnect', 'TraceConnectWithoutContext', 'TraceDisconnect', 'TraceDisconnectWithoutContext', 'Unref', '__add__', '__assign__', '__bool__', '__class__', '__delattr__', '__destruct__', '__dict__', '__dir__', '__dispatch__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__invert__', '__le__', '__lt__', '__module__', '__mul__', '__ne__', '__neg__', '__new__', '__pos__', '__python_owns__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__reshape__', '__rmul__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__smartptr__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__weakref__']
+  >>> print(dir(ns.Time))
+  ['AUTO', 'As', 'Compare', 'D', 'FS', 'From', 'FromDouble', 'FromInteger', 'GetDays', 'GetDouble', 'GetFemtoSeconds', 'GetHours', 'GetInteger', 'GetMicroSeconds', 'GetMilliSeconds', 'GetMinutes', 'GetNanoSeconds', 'GetPicoSeconds', 'GetResolution', 'GetSeconds', 'GetTimeStep', 'GetYears', 'H', 'IsNegative', 'IsPositive', 'IsStrictlyNegative', 'IsStrictlyPositive', 'IsZero', 'LAST', 'MIN', 'MS', 'Max', 'Min', 'NS', 'PS', 'RoundTo', 'S', 'SetResolution', 'StaticInit', 'To', 'ToDouble', 'ToInteger', 'US', 'Y', '__add__', '__assign__', '__bool__', '__class__', '__delattr__', '__destruct__', '__dict__', '__dir__', '__dispatch__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__invert__', '__le__', '__lt__', '__module__', '__mul__', '__ne__', '__neg__', '__new__', '__pos__', '__python_owns__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__reshape__', '__rmul__', '__rsub__', '__rtruediv__', '__setattr__', '__sizeof__', '__smartptr__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__weakref__']
+
+
+To get more information about expected arguments, you can use the ``help``
+function.
+
+.. sourcecode:: bash
+
+  >>> help(ns.DefaultSimulatorImpl)
+  class DefaultSimulatorImpl(SimulatorImpl)
+  |  Method resolution order:
+  |      DefaultSimulatorImpl
+  |      SimulatorImpl
+  |      Object
+  |      SimpleRefCount<ns3::Object,ns3::ObjectBase,ns3::ObjectDeleter>
+  |      ObjectBase
+  |      cppyy.gbl.CPPInstance
+  |      builtins.object
+  |
+  |  Methods defined here:
+  |
+  |  Cancel(...)
+  |      void ns3::DefaultSimulatorImpl::Cancel(const ns3::EventId& id)
+  |
+  |  Destroy(...)
+  |      void ns3::DefaultSimulatorImpl::Destroy()
+  |
+  |  GetContext(...)
+  |      unsigned int ns3::DefaultSimulatorImpl::GetContext()
+  |
+  |  GetDelayLeft(...)
+  |      ns3::Time ns3::DefaultSimulatorImpl::GetDelayLeft(const ns3::EventId& id)
+  |
+  |  GetEventCount(...)
+  |      unsigned long ns3::DefaultSimulatorImpl::GetEventCount()
+  |
+  |  GetMaximumSimulationTime(...)
+  |      ns3::Time ns3::DefaultSimulatorImpl::GetMaximumSimulationTime()
+  |
+  |  GetSystemId(...)
+  |      unsigned int ns3::DefaultSimulatorImpl::GetSystemId()
+  |
+  |  GetTypeId(...)
+  |      static ns3::TypeId ns3::DefaultSimulatorImpl::GetTypeId()
+  |
+  |  IsExpired(...)
+  |      bool ns3::DefaultSimulatorImpl::IsExpired(const ns3::EventId& id)
+  |
+  |  IsFinished(...)
+  |      bool ns3::DefaultSimulatorImpl::IsFinished()
+  |
+  |  Now(...)
+  |      ns3::Time ns3::DefaultSimulatorImpl::Now()
+  |
+  |  Remove(...)
+  |      void ns3::DefaultSimulatorImpl::Remove(const ns3::EventId& id)
+  |
+  |  Run(...)
+  |      void ns3::DefaultSimulatorImpl::Run()
+
+
+Pip wheel packaging
+*******************
+
+This section is meant exclusively for ns-3 maintainers and ns-3
+users that want to redistribute their work as wheels for python.
+
+The packaging process is defined in the following GitLab job.
+The job is split into blocks explained below.
+
+The manylinux image provides an old glibc compatible with most modern Linux
+distributions, resulting on a pip wheel that is compatible across distributions.
+
+.. sourcecode:: yaml
+
+  .manylinux-pip-wheel:
+    image: quay.io/pypa/manylinux_2_28_x86_64
+
+Then we install the required toolchain and dependencies necessary for both
+ns-3 (e.g. libxml2, gsl, sqlite, gtk, etc) and for the bindings and packaging
+(e.g. setuptools, wheel, auditwheel, cmake-build-extension, cppyy).
+
+.. sourcecode:: yaml
+
+      # Install minimal toolchain
+      - yum install -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel
+      # Create Python venv
+      - $PYTHON -m venv ./venv
+      - . ./venv/bin/activate
+      # Upgrade the pip version to reuse the pre-build cppyy
+      - $PYTHON -m pip install pip --upgrade
+      - $PYTHON -m pip install setuptools setuptools_scm --upgrade
+      - $PYTHON -m pip install wheel auditwheel cmake-build-extension cppyy
+
+The project is then configured loading the configuration settings defined
+in the ``ns-3-dev/setup.py`` file.
+
+.. sourcecode:: yaml
+
+      # Configure and build wheel
+      - $PYTHON setup.py bdist_wheel build_ext "-DNS3_USE_LIB64=TRUE"
+
+At this point, we have a wheel that only works in the current system,
+since external libraries are not shipped.
+
+Auditwheel needs to be called resolve and copy external libraries
+that we need to ship along the ns-3 module libraries (e.g. libxml2, sqlite3,
+gtk, gsl, etc). However, we need to prevent auditwheel from shipping copies of
+the libraries built by the ns-3 project. A list of excluded libraries is generated
+by the script ``ns-3-dev/build-support/pip-wheel/auditwheel-exclude-list.py``.
+
+.. sourcecode:: yaml
+
+      - export EXCLUDE_INTERNAL_LIBRARIES=`$PYTHON ./build-support/pip-wheel/auditwheel-exclude-list.py`
+      # Bundle in shared libraries that were not explicitly packaged or depended upon
+      - $PYTHON -m auditwheel repair ./dist/*whl -L /lib64 $EXCLUDE_INTERNAL_LIBRARIES
+
+
+At this point, we should have our final wheel ready, but we need to check if it works
+before submitting it to Pypi servers.
+
+We first clean the environment and uninstall the packages previously installed.
+
+.. sourcecode:: yaml
+
+      # Clean the build directory
+      - $PYTHON ./ns3 clean
+      # Clean up the environment
+      - deactivate
+      - rm -R ./venv
+      # Delete toolchain to check if required headers/libraries were really packaged
+      - yum remove -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel
+
+
+Then we can install our newly built wheel and test it.
+
+.. sourcecode:: yaml
+
+      # Install wheel
+      - $PYTHON -m pip install ./wheelhouse/*whl
+      - $PYTHON -m pip install matplotlib numpy
+      # Test the bindings
+      - $PYTHON ./utils/python-unit-tests.py
+      - $PYTHON ./examples/realtime/realtime-udp-echo.py
+      - $PYTHON ./examples/routing/simple-routing-ping6.py
+      - $PYTHON ./examples/tutorial/first.py
+      - $PYTHON ./examples/tutorial/second.py
+      - $PYTHON ./examples/tutorial/third.py
+      - $PYTHON ./examples/wireless/wifi-ap.py
+      - $PYTHON ./examples/wireless/mixed-wired-wireless.py
+      - $PYTHON ./src/bridge/examples/csma-bridge.py
+      - $PYTHON ./src/brite/examples/brite-generic-example.py
+      - $PYTHON ./src/core/examples/sample-simulator.py
+      - $PYTHON ./src/core/examples/sample-rng-plot.py --not-blocking
+      - $PYTHON ./src/click/examples/nsclick-simple-lan.py
+      - $PYTHON ./src/flow-monitor/examples/wifi-olsr-flowmon.py
+      - $PYTHON ./src/flow-monitor/examples/flowmon-parse-results.py output.xml
+      - $PYTHON ./src/openflow/examples/openflow-switch.py
+
+If all programs finish normally, the bindings are working as expected,
+and will be saved as an artifact.
+
+.. sourcecode:: yaml
+
+    artifacts:
+      paths:
+        - wheelhouse/*.whl
+
+One can use ``gitlab-ci-local`` to build the pip wheels locally. After that, the wheels
+will be stored in ``.gitlab-ci-local/artifacts/manylinux-pip-wheel-py3Lg10/wheelhouse``
+(for Python 3.10).
+
+The wheel names are based on the number of commits since the latest release.
+For example, a wheel built 415 after the release 3.37 will be named
+``ns3-3.37.post415-cp310-cp310-manylinux_2_28_x86_64.whl``.
+
+The wheel name (``ns3``) is defined in the ``/ns-3-dev/setup.cfg`` file, and that
+name should match the build prefix specified in ``/ns-3-dev/setup.py`` file.
+
+The ``cp310-cp310`` indicates that this wheel is compatible from Python 3.10 and up to Python 3.10.
+
+The ``manylinux_2_28`` indicates that this is a manylinux wheel targeting glibc 2.28.
+
+The ``x86_64`` indicates that this is a 64-bit build targeting Intel/AMD processors.
+
+.. _Pypi: https://pypi.org/account/register/
+.. _Twine: https://twine.readthedocs.io/en/stable/
+
+After packaging, we can either deploy that wheel locally or upload the wheel to Pypi for general availability.
+
+Local deployment
+****************
+
+To deploy a wheel locally, simply share the wheel file across the desired machines.
+Then install the wheel and its dependencies running the following command:
+
+.. sourcecode:: bash
+
+    $ pip install *.whl
+
+Publishing the pip wheel via Pypi
+*********************************
+
+Publishing a pip wheel requires a `Pypi`_ account.
+
+After creating your account, install `Twine`_, an utility to upload the wheel to Pypi.
+
+Then run twine to upload the wheel to the Pypi servers.
+
+.. sourcecode:: bash
+
+  $ twine upload .gitlab-ci-local/artifacts/manylinux-pip-wheel-py3Lg10/wheelhouse/*.whl
+
+Enter your Pypi username and password as requested.
+
+Your wheel should be up and running. Give it a try just to make sure.
+
+For the upstream pip wheel, try:
+
+.. sourcecode:: bash
+
+  $ pip install ns3
+  $ python3 -c "from ns import ns; print(ns.Simulator.Now())"
+
 Historical Information
 **********************
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/manual/source/working-with-cmake.rst ns-3.39/doc/manual/source/working-with-cmake.rst
--- ns-3.38/doc/manual/source/working-with-cmake.rst	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/manual/source/working-with-cmake.rst	2023-07-05 14:23:47.633426700 -0700
@@ -423,8 +423,6 @@
 
 .. sourcecode:: python3
 
-  ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
-
   def build(bld):
     module = bld.create_ns3_module('aodv', ['internet', 'wifi'])
     module.includes = '.'
@@ -521,8 +519,6 @@
 
 .. sourcecode:: python3
 
-  ## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
-
   def build(bld):
       obj = bld.create_ns3_program('aodv',
                                    ['wifi', 'internet', 'aodv', 'internet-apps'])
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/manual/source/working-with-gitlab-ci-local.rst ns-3.39/doc/manual/source/working-with-gitlab-ci-local.rst
--- ns-3.38/doc/manual/source/working-with-gitlab-ci-local.rst	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/manual/source/working-with-gitlab-ci-local.rst	2023-07-05 14:23:47.633426700 -0700
@@ -5,7 +5,7 @@
 Working with gitlab-ci-local
 ----------------------------
 
-.. _continuos integration (CI) : https://docs.gitlab.com/ee/ci/
+.. _continuous integration (CI) : https://docs.gitlab.com/ee/ci/
 .. _pipelines : https://docs.gitlab.com/ee/ci/introduction/index.html#continuous-integration
 .. _daily and weekly pipelines : https://gitlab.com/nsnam/ns-3-dev/-/pipeline_schedules
 .. _crypto miners abuse : https://about.gitlab.com/blog/2021/05/17/prevent-crypto-mining-abuse/
@@ -15,7 +15,7 @@
 .. _rootless mode : https://docs.docker.com/engine/security/rootless/
 
 The ns-3 project repository is currently hosted in GitLab, which includes
-`continuos integration (CI)`_ tools to automate build, tests, packaging and
+`continuous integration (CI)`_ tools to automate build, tests, packaging and
 distribution of software. The CI works based on jobs, that are defined
 on YAML files.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/manual/source/working-with-git.rst ns-3.39/doc/manual/source/working-with-git.rst
--- ns-3.38/doc/manual/source/working-with-git.rst	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/manual/source/working-with-git.rst	2023-07-05 14:23:47.633426700 -0700
@@ -256,7 +256,7 @@
 This helps reviewing the code changes proposed in the branch. merge it without conflicts, and it increase the speed of the GitLab CI.
 
 GitLab CI (Continuous Integration)
-+++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++
 
 GitLab provides a CI (Continuous Integration) feature. Shortly put, after every push the code is built and tests are run in one of the GitLab servers.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/models/Makefile ns-3.39/doc/models/Makefile
--- ns-3.38/doc/models/Makefile	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/models/Makefile	2023-07-05 14:23:47.633426700 -0700
@@ -98,7 +98,6 @@
 	$(SRC)/spectrum/doc/spectrum.rst \
 	$(SRC)/netanim/doc/animation.rst \
 	$(SRC)/flow-monitor/doc/flow-monitor.rst \
-	$(SRC)/wave/doc/wave.rst \
 	$(SRC)/sixlowpan/doc/sixlowpan.rst \
 	$(SRC)/lr-wpan/doc/lr-wpan.rst \
 
@@ -161,6 +160,10 @@
 	$(SRC)/wifi/doc/source/figures/wifi-11g-p-1500-infrastructure-r-6-min-5-max-50-step-5-throughput.eps \
 	$(SRC)/wifi/doc/source/figures/default-table-based-error-model-validation.eps \
 	$(SRC)/wifi/doc/source/figures/error-models-comparison.eps \
+	$(SRC)/wifi/doc/source/figures/spectrum-wifi-phy-multiple-interfaces.pdf \
+	$(SRC)/wifi/doc/source/figures/spectrum-wifi-phy-multiple-interfaces.png \
+	$(SRC)/wifi/doc/source/figures/cca-channel-switching-multiple-interfaces.pdf \
+	$(SRC)/wifi/doc/source/figures/cca-channel-switching-multiple-interfaces.png \
 	$(SRC)/wimax/doc/WimaxArchitecture.dia \
 	$(SRC)/lte/doc/source/figures/epc-ctrl-arch.dia \
 	$(SRC)/lte/doc/source/figures/epc-data-flow-dl.dia \
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/models/source/conf.py ns-3.39/doc/models/source/conf.py
--- ns-3.38/doc/models/source/conf.py	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/doc/models/source/conf.py	2023-07-05 14:23:47.633426700 -0700
@@ -73,9 +73,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.38'
+version = u'ns-3.39'
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.38'
+release = u'ns-3.39'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/models/source/index.rst ns-3.39/doc/models/source/index.rst
--- ns-3.38/doc/models/source/index.rst	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/models/source/index.rst	2023-07-05 14:23:47.633426700 -0700
@@ -59,6 +59,5 @@
    topology
    traffic-control
    uan
-   wave
    wifi
    wimax
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/ns3_html_theme/layout.html ns-3.39/doc/ns3_html_theme/layout.html
--- ns-3.38/doc/ns3_html_theme/layout.html	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/ns3_html_theme/layout.html	2023-07-05 14:23:47.633426700 -0700
@@ -74,6 +74,9 @@
                       <div id="mDocs"
                           onmouseover="mcancelclosetime()"
                           onmouseout="mclosetime()">
+                        <a id="ns3_ins"
+                           href="/docs/installation/html/index.html"
+                           >Installation</a><br/>
                         <a id="ns3_man"
                            href="/docs/manual/html/index.html"
                            >Manual</a><br/>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/ns3_html_theme/ns3_doxy_header.html ns-3.39/doc/ns3_html_theme/ns3_doxy_header.html
--- ns-3.38/doc/ns3_html_theme/ns3_doxy_header.html	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/ns3_html_theme/ns3_doxy_header.html	2023-07-05 14:23:47.633426700 -0700
@@ -70,7 +70,7 @@
                       <div id="mDocs"
                           onmouseover="mcancelclosetime()"
                           onmouseout="mclosetime()">
-                        <a id="ns3_man"
+                        <a id="ns3_ins"
                            href="/docs/installation/html/index.html"
                            >Installation</a><br/>
                         <a id="ns3_man"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/ns3_html_theme/static/default.css_t ns-3.39/doc/ns3_html_theme/static/default.css_t
--- ns-3.38/doc/ns3_html_theme/static/default.css_t	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/ns3_html_theme/static/default.css_t	2023-07-05 14:23:47.633426700 -0700
@@ -61,7 +61,7 @@
 }
 
 div.related {
-    background-color: {{ theme_relbarbgcolor }};
+    background-image: linear-gradient(to bottom, white 0%, transparent 80%);
     line-height: 30px;
     color: {{ theme_relbartextcolor }};
 }
@@ -142,9 +142,16 @@
 }
 
 div.sphinxsidebar input {
-    border: 1px solid {{ theme_sidebarlinkcolor }};
+    border: 2px solid {{ theme_sidebarlinkcolor }};
     font-family: sans-serif;
     font-size: 1em;
+    font-weight: bold;
+    background: #f0f1e8;
+}
+
+div.sphinxsidebar input[type="submit"] {
+    background: {{ theme_sidebarlinkcolor }};
+    color: {{theme_sidebartextcolor}};
 }
 
 {% if theme_collapsiblesidebar|tobool %}
@@ -196,12 +203,12 @@
 div.body h5,
 div.body h6 {
     font-family: {{ theme_headfont }};
-    background-color: {{ theme_headbgcolor }};
-    font-weight: normal;
-    color: {{ theme_headtextcolor }};
+    background-color: transparent;
+    font-weight: bold;
     border-bottom: 1px solid #ccc;
     margin: 20px -20px 10px -20px;
-    padding: 3px 0 3px 10px;
+    padding: 10px 8px;
+    border-left: 6px solid #d7d4d2;
 }
 
 div.body h1 { margin-top: 0; font-size: 200%; }
@@ -273,7 +280,7 @@
 
 pre {
     padding: 5px;
-    background-color: {{ theme_codebgcolor }};
+    background-color: transparent;
     color: {{ theme_codetextcolor }};
     line-height: 120%;
     border: 1px solid #ac9;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/ns3_html_theme/static/ns3_links.js ns-3.39/doc/ns3_html_theme/static/ns3_links.js
--- ns-3.38/doc/ns3_html_theme/static/ns3_links.js	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/ns3_html_theme/static/ns3_links.js	2023-07-05 14:23:47.633426700 -0700
@@ -44,6 +44,7 @@
 var ns3_mod  = ns3_rel  + "models/"         + ns3_index;
 var ns3_tut  = ns3_rel  + "tutorial/"       + ns3_index;
 var ns3_con  = ns3_rel  + "contributing/"   + ns3_index;
+var ns3_ins  = ns3_rel  + "installation/"   + ns3_index;
 
 function ns3_write_links() {
   document.getElementById("ns3_home1").href = ns3_home;
@@ -52,6 +53,7 @@
   document.getElementById("ns3_man"  ).href = ns3_man;
   document.getElementById("ns3_mod"  ).href = ns3_mod;
   document.getElementById("ns3_con"  ).href = ns3_con;
+  document.getElementById("ns3_ins"  ).href = ns3_ins;
   document.getElementById("ns3_wiki" ).href = ns3_wiki;
   document.getElementById("ns3_api"  ).href = ns3_api;
   document.getElementById("ns3_bugs" ).href = ns3_bugs;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/ns3_html_theme/static/ns3_stylesheet.css ns-3.39/doc/ns3_html_theme/static/ns3_stylesheet.css
--- ns-3.38/doc/ns3_html_theme/static/ns3_stylesheet.css	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/ns3_html_theme/static/ns3_stylesheet.css	2023-07-05 14:23:47.633426700 -0700
@@ -10,16 +10,27 @@
   font-size: 12px;
 }
 
-/* Dark mode is not supported in Doxygen versions earlier than 1.9.6
+/* Dark mode is not supported in Doxygen versions earlier than 1.9.6 */
 @media (prefers-color-scheme: dark) {
   body,
   table,
   div,
   p,
-  dl    {
+  dl,
+  div.body,
+  div.sphinxsidebar h3,
+  div.sphinxsidebar h4 ,
+  div.sphinxsidebar h3 a{
     color: ivory;
+    background:black;
   }
-} */
+  div.sphinxsidebar input{
+    background: #2c2d24;
+  }
+  div.related{
+    background-image: linear-gradient(to bottom, #66696a 0%, transparent 80%);
+  }
+}
 
 a {
   color: #91A501;
@@ -27,6 +38,7 @@
 }
 
 /* Sphinx headings */
+/*
 div.body h1,
 div.body h2,
 div.body h3,
@@ -35,6 +47,7 @@
 div.body h6 {
   background-image: url('nav_f.png');
 }
+*/
 
 /* Sphinx figure captions */
 p.caption {
@@ -51,9 +64,11 @@
 }
 
 /* Sphinx nav links bar (relbar) */
+/*
 div.related {
   background-image: url('tab_b.png')
 }
+*/
 
 div.related h3 {
   display: none;
@@ -110,8 +125,8 @@
 }
 
 #projecttext {
-  align: left;
-  font-color: white;
+  text-align: left;
+  color: white;
   padding-left: 2em;
   width: 250px;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/ns3_html_theme/theme.conf ns-3.39/doc/ns3_html_theme/theme.conf
--- ns-3.38/doc/ns3_html_theme/theme.conf	2023-03-16 20:54:23.536868300 -0700
+++ ns-3.39/doc/ns3_html_theme/theme.conf	2023-07-05 14:23:47.633426700 -0700
@@ -42,7 +42,7 @@
 footerbgcolor    = #D5D5D5
 footertextcolor	 = #364D7C
 
-codebgcolor      = ##FBFCFD
+codebgcolor      = #FBFCFD
 codetextcolor    = black
 
 bodyfont = 'Lucida Grande', Verdana, Geneva, Arial, sans-serif
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/tutorial/source/conceptual-overview.rst ns-3.39/doc/tutorial/source/conceptual-overview.rst
--- ns-3.38/doc/tutorial/source/conceptual-overview.rst	2023-03-16 20:54:23.564868500 -0700
+++ ns-3.39/doc/tutorial/source/conceptual-overview.rst	2023-07-05 14:23:47.661426800 -0700
@@ -168,30 +168,8 @@
 nodes.  Let's take a look at that script line by line, so go ahead and open
 ``first.cc`` in your favorite editor.
 
-Boilerplate
-+++++++++++
-The first line in the file is an emacs mode line.  This tells emacs about the
-formatting conventions (coding style) we use in our source code.
-
-::
-
-  /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-
-This is always a somewhat controversial subject, so we might as well get it
-out of the way immediately.  The |ns3| project, like most large
-projects, has adopted a coding style to which all contributed code must
-adhere.  If you want to contribute your code to the project, you will
-eventually have to conform to the |ns3| coding standard as described
-in the file ``doc/contributing/source/coding-style.rst`` or shown on the project web page
-`here
-<https://www.nsnam.org/docs/contributing/html/coding-style.html>`_.
-
-We recommend that you, well, just get used to the look and feel of |ns3|
-code and adopt this standard whenever you are working with our code.  All of
-the development team and contributors have done so with various amounts of
-grumbling.  The emacs mode line above makes it easier to get the formatting
-correct if you use the emacs editor.
-
+Copyright
++++++++++
 The |ns3| simulator is licensed using the GNU General Public
 License version 2.  You will see the appropriate GNU legalese at the head of every file
 in the |ns3| distribution.  Often you will see a copyright notice for
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/tutorial/source/conf.py ns-3.39/doc/tutorial/source/conf.py
--- ns-3.38/doc/tutorial/source/conf.py	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/doc/tutorial/source/conf.py	2023-07-05 14:23:47.661426800 -0700
@@ -71,9 +71,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = u'ns-3.38'
+version = u'ns-3.39'
 # The full version, including alpha/beta/rc tags.
-release = u'ns-3.38'
+release = u'ns-3.39'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/tutorial/source/getting-started.rst ns-3.39/doc/tutorial/source/getting-started.rst
--- ns-3.38/doc/tutorial/source/getting-started.rst	2023-03-16 20:54:23.564868500 -0700
+++ ns-3.39/doc/tutorial/source/getting-started.rst	2023-07-05 14:23:47.661426800 -0700
@@ -57,20 +57,11 @@
 installation of Git source code control system.  Most beginning users
 need not concern themselves if their configuration reports some missing
 optional features of |ns3|, but for those wishing a full installation,
-the project provides a wiki that includes pages with many useful hints
-and tips.  One such page is the "Installation" page, with install instructions
+the project provides an installation guide
 for various systems, available at
-https://www.nsnam.org/wiki/Installation.
+https://www.nsnam.org/docs/installation/html/index.html.
 
-The "Prerequisites" section of this wiki page explains which packages are
-required to support common |ns3| options, and also provides the
-commands used to install them for common Linux or macOS variants.
-
-You may want to take this opportunity to explore the |ns3| wiki
-a bit, or the main web site at https://www.nsnam.org, since there is a
-wealth of information there.
-
-As of the most recent |ns3| release (ns-3.38), the following tools
+As of the most recent |ns3| release (ns-3.39), the following tools
 are needed to get started with |ns3|:
 
 ============  ===========================================================
@@ -87,7 +78,8 @@
 
 To check the default version of Python, type ``python -V``.  To check
 the default version of g++, type ``g++ -v``.  If your installation is
-missing or too old, please consult the |ns3| installation wiki for guidance.
+missing or too old, please consult the |ns3|
+`installation guide <https://www.nsnam.org/docs/installation/html/index.html>`_ for guidance.
 
 From this point forward, we are going to assume that the reader is working in
 Linux, macOS, or a Linux emulation environment, and has at least the above
@@ -124,21 +116,21 @@
   $ cd
   $ mkdir workspace
   $ cd workspace
-  $ wget https://www.nsnam.org/release/ns-allinone-3.38.tar.bz2
-  $ tar xjf ns-allinone-3.38.tar.bz2
+  $ wget https://www.nsnam.org/release/ns-allinone-3.39.tar.bz2
+  $ tar xjf ns-allinone-3.39.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.38``, you should see a number of files and directories
+``ns-allinone-3.39``, you should see a number of files and directories
 
 .. sourcecode:: text
 
-  $ cd ns-allinone-3.38
+  $ cd ns-allinone-3.39
   $ ls
-  bake  build.py  constants.py  netanim-3.109 ns-3.38  README.md  util.py
+  bake  build.py  constants.py  netanim-3.109 ns-3.39  README.md  util.py
 
 You are now ready to build the base |ns3| distribution and may skip ahead
 to the section on building |ns3|.
@@ -188,7 +180,7 @@
 
 .. sourcecode:: console
 
-  $ python3 download.py -n ns-3.38
+  $ python3 download.py -n ns-3.39
 
 After this step, the additional repositories of |ns3|, bake, pybindgen,
 and netanim will be downloaded to the ``ns-3-allinone`` directory.
@@ -257,9 +249,9 @@
 
 There are a few configuration targets available:
 
-1.  ``ns-3.38``:  the code corresponding to the release
+1.  ``ns-3.39``:  the code corresponding to the release
 2.  ``ns-3-dev``:  a similar module but using the development code tree
-3.  ``ns-allinone-3.38``:  the module that includes other optional features
+3.  ``ns-allinone-3.39``:  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.
@@ -276,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.38``.
+this tutorial example with ``ns-3.39``.
 
 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.
@@ -305,7 +297,7 @@
 
 .. sourcecode:: console
 
-  $ ./bake.py configure -e ns-allinone-3.38
+  $ ./bake.py configure -e ns-allinone-3.39
 
 Next, we'll ask bake to check whether we have enough tools to download
 various components.  Type:
@@ -352,10 +344,10 @@
   >> Searching for system dependency g++ - OK
   >> Searching for system dependency cmake - OK
   >> Downloading netanim-3.109 - OK
-  >> Downloading click-ns-3.38 - OK
+  >> Downloading click-ns-3.37 - OK
   >> Downloading BRITE - OK
   >> Downloading openflow-dev - OK
-  >> Downloading ns-3.38 (target directory:ns-3.38) - OK
+  >> Downloading ns-3.39 (target directory:ns-3.39) - OK
 
 The above suggests that three sources have been downloaded.  Check the
 ``source`` directory now and type ``ls``; one should see:
@@ -364,7 +356,7 @@
 
   $ cd source
   $ ls
-  BRITE  click-ns-3.37  netanim-3.109  ns-3.38  openflow-dev
+  BRITE  click-ns-3.37  netanim-3.109  ns-3.39  openflow-dev
 
 You are now ready to build the |ns3| distribution.
 
@@ -394,7 +386,7 @@
 
 If you downloaded
 using a tarball you should have a directory called something like
-``ns-allinone-3.38`` under your ``~/workspace`` directory.
+``ns-allinone-3.39`` under your ``~/workspace`` directory.
 Type the following:
 
 .. sourcecode:: console
@@ -427,7 +419,7 @@
 .. sourcecode:: text
 
   >> Building netanim-3.109 - OK
-  >> Building ns-3.38 - OK
+  >> Building ns-3.39 - OK
 
 There may be failures to build all components, but the build will proceed
 anyway if the component is optional.
@@ -604,7 +596,6 @@
   -- Processing src/traffic-control
   -- Processing src/uan
   -- Processing src/virtual-net-device
-  -- Processing src/wave
   -- Processing src/wifi
   -- Processing src/wimax
   -- ---- Summary of optional NS-3 features:
@@ -644,7 +635,7 @@
   sixlowpan                 spectrum                  stats
   tap-bridge                test                      topology-read
   traffic-control           uan                       virtual-net-device
-  wave                      wifi                      wimax
+  wifi                      wimax
 
 
   Modules that cannot be built:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/tutorial/source/quick-start.rst ns-3.39/doc/tutorial/source/quick-start.rst
--- ns-3.38/doc/tutorial/source/quick-start.rst	2023-03-16 20:54:23.564868500 -0700
+++ ns-3.39/doc/tutorial/source/quick-start.rst	2023-07-05 14:23:47.661426800 -0700
@@ -64,13 +64,13 @@
 
    ::
 
-    $ tar xjf ns-allinone-3.38.tar.bz2
+    $ tar xjf ns-allinone-3.39.tar.bz2
 
 3) Change into the |ns3| directory directly; e.g.
 
    ::
 
-    $ cd ns-allinone-3.38/ns-3.38
+    $ cd ns-allinone-3.39/ns-3.39
 
 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.38 as of this
+If instead you want to try the most recent release (version 3.39 as of this
 writing), you can checkout a branch corresponding to that git tag:
 
 ::
 
-  $ git checkout -b ns-3.38-branch ns-3.38
+  $ git checkout -b ns-3.39-branch ns-3.39
 
 Building and testing ns-3
 *************************
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/tutorial/source/resources.rst ns-3.39/doc/tutorial/source/resources.rst
--- ns-3.38/doc/tutorial/source/resources.rst	2023-03-16 20:54:23.564868500 -0700
+++ ns-3.39/doc/tutorial/source/resources.rst	2023-07-05 14:23:47.661426800 -0700
@@ -84,18 +84,14 @@
 
 On macOS, the toolchain used is Xcode.  |ns3| users on a Mac are strongly
 encouraged to install Xcode and the command-line tools packages from the
-Apple App Store, and to look at the |ns3| installation wiki for more
-information (https://www.nsnam.org/wiki/Installation).
+Apple App Store, and to look at the |ns3| installation guide for more
+information (https://www.nsnam.org/docs/installation/html/).
 
 Typically an |ns3| author will work in Linux or a Unix-like environment.
 For those running under Windows, there do exist environments
 which simulate the Linux environment to various degrees.  The |ns3|
-project has in the past (but not presently) supported development in the Cygwin environment for
-these users.  See http://www.cygwin.com/
-for details on downloading, and visit the |ns3| wiki for more information
-about Cygwin and |ns3|.  MinGW is presently not officially supported.
-Another alternative to Cygwin is to install a virtual machine environment
-such as VMware server and install a Linux virtual machine.
+installation guide has information about Windows support
+(https://www.nsnam.org/docs/installation/html/windows.html).
 
 Socket Programming
 ******************
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/doc/tutorial/source/tracing.rst ns-3.39/doc/tutorial/source/tracing.rst
--- ns-3.38/doc/tutorial/source/tracing.rst	2023-03-16 20:54:23.564868500 -0700
+++ ns-3.39/doc/tutorial/source/tracing.rst	2023-07-05 14:23:47.665426700 -0700
@@ -313,7 +313,6 @@
 example of tracing that can be assembled.  You can find this code in
 the tutorial directory as ``fourth.cc``.  Let's walk through it::
 
-  /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
   /*
    * 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
@@ -1390,7 +1389,6 @@
 ``examples/tutorial/fifth.cc`` in your favorite editor.  You should
 see some familiar looking code::
 
-  /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
   /*
    * 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
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/.editorconfig ns-3.39/.editorconfig
--- ns-3.38/.editorconfig	2023-03-16 20:54:23.484868000 -0700
+++ ns-3.39/.editorconfig	2023-07-05 14:23:47.577426400 -0700
@@ -31,6 +31,6 @@
 indent_style = space
 indent_size = 2
 
-[*.yml]
+[*.{md,rst,yml,yaml}]
 indent_style = space
 indent_size = 2
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/channel-models/examples-to-run.py ns-3.39/examples/channel-models/examples-to-run.py
--- ns-3.38/examples/channel-models/examples-to-run.py	2023-03-16 20:54:23.564868500 -0700
+++ ns-3.39/examples/channel-models/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/energy/examples-to-run.py ns-3.39/examples/energy/examples-to-run.py
--- ns-3.38/examples/energy/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/energy/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/error-model/examples-to-run.py ns-3.39/examples/error-model/examples-to-run.py
--- ns-3.38/examples/error-model/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/error-model/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/ipv6/examples-to-run.py ns-3.39/examples/ipv6/examples-to-run.py
--- ns-3.38/examples/ipv6/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/ipv6/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/ipv6/fragmentation-ipv6.cc ns-3.39/examples/ipv6/fragmentation-ipv6.cc
--- ns-3.38/examples/ipv6/fragmentation-ipv6.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/ipv6/fragmentation-ipv6.cc	2023-07-05 14:23:47.665426700 -0700
@@ -30,7 +30,6 @@
 #include "ns3/applications-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
-#include "ns3/internet-apps-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/ipv6-routing-table-entry.h"
 #include "ns3/ipv6-static-routing-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/ipv6/fragmentation-ipv6-PMTU.cc ns-3.39/examples/ipv6/fragmentation-ipv6-PMTU.cc
--- ns-3.38/examples/ipv6/fragmentation-ipv6-PMTU.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/ipv6/fragmentation-ipv6-PMTU.cc	2023-07-05 14:23:47.665426700 -0700
@@ -33,7 +33,6 @@
 #include "ns3/applications-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
-#include "ns3/internet-apps-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/ipv6-routing-table-entry.h"
 #include "ns3/ipv6-static-routing-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/ipv6/fragmentation-ipv6-two-MTU.cc ns-3.39/examples/ipv6/fragmentation-ipv6-two-MTU.cc
--- ns-3.38/examples/ipv6/fragmentation-ipv6-two-MTU.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/ipv6/fragmentation-ipv6-two-MTU.cc	2023-07-05 14:23:47.665426700 -0700
@@ -32,7 +32,6 @@
 #include "ns3/applications-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
-#include "ns3/internet-apps-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/ipv6-routing-table-entry.h"
 #include "ns3/ipv6-static-routing-helper.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/matrix-topology/matrix-topology.cc ns-3.39/examples/matrix-topology/matrix-topology.cc
--- ns-3.38/examples/matrix-topology/matrix-topology.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/matrix-topology/matrix-topology.cc	2023-07-05 14:23:47.665426700 -0700
@@ -60,7 +60,7 @@
 // ---------- Prototypes ------------------------------------------------------
 
 std::vector<std::vector<bool>> readNxNMatrix(std::string adj_mat_file_name);
-std::vector<std::vector<double>> readCordinatesFile(std::string node_coordinates_file_name);
+std::vector<std::vector<double>> readCoordinatesFile(std::string node_coordinates_file_name);
 void printCoordinateArray(const char* description, std::vector<std::vector<double>> coord_array);
 void printMatrix(const char* description, std::vector<std::vector<bool>> array);
 
@@ -115,7 +115,7 @@
     // ---------- Read Node Coordinates File -----------------------------------
 
     std::vector<std::vector<double>> coord_array;
-    coord_array = readCordinatesFile(node_coordinates_file_name);
+    coord_array = readCoordinatesFile(node_coordinates_file_name);
 
     // Optionally display node coordinates file
     // printCoordinateArray (node_coordinates_file_name.c_str (),coord_array);
@@ -361,7 +361,7 @@
 }
 
 std::vector<std::vector<double>>
-readCordinatesFile(std::string node_coordinates_file_name)
+readCoordinatesFile(std::string node_coordinates_file_name)
 {
     std::ifstream node_coordinates_file;
     node_coordinates_file.open(node_coordinates_file_name, std::ios::in);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/naming/examples-to-run.py ns-3.39/examples/naming/examples-to-run.py
--- ns-3.38/examples/naming/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/naming/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/naming/object-names.cc ns-3.39/examples/naming/object-names.cc
--- ns-3.38/examples/naming/object-names.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/naming/object-names.cc	2023-07-05 14:23:47.665426700 -0700
@@ -214,7 +214,7 @@
         outputValidated = false;
     }
 
-    if (outputValidated == false)
+    if (!outputValidated)
     {
         std::cerr << "Program internal checking failed; returning with error" << std::endl;
         return (1);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/realtime/examples-to-run.py ns-3.39/examples/realtime/examples-to-run.py
--- ns-3.38/examples/realtime/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/realtime/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/routing/CMakeLists.txt ns-3.39/examples/routing/CMakeLists.txt
--- ns-3.38/examples/routing/CMakeLists.txt	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/routing/CMakeLists.txt	2023-07-05 14:23:47.665426700 -0700
@@ -87,6 +87,7 @@
     ${libolsr}
     ${libinternet}
     ${libapplications}
+    ${libflow-monitor}
 )
 
 build_example(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/routing/examples-to-run.py ns-3.39/examples/routing/examples-to-run.py
--- ns-3.38/examples/routing/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/routing/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/routing/manet-routing-compare.cc ns-3.39/examples/routing/manet-routing-compare.cc
--- ns-3.38/examples/routing/manet-routing-compare.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/routing/manet-routing-compare.cc	2023-07-05 14:23:47.665426700 -0700
@@ -69,6 +69,7 @@
 #include "ns3/core-module.h"
 #include "ns3/dsdv-module.h"
 #include "ns3/dsr-module.h"
+#include "ns3/flow-monitor-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/mobility-module.h"
 #include "ns3/network-module.h"
@@ -94,20 +95,15 @@
     RoutingExperiment();
     /**
      * Run the experiment.
-     * \param nSinks The number of Sink Nodes.
-     * \param txp The Tx power.
-     * \param CSVfileName The output CSV filename.
      */
-    void Run(int nSinks, double txp, std::string CSVfileName);
-    // static void SetMACParam (ns3::NetDeviceContainer & devices,
-    //                                  int slotDistance);
+    void Run();
+
     /**
      * Handles the command-line parameters.
      * \param argc The argument count.
      * \param argv The argument vector.
-     * \return the CSV filename.
      */
-    std::string CommandSetup(int argc, char** argv);
+    void CommandSetup(int argc, char** argv);
 
   private:
     /**
@@ -127,25 +123,19 @@
      */
     void CheckThroughput();
 
-    uint32_t port;            //!< Receiving port number.
-    uint32_t bytesTotal;      //!< Total received bytes.
-    uint32_t packetsReceived; //!< Total received packets.
-
-    std::string m_CSVfileName;  //!< CSV filename.
-    int m_nSinks;               //!< Number of sink nodes.
-    std::string m_protocolName; //!< Protocol name.
-    double m_txp;               //!< Tx power.
-    bool m_traceMobility;       //!< Enavle mobility tracing.
-    uint32_t m_protocol;        //!< Protocol type.
+    uint32_t port{9};            //!< Receiving port number.
+    uint32_t bytesTotal{0};      //!< Total received bytes.
+    uint32_t packetsReceived{0}; //!< Total received packets.
+
+    std::string m_CSVfileName{"manet-routing.output.csv"}; //!< CSV filename.
+    int m_nSinks{10};                                      //!< Number of sink nodes.
+    std::string m_protocolName{"AODV"};                    //!< Protocol name.
+    double m_txp{7.5};                                     //!< Tx power.
+    bool m_traceMobility{false};                           //!< Enable mobility tracing.
+    bool m_flowMonitor{false};                             //!< Enable FlowMonitor.
 };
 
 RoutingExperiment::RoutingExperiment()
-    : port(9),
-      bytesTotal(0),
-      packetsReceived(0),
-      m_CSVfileName("manet-routing.output.csv"),
-      m_traceMobility(false),
-      m_protocol(2) // AODV
 {
 }
 
@@ -209,25 +199,42 @@
     return sink;
 }
 
-std::string
+void
 RoutingExperiment::CommandSetup(int argc, char** argv)
 {
     CommandLine cmd(__FILE__);
     cmd.AddValue("CSVfileName", "The name of the CSV output file name", m_CSVfileName);
     cmd.AddValue("traceMobility", "Enable mobility tracing", m_traceMobility);
-    cmd.AddValue("protocol", "1=OLSR;2=AODV;3=DSDV;4=DSR", m_protocol);
+    cmd.AddValue("protocol", "Routing protocol (OLSR, AODV, DSDV, DSR)", m_protocolName);
+    cmd.AddValue("flowMonitor", "enable FlowMonitor", m_flowMonitor);
     cmd.Parse(argc, argv);
-    return m_CSVfileName;
+
+    std::vector<std::string> allowedProtocols{"OLSR", "AODV", "DSDV", "DSR"};
+
+    if (std::find(std::begin(allowedProtocols), std::end(allowedProtocols), m_protocolName) ==
+        std::end(allowedProtocols))
+    {
+        NS_FATAL_ERROR("No such protocol:" << m_protocolName);
+    }
 }
 
 int
 main(int argc, char* argv[])
 {
     RoutingExperiment experiment;
-    std::string CSVfileName = experiment.CommandSetup(argc, argv);
+    experiment.CommandSetup(argc, argv);
+    experiment.Run();
+
+    return 0;
+}
+
+void
+RoutingExperiment::Run()
+{
+    Packet::EnablePrinting();
 
     // blank out the last output file and write the column headers
-    std::ofstream out(CSVfileName);
+    std::ofstream out(m_CSVfileName);
     out << "SimulationSecond,"
         << "ReceiveRate,"
         << "PacketsReceived,"
@@ -236,22 +243,6 @@
         << "TransmissionPower" << std::endl;
     out.close();
 
-    int nSinks = 10;
-    double txp = 7.5;
-
-    experiment.Run(nSinks, txp, CSVfileName);
-
-    return 0;
-}
-
-void
-RoutingExperiment::Run(int nSinks, double txp, std::string CSVfileName)
-{
-    Packet::EnablePrinting();
-    m_nSinks = nSinks;
-    m_txp = txp;
-    m_CSVfileName = CSVfileName;
-
     int nWifis = 50;
 
     double TotalTime = 200.0;
@@ -260,7 +251,6 @@
     std::string tr_name("manet-routing-compare");
     int nodeSpeed = 20; // in m/s
     int nodePause = 0;  // in s
-    m_protocolName = "protocol";
 
     Config::SetDefault("ns3::OnOffApplication::PacketSize", StringValue("64"));
     Config::SetDefault("ns3::OnOffApplication::DataRate", StringValue(rate));
@@ -289,8 +279,8 @@
                                  "ControlMode",
                                  StringValue(phyMode));
 
-    wifiPhy.Set("TxPowerStart", DoubleValue(txp));
-    wifiPhy.Set("TxPowerEnd", DoubleValue(txp));
+    wifiPhy.Set("TxPowerStart", DoubleValue(m_txp));
+    wifiPhy.Set("TxPowerEnd", DoubleValue(m_txp));
 
     wifiMac.SetType("ns3::AdhocWifiMac");
     NetDeviceContainer adhocDevices = wifi.Install(wifiPhy, wifiMac, adhocNodes);
@@ -329,36 +319,36 @@
     Ipv4ListRoutingHelper list;
     InternetStackHelper internet;
 
-    switch (m_protocol)
+    if (m_protocolName == "OLSR")
     {
-    case 1:
         list.Add(olsr, 100);
-        m_protocolName = "OLSR";
-        break;
-    case 2:
+        internet.SetRoutingHelper(list);
+        internet.Install(adhocNodes);
+    }
+    else if (m_protocolName == "AODV")
+    {
         list.Add(aodv, 100);
-        m_protocolName = "AODV";
-        break;
-    case 3:
-        list.Add(dsdv, 100);
-        m_protocolName = "DSDV";
-        break;
-    case 4:
-        m_protocolName = "DSR";
-        break;
-    default:
-        NS_FATAL_ERROR("No such protocol:" << m_protocol);
+        internet.SetRoutingHelper(list);
+        internet.Install(adhocNodes);
     }
-
-    if (m_protocol < 4)
+    else if (m_protocolName == "DSDV")
     {
+        list.Add(dsdv, 100);
         internet.SetRoutingHelper(list);
         internet.Install(adhocNodes);
     }
-    else if (m_protocol == 4)
+    else if (m_protocolName == "DSR")
     {
         internet.Install(adhocNodes);
         dsrMain.Install(dsr, adhocNodes);
+        if (m_flowMonitor)
+        {
+            NS_FATAL_ERROR("Error: FlowMonitor does not work with DSR. Terminating.");
+        }
+    }
+    else
+    {
+        NS_FATAL_ERROR("No such protocol:" << m_protocolName);
     }
 
     NS_LOG_INFO("assigning ip address");
@@ -372,7 +362,7 @@
     onoff1.SetAttribute("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1.0]"));
     onoff1.SetAttribute("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0.0]"));
 
-    for (int i = 0; i < nSinks; i++)
+    for (int i = 0; i < m_nSinks; i++)
     {
         Ptr<Socket> sink = SetupPacketReceive(adhocInterfaces.GetAddress(i), adhocNodes.Get(i));
 
@@ -380,7 +370,7 @@
         onoff1.SetAttribute("Remote", remoteAddress);
 
         Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable>();
-        ApplicationContainer temp = onoff1.Install(adhocNodes.Get(i + nSinks));
+        ApplicationContainer temp = onoff1.Install(adhocNodes.Get(i + m_nSinks));
         temp.Start(Seconds(var->GetValue(100.0, 101.0)));
         temp.Stop(Seconds(TotalTime));
     }
@@ -411,9 +401,12 @@
     AsciiTraceHelper ascii;
     MobilityHelper::EnableAsciiAll(ascii.CreateFileStream(tr_name + ".mob"));
 
-    // Ptr<FlowMonitor> flowmon;
-    // FlowMonitorHelper flowmonHelper;
-    // flowmon = flowmonHelper.InstallAll();
+    FlowMonitorHelper flowmonHelper;
+    Ptr<FlowMonitor> flowmon;
+    if (m_flowMonitor)
+    {
+        flowmon = flowmonHelper.InstallAll();
+    }
 
     NS_LOG_INFO("Run Simulation.");
 
@@ -422,7 +415,10 @@
     Simulator::Stop(Seconds(TotalTime));
     Simulator::Run();
 
-    // flowmon->SerializeToXmlFile(tr_name + ".flowmon", false, false);
+    if (m_flowMonitor)
+    {
+        flowmon->SerializeToXmlFile(tr_name + ".flowmon", false, false);
+    }
 
     Simulator::Destroy();
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/socket/socket-options-ipv4.cc ns-3.39/examples/socket/socket-options-ipv4.cc
--- ns-3.38/examples/socket/socket-options-ipv4.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/socket/socket-options-ipv4.cc	2023-07-05 14:23:47.665426700 -0700
@@ -22,7 +22,6 @@
 //
 // - UDP flows from n0 to n1
 
-#include "ns3/applications-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.38/examples/socket/socket-options-ipv6.cc ns-3.39/examples/socket/socket-options-ipv6.cc
--- ns-3.38/examples/socket/socket-options-ipv6.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/socket/socket-options-ipv6.cc	2023-07-05 14:23:47.665426700 -0700
@@ -22,7 +22,6 @@
 //
 // - UDP flows from n0 to n1
 
-#include "ns3/applications-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.38/examples/stats/examples-to-run.py ns-3.39/examples/stats/examples-to-run.py
--- ns-3.38/examples/stats/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/stats/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/tcp/dctcp-example.cc ns-3.39/examples/tcp/dctcp-example.cc
--- ns-3.38/examples/tcp/dctcp-example.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/tcp/dctcp-example.cc	2023-07-05 14:23:47.665426700 -0700
@@ -301,8 +301,6 @@
     Time startTime = Seconds(0);
     Time stopTime = flowStartupWindow + convergenceTime + measurementWindow;
 
-    Time clientStartTime = startTime;
-
     rxS1R1Bytes.reserve(10);
     rxS2R2Bytes.reserve(20);
     rxS3R1Bytes.reserve(10);
@@ -493,7 +491,7 @@
         AddressValue remoteAddress(InetSocketAddress(ipR2T2[i].GetAddress(0), port));
         clientHelper1.SetAttribute("Remote", remoteAddress);
         clientApps1.Add(clientHelper1.Install(S2.Get(i)));
-        clientApps1.Start(i * flowStartupWindow / 20 + clientStartTime + MilliSeconds(i * 5));
+        clientApps1.Start(i * flowStartupWindow / 20 + startTime + MilliSeconds(i * 5));
         clientApps1.Stop(stopTime);
     }
 
@@ -534,13 +532,12 @@
         if (i < 10)
         {
             clientApps1.Add(clientHelper1.Install(S1.Get(i)));
-            clientApps1.Start(i * flowStartupWindow / 10 + clientStartTime + MilliSeconds(i * 5));
+            clientApps1.Start(i * flowStartupWindow / 10 + startTime + MilliSeconds(i * 5));
         }
         else
         {
             clientApps1.Add(clientHelper1.Install(S3.Get(i - 10)));
-            clientApps1.Start((i - 10) * flowStartupWindow / 10 + clientStartTime +
-                              MilliSeconds(i * 5));
+            clientApps1.Start((i - 10) * flowStartupWindow / 10 + startTime + MilliSeconds(i * 5));
         }
 
         clientApps1.Stop(stopTime);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/tcp/examples-to-run.py ns-3.39/examples/tcp/examples-to-run.py
--- ns-3.38/examples/tcp/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/tcp/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/tcp/tcp-validation.cc ns-3.39/examples/tcp/tcp-validation.cc
--- ns-3.38/examples/tcp/tcp-validation.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/tcp/tcp-validation.cc	2023-07-05 14:23:47.665426700 -0700
@@ -746,7 +746,7 @@
         firstTcpTypeId = TcpDctcp::GetTypeId();
         Config::SetDefault("ns3::CoDelQueueDisc::CeThreshold", TimeValue(ceThreshold));
         Config::SetDefault("ns3::FqCoDelQueueDisc::CeThreshold", TimeValue(ceThreshold));
-        if (queueUseEcn == false)
+        if (!queueUseEcn)
         {
             std::cout << "Warning: using DCTCP with queue ECN disabled" << std::endl;
         }
@@ -933,7 +933,7 @@
         proto->SetAttribute("SocketType", TypeIdValue(secondTcpTypeId));
     }
 
-    // InternetStackHelper will install a base TrafficControLayer on the node,
+    // InternetStackHelper will install a base TrafficControlLayer on the node,
     // but the Ipv4AddressHelper below will install the default FqCoDelQueueDisc
     // on all single device nodes.  The below code overrides the configuration
     // that is normally done by the Ipv4AddressHelper::Install() method by
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/tcp/tcp-variants-comparison.cc ns-3.39/examples/tcp/tcp-variants-comparison.cc
--- ns-3.38/examples/tcp/tcp-variants-comparison.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/tcp/tcp-variants-comparison.cc	2023-07-05 14:23:47.665426700 -0700
@@ -79,8 +79,8 @@
 static uint32_t
 GetNodeIdFromContext(std::string context)
 {
-    std::size_t const n1 = context.find_first_of('/', 1);
-    std::size_t const n2 = context.find_first_of('/', n1 + 1);
+    const std::size_t n1 = context.find_first_of('/', 1);
+    const std::size_t n2 = context.find_first_of('/', n1 + 1);
     return std::stoul(context.substr(n1 + 1, n2 - n1 - 1));
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/traffic-control/examples-to-run.py ns-3.39/examples/traffic-control/examples-to-run.py
--- ns-3.38/examples/traffic-control/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/traffic-control/examples-to-run.py	2023-07-05 14:23:47.665426700 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/tutorial/examples-to-run.py ns-3.39/examples/tutorial/examples-to-run.py
--- ns-3.38/examples/tutorial/examples-to-run.py	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/tutorial/examples-to-run.py	2023-07-05 14:23:47.669427000 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/tutorial/seventh.cc ns-3.39/examples/tutorial/seventh.cc
--- ns-3.38/examples/tutorial/seventh.cc	2023-03-16 20:54:23.568868400 -0700
+++ ns-3.39/examples/tutorial/seventh.cc	2023-07-05 14:23:47.669427000 -0700
@@ -125,7 +125,7 @@
     Address anyAddress;
     std::string probeType;
     std::string tracePath;
-    if (useV6 == false)
+    if (!useV6)
     {
         Ipv4AddressHelper address;
         address.SetBase("10.1.1.0", "255.255.255.0");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/udp/examples-to-run.py ns-3.39/examples/udp/examples-to-run.py
--- ns-3.38/examples/udp/examples-to-run.py	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/udp/examples-to-run.py	2023-07-05 14:23:47.669427000 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/udp/udp-echo.cc ns-3.39/examples/udp/udp-echo.cc
--- ns-3.38/examples/udp/udp-echo.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/udp/udp-echo.cc	2023-07-05 14:23:47.669427000 -0700
@@ -81,7 +81,7 @@
     // We've got the "hardware" in place.  Now we need to add IP addresses.
     //
     NS_LOG_INFO("Assign IP Addresses.");
-    if (useV6 == false)
+    if (!useV6)
     {
         Ipv4AddressHelper ipv4;
         ipv4.SetBase("10.1.1.0", "255.255.255.0");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/udp-client-server/udp-client-server.cc ns-3.39/examples/udp-client-server/udp-client-server.cc
--- ns-3.38/examples/udp-client-server/udp-client-server.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/udp-client-server/udp-client-server.cc	2023-07-05 14:23:47.669427000 -0700
@@ -74,7 +74,7 @@
     NetDeviceContainer d = csma.Install(n);
 
     NS_LOG_INFO("Assign IP Addresses.");
-    if (useV6 == false)
+    if (!useV6)
     {
         Ipv4AddressHelper ipv4;
         ipv4.SetBase("10.1.1.0", "255.255.255.0");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/udp-client-server/udp-trace-client-server.cc ns-3.39/examples/udp-client-server/udp-trace-client-server.cc
--- ns-3.38/examples/udp-client-server/udp-trace-client-server.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/udp-client-server/udp-trace-client-server.cc	2023-07-05 14:23:47.669427000 -0700
@@ -73,7 +73,7 @@
     NetDeviceContainer d = csma.Install(n);
 
     NS_LOG_INFO("Assign IP Addresses.");
-    if (useV6 == false)
+    if (!useV6)
     {
         Ipv4AddressHelper ipv4;
         ipv4.SetBase("10.1.1.0", "255.255.255.0");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/examples-to-run.py ns-3.39/examples/wireless/examples-to-run.py
--- ns-3.38/examples/wireless/examples-to-run.py	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/examples-to-run.py	2023-07-05 14:23:47.669427000 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
@@ -30,6 +29,9 @@
     ("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"),
@@ -58,7 +60,7 @@
     ("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.2 --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.22 --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"),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/mixed-wired-wireless.cc ns-3.39/examples/wireless/mixed-wired-wireless.cc
--- ns-3.38/examples/wireless/mixed-wired-wireless.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/mixed-wired-wireless.cc	2023-07-05 14:23:47.669427000 -0700
@@ -414,7 +414,7 @@
     // pcap trace on the application data sink
     wifiPhy.EnablePcap("mixed-wireless", appSink->GetId(), 0);
 
-    if (useCourseChangeCallback == true)
+    if (useCourseChangeCallback)
     {
         Config::Connect("/NodeList/*/$ns3::MobilityModel/CourseChange",
                         MakeCallback(&CourseChangeCallback));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/mixed-wired-wireless.py ns-3.39/examples/wireless/mixed-wired-wireless.py
--- ns-3.38/examples/wireless/mixed-wired-wireless.py	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/mixed-wired-wireless.py	2023-07-05 14:23:47.669427000 -0700
@@ -91,7 +91,7 @@
     cmd.AddValue("backboneNodes", "number of backbone nodes", backboneNodes)
     cmd.AddValue("infraNodes", "number of leaf nodes", infraNodes)
     cmd.AddValue("lanNodes", "number of LAN nodes", lanNodes)
-    cmd.AddValue("stopTime", "simulation stop time(seconds)", stopTime)
+    cmd.AddValue["double"]("stopTime", "simulation stop time(seconds)", stopTime)
 
     #
     #  The system global variables and the local values added to the argument
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-adhoc.cc ns-3.39/examples/wireless/wifi-adhoc.cc
--- ns-3.38/examples/wireless/wifi-adhoc.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-adhoc.cc	2023-07-05 14:23:47.669427000 -0700
@@ -175,8 +175,7 @@
     YansWifiPhyHelper phy = wifiPhy;
     phy.SetChannel(wifiChannel.Create());
 
-    WifiMacHelper mac = wifiMac;
-    NetDeviceContainer devices = wifi.Install(phy, mac, c);
+    NetDeviceContainer devices = wifi.Install(phy, wifiMac, c);
 
     MobilityHelper mobility;
     Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-clear-channel-cmu.cc ns-3.39/examples/wireless/wifi-clear-channel-cmu.cc
--- ns-3.38/examples/wireless/wifi-clear-channel-cmu.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-clear-channel-cmu.cc	2023-07-05 14:23:47.669427000 -0700
@@ -183,8 +183,7 @@
     YansWifiPhyHelper phy = wifiPhy;
     phy.SetChannel(wifiChannel.Create());
 
-    WifiMacHelper mac = wifiMac;
-    NetDeviceContainer devices = wifi.Install(phy, mac, c);
+    NetDeviceContainer devices = wifi.Install(phy, wifiMac, c);
 
     MobilityHelper mobility;
     Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-eht-network.cc ns-3.39/examples/wireless/wifi-eht-network.cc
--- ns-3.38/examples/wireless/wifi-eht-network.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-eht-network.cc	2023-07-05 14:23:47.669427000 -0700
@@ -1,4 +1,3 @@
-/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
 /*
  * Copyright (c) 2022
  *
@@ -244,11 +243,8 @@
                      "AGGR-MU-BAR)");
     }
 
-    double prevThroughput[12];
-    for (uint32_t l = 0; l < 12; l++)
-    {
-        prevThroughput[l] = 0;
-    }
+    double prevThroughput[12] = {0};
+
     std::cout << "MCS value"
               << "\t\t"
               << "Channel width"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-he-network.cc ns-3.39/examples/wireless/wifi-he-network.cc
--- ns-3.38/examples/wireless/wifi-he-network.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-he-network.cc	2023-07-05 14:23:47.669427000 -0700
@@ -167,11 +167,8 @@
         phyModel = "Spectrum";
     }
 
-    double prevThroughput[12];
-    for (uint32_t l = 0; l < 12; l++)
-    {
-        prevThroughput[l] = 0;
-    }
+    double prevThroughput[12] = {0};
+
     std::cout << "MCS value"
               << "\t\t"
               << "Channel width"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-ht-network.cc ns-3.39/examples/wireless/wifi-ht-network.cc
--- ns-3.38/examples/wireless/wifi-ht-network.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-ht-network.cc	2023-07-05 14:23:47.669427000 -0700
@@ -96,11 +96,8 @@
         Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("0"));
     }
 
-    double prevThroughput[8];
-    for (uint32_t l = 0; l < 8; l++)
-    {
-        prevThroughput[l] = 0;
-    }
+    double prevThroughput[8] = {0};
+
     std::cout << "MCS value"
               << "\t\t"
               << "Channel width"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-multirate.cc ns-3.39/examples/wireless/wifi-multirate.cc
--- ns-3.38/examples/wireless/wifi-multirate.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-multirate.cc	2023-07-05 14:23:47.669427000 -0700
@@ -505,8 +505,7 @@
     YansWifiPhyHelper phy = wifiPhy;
     phy.SetChannel(wifiChannel.Create());
 
-    WifiMacHelper mac = wifiMac;
-    NetDeviceContainer devices = wifi.Install(phy, mac, c);
+    NetDeviceContainer devices = wifi.Install(phy, wifiMac, c);
 
     OlsrHelper olsr;
     Ipv4StaticRoutingHelper staticRouting;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-rate-adaptation-distance.cc ns-3.39/examples/wireless/wifi-rate-adaptation-distance.cc
--- ns-3.38/examples/wireless/wifi-rate-adaptation-distance.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-rate-adaptation-distance.cc	2023-07-05 14:23:47.669427000 -0700
@@ -56,6 +56,7 @@
 #include "ns3/boolean.h"
 #include "ns3/command-line.h"
 #include "ns3/config.h"
+#include "ns3/double.h"
 #include "ns3/gnuplot.h"
 #include "ns3/internet-stack-helper.h"
 #include "ns3/ipv4-address-helper.h"
@@ -65,6 +66,7 @@
 #include "ns3/on-off-helper.h"
 #include "ns3/packet-sink-helper.h"
 #include "ns3/ssid.h"
+#include "ns3/string.h"
 #include "ns3/uinteger.h"
 #include "ns3/yans-wifi-channel.h"
 #include "ns3/yans-wifi-helper.h"
@@ -168,10 +170,16 @@
     return m_output;
 }
 
+/**
+ * Callback for 'Rate' trace source
+ *
+ * \param oldRate old MCS rate (bits/sec)
+ * \param newRate new MCS rate (bits/sec)
+ */
 void
-RateCallback(std::string path, uint64_t rate, Mac48Address dest)
+RateCallback(uint64_t oldRate, uint64_t newRate)
 {
-    NS_LOG_INFO((Simulator::Now()).GetSeconds() << " " << dest << " Rate " << rate / 1000000.0);
+    NS_LOG_INFO("Rate " << newRate / 1000000.0 << " Mbps");
 }
 
 int
@@ -194,9 +202,9 @@
     int stepsTime = 1;
 
     CommandLine cmd(__FILE__);
-    cmd.AddValue("staManager", "PRC Manager of the STA", staManager);
-    cmd.AddValue("apManager", "PRC Manager of the AP", apManager);
-    cmd.AddValue("standard", "Wifi Phy Standard", standard);
+    cmd.AddValue("staManager", "Rate adaptation manager of the STA", staManager);
+    cmd.AddValue("apManager", "Rate adaptation manager of the AP", apManager);
+    cmd.AddValue("standard", "Wifi standard (a/b/g/n/ac only)", standard);
     cmd.AddValue("shortGuardInterval",
                  "Enable Short Guard Interval in all stations",
                  shortGuardInterval);
@@ -215,6 +223,12 @@
 
     int simuTime = steps * stepsTime;
 
+    if (standard != "802.11a" && standard != "802.11b" && standard != "802.11g" &&
+        standard == "802.11n-2.4GHz" && standard != "802.11n-5GHz" && standard != "802.11ac")
+    {
+        NS_FATAL_ERROR("Standard " << standard << " is not supported by this program");
+    }
+
     // Define the APs
     NodeContainer wifiApNodes;
     wifiApNodes.Create(1);
@@ -226,6 +240,30 @@
     YansWifiPhyHelper wifiPhy;
     YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
     wifiPhy.SetChannel(wifiChannel.Create());
+    // Channel configuration via ChannelSettings attribute can be performed here
+    std::string frequencyBand;
+    if (standard == "802.11b" || standard == "802.11g" || standard == "802.11n-2.4GHz")
+    {
+        frequencyBand = "BAND_2_4GHZ";
+    }
+    else
+    {
+        frequencyBand = "BAND_5GHZ";
+    }
+    wifiPhy.Set("ChannelSettings",
+                StringValue("{0, " + std::to_string(chWidth) + ", " + frequencyBand + ", 0}"));
+
+    // By default, the CCA sensitivity is -82 dBm, meaning if the RSS is
+    // below this value, the receiver will reject the Wi-Fi frame.
+    // However, we want to allow the rate adaptation to work down to low
+    // SNR values.  To allow this, we need to do three things:  1) disable
+    // the noise figure (set it to 0 dB) so that the noise level in 20 MHz
+    // is around -101 dBm, 2) lower the CCA sensitivity to a value that
+    // disables it (e.g. -110 dBm), and 3) disable the Wi-Fi preamble
+    // detection model.
+    wifiPhy.Set("CcaSensitivity", DoubleValue(-110));
+    wifiPhy.Set("RxNoiseFigure", DoubleValue(0));
+    wifiPhy.DisablePreambleDetectionModel();
 
     NetDeviceContainer wifiApDevices;
     NetDeviceContainer wifiStaDevices;
@@ -296,10 +334,6 @@
     wifiDevices.Add(wifiStaDevices);
     wifiDevices.Add(wifiApDevices);
 
-    // Set channel width
-    Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/ChannelWidth",
-                UintegerValue(chWidth));
-
     // Set guard interval
     Config::Set(
         "/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HtConfiguration/ShortGuardIntervalSupported",
@@ -359,9 +393,9 @@
                     MakeCallback(&NodeStatistics::RxCallback, &atpCounter));
 
     // Callbacks to print every change of rate
-    Config::ConnectFailSafe("/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/RemoteStationManager/$" +
-                                apManager + "/RateChange",
-                            MakeCallback(RateCallback));
+    Config::ConnectWithoutContextFailSafe(
+        "/NodeList/0/DeviceList/*/$ns3::WifiNetDevice/RemoteStationManager/$" + apManager + "/Rate",
+        MakeCallback(RateCallback));
 
     Simulator::Stop(Seconds(simuTime));
     Simulator::Run();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-simple-adhoc-grid.cc ns-3.39/examples/wireless/wifi-simple-adhoc-grid.cc
--- ns-3.38/examples/wireless/wifi-simple-adhoc-grid.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-simple-adhoc-grid.cc	2023-07-05 14:23:47.669427000 -0700
@@ -255,7 +255,7 @@
     InetSocketAddress remote = InetSocketAddress(i.GetAddress(sinkNode, 0), 80);
     source->Connect(remote);
 
-    if (tracing == true)
+    if (tracing)
     {
         AsciiTraceHelper ascii;
         wifiPhy.EnableAsciiAll(ascii.CreateFileStream("wifi-simple-adhoc-grid.tr"));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-spatial-reuse.cc ns-3.39/examples/wireless/wifi-spatial-reuse.cc
--- ns-3.38/examples/wireless/wifi-spatial-reuse.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-spatial-reuse.cc	2023-07-05 14:23:47.669427000 -0700
@@ -54,7 +54,7 @@
 //  time.
 //
 //  Note that, by default, this script configures a network using a
-//  channel bandwidth of 20 MHz. Changing this value alone (through
+//  channel width of 20 MHz. Changing this value alone (through
 //  the --channelWidth argument) without properly adjusting other
 //  parameters will void the effect of spatial reuse: since energy is
 //  measured over the 20 MHz primary channel regardless of the channel
@@ -69,11 +69,11 @@
 //  all these values by 3 dB compared to their defaults.
 //
 //  In addition, consider that adapting the adjustments shown above
-//  for an 80 MHz bandwidth (using a 6 dB threshold adjustment instead
+//  for an 80 MHz channel width (using a 6 dB threshold adjustment instead
 //  of 3 dB) will not produce any changes when enableObssPd is enabled
 //  or disabled. The cause for this is the insufficient amount of
 //  traffic that is generated by default in the example: increasing
-//  the bandwidth shortens the frame durations, and lowers the
+//  the channel width shortens the frame durations, and lowers the
 //  collision probability. Collisions between BSSs are a necessary
 //  condition to see the improvements brought by spatial reuse, and
 //  thus increasing the amount of generated traffic by setting the
@@ -134,7 +134,7 @@
     double ccaEdTrAp1 = -62;     // dBm
     double ccaEdTrAp2 = -62;     // dBm
     double minimumRssi = -82;    // dBm
-    int channelBandwidth = 20;   // MHz
+    int channelWidth = 20;       // MHz
     uint32_t payloadSize = 1500; // bytes
     uint32_t mcs = 0;            // MCS value
     double interval = 0.001;     // seconds
@@ -159,9 +159,7 @@
     cmd.AddValue("minimumRssi",
                  "Minimum RSSI for the ThresholdPreambleDetectionModel",
                  minimumRssi);
-    cmd.AddValue("channelBandwidth",
-                 "Bandwidth of the channel in MHz [20, 40, or 80]",
-                 channelBandwidth);
+    cmd.AddValue("channelWidth", "Bandwidth of the channel in MHz [20, 40, or 80]", channelWidth);
     cmd.AddValue("obssPdThreshold", "Threshold for the OBSS PD Algorithm", obssPdThreshold);
     cmd.AddValue("mcs", "The constant MCS value to transmit HE PPDUs", mcs);
     cmd.Parse(argc, argv);
@@ -182,7 +180,7 @@
 
     spectrumPhy.SetChannel(spectrumChannel);
     spectrumPhy.SetErrorRateModel("ns3::YansErrorRateModel");
-    switch (channelBandwidth)
+    switch (channelWidth)
     {
     case 20:
         spectrumPhy.Set("ChannelSettings", StringValue("{36, 20, BAND_5GHZ, 0}"));
@@ -194,7 +192,7 @@
         spectrumPhy.Set("ChannelSettings", StringValue("{171, 80, BAND_5GHZ, 0}"));
         break;
     default:
-        NS_ABORT_MSG("Unrecognized channel bandwidth: " << channelBandwidth);
+        NS_ABORT_MSG("Unrecognized channel width: " << channelWidth);
         break;
     }
     spectrumPhy.SetPreambleDetectionModel("ns3::ThresholdPreambleDetectionModel",
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/examples/wireless/wifi-vht-network.cc ns-3.39/examples/wireless/wifi-vht-network.cc
--- ns-3.38/examples/wireless/wifi-vht-network.cc	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/examples/wireless/wifi-vht-network.cc	2023-07-05 14:23:47.669427000 -0700
@@ -90,11 +90,8 @@
         Config::SetDefault("ns3::WifiRemoteStationManager::RtsCtsThreshold", StringValue("0"));
     }
 
-    double prevThroughput[8];
-    for (uint32_t l = 0; l < 8; l++)
-    {
-        prevThroughput[l] = 0;
-    }
+    double prevThroughput[8] = {0};
+
     std::cout << "MCS value"
               << "\t\t"
               << "Channel width"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/ns3 ns-3.39/ns3
--- ns-3.38/ns3	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/ns3	2023-07-05 14:23:47.673426900 -0700
@@ -186,8 +186,7 @@
                                   action="store_true", default=None)
 
     parser_configure.add_argument('--out', '--output-directory',
-                                  help=('Use BRITE integration support, given by the indicated path,'
-                                        ' to allow the use of the BRITE topology generator'),
+                                  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,'
@@ -282,7 +281,7 @@
                                         help='Try "./ns3 docs --help" for more documentation options')
     parser_docs.add_argument('docs',
                              help='Build project documentation',
-                             choices=["manual", "models", "tutorial", "contributing",
+                             choices=["contributing", "installation", "manual", "models", "tutorial",
                                       "sphinx", "doxygen-no-build", "doxygen", "all"],
                              action="store", type=str, default=None)
 
@@ -610,6 +609,14 @@
         if args.G is None:
             args.G = current_cmake_generator
 
+        # Get the current Python information to supply
+        # 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}")
+
     # C++ standard
     if args.cxx_standard is not None:
         cmake_args.append("-DCMAKE_CXX_STANDARD=%s" % args.cxx_standard)
@@ -794,7 +801,7 @@
 
             ns3_program_map[shortcut_path] = [program]
 
-            # Add an additional shortcut with .exe suffix when running on Windows
+            # Add a shortcut with .exe suffix when running on Windows
             if sys.platform == "win32":
                 ns3_program_map[shortcut_path.replace("\\", "/")] = [program]
                 ns3_program_map[shortcut_path + ".exe"] = [program]
@@ -846,6 +853,7 @@
     if not cmake:
         print("Error:  CMake not found; please install version 3.10 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"):
@@ -1045,6 +1053,7 @@
                                   "docs",
                                   "doxygen",
                                   "doxygen-no-build",
+                                  "installation",
                                   "sphinx",
                                   "manual",
                                   "models",
@@ -1063,7 +1072,7 @@
                 target = get_target_to_build(target, ns3_version, build_profile)
             else:
                 # Sphinx target should have the sphinx prefix
-                if target in ["contributing", "manual", "models", "tutorial"]:
+                if target in ["contributing", "installation", "manual", "models", "tutorial"]:
                     target = "sphinx_%s" % target
 
                 # Docs should build both doxygen and sphinx based docs
@@ -1584,30 +1593,7 @@
                    output
                    )
 
-    def remove_overlapping_path(base_path, target_path):
-        """
-        Remove overlapping paths from output directory and target_to_run
-        :param base_path: output path of the ns-3 build
-        :param target_path: path to the executable to run
-        :return: target_path without the overlapping parts
-        """
-        target_path = target_path.replace('/', os.sep).split(os.sep)
-        base_path = base_path.replace('/', os.sep).split(os.sep)
-        while target_path[0] in base_path:
-            target_path = target_path[1:]
-        target_path = os.sep.join(target_path)
-        return target_path
-
     if not args.shell and target_to_run and ".py" not in target_to_run:
-        target_to_run = remove_overlapping_path(out_dir, target_to_run)
-
-        # Waf doesn't add version prefix and build type suffix to the scratches, so we remove them
-        if current_cmake_cache_folder is None:
-            if "scratch" in target_to_run and run_only:
-                waf_target_to_run = target_to_run.replace(os.path.basename(target_to_run), args.run)
-                if os.path.exists(os.sep.join([out_dir, waf_target_to_run])):
-                    target_to_run = waf_target_to_run
-        target_to_run = os.sep.join([out_dir, target_to_run])
         if sys.platform == "win32":
             target_to_run += ".exe"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/pyproject.toml ns-3.39/pyproject.toml
--- ns-3.38/pyproject.toml	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/pyproject.toml	2023-07-05 14:23:47.673426900 -0700
@@ -0,0 +1,16 @@
+[build-system]
+build-backend = "setuptools.build_meta"
+requires = [
+    "wheel",
+    "setuptools>=45",
+    "setuptools_scm[toml]>=6.0",
+    "cmake-build-extension>=0.4",
+    "cppyy==2.4.2",
+]
+
+[tool.setuptools_scm]
+version_scheme = "post-release"
+local_scheme = "no-local-version"
+
+[tool.cibuildwheel]
+build-frontend = "build"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/README.md ns-3.39/README.md
--- ns-3.38/README.md	2023-03-16 20:54:23.488868200 -0700
+++ ns-3.39/README.md	2023-07-05 14:23:47.581426600 -0700
@@ -86,6 +86,52 @@
 pcap trace files, which can be read by `tcpdump -tt -r filename.pcap`
 The program source can be found in the examples/routing directory.
 
+
+## Running ns-3 from python
+
+If you do not plan to modify ns-3 upstream modules, you can get
+a pre-built version of the ns-3 python bindings.
+
+```shell
+pip install --user ns3
+```
+
+If you do not have `pip`, check their documents
+on [how to install it](https://pip.pypa.io/en/stable/installation/).
+
+After installing the `ns3` package, you can then create your simulation python script.
+Below is a trivial demo script to get you started.
+
+```python
+from ns import ns
+
+ns.LogComponentEnable("Simulator", ns.LOG_LEVEL_ALL)
+
+ns.Simulator.Stop(ns.Seconds(10))
+ns.Simulator.Run()
+ns.Simulator.Destroy()
+```
+
+The simulation will take a while to start, while the bindings are loaded.
+The script above will print the logging messages for the called commands.
+
+Use `help(ns)` to check the prototypes for all functions defined in the
+ns3 namespace. To get more useful results, query specific classes of
+interest and their functions e.g. `help(ns.Simulator)`.
+
+Smart pointers `Ptr<>` can be differentiated from objects by checking if
+`__deref__` is listed in `dir(variable)`. To dereference the pointer,
+use `variable.__deref__()`.
+
+Most ns-3 simulations are written in C++ and the documentation is
+oriented towards C++ users. The ns-3 tutorial programs (first.cc,
+second.cc, etc.) have Python equivalents, if you are looking for
+some initial guidance on how to use the Python API. The Python
+API may not be as full-featured as the C++ API, and an API guide
+for what C++ APIs are supported or not from Python do not currently exist.
+The project is looking for additional Python maintainers to improve
+the support for future Python users.
+
 ## Getting access to the ns-3 documentation
 
 Once you have verified that your build of ns-3 works by running
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/RELEASE_NOTES.md ns-3.39/RELEASE_NOTES.md
--- ns-3.38/RELEASE_NOTES.md	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/RELEASE_NOTES.md	2023-07-05 14:23:47.585426600 -0700
@@ -13,6 +13,66 @@
 and references prefixed by '!' refer to a
 [GitLab.com merge request](https://gitlab.com/nsnam/ns-3-dev/-/merge_requests) number.
 
+Release 3.39
+------------
+
+### Availability
+
+This release is available from:
+<https://www.nsnam.org/release/ns-allinone-3.39.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++-6 or later
+- Python 3.6 or later
+- CMake 3.10 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).  Specifically, avoid Cppyy version 3; stay with version 2.4.2 for this release.
+
+### New user-visible features
+
+- (applications) !1412 - Add Tx and TxWithAddresses trace sources in UdpClient
+- (bindings) - Package ns-3 as a pip wheel
+- (doc) #876 - Improve dark-mode heading, navigation, and search bars
+- (flowmonitor) #901 - Allow to reset the stats
+- (lr-wpan) !1399 - Add orphan scan support.
+- (lr-wpan) !1402 - Add attributes to MLME-SET and MLME-GET
+- (lr-wpan) !1410 - Add Mac16 and Mac64 functions
+- (network) !1405 - Add ConvertToInt to Mac64Address
+
+Note:  This release has removed the "wave" module from the codebase due to lack of maintenance
+and due to lack of relevance to modern vehicular networks which appear to be moving to cellular
+V2X solutions.  Future users who may wish to use this code should obtain it from an earlier ns-3
+release but should also be aware of a number of issues that arose and can be found in the
+[ns-3 Issue tracker](https://gitlab.com/nsnam/ns-3-dev/-/issues/?state=all&label_name%5B%5D=module%3A%3Awave); in particular,  #249, #879, #872, and #637.
+
+### Bugs fixed
+
+- (build) #881 - Fix scratch targets for file names containing dots
+- (core) #902 - Fix vector inversion in the function `AttributeContainerTestSuite::ReverseList()`
+- (energy) !1422 - Fix null harvester issue in EnergySource
+- (internet) #910 - Release memory when sockets are closed
+- (lr-wpan) !1406 - Fixes issues during MAC scan
+- (lr-wpan) !1481 - Small fixes in MAC orphan scan
+- (lte) #906 - Don't use invalid cell ID
+- (propagation) - fix height assignment in 3gpp propagation loss model
+- (wifi) #880 - Post-install change in WifiPhy::ChannelSettings does not completely reconfigure Wi-Fi
+- (wifi) #862 - sta-wifi-mac.cc cond="!link.bssid.has_value()", High occurrence at higher numbers of STAs per AP in indoor deployment.
+- (wifi) - Fix the equality operator of WifiInformationElement
+- (wifi) - Make the implementation of the Multi-Link Element compliant with the 11be specs by adding support of inheritance
+- (wifi) - Reset intra-BSS NAV when CF-End is an intra-BSS PPDU
+- (wifi) - UL MU CS shall be evaluated a SIFS after end of MU-RTS
+- (wifi) - Fix crash when changing operating channel after configuration but before initialization
+- (wifi) - Fix assert when non-HE STAs receive CTS frames sent using non-HT duplicate following a MU-RTS frame
+- (wifi) - Fix OBSS-PD support for 802.11be
+- (wifi) #917 - Add missing STA-ID in GetMode() call from YansErrorRateModel
+- (wifi) - Fix wifi-rate-adaptation-distance.cc example issues with CCA threshold
+
 Release 3.38
 ------------
 
@@ -50,12 +110,17 @@
 - (network) !938 - Added class `TimestampTag` for associating a timestamp with a packet.
 - (network) !1163 - Initializing an Ipv[4,6]Address from an invalid string do not raise an exception anymore. Instead the address is marked as not initialized.
 - (spectrum) !1046 - Added the TwoRaySpectrumPropagationLossModel fast-fading class, as the outcome of the related GSoC 2022 project titled "A simplified channel and beamforming model for ns-3"
+- (spectrum) !1119 - Added a capability to filter signals (with a new SpectrumTransmitFilter) sent on the SpectrumChannel before they are forwarded to receivers on the channel.  This is motivated by scalability (reducing inconsequential simulation events).
+the TwoRaySpectrumPropagationLossModel fast-fading class, as the outcome of the related GSoC 2022 project titled "A simplified channel and beamforming model for ns-3"
 - (wifi) Added support for 802.11be Multi-Link Operations (MLO), STR mode only
 - (wifi) Added more fields to the EHT Capabilities information element
 - (wifi) Added an initial 802.11be-based example program
 - (wifi) Added 802.11ax dual NAV (basic NAV and intra-BSS NAV)
 - (wifi) Added 802.11ax Uplink Multi-User Carrier Sense (UL MU CS) mechanism and have it used by non-AP STAs when determining if they can reply to a received Trigger Frame
 - (wifi) Added support for 802.11ax MU-RTS/CTS protection
+- (wifi) !1119 - Added, by default, a WifiBandwidthFilter to spectrum channels created by the SpectrumWifiHelper, so as to filter out-of-band signals from the receiver.
+- (internet) InternetStackHelper can be now used on nodes with an InternetStack already installed (it will not install IPv[4,6] twice).
+- (wifi) Added support to attach multiple spectrum channels to a same PHY and the possibility to track signals from multiple RF interfaces
 
 ### Bugs fixed
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/scratch/CMakeLists.txt ns-3.39/scratch/CMakeLists.txt
--- ns-3.38/scratch/CMakeLists.txt	2023-03-16 20:54:23.572868300 -0700
+++ ns-3.39/scratch/CMakeLists.txt	2023-07-05 14:23:47.673426900 -0700
@@ -30,7 +30,17 @@
   string(REPLACE "/" "_" scratch_dirname "${scratch_dirname}")
 
   # Get source name
-  get_filename_component(scratch_name ${scratch_src} NAME_WE)
+  if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0")
+    get_filename_component(scratch_name ${scratch_src} NAME_WLE)
+  else()
+    get_filename_component(scratch_name ${scratch_src} NAME)
+    string(FIND "${scratch_name}" "." ext_position REVERSE)
+    if(${ext_position} EQUAL -1)
+      message(FATAL_ERROR "Source file has no extension: ${scratch_src}")
+    else()
+      string(SUBSTRING "${scratch_name}" 0 ${ext_position} scratch_name)
+    endif()
+  endif()
 
   set(target_prefix scratch_)
   if(scratch_dirname)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/setup.cfg ns-3.39/setup.cfg
--- ns-3.38/setup.cfg	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/setup.cfg	2023-07-05 14:23:47.673426900 -0700
@@ -0,0 +1,44 @@
+[metadata]
+name = ns3
+description = ns-3 network simulator and visualizer
+long_description = file: README.md
+long_description_content_type = text/markdown
+author = nsnam
+author_email = webmaster@nsnam.org
+license = GPL-2.0-only
+platforms = any
+url = https://www.nsnam.org/
+
+project_urls =
+    Tracker = https://gitlab.com/nsnam/ns-3-dev/-/issues
+    Documentation = https://www.nsnam.org/docs/tutorial/html/index.html
+    Source = https://gitlab.com/nsnam/ns-3-dev
+
+keywords =
+    network-simulator
+
+classifiers =
+    Development Status :: 5 - Production/Stable
+    Operating System :: POSIX :: Linux
+    #Operating System :: MacOS
+    #Operating System :: Microsoft :: Windows
+    Intended Audience :: Education
+    Intended Audience :: Developers
+    Intended Audience :: Science/Research
+    Programming Language :: C++
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
+    Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
+    Programming Language :: Python :: 3.10
+    Programming Language :: Python :: 3.11
+    Programming Language :: Python :: 3 :: Only
+    Programming Language :: Python :: Implementation :: CPython
+    License :: OSI Approved :: GNU General Public License v2 (GPLv2)
+
+[options]
+zip_safe = False
+python_requires = >=3.6
+install_requires =
+    cppyy==2.4.2
+
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/setup.py ns-3.39/setup.py
--- ns-3.38/setup.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/setup.py	2023-07-05 14:23:47.673426900 -0700
@@ -0,0 +1,35 @@
+import cmake_build_extension
+import setuptools
+import sys
+import sysconfig
+
+setuptools.setup(
+    cmdclass=dict(build_ext=cmake_build_extension.BuildExtension),
+    packages=['ns', 'visualizer'],
+    package_dir={
+      'ns': './build-support/pip-wheel/ns',
+      'visualizer': './build-support/pip-wheel/visualizer'
+    },
+    ext_modules=[
+        cmake_build_extension.CMakeExtension(
+            name="BuildAndInstall",
+            install_prefix="ns3",
+            cmake_configure_options=[
+                "-DCMAKE_BUILD_TYPE:STRING=release",
+                "-DNS3_ASSERT:BOOL=ON",
+                "-DNS3_LOG:BOOL=ON",
+                "-DNS3_WARNINGS_AS_ERRORS:BOOL=OFF",
+                "-DNS3_PYTHON_BINDINGS:BOOL=ON",
+                "-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}"
+            ]
+        ),
+    ],
+)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/antenna/model/phased-array-model.h ns-3.39/src/antenna/model/phased-array-model.h
--- ns-3.38/src/antenna/model/phased-array-model.h	2023-03-16 20:54:23.576868300 -0700
+++ ns-3.39/src/antenna/model/phased-array-model.h	2023-07-05 14:23:47.673426900 -0700
@@ -140,13 +140,6 @@
     uint32_t GetId() const;
 
   protected:
-    /**
-     * Utility method to compute the euclidean norm of a ComplexVector
-     * \param vector the ComplexVector
-     * \return the euclidean norm
-     */
-    static double ComputeNorm(const ComplexVector& vector);
-
     ComplexVector m_beamformingVector;  //!< the beamforming vector in use
     Ptr<AntennaModel> m_antennaElement; //!< the model of the antenna element in use
     bool m_isBfVectorValid;             //!< ensures the validity of the beamforming vector
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/aodv/model/aodv-id-cache.h ns-3.39/src/aodv/model/aodv-id-cache.h
--- ns-3.38/src/aodv/model/aodv-id-cache.h	2023-03-16 20:54:23.576868300 -0700
+++ ns-3.39/src/aodv/model/aodv-id-cache.h	2023-07-05 14:23:47.673426900 -0700
@@ -109,7 +109,7 @@
          * \param u UniqueId entry
          * \return true if expired, false otherwise
          */
-        bool operator()(const struct UniqueId& u) const
+        bool operator()(const UniqueId& u) const
         {
             return (u.m_expire < Simulator::Now());
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/aodv/model/aodv-routing-protocol.cc ns-3.39/src/aodv/model/aodv-routing-protocol.cc
--- ns-3.38/src/aodv/model/aodv-routing-protocol.cc	2023-03-16 20:54:23.576868300 -0700
+++ ns-3.39/src/aodv/model/aodv-routing-protocol.cc	2023-07-05 14:23:47.673426900 -0700
@@ -497,10 +497,10 @@
 RoutingProtocol::RouteInput(Ptr<const Packet> p,
                             const Ipv4Header& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb)
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p->GetUid() << header.GetDestination() << idev->GetAddress());
     if (m_socketAddresses.empty())
@@ -558,7 +558,7 @@
                 }
                 UpdateRouteLifeTime(origin, m_activeRouteTimeout);
                 Ptr<Packet> packet = p->Copy();
-                if (lcb.IsNull() == false)
+                if (!lcb.IsNull())
                 {
                     NS_LOG_LOGIC("Broadcast local delivery to " << iface.GetLocal());
                     lcb(p, header, iif);
@@ -617,7 +617,7 @@
             UpdateRouteLifeTime(toOrigin.GetNextHop(), m_activeRouteTimeout);
             m_nb.Update(toOrigin.GetNextHop(), m_activeRouteTimeout);
         }
-        if (lcb.IsNull() == false)
+        if (!lcb.IsNull())
         {
             NS_LOG_LOGIC("Unicast local delivery to " << dst);
             lcb(p, header, iif);
@@ -632,7 +632,7 @@
     }
 
     // Check if input device supports IP forwarding
-    if (m_ipv4->IsForwarding(iif) == false)
+    if (!m_ipv4->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         ecb(p, header, Socket::ERROR_NOROUTETOHOST);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/aodv/model/aodv-routing-protocol.h ns-3.39/src/aodv/model/aodv-routing-protocol.h
--- ns-3.38/src/aodv/model/aodv-routing-protocol.h	2023-03-16 20:54:23.576868300 -0700
+++ ns-3.39/src/aodv/model/aodv-routing-protocol.h	2023-07-05 14:23:47.673426900 -0700
@@ -78,10 +78,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
     void NotifyAddAddress(uint32_t interface, Ipv4InterfaceAddress address) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/aodv/model/aodv-rtable.h ns-3.39/src/aodv/model/aodv-rtable.h
--- ns-3.38/src/aodv/model/aodv-rtable.h	2023-03-16 20:54:23.576868300 -0700
+++ ns-3.39/src/aodv/model/aodv-rtable.h	2023-07-05 14:23:47.673426900 -0700
@@ -407,7 +407,7 @@
     /// List of precursors
     std::vector<Ipv4Address> m_precursorList;
     /// When I can send another request
-    Time m_routeRequestTimout;
+    Time m_routeRequestTimeout;
     /// Number of route requests
     uint8_t m_reqCount;
     /// Indicate if this entry is in "blacklist"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/aodv/test/examples-to-run.py ns-3.39/src/aodv/test/examples-to-run.py
--- ns-3.38/src/aodv/test/examples-to-run.py	2023-03-16 20:54:23.576868300 -0700
+++ ns-3.39/src/aodv/test/examples-to-run.py	2023-07-05 14:23:47.673426900 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/examples/three-gpp-http-example.cc ns-3.39/src/applications/examples/three-gpp-http-example.cc
--- ns-3.38/src/applications/examples/three-gpp-http-example.cc	2023-03-16 20:54:23.576868300 -0700
+++ ns-3.39/src/applications/examples/three-gpp-http-example.cc	2023-07-05 14:23:47.677426800 -0700
@@ -93,6 +93,17 @@
     }
 }
 
+void
+ClientPageReceived(Ptr<const ThreeGppHttpClient> client,
+                   const Time& time,
+                   uint32_t numObjects,
+                   uint32_t numBytes)
+{
+    NS_LOG_INFO("Client " << client << " has received a page that took " << time.As(Time::MS)
+                          << " ms to load with " << numObjects << " objects and " << numBytes
+                          << " bytes.");
+}
+
 int
 main(int argc, char* argv[])
 {
@@ -163,6 +174,7 @@
     httpClient->TraceConnectWithoutContext("RxEmbeddedObject",
                                            MakeCallback(&ClientEmbeddedObjectReceived));
     httpClient->TraceConnectWithoutContext("Rx", MakeCallback(&ClientRx));
+    httpClient->TraceConnectWithoutContext("RxPage", MakeCallback(&ClientPageReceived));
 
     // Stop browsing after 30 minutes
     clientApps.Stop(Seconds(simTimeSec));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/model/three-gpp-http-client.cc ns-3.39/src/applications/model/three-gpp-http-client.cc
--- ns-3.38/src/applications/model/three-gpp-http-client.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/applications/model/three-gpp-http-client.cc	2023-07-05 14:23:47.705427000 -0700
@@ -47,6 +47,9 @@
       m_objectClientTs(MilliSeconds(0)),
       m_objectServerTs(MilliSeconds(0)),
       m_embeddedObjectsToBeRequested(0),
+      m_pageLoadStartTs(MilliSeconds(0)),
+      m_numberEmbeddedObjectsRequested(0),
+      m_numberBytesPage(0),
       m_httpVariables(CreateObject<ThreeGppHttpVariables>())
 {
     NS_LOG_FUNCTION(this);
@@ -76,6 +79,10 @@
                           UintegerValue(80), // the default HTTP port
                           MakeUintegerAccessor(&ThreeGppHttpClient::m_remoteServerPort),
                           MakeUintegerChecker<uint16_t>())
+            .AddTraceSource("RxPage",
+                            "A page has been received.",
+                            MakeTraceSourceAccessor(&ThreeGppHttpClient::m_rxPageTrace),
+                            "ns3::ThreeGppHttpClient::RxPageTracedCallback")
             .AddTraceSource(
                 "ConnectionEstablished",
                 "Connection to the destination web server has been established.",
@@ -162,29 +169,21 @@
     {
     case NOT_STARTED:
         return "NOT_STARTED";
-        break;
     case CONNECTING:
         return "CONNECTING";
-        break;
     case EXPECTING_MAIN_OBJECT:
         return "EXPECTING_MAIN_OBJECT";
-        break;
     case PARSING_MAIN_OBJECT:
         return "PARSING_MAIN_OBJECT";
-        break;
     case EXPECTING_EMBEDDED_OBJECT:
         return "EXPECTING_EMBEDDED_OBJECT";
-        break;
     case READING:
         return "READING";
-        break;
     case STOPPED:
         return "STOPPED";
-        break;
     default:
         NS_FATAL_ERROR("Unknown state");
         return "FATAL_ERROR";
-        break;
     }
 }
 
@@ -447,6 +446,7 @@
         else
         {
             SwitchToState(EXPECTING_MAIN_OBJECT);
+            m_pageLoadStartTs = Simulator::Now(); // start counting page loading time
         }
     }
     else
@@ -628,6 +628,7 @@
                  * downloaded completely. Now is the time to read it.
                  */
                 NS_LOG_INFO(this << " Finished receiving a web page.");
+                FinishReceivingPage(); // trigger callback for page loading time
                 EnterReadingTime();
             }
 
@@ -669,6 +670,7 @@
         m_constructedPacket->AddHeader(httpHeader);
     }
     uint32_t contentSize = packet->GetSize();
+    m_numberBytesPage += contentSize; // increment counter of page size
 
     /* Note that the packet does not contain header at this point.
      * The content is purely raw data, which was the only intended data to be received.
@@ -724,6 +726,8 @@
     if (m_state == PARSING_MAIN_OBJECT)
     {
         m_embeddedObjectsToBeRequested = m_httpVariables->GetNumOfEmbeddedObjects();
+        // saving total number of embedded objects
+        m_numberEmbeddedObjectsRequested = m_embeddedObjectsToBeRequested;
         NS_LOG_INFO(this << " Parsing has determined " << m_embeddedObjectsToBeRequested
                          << " embedded object(s) in the main object.");
 
@@ -743,6 +747,7 @@
              * enjoy the plain web page.
              */
             NS_LOG_INFO(this << " Finished receiving a web page.");
+            FinishReceivingPage(); // trigger callback for page loading time
             EnterReadingTime();
         }
     }
@@ -826,4 +831,16 @@
     m_stateTransitionTrace(oldState, newState);
 }
 
+void
+ThreeGppHttpClient::FinishReceivingPage()
+{
+    m_rxPageTrace(this,
+                  Simulator::Now() - m_pageLoadStartTs,
+                  m_numberEmbeddedObjectsRequested,
+                  m_numberBytesPage);
+    // Reset counter variables.
+    m_numberEmbeddedObjectsRequested = 0;
+    m_numberBytesPage = 0;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/model/three-gpp-http-client.h ns-3.39/src/applications/model/three-gpp-http-client.h
--- ns-3.38/src/applications/model/three-gpp-http-client.h	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/applications/model/three-gpp-http-client.h	2023-07-05 14:23:47.705427000 -0700
@@ -172,6 +172,20 @@
      */
     typedef void (*TracedCallback)(Ptr<const ThreeGppHttpClient> httpClient);
 
+    /**
+     * Callback signature for `RxPage` trace sources.
+     * \param httpClient Pointer to this instance of ThreeGppHttpClient,
+     *                               which is where the trace originated.
+     * \param time Elapsed time from the start to the end of the request.
+     * \param numObjects Number of objects downloaded, including main and
+     *                                                  embedded objects.
+     * \param numBytes Total number of bytes included in the page.
+     */
+    typedef void (*RxPageTracedCallback)(Ptr<const ThreeGppHttpClient> httpClient,
+                                         const Time& time,
+                                         uint32_t numObjects,
+                                         uint32_t numBytes);
+
   protected:
     // Inherited from Object base class.
     void DoDispose() override;
@@ -343,6 +357,12 @@
      */
     void SwitchToState(State_t state);
 
+    /**
+     * Finish receiving a page. This function should be called when a full-page
+     * finishes loading, including the main object and all embedded objects.
+     */
+    void FinishReceivingPage();
+
     /// The current state of the client application. Begins with NOT_STARTED.
     State_t m_state;
     /// The socket for sending and receiving packets to/from the web server.
@@ -357,6 +377,12 @@
     Time m_objectServerTs;
     /// Determined after parsing the main object.
     uint32_t m_embeddedObjectsToBeRequested;
+    /// The time stamp when the page started loading.
+    Time m_pageLoadStartTs;
+    /// Number of embedded objects to requested in the current page.
+    uint32_t m_numberEmbeddedObjectsRequested;
+    /// Number of bytes received for the current page.
+    uint32_t m_numberBytesPage;
 
     // ATTRIBUTES
 
@@ -369,6 +395,9 @@
 
     // TRACE SOURCES
 
+    /// The `RxPage` trace source.
+    ns3::TracedCallback<Ptr<const ThreeGppHttpClient>, const Time&, uint32_t, uint32_t>
+        m_rxPageTrace;
     /// The `ConnectionEstablished` trace source.
     ns3::TracedCallback<Ptr<const ThreeGppHttpClient>> m_connectionEstablishedTrace;
     /// The `ConnectionClosed` trace source.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/model/three-gpp-http-server.cc ns-3.39/src/applications/model/three-gpp-http-server.cc
--- ns-3.38/src/applications/model/three-gpp-http-server.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/applications/model/three-gpp-http-server.cc	2023-07-05 14:23:47.705427000 -0700
@@ -155,17 +155,13 @@
     {
     case NOT_STARTED:
         return "NOT_STARTED";
-        break;
     case STARTED:
         return "STARTED";
-        break;
     case STOPPED:
         return "STOPPED";
-        break;
     default:
         NS_FATAL_ERROR("Unknown state");
         return "FATAL_ERROR";
-        break;
     }
 }
 
@@ -197,7 +193,7 @@
             const TypeId tcpSocketTid = TcpSocket::GetTypeId();
             for (uint32_t i = 0; i < tcpSocketTid.GetAttributeN(); i++)
             {
-                struct TypeId::AttributeInformation attrInfo = tcpSocketTid.GetAttribute(i);
+                TypeId::AttributeInformation attrInfo = tcpSocketTid.GetAttribute(i);
                 if (attrInfo.name == "SegmentSize")
                 {
                     previousSocketMtu = attrInfo.initialValue;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/model/udp-client.cc ns-3.39/src/applications/model/udp-client.cc
--- ns-3.38/src/applications/model/udp-client.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/applications/model/udp-client.cc	2023-07-05 14:23:47.705427000 -0700
@@ -76,7 +76,15 @@
                           "the size of the header carrying the sequence number and the time stamp.",
                           UintegerValue(1024),
                           MakeUintegerAccessor(&UdpClient::m_size),
-                          MakeUintegerChecker<uint32_t>(12, 65507));
+                          MakeUintegerChecker<uint32_t>(12, 65507))
+            .AddTraceSource("Tx",
+                            "A new packet is created and sent",
+                            MakeTraceSourceAccessor(&UdpClient::m_txTrace),
+                            "ns3::Packet::TracedCallback")
+            .AddTraceSource("TxWithAddresses",
+                            "A new packet is created and sent",
+                            MakeTraceSourceAccessor(&UdpClient::m_txTraceWithAddresses),
+                            "ns3::Packet::TwoAddressTracedCallback");
     return tid;
 }
 
@@ -125,7 +133,7 @@
     {
         TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
         m_socket = Socket::CreateSocket(GetNode(), tid);
-        if (Ipv4Address::IsMatchingType(m_peerAddress) == true)
+        if (Ipv4Address::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind() == -1)
             {
@@ -134,7 +142,7 @@
             m_socket->Connect(
                 InetSocketAddress(Ipv4Address::ConvertFrom(m_peerAddress), m_peerPort));
         }
-        else if (Ipv6Address::IsMatchingType(m_peerAddress) == true)
+        else if (Ipv6Address::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind6() == -1)
             {
@@ -143,7 +151,7 @@
             m_socket->Connect(
                 Inet6SocketAddress(Ipv6Address::ConvertFrom(m_peerAddress), m_peerPort));
         }
-        else if (InetSocketAddress::IsMatchingType(m_peerAddress) == true)
+        else if (InetSocketAddress::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind() == -1)
             {
@@ -151,7 +159,7 @@
             }
             m_socket->Connect(m_peerAddress);
         }
-        else if (Inet6SocketAddress::IsMatchingType(m_peerAddress) == true)
+        else if (Inet6SocketAddress::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind6() == -1)
             {
@@ -203,9 +211,20 @@
 {
     NS_LOG_FUNCTION(this);
     NS_ASSERT(m_sendEvent.IsExpired());
+
+    Address from;
+    Address to;
+    m_socket->GetSockName(from);
+    m_socket->GetPeerName(to);
     SeqTsHeader seqTs;
     seqTs.SetSeq(m_sent);
-    Ptr<Packet> p = Create<Packet>(m_size - (8 + 4)); // 8+4 : the size of the seqTs header
+    NS_ABORT_IF(m_size < seqTs.GetSerializedSize());
+    Ptr<Packet> p = Create<Packet>(m_size - seqTs.GetSerializedSize());
+
+    // Trace before adding header, for consistency with PacketSink
+    m_txTrace(p);
+    m_txTraceWithAddresses(p, from, to);
+
     p->AddHeader(seqTs);
 
     if ((m_socket->Send(p)) >= 0)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/model/udp-client.h ns-3.39/src/applications/model/udp-client.h
--- ns-3.38/src/applications/model/udp-client.h	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/applications/model/udp-client.h	2023-07-05 14:23:47.705427000 -0700
@@ -26,6 +26,7 @@
 #include "ns3/event-id.h"
 #include "ns3/ipv4-address.h"
 #include "ns3/ptr.h"
+#include <ns3/traced-callback.h>
 
 namespace ns3
 {
@@ -82,6 +83,12 @@
      */
     void Send();
 
+    /// Traced Callback: transmitted packets.
+    TracedCallback<Ptr<const Packet>> m_txTrace;
+
+    /// Callbacks for tracing the packet Tx events, includes source and destination addresses
+    TracedCallback<Ptr<const Packet>, const Address&, const Address&> m_txTraceWithAddresses;
+
     uint32_t m_count; //!< Maximum number of packets the application will send
     Time m_interval;  //!< Packet inter-send time
     uint32_t m_size;  //!< Size of the sent packet (including the SeqTsHeader)
@@ -92,6 +99,7 @@
     Address m_peerAddress; //!< Remote peer address
     uint16_t m_peerPort;   //!< Remote peer port
     EventId m_sendEvent;   //!< Event to send the next packet
+
 #ifdef NS3_LOG_ENABLE
     std::string m_peerAddressString; //!< Remote peer address string
 #endif                               // NS3_LOG_ENABLE
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/model/udp-echo-client.cc ns-3.39/src/applications/model/udp-echo-client.cc
--- ns-3.38/src/applications/model/udp-echo-client.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/applications/model/udp-echo-client.cc	2023-07-05 14:23:47.705427000 -0700
@@ -141,7 +141,7 @@
     {
         TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
         m_socket = Socket::CreateSocket(GetNode(), tid);
-        if (Ipv4Address::IsMatchingType(m_peerAddress) == true)
+        if (Ipv4Address::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind() == -1)
             {
@@ -150,7 +150,7 @@
             m_socket->Connect(
                 InetSocketAddress(Ipv4Address::ConvertFrom(m_peerAddress), m_peerPort));
         }
-        else if (Ipv6Address::IsMatchingType(m_peerAddress) == true)
+        else if (Ipv6Address::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind6() == -1)
             {
@@ -159,7 +159,7 @@
             m_socket->Connect(
                 Inet6SocketAddress(Ipv6Address::ConvertFrom(m_peerAddress), m_peerPort));
         }
-        else if (InetSocketAddress::IsMatchingType(m_peerAddress) == true)
+        else if (InetSocketAddress::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind() == -1)
             {
@@ -167,7 +167,7 @@
             }
             m_socket->Connect(m_peerAddress);
         }
-        else if (Inet6SocketAddress::IsMatchingType(m_peerAddress) == true)
+        else if (Inet6SocketAddress::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind6() == -1)
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/applications/model/udp-trace-client.cc ns-3.39/src/applications/model/udp-trace-client.cc
--- ns-3.38/src/applications/model/udp-trace-client.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/applications/model/udp-trace-client.cc	2023-07-05 14:23:47.705427000 -0700
@@ -48,7 +48,7 @@
 /**
  * \brief Default trace to send
  */
-struct UdpTraceClient::TraceEntry UdpTraceClient::g_defaultEntries[] = {
+UdpTraceClient::TraceEntry UdpTraceClient::g_defaultEntries[] = {
     {0, 534, 'I'},
     {40, 1542, 'P'},
     {120, 134, 'B'},
@@ -258,7 +258,7 @@
     {
         TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
         m_socket = Socket::CreateSocket(GetNode(), tid);
-        if (Ipv4Address::IsMatchingType(m_peerAddress) == true)
+        if (Ipv4Address::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind() == -1)
             {
@@ -267,7 +267,7 @@
             m_socket->Connect(
                 InetSocketAddress(Ipv4Address::ConvertFrom(m_peerAddress), m_peerPort));
         }
-        else if (Ipv6Address::IsMatchingType(m_peerAddress) == true)
+        else if (Ipv6Address::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind6() == -1)
             {
@@ -276,7 +276,7 @@
             m_socket->Connect(
                 Inet6SocketAddress(Ipv6Address::ConvertFrom(m_peerAddress), m_peerPort));
         }
-        else if (InetSocketAddress::IsMatchingType(m_peerAddress) == true)
+        else if (InetSocketAddress::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind() == -1)
             {
@@ -284,7 +284,7 @@
             }
             m_socket->Connect(m_peerAddress);
         }
-        else if (Inet6SocketAddress::IsMatchingType(m_peerAddress) == true)
+        else if (Inet6SocketAddress::IsMatchingType(m_peerAddress))
         {
             if (m_socket->Bind6() == -1)
             {
@@ -329,11 +329,11 @@
     p->AddHeader(seqTs);
 
     std::stringstream addressString;
-    if (Ipv4Address::IsMatchingType(m_peerAddress) == true)
+    if (Ipv4Address::IsMatchingType(m_peerAddress))
     {
         addressString << Ipv4Address::ConvertFrom(m_peerAddress);
     }
-    else if (Ipv6Address::IsMatchingType(m_peerAddress) == true)
+    else if (Ipv6Address::IsMatchingType(m_peerAddress))
     {
         addressString << Ipv6Address::ConvertFrom(m_peerAddress);
     }
@@ -362,7 +362,7 @@
 
     bool cycled = false;
     Ptr<Packet> p;
-    struct TraceEntry* entry = &m_entries[m_currentEntry];
+    TraceEntry* entry = &m_entries[m_currentEntry];
     do
     {
         for (uint32_t i = 0; i < entry->packetSize / m_maxPacketSize; i++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/bridge/test/examples-to-run.py ns-3.39/src/bridge/test/examples-to-run.py
--- ns-3.38/src/bridge/test/examples-to-run.py	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/bridge/test/examples-to-run.py	2023-07-05 14:23:47.709426900 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/brite/examples/brite-generic-example.cc ns-3.39/src/brite/examples/brite-generic-example.cc
--- ns-3.38/src/brite/examples/brite-generic-example.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/brite/examples/brite-generic-example.cc	2023-07-05 14:23:47.709426900 -0700
@@ -1,4 +1,6 @@
 /*
+ * Copyright (c) 2012 The Georgia Institute of Technology
+ *
  * 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;
@@ -12,6 +14,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * Author: Brian Swenson <bswenson3@gatech.edu>
  */
 
 #include "Brite.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/brite/examples/brite-generic-example.py ns-3.39/src/brite/examples/brite-generic-example.py
--- ns-3.38/src/brite/examples/brite-generic-example.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/brite/examples/brite-generic-example.py	2023-07-05 14:23:47.709426900 -0700
@@ -0,0 +1,104 @@
+#
+# Copyright (c) 2012 The Georgia Institute of Technology
+#
+# 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: Brian Swenson <bswenson3@gatech.edu>
+# Modified by: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+#
+
+from ns import ns
+
+ns.LogComponentEnable("BriteTopologyHelper", ns.LOG_LEVEL_ALL)
+
+# BRITE needs a configuration file to build its graph. By default, this
+# example will use the TD_ASBarabasi_RTWaxman.conf file. There are many others
+# which can be found in the BRITE/conf_files directory
+confFile = "src/brite/examples/conf_files/TD_ASBarabasi_RTWaxman.conf"
+
+# Invoke the BriteTopologyHelper and pass in a BRITE
+# configuration file and a seed file. This will use
+# BRITE to build a graph from which we can build the ns-3 topology
+bth = ns.BriteTopologyHelper(confFile)
+bth.AssignStreams(3)
+
+p2p = ns.PointToPointHelper()
+
+stack = ns.InternetStackHelper()
+
+nixRouting = ns.Ipv4NixVectorHelper()
+stack.SetRoutingHelper(nixRouting)
+
+address = ns.Ipv4AddressHelper()
+address.SetBase("10.0.0.0", "255.255.255.252")
+
+bth.BuildBriteTopology(stack)
+bth.AssignIpv4Addresses(address)
+
+print(f"Number of AS created {bth.GetNAs()}")
+
+# The BRITE topology generator generates a topology of routers.  Here we create
+# two subnetworks which we attach to router leaf nodes generated by BRITE
+# use just one node
+client = ns.NodeContainer()
+server = ns.NodeContainer()
+
+client.Create(1)
+stack.Install(client)
+
+# install client node on last leaf node of AS 0
+numLeafNodesInAsZero = bth.GetNLeafNodesForAs(0)
+client.Add(bth.GetLeafNodeForAs(0, numLeafNodesInAsZero - 1))
+
+server.Create(1)
+stack.Install(server)
+
+# install server node on last leaf node on AS 1
+numLeafNodesInAsOne = bth.GetNLeafNodesForAs(1)
+server.Add(bth.GetLeafNodeForAs(1, numLeafNodesInAsOne - 1))
+
+p2p.SetDeviceAttribute("DataRate", ns.StringValue("5Mbps"))
+p2p.SetChannelAttribute("Delay", ns.StringValue("2ms"))
+
+p2pClientDevices = p2p.Install(client)
+p2pServerDevices = p2p.Install(server)
+
+address.SetBase("10.1.0.0", "255.255.0.0")
+clientInterfaces = address.Assign(p2pClientDevices)
+
+address.SetBase("10.2.0.0", "255.255.0.0")
+serverInterfaces = ns.Ipv4InterfaceContainer()
+serverInterfaces = address.Assign(p2pServerDevices)
+
+echoServer = ns.UdpEchoServerHelper(9)
+serverApps = echoServer.Install(server.Get(0))
+serverApps.Start(ns.Seconds(1.0))
+serverApps.Stop(ns.Seconds(5.0))
+
+echoClient = ns.UdpEchoClientHelper(serverInterfaces.GetAddress(0).ConvertTo(), 9)
+echoClient.SetAttribute("MaxPackets", ns.UintegerValue(1))
+echoClient.SetAttribute("Interval", ns.TimeValue(ns.Seconds(1.)))
+echoClient.SetAttribute("PacketSize", ns.UintegerValue(1024))
+
+clientApps = echoClient.Install(client.Get(0))
+clientApps.Start(ns.Seconds(2.0))
+clientApps.Stop(ns.Seconds(5.0))
+
+asciiTrace = ns.AsciiTraceHelper()
+p2p.EnableAsciiAll(asciiTrace.CreateFileStream("briteLeaves.tr"))
+
+# Run the simulator
+ns.Simulator.Stop(ns.Seconds(6.0))
+ns.Simulator.Run()
+ns.Simulator.Destroy()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/brite/examples/brite-MPI-example.cc ns-3.39/src/brite/examples/brite-MPI-example.cc
--- ns-3.38/src/brite/examples/brite-MPI-example.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/brite/examples/brite-MPI-example.cc	2023-07-05 14:23:47.709426900 -0700
@@ -1,4 +1,6 @@
 /*
+ * Copyright (c) 2012 The Georgia Institute of Technology
+ *
  * 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;
@@ -12,6 +14,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * Author: Brian Swenson <bswenson3@gatech.edu>
  */
 
 #include "Brite.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/brite/helper/brite-topology-helper.cc ns-3.39/src/brite/helper/brite-topology-helper.cc
--- ns-3.38/src/brite/helper/brite-topology-helper.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/brite/helper/brite-topology-helper.cc	2023-07-05 14:23:47.713427000 -0700
@@ -412,8 +412,8 @@
     NS_LOG_LOGIC("Assigning << " << m_numAs << " AS to " << systemCount << " MPI instances");
     for (uint32_t i = 0; i < m_numAs; ++i)
     {
-        int val = i % systemCount;
-        m_systemForAs.push_back(val);
+        uint32_t val = i % systemCount;
+        m_systemForAs.push_back(static_cast<int>(val));
         NS_LOG_INFO("AS: " << i << " System: " << val);
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/brite/test/examples-to-run.py ns-3.39/src/brite/test/examples-to-run.py
--- ns-3.38/src/brite/test/examples-to-run.py	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/brite/test/examples-to-run.py	2023-07-05 14:23:47.713427000 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
@@ -17,4 +16,6 @@
 #     (example_name, do_run).
 #
 # See test.py for more information.
-python_examples = []
+python_examples = [
+    ("brite-generic-example.py", "ENABLE_BRITE == True"),
+]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/CMakeLists.txt ns-3.39/src/buildings/CMakeLists.txt
--- ns-3.38/src/buildings/CMakeLists.txt	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/buildings/CMakeLists.txt	2023-07-05 14:23:47.713427000 -0700
@@ -33,11 +33,12 @@
   LIBRARIES_TO_LINK ${libmobility}
                     ${libpropagation}
   TEST_SOURCES
-    test/building-position-allocator-test.cc
+    test/buildings-channel-condition-model-test.cc
     test/buildings-helper-test.cc
     test/buildings-pathloss-test.cc
+    test/buildings-penetration-loss-pathloss-test.cc
+    test/building-position-allocator-test.cc
     test/buildings-shadowing-test.cc
-    test/buildings-channel-condition-model-test.cc
     test/outdoor-random-walk-test.cc
     test/three-gpp-v2v-channel-condition-model-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/doc/source/buildings-user.rst ns-3.39/src/buildings/doc/source/buildings-user.rst
--- ns-3.38/src/buildings/doc/source/buildings-user.rst	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/buildings/doc/source/buildings-user.rst	2023-07-05 14:23:47.713427000 -0700
@@ -194,6 +194,14 @@
 The class BuildingsChannelConditionModel implements a `channel condition model <propagation.html#channelconditionmodel>`_
 which determines the LOS/NLOS channel state based on the buildings deployed in
 the scenario.
+In addition, based on the wall material of the building, low/high building
+penetration losses are considered, as defined in 3GPP TS 38.901 7.4.3.1.
+In particular, for O2I condition, in case of Wood or ConcreteWithWindows material,
+low losses are considered in the pathloss calculation. In case the material has
+been set to ConcreteWithoutWindows or StoneBlocks, high losses are considered.
+Notice that in certain corner cases, such as the I2O2I interference, the model
+underestimates losses by applying either low or high losses based on the wall material
+of the involved nodes. For a more accurate estimation the model can be further extended.
 
 The classes ``ThreeGppV2vUrbanChannelConditionModel`` and
 ``ThreeGppV2vHighwayChannelConditionModel`` implement hybrid channel condition
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/examples/outdoor-group-mobility-example.cc ns-3.39/src/buildings/examples/outdoor-group-mobility-example.cc
--- ns-3.38/src/buildings/examples/outdoor-group-mobility-example.cc	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/buildings/examples/outdoor-group-mobility-example.cc	2023-07-05 14:23:47.713427000 -0700
@@ -176,7 +176,7 @@
     // configuration.
 
     int64_t streamIndex = 1;
-    if (useHelper == false)
+    if (!useHelper)
     {
         // The reference (parent) mobility model starts at coordinate (10, 10, 0)
         // and performs a buildings-aware random walk.
@@ -196,7 +196,7 @@
         outdoorMm->SetPosition(Vector(10, 10, 0));
         streamIndex += outdoorMm->AssignStreams(streamIndex);
 
-        // Each HierachicalMobilityModel contains the above model as the Parent,
+        // Each HierarchicalMobilityModel contains the above model as the Parent,
         // and a user defined model as the Child.  Two MobilityModel objects are
         // instantiated per node, and each node also shares the same parent model.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/helper/building-allocator.h ns-3.39/src/buildings/helper/building-allocator.h
--- ns-3.38/src/buildings/helper/building-allocator.h	2023-03-16 20:54:23.580868500 -0700
+++ ns-3.39/src/buildings/helper/building-allocator.h	2023-07-05 14:23:47.713427000 -0700
@@ -76,11 +76,11 @@
      */
     void PushAttributes() const;
 
-    mutable uint32_t m_current;                          //!< current building number
-    enum GridPositionAllocator::LayoutType m_layoutType; //!< Layout type
-    double m_xMin;                                       //!< The x coordinate where the grid starts
-    double m_yMin;                                       //!< The y coordinate where the grid starts
-    uint32_t m_n;     //!< The number of objects laid out on a line
+    mutable uint32_t m_current;                     //!< Current building number
+    GridPositionAllocator::LayoutType m_layoutType; //!< Layout type
+    double m_xMin;                                  //!< The x coordinate where the grid starts
+    double m_yMin;                                  //!< The y coordinate where the grid starts
+    uint32_t m_n;                                   //!< The number of objects laid out on a line
     double m_lengthX; //!< The length of the wall of each building along the X axis.
     double m_lengthY; //!< The length of the wall of each building along the Y axis.
     double m_deltaX;  //!< The x space between buildings
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/model/buildings-channel-condition-model.cc ns-3.39/src/buildings/model/buildings-channel-condition-model.cc
--- ns-3.38/src/buildings/model/buildings-channel-condition-model.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/buildings/model/buildings-channel-condition-model.cc	2023-07-05 14:23:47.713427000 -0700
@@ -99,6 +99,35 @@
         {
             NS_LOG_DEBUG("a and b are indoor in different buildings");
             cond->SetLosCondition(ChannelCondition::LosConditionValue::NLOS);
+
+            ChannelCondition::O2iLowHighConditionValue lowHighLossConditionA1;
+            ChannelCondition::O2iLowHighConditionValue lowHighLossConditionB1;
+
+            // Low losses considered for Wood or ConcreteWithWindows, while
+            // high losses for ConcreteWithoutWindows and StoneBlocks
+            lowHighLossConditionA1 =
+                a1->GetBuilding()->GetExtWallsType() == Building::ExtWallsType_t::Wood ||
+                        a1->GetBuilding()->GetExtWallsType() ==
+                            Building::ExtWallsType_t::ConcreteWithWindows
+                    ? ChannelCondition::O2iLowHighConditionValue::LOW
+                    : ChannelCondition::O2iLowHighConditionValue::HIGH;
+
+            lowHighLossConditionB1 =
+                b1->GetBuilding()->GetExtWallsType() == Building::ExtWallsType_t::Wood ||
+                        b1->GetBuilding()->GetExtWallsType() ==
+                            Building::ExtWallsType_t::ConcreteWithWindows
+                    ? ChannelCondition::O2iLowHighConditionValue::LOW
+                    : ChannelCondition::O2iLowHighConditionValue::HIGH;
+
+            if (lowHighLossConditionA1 == ChannelCondition::O2iLowHighConditionValue::HIGH ||
+                lowHighLossConditionB1 == ChannelCondition::O2iLowHighConditionValue::HIGH)
+            {
+                cond->SetO2iLowHighCondition(ChannelCondition::O2iLowHighConditionValue::HIGH);
+            }
+            else
+            {
+                cond->SetO2iLowHighCondition(ChannelCondition::O2iLowHighConditionValue::LOW);
+            }
         }
     }
     else // outdoor to indoor case
@@ -107,6 +136,31 @@
 
         NS_LOG_DEBUG("a is indoor and b outdoor or vice-versa");
         cond->SetLosCondition(ChannelCondition::LosConditionValue::NLOS);
+
+        ChannelCondition::O2iLowHighConditionValue lowHighLossCondition;
+        if (isAIndoor)
+        {
+            // Low losses considered for Wood or ConcreteWithWindows, while
+            // high losses for ConcreteWithoutWindows and StoneBlocks
+            lowHighLossCondition =
+                a1->GetBuilding()->GetExtWallsType() == Building::ExtWallsType_t::Wood ||
+                        a1->GetBuilding()->GetExtWallsType() ==
+                            Building::ExtWallsType_t::ConcreteWithWindows
+                    ? ChannelCondition::O2iLowHighConditionValue::LOW
+                    : ChannelCondition::O2iLowHighConditionValue::HIGH;
+
+            cond->SetO2iLowHighCondition(lowHighLossCondition);
+        }
+        else
+        {
+            lowHighLossCondition =
+                b1->GetBuilding()->GetExtWallsType() == Building::ExtWallsType_t::Wood ||
+                        b1->GetBuilding()->GetExtWallsType() ==
+                            Building::ExtWallsType_t::ConcreteWithWindows
+                    ? ChannelCondition::O2iLowHighConditionValue::LOW
+                    : ChannelCondition::O2iLowHighConditionValue::HIGH;
+            cond->SetO2iLowHighCondition(lowHighLossCondition);
+        }
     }
 
     return cond;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/test/buildings-channel-condition-model-test.cc ns-3.39/src/buildings/test/buildings-channel-condition-model-test.cc
--- ns-3.38/src/buildings/test/buildings-channel-condition-model-test.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/buildings/test/buildings-channel-condition-model-test.cc	2023-07-05 14:23:47.717427000 -0700
@@ -121,6 +121,7 @@
     building->SetNRoomsY(1);
     building->SetNFloors(1);
     building->SetBoundaries(Box(0.0, 10.0, 0.0, 10.0, 0.0, 5.0));
+    building->SetExtWallsType(Building::ExtWallsType_t::Wood);
 
     BuildingsHelper::Install(nodes);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/test/buildings-penetration-loss-pathloss-test.cc ns-3.39/src/buildings/test/buildings-penetration-loss-pathloss-test.cc
--- ns-3.38/src/buildings/test/buildings-penetration-loss-pathloss-test.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/buildings/test/buildings-penetration-loss-pathloss-test.cc	2023-07-05 14:23:47.717427000 -0700
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2023 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
+ *
+ * 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 "ns3/abort.h"
+#include "ns3/boolean.h"
+#include "ns3/building.h"
+#include "ns3/buildings-channel-condition-model.h"
+#include "ns3/channel-condition-model.h"
+#include "ns3/config.h"
+#include "ns3/constant-position-mobility-model.h"
+#include "ns3/constant-velocity-mobility-model.h"
+#include "ns3/double.h"
+#include "ns3/log.h"
+#include "ns3/mobility-building-info.h"
+#include "ns3/mobility-helper.h"
+#include "ns3/simulator.h"
+#include "ns3/test.h"
+#include "ns3/three-gpp-propagation-loss-model.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("BuildingsPenetrationLossesTest");
+
+/**
+ * \ingroup propagation-tests
+ *
+ * Test case for the 3GPP channel O2I building penetration losses.
+ * It considers pre-determined scenarios and based on the outdoor/indoor
+ * condition (O2O/O2I) checks whether the calculated received power
+ * is aligned with the calculation in 3GPP TR 38.901.
+ *
+ * For O2O condition the calculation is done according to Table 7.4.1-1
+ * and we check if the calculated received power is equal to the expected
+ * value.
+ * For the O2I condition, the calculation is done considering the building
+ * penetration losses based on the material of the building walls.
+ * In this case, we check if the calculated received power is less than the
+ * received value (thus ensure that losses have been included).
+ */
+
+class BuildingsPenetrationLossesTestCase : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     */
+    BuildingsPenetrationLossesTestCase();
+
+    /**
+     * Destructor
+     */
+    ~BuildingsPenetrationLossesTestCase() override;
+
+  private:
+    /**
+     * Builds the simulation scenario and perform the tests
+     */
+    void DoRun() override;
+
+    /**
+     * Struct containing the parameters for each test
+     */
+    struct TestVector
+    {
+        Vector m_positionA; //!< the position of the first node
+        Vector m_positionB; //!< the position of the second node
+        double m_frequency; //!< carrier frequency in Hz
+        bool m_isLos;       //!< LOS probability
+        TypeId m_condModel; //!< the type ID of the channel condition model to be used
+        TypeId m_propModel; //!< the type ID of the propagation loss model to be used
+        double m_pt;        //!< transmitted power in dBm
+        double m_pr;        //!< received power in dBm
+    };
+
+    TestVectors<TestVector> m_testVectors;         //!< array containing all the test vectors
+    Ptr<ThreeGppPropagationLossModel> m_propModel; //!< the propagation loss model
+    double m_tolerance;                            //!< tolerance
+};
+
+BuildingsPenetrationLossesTestCase::BuildingsPenetrationLossesTestCase()
+    : TestCase("Test case for BuildingsPenetrationLosses"),
+      m_testVectors(),
+      m_tolerance(2e-3)
+{
+}
+
+BuildingsPenetrationLossesTestCase::~BuildingsPenetrationLossesTestCase()
+{
+}
+
+void
+BuildingsPenetrationLossesTestCase::DoRun()
+{
+    // create the test vector
+    TestVector testVector;
+
+    // tests for the RMa scenario
+    testVector.m_positionA = Vector(0, 0, 35.0); // O2I case
+    testVector.m_positionB = Vector(10, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppRmaPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -77.3784;
+    m_testVectors.Add(testVector);
+
+    testVector.m_positionA = Vector(0, 0, 35.0); // O2I case
+    testVector.m_positionB = Vector(100, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppRmaPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -87.2965;
+    m_testVectors.Add(testVector);
+
+    testVector.m_positionA = Vector(0, 0, 35.0); // O2O case
+    testVector.m_positionB = Vector(1000, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppRmaPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -108.5577;
+    m_testVectors.Add(testVector);
+
+    // tests for the UMa scenario
+    testVector.m_positionA = Vector(0, 0, 25.0); // O2I case
+    testVector.m_positionB = Vector(10, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppUmaPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -72.9380;
+    m_testVectors.Add(testVector);
+
+    testVector.m_positionA = Vector(0, 0, 25.0); // O2I case
+    testVector.m_positionB = Vector(100, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppUmaPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -86.2362;
+    m_testVectors.Add(testVector);
+
+    testVector.m_positionA = Vector(0, 0, 25.0); // O2O case
+    testVector.m_positionB = Vector(1000, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppUmaPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -109.7252;
+    m_testVectors.Add(testVector);
+
+    // tests for the UMi scenario
+    testVector.m_positionA = Vector(0, 0, 10.0); // O2I case
+    testVector.m_positionB = Vector(10, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppUmiStreetCanyonPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -69.8591;
+    m_testVectors.Add(testVector);
+
+    testVector.m_positionA = Vector(0, 0, 10.0); // O2I case
+    testVector.m_positionB = Vector(100, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppUmiStreetCanyonPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -88.4122;
+    m_testVectors.Add(testVector);
+
+    testVector.m_positionA = Vector(0, 0, 10.0); // O2O case
+    testVector.m_positionB = Vector(1000, 0, 1.5);
+    testVector.m_frequency = 5.0e9;
+    testVector.m_propModel = ThreeGppUmiStreetCanyonPropagationLossModel::GetTypeId();
+    testVector.m_pt = 0.0;
+    testVector.m_pr = -119.3114;
+    m_testVectors.Add(testVector);
+
+    // create the factory for the propagation loss model
+    ObjectFactory propModelFactory;
+
+    Ptr<Building> building = Create<Building>();
+    building->SetExtWallsType(Building::ExtWallsType_t::ConcreteWithWindows);
+    building->SetNRoomsX(1);
+    building->SetNRoomsY(1);
+    building->SetNFloors(2);
+    building->SetBoundaries(Box(0.0, 100.0, 0.0, 10.0, 0.0, 5.0));
+
+    // create the two nodes
+    NodeContainer nodes;
+    nodes.Create(2);
+
+    // create the mobility models
+    Ptr<MobilityModel> a = CreateObject<ConstantPositionMobilityModel>();
+    Ptr<MobilityModel> b = CreateObject<ConstantPositionMobilityModel>();
+
+    // aggregate the nodes and the mobility models
+    nodes.Get(0)->AggregateObject(a);
+    nodes.Get(1)->AggregateObject(b);
+
+    Ptr<MobilityBuildingInfo> buildingInfoA = CreateObject<MobilityBuildingInfo>();
+    Ptr<MobilityBuildingInfo> buildingInfoB = CreateObject<MobilityBuildingInfo>();
+    a->AggregateObject(buildingInfoA); // operation usually done by BuildingsHelper::Install
+    buildingInfoA->MakeConsistent(a);
+
+    b->AggregateObject(buildingInfoB); // operation usually done by BuildingsHelper::Install
+    buildingInfoB->MakeConsistent(b);
+
+    Ptr<ChannelConditionModel> condModel = CreateObject<BuildingsChannelConditionModel>();
+
+    for (std::size_t i = 0; i < m_testVectors.GetN(); i++)
+    {
+        TestVector testVector = m_testVectors.Get(i);
+        a->SetPosition(testVector.m_positionA);
+        b->SetPosition(testVector.m_positionB);
+
+        Ptr<ChannelCondition> cond = condModel->GetChannelCondition(a, b);
+
+        propModelFactory.SetTypeId(testVector.m_propModel);
+        m_propModel = propModelFactory.Create<ThreeGppPropagationLossModel>();
+        m_propModel->SetAttribute("Frequency", DoubleValue(testVector.m_frequency));
+        m_propModel->SetAttribute("ShadowingEnabled", BooleanValue(false));
+        m_propModel->SetChannelConditionModel(condModel);
+
+        bool isAIndoor = buildingInfoA->IsIndoor();
+        bool isBIndoor = buildingInfoB->IsIndoor();
+
+        if (!isAIndoor && !isBIndoor) // a and b are outdoor
+        {
+            cond->SetLosCondition(ChannelCondition::LosConditionValue::LOS);
+            cond->SetO2iCondition(ChannelCondition::O2iConditionValue::O2O);
+
+            // check rcv power to be equal to the calculated value without losses
+            NS_TEST_EXPECT_MSG_EQ_TOL(m_propModel->CalcRxPower(testVector.m_pt, a, b),
+                                      testVector.m_pr,
+                                      m_tolerance,
+                                      "rcv power is not equal expected value");
+        }
+        else
+        {
+            cond->SetLosCondition(ChannelCondition::LosConditionValue::LOS);
+            cond->SetO2iCondition(ChannelCondition::O2iConditionValue::O2I);
+            cond->SetO2iLowHighCondition(ChannelCondition::O2iLowHighConditionValue::LOW);
+
+            // check rcv power to be lower than the calculated without losses
+            NS_TEST_EXPECT_MSG_LT(m_propModel->CalcRxPower(testVector.m_pt, a, b),
+                                  testVector.m_pr,
+                                  "rcv power is not less than calculated value");
+        }
+        m_propModel = nullptr;
+    }
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup propagation-tests
+ *
+ * Test suite for the buildings penetration losses
+ */
+class BuildingsPenetrationLossesTestSuite : public TestSuite
+{
+  public:
+    BuildingsPenetrationLossesTestSuite();
+};
+
+BuildingsPenetrationLossesTestSuite::BuildingsPenetrationLossesTestSuite()
+    : TestSuite("buildings-penetration-losses", UNIT)
+{
+    AddTestCase(new BuildingsPenetrationLossesTestCase, TestCase::QUICK);
+}
+
+/// Static variable for test initialization
+static BuildingsPenetrationLossesTestSuite g_buildingsPenetrationLossesTestSuite;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/buildings/test/examples-to-run.py ns-3.39/src/buildings/test/examples-to-run.py
--- ns-3.38/src/buildings/test/examples-to-run.py	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/buildings/test/examples-to-run.py	2023-07-05 14:23:47.717427000 -0700
@@ -1,6 +1,4 @@
 #! /usr/bin/env python3
-# -*- coding: utf-8 -*-
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/click/examples/nsclick-simple-lan.py ns-3.39/src/click/examples/nsclick-simple-lan.py
--- ns-3.38/src/click/examples/nsclick-simple-lan.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/click/examples/nsclick-simple-lan.py	2023-07-05 14:23:47.721427000 -0700
@@ -0,0 +1,79 @@
+#
+# 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: Lalith Suresh <suresh.lalith@gmail.com>
+# Modified by: Gabriel Ferreira <gabrielcarvfer@gmail.com>
+#
+
+import os.path
+
+from ns import ns
+
+ns.LogComponentEnable("Ipv4ClickRouting", ns.LOG_LEVEL_ALL)
+ns.LogComponentEnable("Ipv4L3ClickProtocol", ns.LOG_LEVEL_ALL)
+
+clickConfigFolder = os.path.dirname(__file__)
+
+csmaNodes = ns.NodeContainer()
+csmaNodes.Create(2)
+
+# Setup CSMA channel between the nodes
+csma = ns.CsmaHelper()
+csma.SetChannelAttribute("DataRate", ns.DataRateValue(ns.DataRate(5000000)))
+csma.SetChannelAttribute("Delay", ns.TimeValue(ns.MilliSeconds(2)))
+csmaDevices = csma.Install(csmaNodes)
+
+# Install normal internet stack on node B
+internet = ns.InternetStackHelper()
+internet.Install(csmaNodes.Get(1))
+
+# Install Click on node A
+clickinternet = ns.ClickInternetStackHelper()
+clickinternet.SetClickFile(csmaNodes.Get(0),
+                           clickConfigFolder + "/nsclick-lan-single-interface.click")
+clickinternet.SetRoutingTableElement(csmaNodes.Get(0), "rt")
+clickinternet.Install(csmaNodes.Get(0))
+
+# Configure IP addresses for the nodes
+ipv4 = ns.Ipv4AddressHelper()
+ipv4.SetBase("172.16.1.0", "255.255.255.0")
+ipv4.Assign(csmaDevices)
+
+# Configure traffic application and sockets
+LocalAddress = ns.InetSocketAddress(ns.Ipv4Address.GetAny(), 50000).ConvertTo()
+packetSinkHelper = ns.PacketSinkHelper("ns3::TcpSocketFactory", LocalAddress)
+recvapp = packetSinkHelper.Install(csmaNodes.Get(1))
+recvapp.Start(ns.Seconds(5.0))
+recvapp.Stop(ns.Seconds(10.0))
+
+onOffHelper = ns.OnOffHelper("ns3::TcpSocketFactory", ns.Address())
+onOffHelper.SetAttribute("OnTime", ns.StringValue("ns3::ConstantRandomVariable[Constant=1]"))
+onOffHelper.SetAttribute("OffTime", ns.StringValue("ns3::ConstantRandomVariable[Constant=0]"))
+
+appcont = ns.ApplicationContainer()
+
+remoteAddress = ns.InetSocketAddress(ns.Ipv4Address("172.16.1.2"), 50000).ConvertTo()
+onOffHelper.SetAttribute("Remote", ns.AddressValue(remoteAddress))
+appcont.Add(onOffHelper.Install(csmaNodes.Get(0)))
+
+appcont.Start(ns.Seconds(5.0))
+appcont.Stop(ns.Seconds(10.0))
+
+# For tracing
+csma.EnablePcap("nsclick-simple-lan", csmaDevices, False)
+
+ns.Simulator.Stop(ns.Seconds(20.0))
+ns.Simulator.Run()
+
+ns.Simulator.Destroy()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/click/helper/click-internet-stack-helper.cc ns-3.39/src/click/helper/click-internet-stack-helper.cc
--- ns-3.38/src/click/helper/click-internet-stack-helper.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/click/helper/click-internet-stack-helper.cc	2023-07-05 14:23:47.721427000 -0700
@@ -70,7 +70,6 @@
 void
 ClickInternetStackHelper::Initialize()
 {
-    SetTcp("ns3::TcpL4Protocol");
 }
 
 ClickInternetStackHelper::~ClickInternetStackHelper()
@@ -80,7 +79,6 @@
 ClickInternetStackHelper::ClickInternetStackHelper(const ClickInternetStackHelper& o)
 {
     m_ipv4Enabled = o.m_ipv4Enabled;
-    m_tcpFactory = o.m_tcpFactory;
 }
 
 ClickInternetStackHelper&
@@ -101,19 +99,6 @@
 }
 
 void
-ClickInternetStackHelper::SetTcp(const std::string tid)
-{
-    m_tcpFactory.SetTypeId(tid);
-}
-
-void
-ClickInternetStackHelper::SetTcp(std::string tid, std::string n0, const AttributeValue& v0)
-{
-    m_tcpFactory.SetTypeId(tid);
-    m_tcpFactory.Set(n0, v0);
-}
-
-void
 ClickInternetStackHelper::SetClickFile(NodeContainer c, std::string clickfile)
 {
     for (NodeContainer::Iterator i = c.Begin(); i != c.End(); ++i)
@@ -199,7 +184,7 @@
         CreateAndAggregateObjectFromTypeId(node, "ns3::Ipv4L3ClickProtocol");
         CreateAndAggregateObjectFromTypeId(node, "ns3::Icmpv4L4Protocol");
         CreateAndAggregateObjectFromTypeId(node, "ns3::UdpL4Protocol");
-        node->AggregateObject(m_tcpFactory.Create<Object>());
+        CreateAndAggregateObjectFromTypeId(node, "ns3::TcpL4Protocol");
         Ptr<PacketSocketFactory> factory = CreateObject<PacketSocketFactory>();
         node->AggregateObject(factory);
         // Set routing
@@ -488,7 +473,7 @@
                 "Drop",
                 MakeBoundCallback(&Ipv4L3ProtocolDropSinkWithoutContext, theStream));
             NS_ASSERT_MSG(result == true,
-                          "ClickInternetStackHelper::EanableAsciiIpv4Internal():  "
+                          "ClickInternetStackHelper::EnableAsciiIpv4Internal():  "
                           "Unable to connect ipv4L3Protocol \"Drop\"");
         }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/click/helper/click-internet-stack-helper.h ns-3.39/src/click/helper/click-internet-stack-helper.h
--- ns-3.38/src/click/helper/click-internet-stack-helper.h	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/click/helper/click-internet-stack-helper.h	2023-07-05 14:23:47.721427000 -0700
@@ -102,34 +102,6 @@
     void InstallAll() const;
 
     /**
-     * \brief set the Tcp stack which will not need any other parameter.
-     *
-     * This function sets up the tcp stack to the given TypeId. It should not be
-     * used for NSC stack setup because the nsc stack needs the Library attribute
-     * to be setup, please use instead the version that requires an attribute
-     * and a value. If you choose to use this function anyways to set nsc stack
-     * the default value for the linux library will be used: "liblinux2.6.26.so".
-     *
-     * \param tid the type id, typically it is set to  "ns3::TcpL4Protocol"
-     */
-    void SetTcp(std::string tid);
-
-    /**
-     * \brief This function is used to setup the Network Simulation Cradle stack with library value.
-     *
-     * Give the NSC stack a shared library file name to use when creating the
-     * stack implementation.  The attr string is actually the attribute name to
-     * be setup and val is its value. The attribute is the stack implementation
-     * to be used and the value is the shared library name.
-     *
-     * \param tid The type id, for the case of nsc it would be "ns3::NscTcpL4Protocol"
-     * \param attr The attribute name that must be setup, for example "Library"
-     * \param val The attribute value, which will be in fact the shared library name
-     * (example:"liblinux2.6.26.so")
-     */
-    void SetTcp(std::string tid, std::string attr, const AttributeValue& val);
-
-    /**
      * \brief Set a Click file to be used for a group of nodes.
      * \param c NodeContainer of nodes
      * \param clickfile Click file to be used
@@ -200,7 +172,6 @@
                                  bool explicitFilename) override;
 
     void Initialize();
-    ObjectFactory m_tcpFactory;
 
     static void CreateAndAggregateObjectFromTypeId(Ptr<Node> node, const std::string typeId);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/click/model/ipv4-click-routing.cc ns-3.39/src/click/model/ipv4-click-routing.cc
--- ns-3.38/src/click/model/ipv4-click-routing.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/click/model/ipv4-click-routing.cc	2023-07-05 14:23:47.721427000 -0700
@@ -227,14 +227,7 @@
 bool
 Ipv4ClickRouting::IsInterfaceReady(int ifid)
 {
-    if (ifid >= 0 && ifid < (int)m_ipv4->GetNInterfaces())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return ifid >= 0 && ifid < static_cast<int>(m_ipv4->GetNInterfaces());
 }
 
 std::string
@@ -285,19 +278,21 @@
     struct timeval curtime;
     uint64_t remainder = 0;
 
-    curtime.tv_sec = Simulator::Now().GetSeconds();
-    curtime.tv_usec = Simulator::Now().GetMicroSeconds() % 1000000;
+    Time now = Simulator::Now();
+
+    curtime.tv_sec = now.GetSeconds();
+    curtime.tv_usec = now.GetMicroSeconds() % 1000000;
 
-    switch (Simulator::Now().GetResolution())
+    switch (Time::GetResolution())
     {
     case Time::NS:
-        remainder = Simulator::Now().GetNanoSeconds() % 1000;
+        remainder = now.GetNanoSeconds() % 1000;
         break;
     case Time::PS:
-        remainder = Simulator::Now().GetPicoSeconds() % 1000000;
+        remainder = now.GetPicoSeconds() % 1000000;
         break;
     case Time::FS:
-        remainder = Simulator::Now().GetFemtoSeconds() % 1000000000;
+        remainder = now.GetFemtoSeconds() % 1000000000;
         break;
     default:
         break;
@@ -563,10 +558,10 @@
 Ipv4ClickRouting::RouteInput(Ptr<const Packet> p,
                              const Ipv4Header& header,
                              Ptr<const NetDevice> idev,
-                             UnicastForwardCallback ucb,
-                             MulticastForwardCallback mcb,
-                             LocalDeliverCallback lcb,
-                             ErrorCallback ecb)
+                             const UnicastForwardCallback& ucb,
+                             const MulticastForwardCallback& mcb,
+                             const LocalDeliverCallback& lcb,
+                             const ErrorCallback& ecb)
 {
     NS_FATAL_ERROR("Click router does not have a RouteInput() interface!");
     return false;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/click/model/ipv4-click-routing.h ns-3.39/src/click/model/ipv4-click-routing.h
--- ns-3.38/src/click/model/ipv4-click-routing.h	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/click/model/ipv4-click-routing.h	2023-07-05 14:23:47.721427000 -0700
@@ -262,10 +262,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void PrintRoutingTable(Ptr<OutputStreamWrapper> stream,
                            Time::Unit unit = Time::S) const override;
     void NotifyInterfaceUp(uint32_t interface) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/click/model/ipv4-l3-click-protocol.cc ns-3.39/src/click/model/ipv4-l3-click-protocol.cc
--- ns-3.38/src/click/model/ipv4-l3-click-protocol.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/click/model/ipv4-l3-click-protocol.cc	2023-07-05 14:23:47.721427000 -0700
@@ -229,11 +229,10 @@
 #ifdef NOTYET
         if (MulticastCheckGroup(iif, address))
 #endif
-            if (true)
-            {
-                NS_LOG_LOGIC("For me (Ipv4Addr multicast address");
-                return true;
-            }
+        {
+            NS_LOG_LOGIC("For me (Ipv4Addr multicast address");
+            return true;
+        }
     }
 
     if (address.IsBroadcast())
@@ -470,7 +469,7 @@
         Ipv4InterfaceAddress test = GetAddress(interfaceIdx, i);
         if (test.GetLocal().CombineMask(test.GetMask()) == dest.CombineMask(test.GetMask()))
         {
-            if (test.IsSecondary() == false)
+            if (!test.IsSecondary())
             {
                 return test.GetLocal();
             }
@@ -677,7 +676,7 @@
     ipHeader.SetProtocol(protocol);
     ipHeader.SetPayloadSize(payloadSize);
     ipHeader.SetTtl(ttl);
-    if (mayFragment == true)
+    if (mayFragment)
     {
         ipHeader.SetMayFragment();
         ipHeader.SetIdentification(m_identification);
@@ -786,7 +785,7 @@
     NS_LOG_LOGIC("Packet from " << from << " received on node " << m_node->GetId());
 
     // Forward packet to raw sockets, if any
-    if (protocol == Ipv4L3ClickProtocol::PROT_NUMBER && m_sockets.size() > 0)
+    if (protocol == Ipv4L3ClickProtocol::PROT_NUMBER && !m_sockets.empty())
     {
         Ptr<Packet> packetForRawSocket = p->Copy();
         int32_t interface = GetInterfaceForDevice(device);
@@ -844,7 +843,7 @@
         // we need to make a copy in the unlikely event we hit the
         // RX_ENDPOINT_UNREACH codepath
         Ptr<Packet> copy = p->Copy();
-        enum IpL4Protocol::RxStatus status = protocol->Receive(p, ip, GetInterface(iif));
+        IpL4Protocol::RxStatus status = protocol->Receive(p, ip, GetInterface(iif));
         switch (status)
         {
         case IpL4Protocol::RX_OK:
@@ -854,8 +853,7 @@
         case IpL4Protocol::RX_CSUM_FAILED:
             break;
         case IpL4Protocol::RX_ENDPOINT_UNREACH:
-            if (ip.GetDestination().IsBroadcast() == true ||
-                ip.GetDestination().IsMulticast() == true)
+            if (ip.GetDestination().IsBroadcast() || ip.GetDestination().IsMulticast())
             {
                 break; // Do not reply to broadcast or multicast
             }
@@ -871,7 +869,7 @@
                     subnetDirected = true;
                 }
             }
-            if (subnetDirected == false)
+            if (!subnetDirected)
             {
                 GetIcmp()->SendDestUnreachPort(ip, copy);
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/click/test/examples-to-run.py ns-3.39/src/click/test/examples-to-run.py
--- ns-3.38/src/click/test/examples-to-run.py	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/click/test/examples-to-run.py	2023-07-05 14:23:47.737427000 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
@@ -22,4 +21,6 @@
 #     (example_name, do_run).
 #
 # See test.py for more information.
-python_examples = []
+python_examples = [
+    ("nsclick-simple-lan.py", "NSCLICK == True"),
+]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/config-store/CMakeLists.txt ns-3.39/src/config-store/CMakeLists.txt
--- ns-3.38/src/config-store/CMakeLists.txt	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/config-store/CMakeLists.txt	2023-07-05 14:23:47.737427000 -0700
@@ -12,6 +12,13 @@
   set(gtk_libraries
       ${GTK3_LIBRARIES}
   )
+  if(${NS3_PIP_PACKAGING})
+    # In case we are packaging ns-3, leave gtk symbols undefined and pray for
+    # the linker to find the correct libraries locally
+    set(gtk_libraries
+        -Wl,--allow-shlib-undefined
+    )
+  endif()
   if(${GCC})
     add_definitions(-Wno-parentheses)
   endif()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/config-store/model/attribute-default-iterator.cc ns-3.39/src/config-store/model/attribute-default-iterator.cc
--- ns-3.38/src/config-store/model/attribute-default-iterator.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/config-store/model/attribute-default-iterator.cc	2023-07-05 14:23:47.741427200 -0700
@@ -45,7 +45,7 @@
         bool calledStart = false;
         for (uint32_t j = 0; j < tid.GetAttributeN(); j++)
         {
-            struct TypeId::AttributeInformation info = tid.GetAttribute(j);
+            TypeId::AttributeInformation info = tid.GetAttribute(j);
             if (!(info.flags & TypeId::ATTR_CONSTRUCT))
             {
                 // we can't construct the attribute, so, there is no
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/config-store/model/attribute-iterator.cc ns-3.39/src/config-store/model/attribute-iterator.cc
--- ns-3.38/src/config-store/model/attribute-iterator.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/config-store/model/attribute-iterator.cc	2023-07-05 14:23:47.741427200 -0700
@@ -226,7 +226,7 @@
         NS_LOG_DEBUG("store " << tid.GetName());
         for (uint32_t i = 0; i < tid.GetAttributeN(); ++i)
         {
-            struct TypeId::AttributeInformation info = tid.GetAttribute(i);
+            TypeId::AttributeInformation info = tid.GetAttribute(i);
             const PointerChecker* ptrChecker =
                 dynamic_cast<const PointerChecker*>(PeekPointer(info.checker));
             if (ptrChecker != nullptr)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/config-store/model/display-functions.cc ns-3.39/src/config-store/model/display-functions.cc
--- ns-3.38/src/config-store/model/display-functions.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/config-store/model/display-functions.cc	2023-07-05 14:23:47.741427200 -0700
@@ -240,7 +240,7 @@
         }
         else
         {
-            struct TypeId::AttributeInformation info = tid.GetAttribute(attrIndex);
+            TypeId::AttributeInformation info = tid.GetAttribute(attrIndex);
             Ptr<const AttributeChecker> checker = info.checker;
             std::string tip;
             tip = "This attribute is of type " + checker->GetValueTypeName();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/config-store/model/raw-text-config.cc ns-3.39/src/config-store/model/raw-text-config.cc
--- ns-3.38/src/config-store/model/raw-text-config.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/config-store/model/raw-text-config.cc	2023-07-05 14:23:47.741427200 -0700
@@ -93,7 +93,7 @@
             ns3::TypeId::SupportLevel supportLevel = TypeId::SupportLevel::SUPPORTED;
             for (std::size_t i = 0; i < tid.GetAttributeN(); i++)
             {
-                struct TypeId::AttributeInformation tmp = tid.GetAttribute(i);
+                TypeId::AttributeInformation tmp = tid.GetAttribute(i);
                 if (tmp.name == name)
                 {
                     supportLevel = tmp.supportLevel;
@@ -105,8 +105,7 @@
                 NS_LOG_WARN("Global attribute " << m_typeId << "::" << name
                                                 << " was not saved because it is OBSOLETE");
             }
-            else if ((supportLevel == TypeId::SupportLevel::DEPRECATED) &&
-                     (m_saveDeprecated == false))
+            else if (supportLevel == TypeId::SupportLevel::DEPRECATED && !m_saveDeprecated)
             {
                 NS_LOG_WARN("Global attribute " << m_typeId << "::" << name
                                                 << " was not saved because it is DEPRECATED");
@@ -169,7 +168,7 @@
 
             for (std::size_t i = 0; i < tid.GetAttributeN(); i++)
             {
-                struct TypeId::AttributeInformation tmp = tid.GetAttribute(i);
+                TypeId::AttributeInformation tmp = tid.GetAttribute(i);
                 if (tmp.name == name)
                 {
                     supportLevel = tmp.supportLevel;
@@ -181,8 +180,7 @@
                 NS_LOG_WARN("Attribute " << GetCurrentPath()
                                          << " was not saved because it is OBSOLETE");
             }
-            else if ((supportLevel == TypeId::SupportLevel::DEPRECATED) &&
-                     (m_saveDeprecated == false))
+            else if (supportLevel == TypeId::SupportLevel::DEPRECATED && !m_saveDeprecated)
             {
                 NS_LOG_WARN("Attribute " << GetCurrentPath()
                                          << " was not saved because it is DEPRECATED");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/config-store/model/xml-config.cc ns-3.39/src/config-store/model/xml-config.cc
--- ns-3.38/src/config-store/model/xml-config.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/config-store/model/xml-config.cc	2023-07-05 14:23:47.741427200 -0700
@@ -132,7 +132,7 @@
             ns3::TypeId::SupportLevel supportLevel = TypeId::SupportLevel::SUPPORTED;
             for (std::size_t i = 0; i < tid.GetAttributeN(); i++)
             {
-                struct TypeId::AttributeInformation tmp = tid.GetAttribute(i);
+                TypeId::AttributeInformation tmp = tid.GetAttribute(i);
                 if (tmp.name == name)
                 {
                     supportLevel = tmp.supportLevel;
@@ -145,8 +145,7 @@
                                                 << " was not saved because it is OBSOLETE");
                 return;
             }
-            else if ((supportLevel == TypeId::SupportLevel::DEPRECATED) &&
-                     (m_saveDeprecated == false))
+            else if (supportLevel == TypeId::SupportLevel::DEPRECATED && !m_saveDeprecated)
             {
                 NS_LOG_WARN("Global attribute " << m_typeid << "::" << name
                                                 << " was not saved because it is DEPRECATED");
@@ -212,7 +211,7 @@
             ns3::TypeId::SupportLevel supportLevel = TypeId::SupportLevel::SUPPORTED;
             for (std::size_t i = 0; i < tid.GetAttributeN(); i++)
             {
-                struct TypeId::AttributeInformation tmp = tid.GetAttribute(i);
+                TypeId::AttributeInformation tmp = tid.GetAttribute(i);
                 if (tmp.name == name)
                 {
                     supportLevel = tmp.supportLevel;
@@ -225,8 +224,7 @@
                                          << " was not saved because it is OBSOLETE");
                 return;
             }
-            else if ((supportLevel == TypeId::SupportLevel::DEPRECATED) &&
-                     (m_saveDeprecated == false))
+            else if (supportLevel == TypeId::SupportLevel::DEPRECATED && !m_saveDeprecated)
             {
                 NS_LOG_WARN("Attribute " << GetCurrentPath()
                                          << " was not saved because it is DEPRECATED");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/config-store/test/examples-to-run.py ns-3.39/src/config-store/test/examples-to-run.py
--- ns-3.38/src/config-store/test/examples-to-run.py	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/config-store/test/examples-to-run.py	2023-07-05 14:23:47.741427200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/examples/command-line-example.cc ns-3.39/src/core/examples/command-line-example.cc
--- ns-3.38/src/core/examples/command-line-example.cc	2023-03-16 20:54:23.584868400 -0700
+++ ns-3.39/src/core/examples/command-line-example.cc	2023-07-05 14:23:47.741427200 -0700
@@ -101,7 +101,7 @@
     const TypeId tid = TypeId::LookupByName(attrClass);
     std::string attrDef;
     {
-        struct TypeId::AttributeInformation info;
+        TypeId::AttributeInformation info;
         tid.LookupAttributeByName(attrName, &info);
         attrDef = info.originalInitialValue->SerializeToString(info.checker);
     }
@@ -144,7 +144,7 @@
     // Look up new default value for attribute
     std::string antiArg;
     {
-        struct TypeId::AttributeInformation info;
+        TypeId::AttributeInformation info;
         tid.LookupAttributeByName(attrName, &info);
         antiArg = info.initialValue->SerializeToString(info.checker);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/examples/log-example.cc ns-3.39/src/core/examples/log-example.cc
--- ns-3.38/src/core/examples/log-example.cc	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/examples/log-example.cc	2023-07-05 14:23:47.745427100 -0700
@@ -51,9 +51,9 @@
 
     NS_LOG_ERROR("FreeEvent: error msg");
     NS_LOG_WARN("FreeEvent: warning msg");
-    NS_LOG_DEBUG("FreeEvent: debug msg");
     NS_LOG_INFO("FreeEvent: info msg");
     NS_LOG_LOGIC("FreeEvent: logic msg");
+    NS_LOG_DEBUG("FreeEvent: debug msg");
 }
 
 /** Simple object to aggregate to a node.
@@ -93,9 +93,9 @@
 
         NS_LOG_ERROR("MyEventObject:Event: error msg");
         NS_LOG_WARN("MyEventObject:Event: warning msg");
-        NS_LOG_DEBUG("MyEventObject:Event: debug msg");
         NS_LOG_INFO("MyEventObject:Event: info msg");
         NS_LOG_LOGIC("MyEventObject:Event: logic msg");
+        NS_LOG_DEBUG("MyEventObject:Event: debug msg");
     }
 
 }; // MyEventObject
@@ -110,26 +110,26 @@
     CommandLine cmd;
     cmd.Parse(argc, argv);
 
-    NS_LOG_UNCOND("Creating a Node");
+    NS_LOG_DEBUG("Creating a Node");
     auto node = CreateObject<Node>();
 
-    NS_LOG_UNCOND("Creating MyEventObject");
+    NS_LOG_DEBUG("Creating MyEventObject");
     auto myObj = CreateObject<MyEventObject>();
 
-    NS_LOG_UNCOND("Aggregating MyEventObject to Node");
+    NS_LOG_DEBUG("Aggregating MyEventObject to Node");
     node->AggregateObject(myObj);
 
-    NS_LOG_UNCOND("Scheduling the MyEventObject::Event with node context");
+    NS_LOG_INFO("Scheduling the MyEventObject::Event with node context");
     Simulator::ScheduleWithContext(node->GetId(), Seconds(3), &MyEventObject::Event, &(*myObj));
 
-    NS_LOG_UNCOND("Scheduling FreeEvent");
+    NS_LOG_INFO("Scheduling FreeEvent");
     Simulator::Schedule(Seconds(5), FreeEvent);
 
-    NS_LOG_UNCOND("Starting run...");
+    NS_LOG_DEBUG("Starting run...");
     Simulator::Run();
-    NS_LOG_UNCOND("... run complete");
+    NS_LOG_DEBUG("... run complete");
     Simulator::Destroy();
-    NS_LOG_UNCOND("Goodbye");
+    NS_LOG_DEBUG("Goodbye");
 
     return 0;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/examples/sample-log-time-format.cc ns-3.39/src/core/examples/sample-log-time-format.cc
--- ns-3.38/src/core/examples/sample-log-time-format.cc	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/examples/sample-log-time-format.cc	2023-07-05 14:23:47.745427100 -0700
@@ -89,6 +89,8 @@
 #include "ns3/random-variable-stream.h"
 #include "ns3/simulator.h"
 
+#include <map>
+
 using namespace ns3;
 
 namespace
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/helper/csv-reader.cc ns-3.39/src/core/helper/csv-reader.cc
--- ns-3.38/src/core/helper/csv-reader.cc	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/helper/csv-reader.cc	2023-07-05 14:23:47.745427100 -0700
@@ -180,7 +180,7 @@
 
     std::istringstream tempStream(input);
 
-    std::int16_t tempOutput = 0;
+    int16_t tempOutput = 0;
     tempStream >> tempOutput;
 
     if (tempOutput >= std::numeric_limits<byte_type>::min() &&
@@ -247,7 +247,7 @@
 
     std::istringstream tempStream(input);
 
-    std::uint16_t tempOutput = 0;
+    uint16_t tempOutput = 0;
     tempStream >> tempOutput;
 
     if (tempOutput >= std::numeric_limits<byte_type>::min() &&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/helper/random-variable-stream-helper.h ns-3.39/src/core/helper/random-variable-stream-helper.h
--- ns-3.38/src/core/helper/random-variable-stream-helper.h	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/helper/random-variable-stream-helper.h	2023-07-05 14:23:47.745427100 -0700
@@ -18,6 +18,7 @@
 #ifndef RANDOM_VARIABLE_STREAM_HELPER_H
 #define RANDOM_VARIABLE_STREAM_HELPER_H
 
+#include <cstdint>
 #include <string>
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/attribute-accessor-helper.h ns-3.39/src/core/model/attribute-accessor-helper.h
--- ns-3.38/src/core/model/attribute-accessor-helper.h	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/attribute-accessor-helper.h	2023-07-05 14:23:47.749427000 -0700
@@ -247,7 +247,7 @@
 inline Ptr<const AttributeAccessor>
 DoMakeAccessorHelperOne(U T::*memberVariable)
 {
-    /* AttributeAcessor implementation for a class member variable. */
+    /* AttributeAccessor implementation for a class member variable. */
     class MemberVariable : public AccessorHelper<T, V>
     {
       public:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/attribute-construction-list.cc ns-3.39/src/core/model/attribute-construction-list.cc
--- ns-3.38/src/core/model/attribute-construction-list.cc	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/attribute-construction-list.cc	2023-07-05 14:23:47.749427000 -0700
@@ -45,7 +45,7 @@
     // vector of values.
     NS_LOG_FUNCTION(this << name << checker << value);
 
-    for (std::list<struct Item>::iterator k = m_list.begin(); k != m_list.end(); k++)
+    for (std::list<Item>::iterator k = m_list.begin(); k != m_list.end(); k++)
     {
         if (k->checker == checker)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/attribute-construction-list.h ns-3.39/src/core/model/attribute-construction-list.h
--- ns-3.38/src/core/model/attribute-construction-list.h	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/attribute-construction-list.h	2023-07-05 14:23:47.749427000 -0700
@@ -50,8 +50,9 @@
         /** The name of the Attribute. */
         std::string name;
     };
+
     /** Iterator type. */
-    typedef std::list<struct Item>::const_iterator CIterator;
+    typedef std::list<Item>::const_iterator CIterator;
 
     /** Constructor */
     AttributeConstructionList();
@@ -81,7 +82,7 @@
 
   private:
     /** The list of Items */
-    std::list<struct Item> m_list;
+    std::list<Item> m_list;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/attribute-container.h ns-3.39/src/core/model/attribute-container.h
--- ns-3.38/src/core/model/attribute-container.h	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/attribute-container.h	2023-07-05 14:23:47.749427000 -0700
@@ -45,9 +45,10 @@
  * can return the items in a container specified by \p C.
  *
  * @tparam A AttributeValue type to be contained.
+ * @tparam Sep Character separator between elements for parsing.
  * @tparam C Possibly templated container class returned by Get.
  */
-template <class A, template <class...> class C = std::list>
+template <class A, char Sep = ',', template <class...> class C = std::list>
 class AttributeContainerValue : public AttributeValue
 {
   public:
@@ -74,9 +75,8 @@
 
     /**
      * Default constructor.
-     * \param[in] sep Character separator between elements for parsing.
      */
-    AttributeContainerValue(char sep = ',');
+    AttributeContainerValue();
 
     /**
      * Construct from another container.
@@ -186,9 +186,8 @@
      * \return This object with items copied.
      */
     template <class ITER>
-    Ptr<AttributeContainerValue<A, C>> CopyFrom(const ITER begin, const ITER end);
+    Ptr<AttributeContainerValue<A, Sep, C>> CopyFrom(const ITER begin, const ITER end);
 
-    char m_sep;                 //!< Item separator
     container_type m_container; //!< Internal container
 };
 
@@ -210,35 +209,40 @@
 /**
  * Make AttributeContainerChecker from AttributeContainerValue.
  * @tparam A \deduced AttributeValue type in container.
+ * @tparam Sep \deduced Character separator between elements for parsing.
  * @tparam C \deduced Container type returned by Get.
  * \param[in] value AttributeContainerValue from which to deduce types.
  * \return AttributeContainerChecker for value.
  */
-template <class A, template <class...> class C>
-Ptr<AttributeChecker> MakeAttributeContainerChecker(const AttributeContainerValue<A, C>& value);
+template <class A, char Sep, template <class...> class C>
+Ptr<AttributeChecker> MakeAttributeContainerChecker(
+    const AttributeContainerValue<A, Sep, C>& value);
 
 /**
  * Make AttributeContainerChecker using explicit types, initialize item checker.
  * @tparam A AttributeValue type in container.
+ * @tparam Sep Character separator between elements for parsing.
  * @tparam C Container type returned by Get.
  * \param[in] itemchecker AttributeChecker used for each item in the container.
  * \return AttributeContainerChecker.
  */
-template <class A, template <class...> class C = std::list>
+template <class A, char Sep = ',', template <class...> class C = std::list>
 Ptr<const AttributeChecker> MakeAttributeContainerChecker(Ptr<const AttributeChecker> itemchecker);
 
 /**
  * Make uninitialized AttributeContainerChecker using explicit types.
  * @tparam A AttributeValue type in container.
+ * @tparam Sep Character separator between elements for parsing.
  * @tparam C Container type returned by Get.
  * \return AttributeContainerChecker.
  */
-template <class A, template <class...> class C = std::list>
+template <class A, char Sep = ',', template <class...> class C = std::list>
 Ptr<AttributeChecker> MakeAttributeContainerChecker();
 
 /**
  * Make AttributeContainerAccessor  using explicit types.
  * @tparam A AttributeValue type in container.
+ * @tparam Sep Character separator between elements for parsing.
  * @tparam C Container type returned by Get.
  * \tparam T1 \deduced The type of the class data member,
  *            or the type of the class get functor or set method.
@@ -246,12 +250,13 @@
  *            or the get or set method.
  * \return AttributeContainerAccessor.
  */
-template <typename A, template <typename...> class C = std::list, typename T1>
+template <typename A, char Sep = ',', template <typename...> class C = std::list, typename T1>
 Ptr<const AttributeAccessor> MakeAttributeContainerAccessor(T1 a1);
 
 /**
  * Make AttributeContainerAccessor  using explicit types.
  * @tparam A AttributeValue type in container.
+ * @tparam Sep Character separator between elements for parsing.
  * @tparam C Container type returned by Get.
  * \tparam T1 \deduced The type of the class data member,
  *            or the type of the class get functor or set method.
@@ -262,7 +267,11 @@
  *            or the get or set method.
  * \return AttributeContainerAccessor.
  */
-template <typename A, template <typename...> class C = std::list, typename T1, typename T2>
+template <typename A,
+          char Sep = ',',
+          template <typename...> class C = std::list,
+          typename T1,
+          typename T2>
 Ptr<const AttributeAccessor> MakeAttributeContainerAccessor(T1 a1, T2 a2);
 
 } // namespace ns3
@@ -283,7 +292,7 @@
  * in MakeAttributeContainerChecker. The non-templated base ns3::AttributeContainerChecker
  * is returned from that function. This is the same pattern as ObjectPtrContainer.
  */
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 class AttributeContainerChecker : public ns3::AttributeContainerChecker
 {
   public:
@@ -300,58 +309,59 @@
     Ptr<const AttributeChecker> m_itemchecker; //!< The AttributeChecker
 };
 
-template <class A, template <class...> class C>
-AttributeContainerChecker<A, C>::AttributeContainerChecker()
+template <class A, char Sep, template <class...> class C>
+AttributeContainerChecker<A, Sep, C>::AttributeContainerChecker()
     : m_itemchecker(nullptr)
 {
 }
 
-template <class A, template <class...> class C>
-AttributeContainerChecker<A, C>::AttributeContainerChecker(Ptr<const AttributeChecker> itemchecker)
+template <class A, char Sep, template <class...> class C>
+AttributeContainerChecker<A, Sep, C>::AttributeContainerChecker(
+    Ptr<const AttributeChecker> itemchecker)
     : m_itemchecker(itemchecker)
 {
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 void
-AttributeContainerChecker<A, C>::SetItemChecker(Ptr<const AttributeChecker> itemchecker)
+AttributeContainerChecker<A, Sep, C>::SetItemChecker(Ptr<const AttributeChecker> itemchecker)
 {
     m_itemchecker = itemchecker;
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 Ptr<const AttributeChecker>
-AttributeContainerChecker<A, C>::GetItemChecker() const
+AttributeContainerChecker<A, Sep, C>::GetItemChecker() const
 {
     return m_itemchecker;
 }
 
 } // namespace internal
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 Ptr<AttributeChecker>
-MakeAttributeContainerChecker(const AttributeContainerValue<A, C>& value)
+MakeAttributeContainerChecker(const AttributeContainerValue<A, Sep, C>& value)
 {
-    return MakeAttributeContainerChecker<A, C>();
+    return MakeAttributeContainerChecker<A, Sep, C>();
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 Ptr<const AttributeChecker>
 MakeAttributeContainerChecker(Ptr<const AttributeChecker> itemchecker)
 {
-    auto checker = MakeAttributeContainerChecker<A, C>();
+    auto checker = MakeAttributeContainerChecker<A, Sep, C>();
     auto acchecker = DynamicCast<AttributeContainerChecker>(checker);
     acchecker->SetItemChecker(itemchecker);
     return checker;
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 Ptr<AttributeChecker>
 MakeAttributeContainerChecker()
 {
     std::string containerType;
     std::string underlyingType;
-    typedef AttributeContainerValue<A, C> T;
+    typedef AttributeContainerValue<A, Sep, C> T;
     {
         std::ostringstream oss;
         oss << "ns3::AttributeContainerValue<" << typeid(typename T::attribute_type).name() << ", "
@@ -365,51 +375,50 @@
         underlyingType = oss.str();
     }
 
-    return MakeSimpleAttributeChecker<T, internal::AttributeContainerChecker<A, C>>(containerType,
-                                                                                    underlyingType);
+    return MakeSimpleAttributeChecker<T, internal::AttributeContainerChecker<A, Sep, C>>(
+        containerType,
+        underlyingType);
 }
 
-template <class A, template <class...> class C>
-AttributeContainerValue<A, C>::AttributeContainerValue(char sep)
-    : m_sep(sep)
+template <class A, char Sep, template <class...> class C>
+AttributeContainerValue<A, Sep, C>::AttributeContainerValue()
 {
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 template <class CONTAINER>
-AttributeContainerValue<A, C>::AttributeContainerValue(const CONTAINER& c)
-    : AttributeContainerValue<A, C>(c.begin(), c.end())
+AttributeContainerValue<A, Sep, C>::AttributeContainerValue(const CONTAINER& c)
+    : AttributeContainerValue<A, Sep, C>(c.begin(), c.end())
 {
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 template <class ITER>
-AttributeContainerValue<A, C>::AttributeContainerValue(const ITER begin, const ITER end)
+AttributeContainerValue<A, Sep, C>::AttributeContainerValue(const ITER begin, const ITER end)
     : AttributeContainerValue()
 {
     CopyFrom(begin, end);
 }
 
-template <class A, template <class...> class C>
-AttributeContainerValue<A, C>::~AttributeContainerValue()
+template <class A, char Sep, template <class...> class C>
+AttributeContainerValue<A, Sep, C>::~AttributeContainerValue()
 {
     m_container.clear();
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 Ptr<AttributeValue>
-AttributeContainerValue<A, C>::Copy() const
+AttributeContainerValue<A, Sep, C>::Copy() const
 {
-    auto c = Create<AttributeContainerValue<A, C>>();
-    c->m_sep = m_sep;
+    auto c = Create<AttributeContainerValue<A, Sep, C>>();
     c->m_container = m_container;
     return c;
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 bool
-AttributeContainerValue<A, C>::DeserializeFromString(std::string value,
-                                                     Ptr<const AttributeChecker> checker)
+AttributeContainerValue<A, Sep, C>::DeserializeFromString(std::string value,
+                                                          Ptr<const AttributeChecker> checker)
 {
     auto acchecker = DynamicCast<const AttributeContainerChecker>(checker);
     if (!acchecker)
@@ -418,7 +427,7 @@
     }
 
     std::istringstream iss(value); // copies value
-    while (std::getline(iss, value, m_sep))
+    while (std::getline(iss, value, Sep))
     {
         auto avalue = acchecker->GetItemChecker()->CreateValidValue(StringValue(value));
         if (!avalue)
@@ -438,9 +447,9 @@
     return true;
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 std::string
-AttributeContainerValue<A, C>::SerializeToString(Ptr<const AttributeChecker> checker) const
+AttributeContainerValue<A, Sep, C>::SerializeToString(Ptr<const AttributeChecker> checker) const
 {
     std::ostringstream oss;
     bool first = true;
@@ -448,7 +457,7 @@
     {
         if (!first)
         {
-            oss << m_sep;
+            oss << Sep;
         }
         oss << attr->SerializeToString(checker);
         first = false;
@@ -456,9 +465,9 @@
     return oss.str();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::result_type
-AttributeContainerValue<A, C>::Get() const
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::result_type
+AttributeContainerValue<A, Sep, C>::Get() const
 {
     result_type c;
     for (const value_type& a : *this)
@@ -468,10 +477,10 @@
     return c;
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 template <typename T>
 bool
-AttributeContainerValue<A, C>::GetAccessor(T& value) const
+AttributeContainerValue<A, Sep, C>::GetAccessor(T& value) const
 {
     result_type src = Get();
     value.clear();
@@ -479,75 +488,75 @@
     return true;
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 template <class T>
 void
-AttributeContainerValue<A, C>::Set(const T& c)
+AttributeContainerValue<A, Sep, C>::Set(const T& c)
 {
     m_container.clear();
     CopyFrom(c.begin(), c.end());
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::size_type
-AttributeContainerValue<A, C>::GetN() const
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::size_type
+AttributeContainerValue<A, Sep, C>::GetN() const
 {
     return size();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::Iterator
-AttributeContainerValue<A, C>::Begin()
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::Iterator
+AttributeContainerValue<A, Sep, C>::Begin()
 {
     return begin();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::Iterator
-AttributeContainerValue<A, C>::End()
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::Iterator
+AttributeContainerValue<A, Sep, C>::End()
 {
     return end();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::size_type
-AttributeContainerValue<A, C>::size() const
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::size_type
+AttributeContainerValue<A, Sep, C>::size() const
 {
     return m_container.size();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::iterator
-AttributeContainerValue<A, C>::begin()
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::iterator
+AttributeContainerValue<A, Sep, C>::begin()
 {
     return m_container.begin();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::iterator
-AttributeContainerValue<A, C>::end()
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::iterator
+AttributeContainerValue<A, Sep, C>::end()
 {
     return m_container.end();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::const_iterator
-AttributeContainerValue<A, C>::begin() const
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::const_iterator
+AttributeContainerValue<A, Sep, C>::begin() const
 {
     return m_container.cbegin();
 }
 
-template <class A, template <class...> class C>
-typename AttributeContainerValue<A, C>::const_iterator
-AttributeContainerValue<A, C>::end() const
+template <class A, char Sep, template <class...> class C>
+typename AttributeContainerValue<A, Sep, C>::const_iterator
+AttributeContainerValue<A, Sep, C>::end() const
 {
     return m_container.cend();
 }
 
-template <class A, template <class...> class C>
+template <class A, char Sep, template <class...> class C>
 template <class ITER>
-Ptr<AttributeContainerValue<A, C>>
-AttributeContainerValue<A, C>::CopyFrom(const ITER begin, const ITER end)
+Ptr<AttributeContainerValue<A, Sep, C>>
+AttributeContainerValue<A, Sep, C>::CopyFrom(const ITER begin, const ITER end)
 {
     for (ITER iter = begin; iter != end; ++iter)
     {
@@ -556,18 +565,18 @@
     return this;
 }
 
-template <typename A, template <typename...> class C, typename T1>
+template <typename A, char Sep, template <typename...> class C, typename T1>
 Ptr<const AttributeAccessor>
 MakeAttributeContainerAccessor(T1 a1)
 {
-    return MakeAccessorHelper<AttributeContainerValue<A, C>>(a1);
+    return MakeAccessorHelper<AttributeContainerValue<A, Sep, C>>(a1);
 }
 
-template <typename A, template <typename...> class C, typename T1, typename T2>
+template <typename A, char Sep, template <typename...> class C, typename T1, typename T2>
 Ptr<const AttributeAccessor>
 MakeAttributeContainerAccessor(T1 a1, T2 a2)
 {
-    return MakeAccessorHelper<AttributeContainerValue<A, C>>(a1, a2);
+    return MakeAccessorHelper<AttributeContainerValue<A, Sep, C>>(a1, a2);
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/callback.h ns-3.39/src/core/model/callback.h
--- ns-3.38/src/core/model/callback.h	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/callback.h	2023-07-05 14:23:47.749427000 -0700
@@ -184,12 +184,7 @@
         auto p = std::dynamic_pointer_cast<const CallbackComponent<T>>(other);
 
         // other must have the same type and value as ours
-        if (p == nullptr || p->m_comp != m_comp)
-        {
-            return false;
-        }
-
-        return true;
+        return !(p == nullptr || p->m_comp != m_comp);
     }
 
   private:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/command-line.cc ns-3.39/src/core/model/command-line.cc
--- ns-3.38/src/core/model/command-line.cc	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/command-line.cc	2023-07-05 14:23:47.749427000 -0700
@@ -572,7 +572,7 @@
     {
         std::stringstream ss;
         ss << "    --" << tid.GetAttributeFullName(i) << "=[";
-        struct TypeId::AttributeInformation info = tid.GetAttribute(i);
+        TypeId::AttributeInformation info = tid.GetAttribute(i);
         ss << info.initialValue->SerializeToString(info.checker) << "]\n"
            << "        " << info.help << "\n";
         attributes.push_back(ss.str());
@@ -790,7 +790,7 @@
     }
 
     const std::string attrName = attributePath.substr(colon + 2);
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     if (!tid.LookupAttributeByName(attrName, &info))
     {
         NS_FATAL_ERROR("Attribute not found: " << attributePath);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/config.cc ns-3.39/src/core/model/config.cc
--- ns-3.38/src/core/model/config.cc	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/config.cc	2023-07-05 14:23:47.753427000 -0700
@@ -542,7 +542,7 @@
 
             for (uint32_t i = 0; i < tid.GetAttributeN(); i++)
             {
-                struct TypeId::AttributeInformation info;
+                TypeId::AttributeInformation info;
                 info = tid.GetAttribute(i);
                 if (info.name != item && item != "*")
                 {
@@ -638,7 +638,7 @@
 {
   public:
     // Keep Set and SetFailSafe since their errors are triggered
-    // by the underlying ObjecBase functions.
+    // by the underlying ObjectBase functions.
     /** \copydoc ns3::Config::Set() */
     void Set(std::string path, const AttributeValue& value);
     /** \copydoc ns3::Config::SetFailSafe() */
@@ -862,7 +862,7 @@
         TypeId tid = TypeId::GetRegistered(i);
         for (uint32_t j = 0; j < tid.GetAttributeN(); j++)
         {
-            struct TypeId::AttributeInformation info = tid.GetAttribute(j);
+            TypeId::AttributeInformation info = tid.GetAttribute(j);
             tid.SetAttributeInitialValue(j, info.originalInitialValue);
         }
     }
@@ -914,11 +914,11 @@
     {
         return false;
     }
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     tid.LookupAttributeByName(paramName, &info);
     for (uint32_t j = 0; j < tid.GetAttributeN(); j++)
     {
-        struct TypeId::AttributeInformation tmp = tid.GetAttribute(j);
+        TypeId::AttributeInformation tmp = tid.GetAttribute(j);
         if (tmp.name == paramName)
         {
             Ptr<AttributeValue> v = tmp.checker->CreateValidValue(value);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/default-simulator-impl.cc ns-3.39/src/core/model/default-simulator-impl.cc
--- ns-3.38/src/core/model/default-simulator-impl.cc	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/default-simulator-impl.cc	2023-07-05 14:23:47.753427000 -0700
@@ -375,16 +375,9 @@
         }
         return true;
     }
-    if (id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
-        (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
-        id.PeekEventImpl()->IsCancelled())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
+           (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
+           id.PeekEventImpl()->IsCancelled();
 }
 
 Time
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/default-simulator-impl.h ns-3.39/src/core/model/default-simulator-impl.h
--- ns-3.38/src/core/model/default-simulator-impl.h	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/default-simulator-impl.h	2023-07-05 14:23:47.753427000 -0700
@@ -96,8 +96,9 @@
         /** The event implementation. */
         EventImpl* event;
     };
+
     /** Container type for the events from a different context. */
-    typedef std::list<struct EventWithContext> EventsWithContext;
+    typedef std::list<EventWithContext> EventsWithContext;
     /** The container of events from a different context. */
     EventsWithContext m_eventsWithContext;
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/deprecated.h ns-3.39/src/core/model/deprecated.h
--- ns-3.38/src/core/model/deprecated.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/core/model/deprecated.h	2023-07-05 14:23:47.753427000 -0700
@@ -76,6 +76,13 @@
 
 /**
  * \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.
  */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/hash-fnv.h ns-3.39/src/core/model/hash-fnv.h
--- ns-3.38/src/core/model/hash-fnv.h	2023-03-16 20:54:23.588868400 -0700
+++ ns-3.39/src/core/model/hash-fnv.h	2023-07-05 14:23:47.753427000 -0700
@@ -98,10 +98,7 @@
     /**
      * Seed value
      */
-    enum Seed
-    {
-        SEED = 0x8BADF00D /**< Ate bad food */
-    };
+    static constexpr auto SEED{0x8BADF00D}; // Ate bad food
 
     /** Cache last hash value, for incremental hashing. */
     /**@{*/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/hash-murmur3.h ns-3.39/src/core/model/hash-murmur3.h
--- ns-3.38/src/core/model/hash-murmur3.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/hash-murmur3.h	2023-07-05 14:23:47.757427200 -0700
@@ -101,10 +101,7 @@
      * This has to be a constant for all MPI ranks to generate
      * the same hash from the same string.
      */
-    enum Seed
-    {
-        SEED = 0x8BADF00D // Ate bad food
-    };
+    static constexpr auto SEED{0x8BADF00D}; // Ate bad food
 
     /**
      * Cache last hash value, and total bytes hashed (needed to finalize),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/log.cc ns-3.39/src/core/model/log.cc
--- ns-3.38/src/core/model/log.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/log.cc	2023-07-05 14:23:47.757427200 -0700
@@ -168,15 +168,12 @@
     EnvVarCheck();
 
     LogComponent::ComponentList* components = GetComponentList();
-    for (LogComponent::ComponentList::const_iterator i = components->begin();
-         i != components->end();
-         i++)
+
+    if (components->find(name) != components->end())
     {
-        if (i->first == name)
-        {
-            NS_FATAL_ERROR("Log component \"" << name << "\" has already been registered once.");
-        }
+        NS_FATAL_ERROR("Log component \"" << name << "\" has already been registered once.");
     }
+
     components->insert(std::make_pair(name, this));
 }
 
@@ -305,24 +302,18 @@
 LogComponentEnable(const std::string& name, LogLevel level)
 {
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
-    LogComponent::ComponentList::const_iterator i;
-    for (i = components->begin(); i != components->end(); i++)
-    {
-        if (i->first == name)
-        {
-            i->second->Enable(level);
-            return;
-        }
-    }
-    if (i == components->end())
+    auto logComponent = components->find(name);
+
+    if (logComponent == components->end())
     {
-        // nothing matched
         NS_LOG_UNCOND("Logging component \"" << name << "\" not found.");
         LogComponentPrintList();
         NS_FATAL_ERROR("Logging component \""
                        << name << "\" not found."
                        << " See above for a list of available log components");
     }
+
+    logComponent->second->Enable(level);
 }
 
 void
@@ -341,15 +332,11 @@
 LogComponentDisable(const std::string& name, LogLevel level)
 {
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
-    for (LogComponent::ComponentList::const_iterator i = components->begin();
-         i != components->end();
-         i++)
+    auto logComponent = components->find(name);
+
+    if (logComponent != components->end())
     {
-        if (i->first == name)
-        {
-            i->second->Disable(level);
-            break;
-        }
+        logComponent->second->Disable(level);
     }
 }
 
@@ -368,67 +355,73 @@
 void
 LogComponentPrintList()
 {
-    LogComponent::ComponentList* components = LogComponent::GetComponentList();
-    for (LogComponent::ComponentList::const_iterator i = components->begin();
-         i != components->end();
-         i++)
+    // Create sorted map of components by inserting them into a map
+    std::map<std::string, LogComponent*> componentsSorted;
+
+    for (const auto& component : *LogComponent::GetComponentList())
     {
-        std::cout << i->first << "=";
-        if (i->second->IsNoneEnabled())
+        componentsSorted.insert(component);
+    }
+
+    // Iterate through sorted components
+    for (const auto& [name, component] : componentsSorted)
+    {
+        std::cout << name << "=";
+        if (component->IsNoneEnabled())
         {
             std::cout << "0" << std::endl;
             continue;
         }
-        if (i->second->IsEnabled(LOG_LEVEL_ALL))
+        if (component->IsEnabled(LOG_LEVEL_ALL))
         {
             std::cout << "all";
         }
         else
         {
-            if (i->second->IsEnabled(LOG_ERROR))
+            if (component->IsEnabled(LOG_ERROR))
             {
                 std::cout << "error";
             }
-            if (i->second->IsEnabled(LOG_WARN))
+            if (component->IsEnabled(LOG_WARN))
             {
                 std::cout << "|warn";
             }
-            if (i->second->IsEnabled(LOG_DEBUG))
+            if (component->IsEnabled(LOG_DEBUG))
             {
                 std::cout << "|debug";
             }
-            if (i->second->IsEnabled(LOG_INFO))
+            if (component->IsEnabled(LOG_INFO))
             {
                 std::cout << "|info";
             }
-            if (i->second->IsEnabled(LOG_FUNCTION))
+            if (component->IsEnabled(LOG_FUNCTION))
             {
                 std::cout << "|function";
             }
-            if (i->second->IsEnabled(LOG_LOGIC))
+            if (component->IsEnabled(LOG_LOGIC))
             {
                 std::cout << "|logic";
             }
         }
-        if (i->second->IsEnabled(LOG_PREFIX_ALL))
+        if (component->IsEnabled(LOG_PREFIX_ALL))
         {
             std::cout << "|prefix_all";
         }
         else
         {
-            if (i->second->IsEnabled(LOG_PREFIX_FUNC))
+            if (component->IsEnabled(LOG_PREFIX_FUNC))
             {
                 std::cout << "|func";
             }
-            if (i->second->IsEnabled(LOG_PREFIX_TIME))
+            if (component->IsEnabled(LOG_PREFIX_TIME))
             {
                 std::cout << "|time";
             }
-            if (i->second->IsEnabled(LOG_PREFIX_NODE))
+            if (component->IsEnabled(LOG_PREFIX_NODE))
             {
                 std::cout << "|node";
             }
-            if (i->second->IsEnabled(LOG_PREFIX_LEVEL))
+            if (component->IsEnabled(LOG_PREFIX_LEVEL))
             {
                 std::cout << "|level";
             }
@@ -449,17 +442,8 @@
 ComponentExists(std::string componentName)
 {
     LogComponent::ComponentList* components = LogComponent::GetComponentList();
-    LogComponent::ComponentList::const_iterator i;
-    for (i = components->begin(); i != components->end(); i++)
-    {
-        if (i->first == componentName)
-        {
-            return true;
-        }
-    }
-    NS_ASSERT(i == components->end());
-    // nothing matched
-    return false;
+
+    return components->find(componentName) != components->end();
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/log.h ns-3.39/src/core/model/log.h
--- ns-3.38/src/core/model/log.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/log.h	2023-07-05 14:23:47.757427200 -0700
@@ -26,10 +26,10 @@
 #include "time-printer.h"
 
 #include <iostream>
-#include <map>
 #include <stdint.h>
 #include <string>
 #include <type_traits>
+#include <unordered_map>
 #include <vector>
 
 /**
@@ -100,17 +100,17 @@
     LOG_WARN = 0x00000002,       //!< Warning messages.
     LOG_LEVEL_WARN = 0x00000003, //!< LOG_WARN and above.
 
-    LOG_DEBUG = 0x00000004,       //!< Rare ad-hoc debug messages.
-    LOG_LEVEL_DEBUG = 0x00000007, //!< LOG_DEBUG and above.
+    LOG_INFO = 0x00000004,       //!< Something happened to change state.
+    LOG_LEVEL_INFO = 0x00000007, //!< LOG_INFO and above.
 
-    LOG_INFO = 0x00000008,       //!< Informational messages (e.g., banners).
-    LOG_LEVEL_INFO = 0x0000000f, //!< LOG_INFO and above.
+    LOG_FUNCTION = 0x00000008,       //!< Function tracing for non-trivial function calls.
+    LOG_LEVEL_FUNCTION = 0x0000000f, //!< LOG_FUNCTION and above.
 
-    LOG_FUNCTION = 0x00000010,       //!< Function tracing.
-    LOG_LEVEL_FUNCTION = 0x0000001f, //!< LOG_FUNCTION and above.
+    LOG_LOGIC = 0x00000010,       //!< Debugging logs for key branches and decisions in a function.
+    LOG_LEVEL_LOGIC = 0x0000001f, //!< LOG_LOGIC and above.
 
-    LOG_LOGIC = 0x00000020,       //!< Control flow tracing within functions.
-    LOG_LEVEL_LOGIC = 0x0000003f, //!< LOG_LOGIC and above.
+    LOG_DEBUG = 0x00000020,       //!< Full voluminous logging to support debugging.
+    LOG_LEVEL_DEBUG = 0x0000003f, //!< LOG_DEBUG and above.
 
     LOG_ALL = 0x0fffffff,    //!< Print everything.
     LOG_LEVEL_ALL = LOG_ALL, //!< Print everything.
@@ -395,7 +395,7 @@
      * It is exposed here to allow print-introspected-doxygen.cc
      * to generate a list of all LogComponents.
      */
-    typedef std::map<std::string, LogComponent*> ComponentList;
+    using ComponentList = std::unordered_map<std::string, LogComponent*>;
 
     /**
      * Get the list of LogComponents.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/matrix-array.cc ns-3.39/src/core/model/matrix-array.cc
--- ns-3.38/src/core/model/matrix-array.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/matrix-array.cc	2023-07-05 14:23:47.757427200 -0700
@@ -34,7 +34,7 @@
 #endif
 
 template <class T>
-MatrixArray<T>::MatrixArray(uint16_t numRows, uint16_t numCols, uint16_t numPages)
+MatrixArray<T>::MatrixArray(size_t numRows, size_t numCols, size_t numPages)
     : ValArray<T>(numRows, numCols, numPages)
 {
 }
@@ -58,30 +58,30 @@
 }
 
 template <class T>
-MatrixArray<T>::MatrixArray(uint16_t numRows, uint16_t numCols, const std::valarray<T>& values)
+MatrixArray<T>::MatrixArray(size_t numRows, size_t numCols, const std::valarray<T>& values)
     : ValArray<T>(numRows, numCols, values)
 {
 }
 
 template <class T>
-MatrixArray<T>::MatrixArray(uint16_t numRows, uint16_t numCols, std::valarray<T>&& values)
+MatrixArray<T>::MatrixArray(size_t numRows, size_t numCols, std::valarray<T>&& values)
     : ValArray<T>(numRows, numCols, std::move(values))
 {
 }
 
 template <class T>
-MatrixArray<T>::MatrixArray(uint16_t numRows,
-                            uint16_t numCols,
-                            uint16_t numPages,
+MatrixArray<T>::MatrixArray(size_t numRows,
+                            size_t numCols,
+                            size_t numPages,
                             const std::valarray<T>& values)
     : ValArray<T>(numRows, numCols, numPages, values)
 {
 }
 
 template <class T>
-MatrixArray<T>::MatrixArray(uint16_t numRows,
-                            uint16_t numCols,
-                            uint16_t numPages,
+MatrixArray<T>::MatrixArray(size_t numRows,
+                            size_t numCols,
+                            size_t numPages,
                             std::valarray<T>&& values)
     : ValArray<T>(numRows, numCols, numPages, std::move(values))
 {
@@ -96,7 +96,7 @@
 
     MatrixArray<T> res{m_numRows, rhs.m_numCols, m_numPages};
 
-    for (uint16_t page = 0; page < res.m_numPages; ++page)
+    for (size_t page = 0; page < res.m_numPages; ++page)
     {
 #ifdef HAVE_EIGEN3 // Eigen found and enabled Eigen optimizations
 
@@ -108,14 +108,16 @@
 #else // Eigen not found or Eigen optimizations not enabled
 
         size_t matrixOffset = page * m_numRows * m_numCols;
-        for (uint16_t i = 0; i < res.m_numRows; ++i)
+        size_t rhsMatrixOffset = page * rhs.m_numRows * rhs.m_numCols;
+        for (size_t i = 0; i < res.m_numRows; ++i)
         {
-            for (uint16_t j = 0; j < res.m_numCols; ++j)
+            for (size_t j = 0; j < res.m_numCols; ++j)
             {
-                res(i, j, page) =
-                    (m_values[std::slice(matrixOffset + i, m_numCols, m_numRows)] *
-                     rhs.m_values[std::slice(matrixOffset + j * rhs.m_numRows, rhs.m_numRows, 1)])
-                        .sum();
+                res(i, j, page) = (m_values[std::slice(matrixOffset + i, m_numCols, m_numRows)] *
+                                   rhs.m_values[std::slice(rhsMatrixOffset + j * rhs.m_numRows,
+                                                           rhs.m_numRows,
+                                                           1)])
+                                      .sum();
             }
         }
 
@@ -132,7 +134,7 @@
     // m_numPages = this.m_numPages
     MatrixArray<T> res{m_numCols, m_numRows, m_numPages};
 
-    for (uint16_t page = 0; page < m_numPages; ++page)
+    for (size_t page = 0; page < m_numPages; ++page)
     {
 #ifdef HAVE_EIGEN3 // Eigen found and Eigen optimizations enabled
 
@@ -143,7 +145,7 @@
 #else // Eigen not found or Eigen optimizations not enabled
 
         size_t matrixIndex = page * m_numRows * m_numCols;
-        for (uint16_t i = 0; i < m_numRows; ++i)
+        for (size_t i = 0; i < m_numRows; ++i)
         {
             res.m_values[std::slice(matrixIndex + i * res.m_numRows, res.m_numRows, 1)] =
                 m_values[std::slice(matrixIndex + i, m_numCols, m_numRows)];
@@ -174,7 +176,7 @@
     ConstEigenMatrix<T> rMatrixEigen(rMatrix.GetPagePtr(0), rMatrix.m_numRows, rMatrix.m_numCols);
 #endif
 
-    for (uint16_t page = 0; page < m_numPages; ++page)
+    for (size_t page = 0; page < m_numPages; ++page)
     {
 #ifdef HAVE_EIGEN3 // Eigen found and Eigen optimizations enabled
 
@@ -186,14 +188,14 @@
 #else // Eigen not found or Eigen optimizations not enabled
 
         size_t matrixOffset = page * m_numRows * m_numCols;
-        for (uint16_t resRow = 0; resRow < res.m_numRows; ++resRow)
+        for (size_t resRow = 0; resRow < res.m_numRows; ++resRow)
         {
-            for (uint16_t resCol = 0; resCol < res.m_numCols; ++resCol)
+            for (size_t resCol = 0; resCol < res.m_numCols; ++resCol)
             {
                 // create intermediate row result, a multiply of resRow row of lMatrix and each
                 // column of this matrix
                 std::valarray<T> interRes(m_numCols);
-                for (uint16_t thisCol = 0; thisCol < m_numCols; ++thisCol)
+                for (size_t thisCol = 0; thisCol < m_numCols; ++thisCol)
                 {
                     interRes[thisCol] =
                         (lMatrix
@@ -222,7 +224,7 @@
 
     for (size_t index = 0; index < this->GetSize(); ++index)
     {
-        retMatrix[index] = std::conj(m_values[index]);
+        retMatrix.m_values[index] = std::conj(retMatrix.m_values[index]);
     }
     return retMatrix;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/matrix-array.h ns-3.39/src/core/model/matrix-array.h
--- ns-3.38/src/core/model/matrix-array.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/matrix-array.h	2023-07-05 14:23:47.757427200 -0700
@@ -92,7 +92,7 @@
      * \param numCols the number of columns
      * \param numPages the number of pages
      */
-    MatrixArray<T>(uint16_t numRows, uint16_t numCols = 1, uint16_t numPages = 1);
+    MatrixArray<T>(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.
@@ -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>(uint16_t numRows, uint16_t numCols, const std::valarray<T>& values);
+    MatrixArray<T>(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>(uint16_t numRows, uint16_t numCols, std::valarray<T>&& values);
+    MatrixArray<T>(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,10 +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>(uint16_t numRows,
-                   uint16_t numCols,
-                   uint16_t numPages,
-                   const std::valarray<T>& values);
+    MatrixArray<T>(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.
@@ -147,10 +144,7 @@
      * \param numPages the number of pages
      * \param values std::valarray<T> that will be used to initialize elements of 3D array
      */
-    MatrixArray<T>(uint16_t numRows,
-                   uint16_t numCols,
-                   uint16_t numPages,
-                   std::valarray<T>&& values);
+    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*/
@@ -179,13 +173,13 @@
     /**
      * \brief operator+ definition for MatrixArray<T>.
      * \param rhs The rhs MatrixArray object
-     * \returns The result of operator+ of this MatrixArray and rhs MatrixAray
+     * \returns The result of operator+ of this MatrixArray and rhs MatrixArray
      */
     MatrixArray<T> 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 MatrixAray
+     * \returns The result of operator- of this MatrixArray and rhs MatrixArray
      */
     MatrixArray<T> operator-(const MatrixArray<T>& rhs) const;
     /**
@@ -253,7 +247,7 @@
                                                   EnableBool)>::type>
     MatrixArray<T> HermitianTranspose() const;
 
-  private:
+  protected:
     // To simplify functions in MatrixArray that are using members from the template base class
     using ValArray<T>::m_numRows;
     using ValArray<T>::m_numCols;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/names.cc ns-3.39/src/core/model/names.cc
--- ns-3.38/src/core/model/names.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/names.cc	2023-07-05 14:23:47.757427200 -0700
@@ -307,7 +307,7 @@
          ++i)
     {
         delete i->second;
-        i->second = 0;
+        i->second = nullptr;
     }
 
     m_objectMap.clear();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/nstime.h ns-3.39/src/core/model/nstime.h
--- ns-3.38/src/core/model/nstime.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/nstime.h	2023-07-05 14:23:47.761427200 -0700
@@ -497,7 +497,7 @@
      */
     inline static Time FromInteger(uint64_t value, Unit unit)
     {
-        struct Information* info = PeekInformation(unit);
+        Information* info = PeekInformation(unit);
 
         NS_ASSERT_MSG(info->isValid, "Attempted a conversion from an unavailable unit.");
 
@@ -519,7 +519,7 @@
 
     inline static Time From(const int64x64_t& value, Unit unit)
     {
-        struct Information* info = PeekInformation(unit);
+        Information* info = PeekInformation(unit);
 
         NS_ASSERT_MSG(info->isValid, "Attempted a conversion from an unavailable unit.");
 
@@ -553,7 +553,7 @@
      */
     inline int64_t ToInteger(Unit unit) const
     {
-        struct Information* info = PeekInformation(unit);
+        Information* info = PeekInformation(unit);
 
         NS_ASSERT_MSG(info->isValid, "Attempted a conversion to an unavailable unit.");
 
@@ -576,7 +576,7 @@
 
     inline int64x64_t To(Unit unit) const
     {
-        struct Information* info = PeekInformation(unit);
+        Information* info = PeekInformation(unit);
 
         NS_ASSERT_MSG(info->isValid, "Attempted a conversion to an unavailable unit.");
 
@@ -651,9 +651,9 @@
      *
      *  \return A pointer to the current Resolution
      */
-    static inline struct Resolution* PeekResolution()
+    static inline Resolution* PeekResolution()
     {
-        static struct Time::Resolution& resolution{SetDefaultNsResolution()};
+        static Time::Resolution& resolution{SetDefaultNsResolution()};
         return &resolution;
     }
 
@@ -663,7 +663,7 @@
      *  \param [in] timeUnit The Unit to get Information for
      *  \return The Information for \pname{timeUnit}
      */
-    static inline struct Information* PeekInformation(Unit timeUnit)
+    static inline Information* PeekInformation(Unit timeUnit)
     {
         return &(PeekResolution()->info[timeUnit]);
     }
@@ -673,7 +673,7 @@
      *
      *  \return The Resolution object for the default resolution.
      */
-    static struct Resolution& SetDefaultNsResolution();
+    static Resolution& SetDefaultNsResolution();
     /**
      *  Set the current Resolution.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/object-base.cc ns-3.39/src/core/model/object-base.cc
--- ns-3.38/src/core/model/object-base.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/object-base.cc	2023-07-05 14:23:47.761427200 -0700
@@ -89,7 +89,7 @@
         NS_LOG_DEBUG("construct tid=" << tid.GetName() << ", params=" << tid.GetAttributeN());
         for (uint32_t i = 0; i < tid.GetAttributeN(); i++)
         {
-            struct TypeId::AttributeInformation info = tid.GetAttribute(i);
+            TypeId::AttributeInformation info = tid.GetAttribute(i);
             NS_LOG_DEBUG("try to construct \"" << tid.GetName() << "::" << info.name << "\"");
             // is this attribute stored in this AttributeConstructionList instance ?
             Ptr<const AttributeValue> value = attributes.Find(info.checker);
@@ -200,7 +200,7 @@
 ObjectBase::SetAttribute(std::string name, const AttributeValue& value)
 {
     NS_LOG_FUNCTION(this << name << &value);
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     TypeId tid = GetInstanceTypeId();
     if (!tid.LookupAttributeByName(name, &info))
     {
@@ -223,7 +223,7 @@
 ObjectBase::SetAttributeFailSafe(std::string name, const AttributeValue& value)
 {
     NS_LOG_FUNCTION(this << name << &value);
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     TypeId tid = GetInstanceTypeId();
     if (!tid.LookupAttributeByName(name, &info))
     {
@@ -240,7 +240,7 @@
 ObjectBase::GetAttribute(std::string name, AttributeValue& value) const
 {
     NS_LOG_FUNCTION(this << name << &value);
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     TypeId tid = GetInstanceTypeId();
     if (!tid.LookupAttributeByName(name, &info))
     {
@@ -277,7 +277,7 @@
 ObjectBase::GetAttributeFailSafe(std::string name, AttributeValue& value) const
 {
     NS_LOG_FUNCTION(this << name << &value);
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     TypeId tid = GetInstanceTypeId();
     if (!tid.LookupAttributeByName(name, &info))
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/object.cc ns-3.39/src/core/model/object.cc
--- ns-3.38/src/core/model/object.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/object.cc	2023-07-05 14:23:47.761427200 -0700
@@ -96,7 +96,7 @@
     : m_tid(Object::GetTypeId()),
       m_disposed(false),
       m_initialized(false),
-      m_aggregates((struct Aggregates*)std::malloc(sizeof(struct Aggregates))),
+      m_aggregates((Aggregates*)std::malloc(sizeof(Aggregates))),
       m_getObjectCount(0)
 {
     NS_LOG_FUNCTION(this);
@@ -133,7 +133,7 @@
     : m_tid(o.m_tid),
       m_disposed(false),
       m_initialized(false),
-      m_aggregates((struct Aggregates*)std::malloc(sizeof(struct Aggregates))),
+      m_aggregates((Aggregates*)std::malloc(sizeof(Aggregates))),
       m_getObjectCount(0)
 {
     m_aggregates->n = 1;
@@ -242,7 +242,7 @@
 }
 
 void
-Object::UpdateSortedArray(struct Aggregates* aggregates, uint32_t j) const
+Object::UpdateSortedArray(Aggregates* aggregates, uint32_t j) const
 {
     NS_LOG_FUNCTION(this << aggregates << j);
     while (j > 0 &&
@@ -267,8 +267,8 @@
     Object* other = PeekPointer(o);
     // first create the new aggregate buffer.
     uint32_t total = m_aggregates->n + other->m_aggregates->n;
-    struct Aggregates* aggregates =
-        (struct Aggregates*)std::malloc(sizeof(struct Aggregates) + (total - 1) * sizeof(Object*));
+    Aggregates* aggregates =
+        (Aggregates*)std::malloc(sizeof(Aggregates) + (total - 1) * sizeof(Object*));
     aggregates->n = total;
 
     // copy our buffer to the new buffer
@@ -292,8 +292,8 @@
 
     // keep track of the old aggregate buffers for the iteration
     // of NotifyNewAggregates
-    struct Aggregates* a = m_aggregates;
-    struct Aggregates* b = other->m_aggregates;
+    Aggregates* a = m_aggregates;
+    Aggregates* b = other->m_aggregates;
 
     // Then, assign the new aggregation buffer to every object
     uint32_t n = aggregates->n;
@@ -424,7 +424,7 @@
     }
 
     // Now, actually delete all objects
-    struct Aggregates* aggregates = m_aggregates;
+    Aggregates* aggregates = m_aggregates;
     for (uint32_t i = 0; i < n; i++)
     {
         // There is a trick here: each time we call delete below,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/object-factory.cc ns-3.39/src/core/model/object-factory.cc
--- ns-3.38/src/core/model/object-factory.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/object-factory.cc	2023-07-05 14:23:47.761427200 -0700
@@ -55,11 +55,7 @@
 bool
 ObjectFactory::IsTypeIdSet() const
 {
-    if (m_tid.GetUid() != 0)
-    {
-        return true;
-    }
-    return false;
+    return m_tid.GetUid() != 0;
 }
 
 void
@@ -71,7 +67,7 @@
         return;
     }
 
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     if (!m_tid.LookupAttributeByName(name, &info))
     {
         NS_FATAL_ERROR("Invalid attribute set (" << name << ") on " << m_tid.GetName());
@@ -162,7 +158,7 @@
         else
         {
             std::string name = parameters.substr(cur, equal - cur);
-            struct TypeId::AttributeInformation info;
+            TypeId::AttributeInformation info;
             if (!factory.m_tid.LookupAttributeByName(name, &info))
             {
                 is.setstate(std::ios_base::failbit);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/object.h ns-3.39/src/core/model/object.h
--- ns-3.38/src/core/model/object.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/object.h	2023-07-05 14:23:47.761427200 -0700
@@ -404,7 +404,7 @@
      * \param [in,out] aggregates The list of aggregated Objects.
      * \param [in] i The most recently used entry in the list.
      */
-    void UpdateSortedArray(struct Aggregates* aggregates, uint32_t i) const;
+    void UpdateSortedArray(Aggregates* aggregates, uint32_t i) const;
     /**
      * Attempt to delete this Object.
      *
@@ -435,7 +435,7 @@
      * array.  The array is shared by all aggregated Objects
      * so the size of the array is indirectly a reference count.
      */
-    struct Aggregates* m_aggregates;
+    Aggregates* m_aggregates;
     /**
      * The number of times the Object was accessed with a
      * call to GetObject().
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/object-map.h ns-3.39/src/core/model/object-map.h
--- ns-3.38/src/core/model/object-map.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/object-map.h	2023-07-05 14:23:47.761427200 -0700
@@ -102,7 +102,6 @@
                 {
                     *index = (*j).first;
                     return (*j).second;
-                    break;
                 }
             }
             NS_ASSERT(false);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/object-vector.h ns-3.39/src/core/model/object-vector.h
--- ns-3.38/src/core/model/object-vector.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/object-vector.h	2023-07-05 14:23:47.761427200 -0700
@@ -102,7 +102,6 @@
                 {
                     *index = k;
                     return *j;
-                    break;
                 }
             }
             NS_ASSERT(false);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/pointer.h ns-3.39/src/core/model/pointer.h
--- ns-3.38/src/core/model/pointer.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/pointer.h	2023-07-05 14:23:47.761427200 -0700
@@ -140,11 +140,7 @@
             return true;
         }
         T* ptr = dynamic_cast<T*>(PeekPointer(value->GetObject()));
-        if (ptr == nullptr)
-        {
-            return false;
-        }
-        return true;
+        return ptr;
     }
 
     std::string GetValueTypeName() const override
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/random-variable-stream.cc ns-3.39/src/core/model/random-variable-stream.cc
--- ns-3.38/src/core/model/random-variable-stream.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/random-variable-stream.cc	2023-07-05 14:23:47.761427200 -0700
@@ -802,6 +802,7 @@
 }
 
 LogNormalRandomVariable::LogNormalRandomVariable()
+    : m_nextValid(false)
 {
     // m_mu and m_sigma are initialized after constructor by
     // attributes
@@ -851,6 +852,14 @@
 double
 LogNormalRandomVariable::GetValue(double mu, double sigma)
 {
+    if (m_nextValid)
+    { // use previously generated
+        m_nextValid = false;
+        double normal = m_v2 * m_normal;
+
+        return std::exp(sigma * normal + mu);
+    }
+
     double v1;
     double v2;
     double r2;
@@ -878,7 +887,10 @@
         r2 = v1 * v1 + v2 * v2;
     } while (r2 > 1.0 || r2 == 0);
 
-    normal = v1 * std::sqrt(-2.0 * std::log(r2) / r2);
+    m_normal = std::sqrt(-2.0 * std::log(r2) / r2);
+    normal = v1 * m_normal;
+    m_nextValid = true;
+    m_v2 = v2;
 
     x = std::exp(sigma * normal + mu);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/random-variable-stream.h ns-3.39/src/core/model/random-variable-stream.h
--- ns-3.38/src/core/model/random-variable-stream.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/random-variable-stream.h	2023-07-05 14:23:47.761427200 -0700
@@ -1175,6 +1175,15 @@
     /** The sigma value for the log-normal distribution returned by this RNG stream. */
     double m_sigma;
 
+    /** True if m_normal is valid. */
+    bool m_nextValid;
+
+    /** The algorithm produces two values at a time. Cache parameters for possible reuse.*/
+    double m_v2;
+
+    /** The algorithm produces two values at a time. Cache parameters for possible reuse.*/
+    double m_normal;
+
 }; // class LogNormalRandomVariable
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/realtime-simulator-impl.cc ns-3.39/src/core/model/realtime-simulator-impl.cc
--- ns-3.38/src/core/model/realtime-simulator-impl.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/realtime-simulator-impl.cc	2023-07-05 14:23:47.761427200 -0700
@@ -125,12 +125,12 @@
     // means shutting down the workers and doing a Join() before calling the
     // Simulator::Destroy().
     //
-    while (m_destroyEvents.empty() == false)
+    while (!m_destroyEvents.empty())
     {
         Ptr<EventImpl> ev = m_destroyEvents.front().PeekEventImpl();
         m_destroyEvents.pop_front();
         NS_LOG_LOGIC("handle destroy " << ev);
-        if (ev->IsCancelled() == false)
+        if (!ev->IsCancelled())
         {
             ev->Invoke();
         }
@@ -149,7 +149,7 @@
 
         if (m_events)
         {
-            while (m_events->IsEmpty() == false)
+            while (!m_events->IsEmpty())
             {
                 Scheduler::Event next = m_events->RemoveNext();
                 scheduler->Insert(next);
@@ -738,7 +738,7 @@
 void
 RealtimeSimulatorImpl::Cancel(const EventId& id)
 {
-    if (IsExpired(id) == false)
+    if (!IsExpired(id))
     {
         id.PeekEventImpl()->Cancel();
     }
@@ -773,16 +773,9 @@
     //
     // The same is true for the next line involving the m_currentUid.
     //
-    if (id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
-        (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
-        id.PeekEventImpl()->IsCancelled())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
+           (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
+           id.PeekEventImpl()->IsCancelled();
 }
 
 Time
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/rng-seed-manager.h ns-3.39/src/core/model/rng-seed-manager.h
--- ns-3.38/src/core/model/rng-seed-manager.h	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/rng-seed-manager.h	2023-07-05 14:23:47.761427200 -0700
@@ -46,7 +46,7 @@
      * subsequently instantiated RandomVariableStream objects.
      *
      * \code
-     *   RngSeedManger::SetSeed(15);
+     *   RngSeedManager::SetSeed(15);
      *   UniformVariable x(2,3);     // These will give the same output every time
      *   ExponentialVariable y(120); // as long as the seed stays the same.
      * \endcode
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/rng-stream.cc ns-3.39/src/core/model/rng-stream.cc
--- ns-3.38/src/core/model/rng-stream.cc	2023-03-16 20:54:23.592868600 -0700
+++ ns-3.39/src/core/model/rng-stream.cc	2023-07-05 14:23:47.761427200 -0700
@@ -297,9 +297,9 @@
  *
  * \returns The precalculated powers of the transition matrices.
  */
-struct Precalculated PowerOfTwoConstants ()
+Precalculated PowerOfTwoConstants ()
 {
-  struct Precalculated precalculated;
+  Precalculated precalculated;
   for (int i = 0; i < 190; i++)
     {
       int power = i + 1;
@@ -317,7 +317,7 @@
  */
 void PowerOfTwoMatrix (int n, Matrix a1p, Matrix a2p)
 {
-  static struct Precalculated constants = PowerOfTwoConstants ();
+  static  Precalculated constants = PowerOfTwoConstants ();
   for (int i = 0; i < 3; i ++)
     {
       for (int j = 0; j < 3; j++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/test.cc ns-3.39/src/core/model/test.cc
--- ns-3.38/src/core/model/test.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/test.cc	2023-07-05 14:23:47.765427000 -0700
@@ -63,11 +63,7 @@
     delta = std::ldexp(epsilon, exponent);
     difference = x1 - x2;
 
-    if (difference > delta || difference < -delta)
-    {
-        return false;
-    }
-    return true;
+    return difference <= delta && difference >= -delta;
 }
 
 /**
@@ -241,8 +237,8 @@
      * \returns The list of tests matching the filter constraints.
      */
     std::list<TestCase*> FilterTests(std::string testName,
-                                     enum TestSuite::Type testType,
-                                     enum TestCase::TestDuration maximumTestDuration);
+                                     TestSuite::Type testType,
+                                     TestCase::TestDuration maximumTestDuration);
 
     /** Container type for the test. */
     typedef std::vector<TestSuite*> TestSuiteVector;
@@ -302,7 +298,7 @@
 }
 
 void
-TestCase::AddTestCase(TestCase* testCase, enum TestCase::TestDuration duration)
+TestCase::AddTestCase(TestCase* testCase, TestCase::TestDuration duration)
 {
     NS_LOG_FUNCTION(&testCase << duration);
 
@@ -836,8 +832,8 @@
 
 std::list<TestCase*>
 TestRunnerImpl::FilterTests(std::string testName,
-                            enum TestSuite::Type testType,
-                            enum TestCase::TestDuration maximumTestDuration)
+                            TestSuite::Type testType,
+                            TestCase::TestDuration maximumTestDuration)
 {
     NS_LOG_FUNCTION(this << testName << testType);
     std::list<TestCase*> tests;
@@ -899,7 +895,7 @@
     bool printTestTypeList = false;
     bool printTestNameList = false;
     bool printTestTypeAndName = false;
-    enum TestCase::TestDuration maximumTestDuration = TestCase::QUICK;
+    TestCase::TestDuration maximumTestDuration = TestCase::QUICK;
     char* progname = argv[0];
 
     char** argi = argv;
@@ -1006,7 +1002,7 @@
         }
         argi++;
     }
-    enum TestSuite::Type testType;
+    TestSuite::Type testType;
     if (testTypeString.empty())
     {
         testType = TestSuite::ALL;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/test.h ns-3.39/src/core/model/test.h
--- ns-3.38/src/core/model/test.h	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/test.h	2023-07-05 14:23:47.765427000 -0700
@@ -1240,7 +1240,7 @@
     std::vector<TestCase*> m_children; //!< Vector of my children
     std::string m_dataDir;             //!< My data directory
     TestRunnerImpl* m_runner;          //!< Pointer to the TestRunner
-    struct Result* m_result;           //!< Results data
+    Result* m_result;                  //!< Results data
     std::string m_name;                //!< TestCase name
     TestDuration m_duration;           //!< TestCase duration
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/time.cc ns-3.39/src/core/model/time.cc
--- ns-3.38/src/core/model/time.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/time.cc	2023-07-05 14:23:47.765427000 -0700
@@ -137,43 +137,43 @@
         double r;
         iss >> r;
         std::string trailer = s.substr(n, std::string::npos);
-        if (trailer == std::string("s"))
+        if (trailer == "s")
         {
             *this = Time::FromDouble(r, Time::S);
         }
-        else if (trailer == std::string("ms"))
+        else if (trailer == "ms")
         {
             *this = Time::FromDouble(r, Time::MS);
         }
-        else if (trailer == std::string("us"))
+        else if (trailer == "us")
         {
             *this = Time::FromDouble(r, Time::US);
         }
-        else if (trailer == std::string("ns"))
+        else if (trailer == "ns")
         {
             *this = Time::FromDouble(r, Time::NS);
         }
-        else if (trailer == std::string("ps"))
+        else if (trailer == "ps")
         {
             *this = Time::FromDouble(r, Time::PS);
         }
-        else if (trailer == std::string("fs"))
+        else if (trailer == "fs")
         {
             *this = Time::FromDouble(r, Time::FS);
         }
-        else if (trailer == std::string("min"))
+        else if (trailer == "min")
         {
             *this = Time::FromDouble(r, Time::MIN);
         }
-        else if (trailer == std::string("h"))
+        else if (trailer == "h")
         {
             *this = Time::FromDouble(r, Time::H);
         }
-        else if (trailer == std::string("d"))
+        else if (trailer == "d")
         {
             *this = Time::FromDouble(r, Time::D);
         }
-        else if (trailer == std::string("y"))
+        else if (trailer == "y")
         {
             *this = Time::FromDouble(r, Time::Y);
         }
@@ -199,7 +199,7 @@
 }
 
 // static
-struct Time::Resolution&
+Time::Resolution&
 Time::SetDefaultNsResolution()
 {
     NS_LOG_FUNCTION_NOARGS();
@@ -246,7 +246,7 @@
                                                << " has shift " << shift << " has quotient "
                                                << quotient);
 
-        struct Information* info = &resolution->info[i];
+        Information* info = &resolution->info[i];
         if ((std::pow(10, std::fabs(shift)) * quotient) >
             static_cast<double>(std::numeric_limits<int64_t>::max()))
         {
@@ -338,7 +338,7 @@
         ret = g_markingTimes->insert(time);
         NS_LOG_LOGIC("\t[" << g_markingTimes->size() << "] recording " << time);
 
-        if (ret.second == false)
+        if (!ret.second)
         {
             NS_LOG_WARN("already recorded " << time << "!");
         }
@@ -388,8 +388,8 @@
     for (MarkedTimes::iterator it = g_markingTimes->begin(); it != g_markingTimes->end(); it++)
     {
         Time* const tp = *it;
-        if (!((tp->m_data == std::numeric_limits<int64_t>::min()) ||
-              (tp->m_data == std::numeric_limits<int64_t>::max())))
+        if (!(tp->m_data == std::numeric_limits<int64_t>::min() ||
+              tp->m_data == std::numeric_limits<int64_t>::max()))
         {
             tp->m_data = tp->ToInteger(unit);
         }
@@ -406,7 +406,7 @@
 } // Time::ConvertTimes ()
 
 // static
-enum Time::Unit
+Time::Unit
 Time::GetResolution()
 {
     // No function log b/c it interferes with operator<<
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/timer.cc ns-3.39/src/core/model/timer.cc
--- ns-3.38/src/core/model/timer.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/timer.cc	2023-07-05 14:23:47.765427000 -0700
@@ -94,17 +94,13 @@
     {
     case Timer::RUNNING:
         return Simulator::GetDelayLeft(m_event);
-        break;
     case Timer::EXPIRED:
         return TimeStep(0);
-        break;
     case Timer::SUSPENDED:
         return m_delayLeft;
-        break;
     default:
         NS_ASSERT(false);
         return TimeStep(0);
-        break;
     }
 }
 
@@ -143,7 +139,7 @@
     return (m_flags & TIMER_SUSPENDED) == TIMER_SUSPENDED;
 }
 
-enum Timer::State
+Timer::State
 Timer::GetState() const
 {
     NS_LOG_FUNCTION(this);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/timer.h ns-3.39/src/core/model/timer.h
--- ns-3.38/src/core/model/timer.h	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/timer.h	2023-07-05 14:23:47.765427000 -0700
@@ -198,7 +198,7 @@
     /**
      * \returns The current state of the timer.
      */
-    enum Timer::State GetState() const;
+    Timer::State GetState() const;
     /**
      * Schedule a new event using the currently-configured delay, function,
      * and arguments.
@@ -233,11 +233,8 @@
     void Resume();
 
   private:
-    /** Internal bit marking the suspended state. */
-    enum InternalSuspended
-    {
-        TIMER_SUSPENDED = (1 << 7) /** Timer suspended. */
-    };
+    /** Internal bit marking the suspended timer state */
+    static constexpr auto TIMER_SUSPENDED{1 << 7};
 
     /**
      * Bitfield for Timer State, DestroyPolicy and InternalSuspended.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/timer-impl.h ns-3.39/src/core/model/timer-impl.h
--- ns-3.38/src/core/model/timer-impl.h	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/timer-impl.h	2023-07-05 14:23:47.765427000 -0700
@@ -1084,7 +1084,7 @@
 void
 TimerImpl::SetArgs(T1 a1)
 {
-    typedef struct TimerImplOne<typename TimerTraits<T1>::ParameterType> TimerImplBase;
+    typedef TimerImplOne<typename TimerTraits<T1>::ParameterType> TimerImplBase;
     TimerImplBase* impl = dynamic_cast<TimerImplBase*>(this);
     if (impl == nullptr)
     {
@@ -1098,8 +1098,8 @@
 void
 TimerImpl::SetArgs(T1 a1, T2 a2)
 {
-    typedef struct TimerImplTwo<typename TimerTraits<T1>::ParameterType,
-                                typename TimerTraits<T2>::ParameterType>
+    typedef TimerImplTwo<typename TimerTraits<T1>::ParameterType,
+                         typename TimerTraits<T2>::ParameterType>
         TimerImplBase;
     TimerImplBase* impl = dynamic_cast<TimerImplBase*>(this);
     if (impl == nullptr)
@@ -1114,9 +1114,9 @@
 void
 TimerImpl::SetArgs(T1 a1, T2 a2, T3 a3)
 {
-    typedef struct TimerImplThree<typename TimerTraits<T1>::ParameterType,
-                                  typename TimerTraits<T2>::ParameterType,
-                                  typename TimerTraits<T3>::ParameterType>
+    typedef TimerImplThree<typename TimerTraits<T1>::ParameterType,
+                           typename TimerTraits<T2>::ParameterType,
+                           typename TimerTraits<T3>::ParameterType>
         TimerImplBase;
     TimerImplBase* impl = dynamic_cast<TimerImplBase*>(this);
     if (impl == nullptr)
@@ -1131,10 +1131,10 @@
 void
 TimerImpl::SetArgs(T1 a1, T2 a2, T3 a3, T4 a4)
 {
-    typedef struct TimerImplFour<typename TimerTraits<T1>::ParameterType,
-                                 typename TimerTraits<T2>::ParameterType,
-                                 typename TimerTraits<T3>::ParameterType,
-                                 typename TimerTraits<T4>::ParameterType>
+    typedef TimerImplFour<typename TimerTraits<T1>::ParameterType,
+                          typename TimerTraits<T2>::ParameterType,
+                          typename TimerTraits<T3>::ParameterType,
+                          typename TimerTraits<T4>::ParameterType>
         TimerImplBase;
     TimerImplBase* impl = dynamic_cast<TimerImplBase*>(this);
     if (impl == nullptr)
@@ -1149,11 +1149,11 @@
 void
 TimerImpl::SetArgs(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5)
 {
-    typedef struct TimerImplFive<typename TimerTraits<T1>::ParameterType,
-                                 typename TimerTraits<T2>::ParameterType,
-                                 typename TimerTraits<T3>::ParameterType,
-                                 typename TimerTraits<T4>::ParameterType,
-                                 typename TimerTraits<T5>::ParameterType>
+    typedef TimerImplFive<typename TimerTraits<T1>::ParameterType,
+                          typename TimerTraits<T2>::ParameterType,
+                          typename TimerTraits<T3>::ParameterType,
+                          typename TimerTraits<T4>::ParameterType,
+                          typename TimerTraits<T5>::ParameterType>
         TimerImplBase;
     TimerImplBase* impl = dynamic_cast<TimerImplBase*>(this);
     if (impl == nullptr)
@@ -1168,12 +1168,12 @@
 void
 TimerImpl::SetArgs(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6)
 {
-    typedef struct TimerImplSix<typename TimerTraits<T1>::ParameterType,
-                                typename TimerTraits<T2>::ParameterType,
-                                typename TimerTraits<T3>::ParameterType,
-                                typename TimerTraits<T4>::ParameterType,
-                                typename TimerTraits<T5>::ParameterType,
-                                typename TimerTraits<T6>::ParameterType>
+    typedef TimerImplSix<typename TimerTraits<T1>::ParameterType,
+                         typename TimerTraits<T2>::ParameterType,
+                         typename TimerTraits<T3>::ParameterType,
+                         typename TimerTraits<T4>::ParameterType,
+                         typename TimerTraits<T5>::ParameterType,
+                         typename TimerTraits<T6>::ParameterType>
         TimerImplBase;
     TimerImplBase* impl = dynamic_cast<TimerImplBase*>(this);
     if (impl == 0)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/type-id.cc ns-3.39/src/core/model/type-id.cc
--- ns-3.38/src/core/model/type-id.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/type-id.cc	2023-07-05 14:23:47.765427000 -0700
@@ -228,7 +228,7 @@
      * \param [in] i Index into attribute array
      * \returns The information associated to attribute whose index is \pname{i}.
      */
-    struct TypeId::AttributeInformation GetAttribute(uint16_t uid, std::size_t i) const;
+    TypeId::AttributeInformation GetAttribute(uint16_t uid, std::size_t i) const;
     /**
      * Record a new TraceSource.
      * \param [in] uid The id.
@@ -262,7 +262,7 @@
      * \param [in] i Index into trace source array.
      * \returns Detailed information about the requested trace source.
      */
-    struct TypeId::TraceSourceInformation GetTraceSource(uint16_t uid, std::size_t i) const;
+    TypeId::TraceSourceInformation GetTraceSource(uint16_t uid, std::size_t i) const;
     /**
      * Check if this TypeId should not be listed in documentation.
      * \param [in] uid The id.
@@ -312,26 +312,27 @@
         /** \c true if this type should be omitted from documentation. */
         bool mustHideFromDocumentation;
         /** The container of Attributes. */
-        std::vector<struct TypeId::AttributeInformation> attributes;
+        std::vector<TypeId::AttributeInformation> attributes;
         /** The container of TraceSources. */
-        std::vector<struct TypeId::TraceSourceInformation> traceSources;
+        std::vector<TypeId::TraceSourceInformation> traceSources;
         /** Support level/deprecation. */
         TypeId::SupportLevel supportLevel;
         /** Support message. */
         std::string supportMsg;
     };
+
     /** Iterator type. */
-    typedef std::vector<struct IidInformation>::const_iterator Iterator;
+    typedef std::vector<IidInformation>::const_iterator Iterator;
 
     /**
      * Retrieve the information record for a type.
      * \param [in] uid The id.
      * \returns The information record.
      */
-    struct IidManager::IidInformation* LookupInformation(uint16_t uid) const;
+    IidManager::IidInformation* LookupInformation(uint16_t uid) const;
 
     /** The container of all type id records. */
-    std::vector<struct IidInformation> m_information;
+    std::vector<IidInformation> m_information;
 
     /** Type of the by-name index. */
     typedef std::map<std::string, uint16_t> namemap_t;
@@ -413,7 +414,7 @@
         // into ns3.  -- Peter Barnes, LLNL
 
         // Alphabetize the two types, so it's deterministic
-        struct IidInformation* hinfo = LookupInformation(GetUid(hash));
+        IidInformation* hinfo = LookupInformation(GetUid(hash));
         if (name > hinfo->name)
         { // new type gets chained
             NS_LOG_LOGIC(IIDL << "New TypeId '" << name << "' getting chained.");
@@ -451,13 +452,13 @@
     return uid;
 }
 
-struct IidManager::IidInformation*
+IidManager::IidInformation*
 IidManager::LookupInformation(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
     NS_ASSERT(uid <= m_information.size() && uid != 0);
     NS_LOG_LOGIC(IIDL << m_information[uid - 1].name);
-    return const_cast<struct IidInformation*>(&m_information[uid - 1]);
+    return const_cast<IidInformation*>(&m_information[uid - 1]);
 }
 
 void
@@ -465,7 +466,7 @@
 {
     NS_LOG_FUNCTION(IID << uid << parent);
     NS_ASSERT(parent <= m_information.size());
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     information->parent = parent;
 }
 
@@ -473,7 +474,7 @@
 IidManager::SetGroupName(uint16_t uid, std::string groupName)
 {
     NS_LOG_FUNCTION(IID << uid << groupName);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     information->groupName = groupName;
 }
 
@@ -481,7 +482,7 @@
 IidManager::SetSize(uint16_t uid, std::size_t size)
 {
     NS_LOG_FUNCTION(IID << uid << size);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     information->size = size;
 }
 
@@ -489,7 +490,7 @@
 IidManager::HideFromDocumentation(uint16_t uid)
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     information->mustHideFromDocumentation = true;
 }
 
@@ -497,7 +498,7 @@
 IidManager::AddConstructor(uint16_t uid, Callback<ObjectBase*> callback)
 {
     NS_LOG_FUNCTION(IID << uid << &callback);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     if (information->hasConstructor)
     {
         NS_FATAL_ERROR(information->name << " already has a constructor.");
@@ -538,7 +539,7 @@
 IidManager::GetName(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     NS_LOG_LOGIC(IIDL << information->name);
     return information->name;
 }
@@ -547,7 +548,7 @@
 IidManager::GetHash(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     TypeId::hash_t hash = information->hash;
     NS_LOG_LOGIC(IIDL << hash);
     return hash;
@@ -557,7 +558,7 @@
 IidManager::GetParent(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     uint16_t pid = information->parent;
     NS_LOG_LOGIC(IIDL << pid);
     return pid;
@@ -567,7 +568,7 @@
 IidManager::GetGroupName(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     NS_LOG_LOGIC(IIDL << information->groupName);
     return information->groupName;
 }
@@ -576,7 +577,7 @@
 IidManager::GetSize(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     std::size_t size = information->size;
     NS_LOG_LOGIC(IIDL << size);
     return size;
@@ -586,7 +587,7 @@
 IidManager::GetConstructor(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     if (!information->hasConstructor)
     {
         NS_FATAL_ERROR("Requested constructor for " << information->name
@@ -599,7 +600,7 @@
 IidManager::HasConstructor(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     bool hasC = information->hasConstructor;
     NS_LOG_LOGIC(IIDL << hasC);
     return hasC;
@@ -623,10 +624,10 @@
 IidManager::HasAttribute(uint16_t uid, std::string name)
 {
     NS_LOG_FUNCTION(IID << uid << name);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     while (true)
     {
-        for (std::vector<struct TypeId::AttributeInformation>::const_iterator i =
+        for (std::vector<TypeId::AttributeInformation>::const_iterator i =
                  information->attributes.begin();
              i != information->attributes.end();
              ++i)
@@ -637,7 +638,7 @@
                 return true;
             }
         }
-        struct IidInformation* parent = LookupInformation(information->parent);
+        IidInformation* parent = LookupInformation(information->parent);
         if (parent == information)
         {
             // top of inheritance tree
@@ -664,7 +665,7 @@
 {
     NS_LOG_FUNCTION(IID << uid << name << help << flags << initialValue << accessor << checker
                         << supportLevel << supportMsg);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     if (name.find(' ') != std::string::npos)
     {
         NS_FATAL_ERROR("Attribute name \"" << name << "\" may not contain spaces ' ', "
@@ -676,7 +677,7 @@
         NS_FATAL_ERROR("Attribute \"" << name << "\" already registered on tid=\""
                                       << information->name << "\"");
     }
-    struct TypeId::AttributeInformation info;
+    TypeId::AttributeInformation info;
     info.name = name;
     info.help = help;
     info.flags = flags;
@@ -696,7 +697,7 @@
                                      Ptr<const AttributeValue> initialValue)
 {
     NS_LOG_FUNCTION(IID << uid << i << initialValue);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     NS_ASSERT(i < information->attributes.size());
     information->attributes[i].initialValue = initialValue;
 }
@@ -705,17 +706,17 @@
 IidManager::GetAttributeN(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     std::size_t size = information->attributes.size();
     NS_LOG_LOGIC(IIDL << size);
     return size;
 }
 
-struct TypeId::AttributeInformation
+TypeId::AttributeInformation
 IidManager::GetAttribute(uint16_t uid, std::size_t i) const
 {
     NS_LOG_FUNCTION(IID << uid << i);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     NS_ASSERT(i < information->attributes.size());
     NS_LOG_LOGIC(IIDL << information->name);
     return information->attributes[i];
@@ -725,10 +726,10 @@
 IidManager::HasTraceSource(uint16_t uid, std::string name)
 {
     NS_LOG_FUNCTION(IID << uid << name);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     while (true)
     {
-        for (std::vector<struct TypeId::TraceSourceInformation>::const_iterator i =
+        for (std::vector<TypeId::TraceSourceInformation>::const_iterator i =
                  information->traceSources.begin();
              i != information->traceSources.end();
              ++i)
@@ -739,7 +740,7 @@
                 return true;
             }
         }
-        struct IidInformation* parent = LookupInformation(information->parent);
+        IidInformation* parent = LookupInformation(information->parent);
         if (parent == information)
         {
             // top of inheritance tree
@@ -764,13 +765,13 @@
 {
     NS_LOG_FUNCTION(IID << uid << name << help << accessor << callback << supportLevel
                         << supportMsg);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     if (HasTraceSource(uid, name))
     {
         NS_FATAL_ERROR("Trace source \"" << name << "\" already registered on tid=\""
                                          << information->name << "\"");
     }
-    struct TypeId::TraceSourceInformation source;
+    TypeId::TraceSourceInformation source;
     source.name = name;
     source.help = help;
     source.accessor = accessor;
@@ -785,17 +786,17 @@
 IidManager::GetTraceSourceN(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     std::size_t size = information->traceSources.size();
     NS_LOG_LOGIC(IIDL << size);
     return size;
 }
 
-struct TypeId::TraceSourceInformation
+TypeId::TraceSourceInformation
 IidManager::GetTraceSource(uint16_t uid, std::size_t i) const
 {
     NS_LOG_FUNCTION(IID << uid << i);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     NS_ASSERT(i < information->traceSources.size());
     NS_LOG_LOGIC(IIDL << information->name);
     return information->traceSources[i];
@@ -805,7 +806,7 @@
 IidManager::MustHideFromDocumentation(uint16_t uid) const
 {
     NS_LOG_FUNCTION(IID << uid);
-    struct IidInformation* information = LookupInformation(uid);
+    IidInformation* information = LookupInformation(uid);
     bool hide = information->mustHideFromDocumentation;
     NS_LOG_LOGIC(IIDL << hide);
     return hide;
@@ -894,7 +895,7 @@
 }
 
 bool
-TypeId::LookupAttributeByName(std::string name, struct TypeId::AttributeInformation* info) const
+TypeId::LookupAttributeByName(std::string name, TypeId::AttributeInformation* info) const
 {
     NS_LOG_FUNCTION(this << name << info);
     TypeId tid;
@@ -904,7 +905,7 @@
         tid = nextTid;
         for (std::size_t i = 0; i < tid.GetAttributeN(); i++)
         {
-            struct TypeId::AttributeInformation tmp = tid.GetAttribute(i);
+            TypeId::AttributeInformation tmp = tid.GetAttribute(i);
             if (tmp.name == name)
             {
                 if (tmp.supportLevel == TypeId::SUPPORTED)
@@ -1108,7 +1109,7 @@
     return n;
 }
 
-struct TypeId::AttributeInformation
+TypeId::AttributeInformation
 TypeId::GetAttribute(std::size_t i) const
 {
     NS_LOG_FUNCTION(this << i);
@@ -1119,7 +1120,7 @@
 TypeId::GetAttributeFullName(std::size_t i) const
 {
     NS_LOG_FUNCTION(this << i);
-    struct TypeId::AttributeInformation info = GetAttribute(i);
+    TypeId::AttributeInformation info = GetAttribute(i);
     return GetName() + "::" + info.name;
 }
 
@@ -1130,7 +1131,7 @@
     return IidManager::Get()->GetTraceSourceN(m_tid);
 }
 
-struct TypeId::TraceSourceInformation
+TypeId::TraceSourceInformation
 TypeId::GetTraceSource(std::size_t i) const
 {
     NS_LOG_FUNCTION(this << i);
@@ -1160,12 +1161,12 @@
 }
 
 Ptr<const TraceSourceAccessor>
-TypeId::LookupTraceSourceByName(std::string name, struct TraceSourceInformation* info) const
+TypeId::LookupTraceSourceByName(std::string name, TraceSourceInformation* info) const
 {
     NS_LOG_FUNCTION(this << name);
     TypeId tid;
     TypeId nextTid = *this;
-    struct TypeId::TraceSourceInformation tmp;
+    TypeId::TraceSourceInformation tmp;
     do
     {
         tid = nextTid;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/type-id.h ns-3.39/src/core/model/type-id.h
--- ns-3.38/src/core/model/type-id.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/core/model/type-id.h	2023-07-05 14:23:47.765427000 -0700
@@ -23,7 +23,6 @@
 #include "attribute-helper.h"
 #include "attribute.h"
 #include "callback.h"
-#include "deprecated.h"
 #include "hash.h"
 #include "trace-source-accessor.h"
 
@@ -262,7 +261,7 @@
      * \param [in] i Index into attribute array
      * \returns The information associated to attribute whose index is \pname{i}.
      */
-    struct TypeId::AttributeInformation GetAttribute(std::size_t i) const;
+    TypeId::AttributeInformation GetAttribute(std::size_t i) const;
     /**
      * Get the Attribute name by index.
      *
@@ -298,7 +297,7 @@
      * \param [in] i Index into trace source array.
      * \returns Detailed information about the requested trace source.
      */
-    struct TypeId::TraceSourceInformation GetTraceSource(std::size_t i) const;
+    TypeId::TraceSourceInformation GetTraceSource(std::size_t i) const;
 
     /**
      * Set the parent TypeId.
@@ -474,7 +473,7 @@
      *              will be stored.
      * \returns \c true if the requested attribute could be found.
      */
-    bool LookupAttributeByName(std::string name, struct AttributeInformation* info) const;
+    bool LookupAttributeByName(std::string name, AttributeInformation* info) const;
     /**
      * Find a TraceSource by name.
      *
@@ -497,9 +496,8 @@
      *  and disconnect trace sinks with the requested trace source on
      *  an object instance.
      */
-    Ptr<const TraceSourceAccessor> LookupTraceSourceByName(
-        std::string name,
-        struct TraceSourceInformation* info) const;
+    Ptr<const TraceSourceAccessor> LookupTraceSourceByName(std::string name,
+                                                           TraceSourceInformation* info) const;
 
     /**
      * Get the internal id of this TypeId.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/unix-fd-reader.cc ns-3.39/src/core/model/unix-fd-reader.cc
--- ns-3.38/src/core/model/unix-fd-reader.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/unix-fd-reader.cc	2023-07-05 14:23:47.765427000 -0700
@@ -218,7 +218,7 @@
 
         if (FD_ISSET(m_fd, &readfds))
         {
-            struct FdReader::Data data = DoRead();
+            FdReader::Data data = DoRead();
             // reading stops when m_len is zero
             if (data.m_len == 0)
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/val-array.h ns-3.39/src/core/model/val-array.h
--- ns-3.38/src/core/model/val-array.h	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/val-array.h	2023-07-05 14:23:47.765427000 -0700
@@ -89,7 +89,7 @@
      * \param numCols the number of columns
      * \param numPages the number of pages
      */
-    ValArray<T>(uint16_t numRows, uint16_t numCols = 1, uint16_t numPages = 1);
+    ValArray<T>(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.
@@ -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>(uint16_t numRows, uint16_t numCols, const std::valarray<T>& values);
+    ValArray<T>(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>(uint16_t numRows, uint16_t numCols, std::valarray<T>&& values);
+    ValArray<T>(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,10 +133,7 @@
      * \param numPages the number of pages
      * \param values valarray<T> that will be used to initialize elements of 3D array
      */
-    ValArray<T>(uint16_t numRows,
-                uint16_t numCols,
-                uint16_t numPages,
-                const std::valarray<T>& values);
+    ValArray<T>(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
@@ -146,7 +143,7 @@
      * \param numPages the number of pages
      * \param values valarray<T> that will be used to initialize elements of 3D array
      */
-    ValArray<T>(uint16_t numRows, uint16_t numCols, uint16_t numPages, std::valarray<T>&& values);
+    ValArray<T>(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;
     /** instruct the compiler to generate the implicitly declared copy constructor*/
@@ -168,15 +165,15 @@
     /**
      * \returns Number of rows
      */
-    uint16_t GetNumRows() const;
+    size_t GetNumRows() const;
     /**
      * \returns Number of columns
      */
-    uint16_t GetNumCols() const;
+    size_t GetNumCols() const;
     /**
      * \returns Number of pages, i.e., the number of 2D arrays
      */
-    uint16_t GetNumPages() const;
+    size_t GetNumPages() const;
     /**
      * \returns Total number of elements
      */
@@ -188,7 +185,7 @@
      * \param pageIndex The index of the page
      * \returns A const reference to the element with with rowIndex, colIndex and pageIndex indices.
      */
-    T& operator()(uint16_t rowIndex, uint16_t colIndex, uint16_t pageIndex);
+    T& operator()(size_t rowIndex, size_t colIndex, size_t pageIndex);
     /**
      * \brief Const access operator, with bound-checking in debug profile
      * \param rowIndex The index of the row
@@ -196,7 +193,7 @@
      * \param pageIndex The index of the page
      * \returns A const reference to the element with with rowIndex, colIndex and pageIndex indices.
      */
-    const T& operator()(uint16_t rowIndex, uint16_t colIndex, uint16_t pageIndex) const;
+    const T& operator()(size_t rowIndex, size_t colIndex, size_t pageIndex) const;
     /**
      * \brief Access operator for 2D ValArrays.
      * Assuming that the third dimension is equal to 1, e.g. ValArray contains
@@ -209,7 +206,7 @@
      * \param colIndex The index of the column
      * \returns A reference to the element with the specified indices
      */
-    T& operator()(uint16_t rowIndex, uint16_t colIndex);
+    T& operator()(size_t rowIndex, size_t colIndex);
     /**
      * \brief Const access operator for 2D ValArrays.
      * Assuming that the third dimension is equal to 1, e.g. ValArray contains
@@ -218,7 +215,7 @@
      * \param colIndex column index
      * \returns a Const reference to the value with the specified row and column index.
      */
-    const T& operator()(uint16_t rowIndex, uint16_t colIndex) const;
+    const T& operator()(size_t rowIndex, size_t colIndex) const;
     /**
      * \brief Single-element access operator() for 1D ValArrays.
      * Assuming that the number of columns and pages is equal to 1, e.g. ValArray
@@ -230,13 +227,13 @@
      * \param index The index of the 1D ValArray.
      * \returns A reference to the value with the specified index.
      */
-    T& operator()(uint16_t index);
+    T& operator()(size_t index);
     /**
      * \brief Single-element access operator() for 1D ValArrays.
      * \param index The index of the 1D ValArray.
      * \returns The const reference to the values with the specified index.
      */
-    const T& operator()(uint16_t index) const;
+    const T& operator()(size_t index) const;
     /**
      * \brief Element-wise multiplication with a scalar value.
      * \param rhs A scalar value of type T
@@ -302,14 +299,14 @@
      * \param pageIndex The index of the desired 2D array
      * \returns a pointer to the data elements of the 2D array
      */
-    T* GetPagePtr(uint16_t pageIndex);
+    T* GetPagePtr(size_t pageIndex);
     /**
      * \brief Get a data pointer to a specific 2D array for use in linear
      * algebra libraries
      * \param pageIndex An index of the desired 2D array
      * \returns a pointer to the data elements of the 2D array
      */
-    const T* GetPagePtr(uint16_t pageIndex) const;
+    const T* GetPagePtr(size_t pageIndex) const;
     /**
      * \brief Checks whether rhs and lhs ValArray objects have the same dimensions.
      * \param rhs The rhs ValArray
@@ -365,11 +362,11 @@
     const T& Elem(size_t row, size_t col, size_t page) const;
 
   protected:
-    uint16_t m_numRows =
+    size_t m_numRows =
         0; //!< The size of the first dimension, i.e., the number of rows of each 2D array
-    uint16_t m_numCols =
+    size_t m_numCols =
         0; //!< The size of the second dimension, i.e., the number of columns of each 2D array
-    uint16_t m_numPages = 0;   //!< The size of the third dimension, i.e., the number of 2D arrays
+    size_t m_numPages = 0;     //!< The size of the third dimension, i.e., the number of 2D arrays
     std::valarray<T> m_values; //!< The data values
 };
 
@@ -378,21 +375,21 @@
  ************************************************/
 
 template <class T>
-inline uint16_t
+inline size_t
 ValArray<T>::GetNumRows() const
 {
     return m_numRows;
 };
 
 template <class T>
-inline uint16_t
+inline size_t
 ValArray<T>::GetNumCols() const
 {
     return m_numCols;
 };
 
 template <class T>
-inline uint16_t
+inline size_t
 ValArray<T>::GetNumPages() const
 {
     return m_numPages;
@@ -407,7 +404,7 @@
 
 template <class T>
 inline T&
-ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex, uint16_t pageIndex)
+ValArray<T>::operator()(size_t rowIndex, size_t colIndex, size_t pageIndex)
 {
     NS_ASSERT_MSG(rowIndex < m_numRows, "Row index out of bounds");
     NS_ASSERT_MSG(colIndex < m_numCols, "Column index out of bounds");
@@ -418,7 +415,7 @@
 
 template <class T>
 inline const T&
-ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex, uint16_t pageIndex) const
+ValArray<T>::operator()(size_t rowIndex, size_t colIndex, size_t pageIndex) const
 {
     NS_ASSERT_MSG(rowIndex < m_numRows, "Row index out of bounds");
     NS_ASSERT_MSG(colIndex < m_numCols, "Column index out of bounds");
@@ -429,7 +426,7 @@
 
 template <class T>
 inline T&
-ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex)
+ValArray<T>::operator()(size_t rowIndex, size_t colIndex)
 {
     NS_ASSERT_MSG(m_numPages == 1, "Cannot use 2D access operator for 3D ValArray.");
     return (*this)(rowIndex, colIndex, 0);
@@ -437,7 +434,7 @@
 
 template <class T>
 inline const T&
-ValArray<T>::operator()(uint16_t rowIndex, uint16_t colIndex) const
+ValArray<T>::operator()(size_t rowIndex, size_t colIndex) const
 {
     NS_ASSERT_MSG(m_numPages == 1, "Cannot use 2D access operator for 3D ValArray.");
     return (*this)(rowIndex, colIndex, 0);
@@ -445,7 +442,7 @@
 
 template <class T>
 inline T&
-ValArray<T>::operator()(uint16_t index)
+ValArray<T>::operator()(size_t index)
 {
     NS_ASSERT_MSG(index < m_values.size(),
                   "Invalid index to 1D ValArray. The size of the array should be set through "
@@ -458,7 +455,7 @@
 
 template <class T>
 inline const T&
-ValArray<T>::operator()(uint16_t index) const
+ValArray<T>::operator()(size_t index) const
 {
     NS_ASSERT_MSG(index < m_values.size(),
                   "Invalid index to 1D ValArray.The size of the array should be set through "
@@ -522,7 +519,7 @@
 
 template <class T>
 inline T*
-ValArray<T>::GetPagePtr(uint16_t pageIndex)
+ValArray<T>::GetPagePtr(size_t pageIndex)
 {
     NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index.");
     return &(m_values[m_numRows * m_numCols * pageIndex]);
@@ -530,7 +527,7 @@
 
 template <class T>
 inline const T*
-ValArray<T>::GetPagePtr(uint16_t pageIndex) const
+ValArray<T>::GetPagePtr(size_t pageIndex) const
 {
     NS_ASSERT_MSG(pageIndex < m_numPages, "Invalid page index.");
     return &(m_values[m_numRows * m_numCols * pageIndex]);
@@ -584,7 +581,7 @@
  ************************************************/
 
 template <class T>
-ValArray<T>::ValArray(uint16_t numRows, uint16_t numCols, uint16_t numPages)
+ValArray<T>::ValArray(size_t numRows, size_t numCols, size_t numPages)
     : m_numRows{numRows},
       m_numCols{numCols},
       m_numPages{numPages}
@@ -594,7 +591,7 @@
 
 template <class T>
 ValArray<T>::ValArray(const std::valarray<T>& values)
-    : m_numRows{(uint16_t)values.size()},
+    : m_numRows{values.size()},
       m_numCols{1},
       m_numPages{1},
       m_values{values}
@@ -603,7 +600,7 @@
 
 template <class T>
 ValArray<T>::ValArray(std::valarray<T>&& values)
-    : m_numRows{(uint16_t)values.size()},
+    : m_numRows{values.size()},
       m_numCols{1},
       m_numPages{1},
       m_values{std::move(values)}
@@ -612,7 +609,7 @@
 
 template <class T>
 ValArray<T>::ValArray(const std::vector<T>& values)
-    : m_numRows{(uint16_t)values.size()},
+    : m_numRows{values.size()},
       m_numCols{1},
       m_numPages{1}
 {
@@ -621,7 +618,7 @@
 }
 
 template <class T>
-ValArray<T>::ValArray(uint16_t numRows, uint16_t numCols, const std::valarray<T>& values)
+ValArray<T>::ValArray(size_t numRows, size_t numCols, const std::valarray<T>& values)
     : m_numRows{numRows},
       m_numCols{numCols},
       m_numPages{1},
@@ -632,7 +629,7 @@
 };
 
 template <class T>
-ValArray<T>::ValArray(uint16_t numRows, uint16_t numCols, std::valarray<T>&& values)
+ValArray<T>::ValArray(size_t numRows, size_t numCols, std::valarray<T>&& values)
     : m_numRows{numRows},
       m_numCols{numCols},
       m_numPages{1}
@@ -643,9 +640,9 @@
 };
 
 template <class T>
-ValArray<T>::ValArray(uint16_t numRows,
-                      uint16_t numCols,
-                      uint16_t numPages,
+ValArray<T>::ValArray(size_t numRows,
+                      size_t numCols,
+                      size_t numPages,
                       const std::valarray<T>& values)
     : m_numRows{numRows},
       m_numCols{numCols},
@@ -657,10 +654,7 @@
 };
 
 template <class T>
-ValArray<T>::ValArray(uint16_t numRows,
-                      uint16_t numCols,
-                      uint16_t numPages,
-                      std::valarray<T>&& values)
+ValArray<T>::ValArray(size_t numRows, size_t numCols, size_t numPages, std::valarray<T>&& values)
     : m_numRows{numRows},
       m_numCols{numCols},
       m_numPages{numPages}
@@ -723,12 +717,12 @@
 operator<<(std::ostream& os, const ValArray<T>& a)
 {
     os << "\n";
-    for (auto p = 0; p != a.GetNumPages(); ++p)
+    for (size_t p = 0; p != a.GetNumPages(); ++p)
     {
         os << "Page " << p << ":\n";
-        for (auto i = 0; i != a.GetNumRows(); ++i)
+        for (size_t i = 0; i != a.GetNumRows(); ++i)
         {
-            for (auto j = 0; j != a.GetNumCols(); ++j)
+            for (size_t j = 0; j != a.GetNumCols(); ++j)
             {
                 os << "\t" << a(i, j, p);
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/version.cache ns-3.39/src/core/model/version.cache
--- ns-3.38/src/core/model/version.cache	2023-03-16 20:55:00.740985900 -0700
+++ ns-3.39/src/core/model/version.cache	2023-07-05 14:24:26.185557800 -0700
@@ -1,10 +1,10 @@
-CLOSEST_TAG = '"ns-3.38"'
-VERSION_COMMIT_HASH = '"g392e36833"'
+CLOSEST_TAG = '"ns-3.39"'
+VERSION_COMMIT_HASH = '"g10b9bc1af"'
 VERSION_DIRTY_FLAG = '0'
 VERSION_MAJOR = '3'
-VERSION_MINOR = '38'
+VERSION_MINOR = '39'
 VERSION_PATCH = '0'
 VERSION_RELEASE_CANDIDATE = '""'
-VERSION_TAG = '"ns-3.38"'
+VERSION_TAG = '"ns-3.39"'
 VERSION_TAG_DISTANCE = '0'
 VERSION_BUILD_PROFILE = 'default'
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/wall-clock-synchronizer.cc ns-3.39/src/core/model/wall-clock-synchronizer.cc
--- ns-3.38/src/core/model/wall-clock-synchronizer.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/wall-clock-synchronizer.cc	2023-07-05 14:23:47.769427300 -0700
@@ -227,7 +227,7 @@
         // interrupted by a Signal.  In this case, we need to return and let the
         // simulator re-evaluate what to do.
         //
-        if (SleepWait((numberJiffies - 3) * m_jiffy) == false)
+        if (!SleepWait((numberJiffies - 3) * m_jiffy))
         {
             NS_LOG_INFO("SleepWait interrupted");
             return false;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/model/win32-fd-reader.cc ns-3.39/src/core/model/win32-fd-reader.cc
--- ns-3.38/src/core/model/win32-fd-reader.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/model/win32-fd-reader.cc	2023-07-05 14:23:47.769427300 -0700
@@ -225,7 +225,7 @@
 
         if (FD_ISSET(m_fd, &readfds))
         {
-            struct FdReader::Data data = DoRead();
+            FdReader::Data data = DoRead();
             // reading stops when m_len is zero
             if (data.m_len == 0)
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/attribute-container-test-suite.cc ns-3.39/src/core/test/attribute-container-test-suite.cc
--- ns-3.38/src/core/test/attribute-container-test-suite.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/test/attribute-container-test-suite.cc	2023-07-05 14:23:47.769427300 -0700
@@ -32,7 +32,6 @@
 #include <iterator>
 #include <list>
 #include <map>
-#include <sstream>
 #include <utility>
 #include <vector>
 
@@ -59,7 +58,7 @@
     /**
      * Reverses the list of doubles.
      */
-    void ReverseList();
+    void ReverseDoubleList();
 
     /**
      * \brief Get the type ID.
@@ -68,6 +67,19 @@
     static TypeId GetTypeId();
 
     /**
+     * Set the list of doubles to the given list
+     *
+     * \param doubleList the given list
+     */
+    void SetDoubleList(const std::list<double>& doubleList);
+    /**
+     * Get the list of doubles
+     *
+     * \return the list of doubles
+     */
+    std::list<double> GetDoubleList() const;
+
+    /**
      * Set the vector of ints to the given vector
      *
      * \param vec the given vector
@@ -80,20 +92,12 @@
      */
     std::vector<int> GetIntVec() const;
 
-    /**
-     * \brief Stream insertion operator.
-     *
-     * \param [in] os The reference to the output stream.
-     * \param [in] obj The AttributeContainer object.
-     * \returns The reference to the output stream.
-     */
-    friend std::ostream& operator<<(std::ostream& os, const AttributeContainerObject& obj);
-
   private:
     std::list<double> m_doublelist; //!< List of doubles.
     std::vector<int> m_intvec;      //!< Vector of ints.
     // TODO(jared): need PairValue attributevalue to handle std::pair elements
-    std::map<std::string, int> m_map; //!< Map of <std::string, int>.
+    std::map<std::string, int> m_map;                         //!< Map of <std::string, int>.
+    std::map<int64_t, std::list<int64_t>> m_intVecIntMapping; //!< Mapping integers to vectors
 };
 
 AttributeContainerObject::AttributeContainerObject()
@@ -107,6 +111,8 @@
 TypeId
 AttributeContainerObject::GetTypeId()
 {
+    using IntVecMapValue = PairValue<IntegerValue, AttributeContainerValue<IntegerValue>>;
+
     static TypeId tid =
         TypeId("ns3::AttributeContainerObject")
             .SetParent<Object>()
@@ -118,15 +124,16 @@
                           MakeAttributeContainerAccessor<DoubleValue>(
                               &AttributeContainerObject::m_doublelist),
                           MakeAttributeContainerChecker<DoubleValue>(MakeDoubleChecker<double>()))
-            .AddAttribute("IntegerVector",
-                          "Vector of integers",
-                          // the container value container differs from the underlying object
-                          AttributeContainerValue<IntegerValue>(),
-                          // the type of the underlying container cannot be deduced
-                          MakeAttributeContainerAccessor<IntegerValue, std::list>(
-                              &AttributeContainerObject::SetIntVec,
-                              &AttributeContainerObject::GetIntVec),
-                          MakeAttributeContainerChecker<IntegerValue>(MakeIntegerChecker<int>()))
+            .AddAttribute(
+                "IntegerVector",
+                "Vector of integers",
+                // the container value container differs from the underlying object
+                AttributeContainerValue<IntegerValue>(),
+                // the type of the underlying container cannot be deduced
+                MakeAttributeContainerAccessor<IntegerValue, ';', std::list>(
+                    &AttributeContainerObject::SetIntVec,
+                    &AttributeContainerObject::GetIntVec),
+                MakeAttributeContainerChecker<IntegerValue, ';'>(MakeIntegerChecker<int>()))
             .AddAttribute(
                 "MapStringInt",
                 "Map of strings to ints",
@@ -136,17 +143,42 @@
                     &AttributeContainerObject::m_map),
                 MakeAttributeContainerChecker<PairValue<StringValue, IntegerValue>>(
                     MakePairChecker<StringValue, IntegerValue>(MakeStringChecker(),
-                                                               MakeIntegerChecker<int>())));
+                                                               MakeIntegerChecker<int>())))
+            .AddAttribute(
+                "IntVecPairVec",
+                "An example of complex attribute that is defined by a vector of pairs consisting "
+                "of an integer value and a vector of integers. In case a string is used to set "
+                "this attribute, the string shall contain the pairs separated by a semicolon (;); "
+                "in every pair, the integer value and the vector of integers are separated by a "
+                "blank space, and the elements of the vectors are separated by a comma (,) "
+                "without spaces. E.g. \"0 1,2,3; 1 0; 2 0,1\" consists of three pairs containing "
+                "vectors of 3, 1 and 2 elements, respectively.",
+                StringValue(""),
+                MakeAttributeContainerAccessor<IntVecMapValue, ';'>(
+                    &AttributeContainerObject::m_intVecIntMapping),
+                MakeAttributeContainerChecker<IntVecMapValue, ';'>(
+                    MakePairChecker<IntegerValue, AttributeContainerValue<IntegerValue>>(
+                        MakeIntegerChecker<int>(),
+                        MakeAttributeContainerChecker<IntegerValue>(MakeIntegerChecker<int>()))));
     return tid;
 }
 
 void
-AttributeContainerObject::ReverseList()
+AttributeContainerObject::ReverseDoubleList()
 {
     m_doublelist.reverse();
-    std::vector<int> tmp;
-    std::copy_backward(m_intvec.begin(), m_intvec.end(), tmp.begin());
-    m_intvec = tmp;
+}
+
+void
+AttributeContainerObject::SetDoubleList(const std::list<double>& doubleList)
+{
+    m_doublelist = doubleList;
+}
+
+std::list<double>
+AttributeContainerObject::GetDoubleList() const
+{
+    return m_doublelist;
 }
 
 void
@@ -161,23 +193,6 @@
     return m_intvec;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const AttributeContainerObject& obj)
-{
-    os << "AttributeContainerObject: ";
-    bool first = true;
-    for (auto d : obj.m_doublelist)
-    {
-        if (!first)
-        {
-            os << ", ";
-        }
-        os << d;
-        first = false;
-    }
-    return os;
-}
-
 /**
  * \ingroup attribute-tests
  *
@@ -256,7 +271,7 @@
         auto ref = {"one", "two", "three"};
         AttributeContainerValue<StringValue> ac(ref.begin(), ref.end());
 
-        NS_TEST_ASSERT_MSG_EQ(3, ac.GetN(), "Container size mismatch");
+        NS_TEST_ASSERT_MSG_EQ(ref.size(), ac.GetN(), "Container size mismatch");
         auto aciter = ac.Begin();
         for (auto v : ref)
         {
@@ -269,9 +284,9 @@
 
     {
         auto ref = {"one", "two", "three"};
-        AttributeContainerValue<StringValue, std::vector> ac(ref);
+        AttributeContainerValue<StringValue, ',', std::vector> ac(ref);
 
-        NS_TEST_ASSERT_MSG_EQ(3, ac.GetN(), "Container size mismatch");
+        NS_TEST_ASSERT_MSG_EQ(ref.size(), ac.GetN(), "Container size mismatch");
         auto aciter = ac.Begin();
         for (auto v : ref)
         {
@@ -287,7 +302,7 @@
         std::map<std::string, int64_t> ref = {{"one", 1}, {"two", 2}, {"three", 3}};
         AttributeContainerValue<PairValue<StringValue, IntegerValue>> ac(ref);
 
-        NS_TEST_ASSERT_MSG_EQ(3, ac.GetN(), "Container size mismatch");
+        NS_TEST_ASSERT_MSG_EQ(ref.size(), ac.GetN(), "Container size mismatch");
         auto aciter = ac.Begin();
         for (const auto& v : ref)
         {
@@ -366,7 +381,7 @@
     {
         std::string strings = "this is a sentence with words";
 
-        AttributeContainerValue<StringValue> attr(' ');
+        AttributeContainerValue<StringValue, ' '> attr;
         auto checker = MakeAttributeContainerChecker(attr);
         auto acchecker = DynamicCast<AttributeContainerChecker>(checker);
         acchecker->SetItemChecker(MakeStringChecker());
@@ -376,8 +391,7 @@
         NS_TEST_ASSERT_MSG_EQ(attr.GetN(), 6, "Incorrect container size");
 
         std::string reserialized = attr.SerializeToString(checker);
-        std::string canonical = strings;
-        NS_TEST_ASSERT_MSG_EQ(reserialized, canonical, "Reserialization failed");
+        NS_TEST_ASSERT_MSG_EQ(reserialized, strings, "Reserialization failed");
     }
 
     {
@@ -394,8 +408,7 @@
         NS_TEST_ASSERT_MSG_EQ(attr.GetN(), 3, "Incorrect container size");
 
         std::string reserialized = attr.SerializeToString(checker);
-        std::string canonical = pairs;
-        NS_TEST_ASSERT_MSG_EQ(reserialized, canonical, "Reserealization failed");
+        NS_TEST_ASSERT_MSG_EQ(reserialized, pairs, "Reserealization failed");
     }
 }
 
@@ -427,29 +440,24 @@
 {
     Ptr<AttributeContainerObject> obj = CreateObject<AttributeContainerObject>();
     {
-        std::ostringstream oss;
-        oss << *obj;
-        NS_TEST_ASSERT_MSG_EQ(oss.str(),
-                              "AttributeContainerObject: ",
-                              "DoubleList initialized incorrectly");
+        auto doubleList = obj->GetDoubleList();
+        NS_TEST_ASSERT_MSG_EQ(doubleList.empty(), true, "DoubleList initialized incorrectly");
     }
 
-    std::list<double> doubles = {1.1, 2.22, 3.333};
+    const std::list<double> doubles = {1.1, 2.22, 3.333};
     obj->SetAttribute("DoubleList", AttributeContainerValue<DoubleValue>(doubles));
     {
-        std::ostringstream oss;
-        oss << *obj;
-        NS_TEST_ASSERT_MSG_EQ(oss.str(),
-                              "AttributeContainerObject: 1.1, 2.22, 3.333",
+        auto doubleList = obj->GetDoubleList();
+        NS_TEST_ASSERT_MSG_EQ(std::equal(doubles.begin(), doubles.end(), doubleList.begin()),
+                              true,
                               "DoubleList incorrectly set");
     }
 
-    obj->ReverseList();
+    obj->ReverseDoubleList();
     {
-        std::ostringstream oss;
-        oss << *obj;
-        NS_TEST_ASSERT_MSG_EQ(oss.str(),
-                              "AttributeContainerObject: 3.333, 2.22, 1.1",
+        auto doubleList = obj->GetDoubleList();
+        NS_TEST_ASSERT_MSG_EQ(std::equal(doubles.rbegin(), doubles.rend(), doubleList.begin()),
+                              true,
                               "DoubleList incorrectly reversed");
 
         // NOTE: changing the return container here too!
@@ -459,32 +467,58 @@
 
         AttributeContainerValue<DoubleValue>::result_type doublevec = value.Get();
         NS_TEST_ASSERT_MSG_EQ(doubles.size(), doublevec.size(), "DoublesVec wrong size");
-        auto iter = doubles.rbegin();
-        for (auto d : doublevec)
-        {
-            NS_TEST_ASSERT_MSG_EQ(d, *iter, "Incorrect value in doublesvec");
-            ++iter;
-        }
+        NS_TEST_ASSERT_MSG_EQ(std::equal(doubles.rbegin(), doubles.rend(), doublevec.begin()),
+                              true,
+                              "Incorrect value in doublesvec");
     }
 
-    std::vector<int> ints = {-1, 0, 1, 2, 3};
+    const std::vector<int> ints = {-1, 0, 1, 2, 3};
     // NOTE: here the underlying attribute container type differs from the actual container
-    obj->SetAttribute("IntegerVector", AttributeContainerValue<IntegerValue>(ints));
+    obj->SetAttribute("IntegerVector", AttributeContainerValue<IntegerValue, ';'>(ints));
 
     {
         // NOTE: changing the container here too!
-        AttributeContainerValue<IntegerValue> value;
+        AttributeContainerValue<IntegerValue, ';'> value;
         obj->GetAttribute("IntegerVector", value);
         NS_TEST_ASSERT_MSG_EQ(ints.size(), value.GetN(), "AttributeContainerValue wrong size");
 
         AttributeContainerValue<IntegerValue>::result_type intlist = value.Get();
         NS_TEST_ASSERT_MSG_EQ(ints.size(), intlist.size(), "Intvec wrong size");
-        auto iter = ints.begin();
-        for (auto d : intlist)
-        {
-            NS_TEST_ASSERT_MSG_EQ(d, *iter, "Incorrect value in intvec");
-            ++iter;
-        }
+
+        NS_TEST_ASSERT_MSG_EQ(std::equal(ints.begin(), ints.end(), intlist.begin()),
+                              true,
+                              "Incorrect value in intvec");
+    }
+
+    std::string intVecPairString("0 1,2,3; 1 0; 2 0,1");
+    // NOTE: here the underlying attribute container type differs from the actual container
+    obj->SetAttribute("IntVecPairVec", StringValue(intVecPairString));
+
+    {
+        using IntVecMapValue = PairValue<IntegerValue, AttributeContainerValue<IntegerValue>>;
+
+        // NOTE: changing the container here too!
+        AttributeContainerValue<IntVecMapValue, ';'> value;
+        obj->GetAttribute("IntVecPairVec", value);
+        NS_TEST_ASSERT_MSG_EQ(3, value.GetN(), "AttributeContainerValue wrong size"); // 3 pairs
+
+        AttributeContainerValue<IntVecMapValue>::result_type reslist = value.Get();
+        NS_TEST_ASSERT_MSG_EQ(3, reslist.size(), "IntVecMapValue wrong size");
+        auto reslistIt = reslist.begin();
+        NS_TEST_ASSERT_MSG_EQ(reslistIt->first, 0, "Incorrect integer value in first pair");
+        NS_TEST_ASSERT_MSG_EQ(reslistIt->second.size(),
+                              3,
+                              "Incorrect number of integer values in first pair");
+        ++reslistIt;
+        NS_TEST_ASSERT_MSG_EQ(reslistIt->first, 1, "Incorrect integer value in second pair");
+        NS_TEST_ASSERT_MSG_EQ(reslistIt->second.size(),
+                              1,
+                              "Incorrect number of integer values in second pair");
+        ++reslistIt;
+        NS_TEST_ASSERT_MSG_EQ(reslistIt->first, 2, "Incorrect integer value in third pair");
+        NS_TEST_ASSERT_MSG_EQ(reslistIt->second.size(),
+                              2,
+                              "Incorrect number of integer values in third pair");
     }
 
     std::map<std::string, int> map = {{"one", 1}, {"two", 2}, {"three", 3}};
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/attribute-test-suite.cc ns-3.39/src/core/test/attribute-test-suite.cc
--- ns-3.38/src/core/test/attribute-test-suite.cc	2023-03-16 20:54:23.596868500 -0700
+++ ns-3.39/src/core/test/attribute-test-suite.cc	2023-07-05 14:23:47.769427300 -0700
@@ -237,6 +237,11 @@
                               ObjectMapValue(),
                               MakeObjectMapAccessor(&AttributeObjectTest::m_map1),
                               MakeObjectMapChecker<Derived>())
+                .AddAttribute("TestUnorderedMap",
+                              "help text",
+                              ObjectMapValue(),
+                              MakeObjectMapAccessor(&AttributeObjectTest::m_unorderedMap),
+                              MakeObjectMapChecker<Derived>())
                 .AddAttribute("IntegerTraceSource1",
                               "help text",
                               IntegerValue(-2),
@@ -351,6 +356,24 @@
     }
 
     /**
+     * Adds an object to the unordered map.
+     * \param i The index to assign to the object.
+     */
+    void AddToUnorderedMap(uint64_t i)
+    {
+        m_unorderedMap.insert({i, CreateObject<Derived>()});
+    }
+
+    /**
+     * Remove an object from the first map.
+     * \param i The index to assign to the object.
+     */
+    void RemoveFromUnorderedMap(uint64_t i)
+    {
+        m_unorderedMap.erase(i);
+    }
+
+    /**
      * Invoke the m_cb callback.
      * \param a The first argument of the callback.
      * \param b The second argument of the callback.
@@ -477,15 +500,17 @@
     int16_t m_int16SetGet;                   //!< 16-bit integer set-get.
     uint8_t m_uint8;                         //!< 8-bit integer.
     float m_float;                           //!< float.
-    enum Test_e m_enum;                      //!< Enum.
-    enum Test_e m_enumSetGet;                //!< Enum set-get.
+    Test_e m_enum;                           //!< Enum.
+    Test_e m_enumSetGet;                     //!< Enum 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.
     std::map<uint32_t, Ptr<Derived>> m_map1; //!< Map of uint32_t, derived objects.
-    Callback<void, int8_t> m_cbValue;        //!< Callback accepting an integer.
-    TracedValue<int8_t> m_intSrc1;           //!< First int8_t Traced value.
-    TracedValue<int8_t> m_intSrc2;           //!< Second int8_t Traced value.
+    std::unordered_map<uint64_t, Ptr<Derived>>
+        m_unorderedMap;               //!< Unordered map of uint64_t, derived objects.
+    Callback<void, int8_t> m_cbValue; //!< Callback accepting an integer.
+    TracedValue<int8_t> m_intSrc1;    //!< First int8_t Traced value.
+    TracedValue<int8_t> m_intSrc2;    //!< Second int8_t Traced value.
 
     /// Traced callbacks for (double, int, float) values.
     typedef void (*NumericTracedCallback)(double, int, float);
@@ -1329,6 +1354,42 @@
     //
     p->GetAttribute("TestMap1", map);
     NS_TEST_ASSERT_MSG_EQ(map.GetN(), 2, "ObjectVectorValue \"TestMap1\" should be incremented");
+
+    //
+    // Test that ObjectMapValue is iterable with an underlying unordered_map
+    //
+    ObjectMapValue unorderedMap;
+    // Add objects at 1, 2, 3, 4
+    p->AddToUnorderedMap(4);
+    p->AddToUnorderedMap(2);
+    p->AddToUnorderedMap(1);
+    p->AddToUnorderedMap(3);
+    // Remove object 2
+    p->RemoveFromUnorderedMap(2);
+    p->GetAttribute("TestUnorderedMap", unorderedMap);
+    NS_TEST_ASSERT_MSG_EQ(unorderedMap.GetN(),
+                          3,
+                          "ObjectMapValue \"TestUnorderedMap\" should have three values");
+    Ptr<Object> o1 = unorderedMap.Get(1);
+    NS_TEST_ASSERT_MSG_NE(o1,
+                          nullptr,
+                          "ObjectMapValue \"TestUnorderedMap\" should have value with key 1");
+    Ptr<Object> o2 = unorderedMap.Get(2);
+    NS_TEST_ASSERT_MSG_EQ(o2,
+                          nullptr,
+                          "ObjectMapValue \"TestUnorderedMap\" should not have value with key 2");
+    auto it = unorderedMap.Begin();
+    NS_TEST_ASSERT_MSG_EQ(it->first,
+                          1,
+                          "ObjectMapValue \"TestUnorderedMap\" should have a value with key 1");
+    it++;
+    NS_TEST_ASSERT_MSG_EQ(it->first,
+                          3,
+                          "ObjectMapValue \"TestUnorderedMap\" should have a value with key 3");
+    it++;
+    NS_TEST_ASSERT_MSG_EQ(it->first,
+                          4,
+                          "ObjectMapValue \"TestUnorderedMap\" should have a value with key 4");
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/examples-to-run.py ns-3.39/src/core/test/examples-to-run.py
--- ns-3.38/src/core/test/examples-to-run.py	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/examples-to-run.py	2023-07-05 14:23:47.769427300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/matrix-array-test-suite.cc ns-3.39/src/core/test/matrix-array-test-suite.cc
--- ns-3.38/src/core/test/matrix-array-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/matrix-array-test-suite.cc	2023-07-05 14:23:47.769427300 -0700
@@ -38,7 +38,7 @@
 
 /**
  * \ingroup matrixArray-tests
- *  MatrixArray test for testing constructors
+ *  MatrixArray test case for testing constructors, operators and other functions
  */
 template <class T>
 class MatrixArrayTestCase : public TestCase
@@ -50,7 +50,7 @@
      *
      * \param [in] name reference name
      */
-    MatrixArrayTestCase<T>(const std::string name);
+    MatrixArrayTestCase<T>(const std::string& name);
 
     /** Destructor. */
     ~MatrixArrayTestCase<T>() override;
@@ -83,7 +83,7 @@
 };
 
 template <class T>
-MatrixArrayTestCase<T>::MatrixArrayTestCase(const std::string name)
+MatrixArrayTestCase<T>::MatrixArrayTestCase(const std::string& name)
     : TestCase(name)
 {
 }
@@ -100,9 +100,9 @@
     // test multiplication of matrices (MatrixArray containing only 1 matrix)
     MatrixArray<T> m1 = MatrixArray<T>(2, 3);
     MatrixArray<T> m2 = MatrixArray<T>(m1.GetNumCols(), m1.GetNumRows());
-    for (auto i = 0; i < m1.GetNumRows(); ++i)
+    for (size_t i = 0; i < m1.GetNumRows(); ++i)
     {
-        for (auto j = 0; j < m1.GetNumCols(); ++j)
+        for (size_t j = 0; j < m1.GetNumCols(); ++j)
         {
             m1(i, j) = 1;
             m2(j, i) = 1;
@@ -121,9 +121,9 @@
     NS_TEST_ASSERT_MSG_EQ(m3.GetNumRows(),
                           m3.GetNumCols(),
                           "The number of rows and cols should be equal");
-    for (auto i = 0; i < m3.GetNumCols(); ++i)
+    for (size_t i = 0; i < m3.GetNumCols(); ++i)
     {
-        for (auto j = 0; j < m3.GetNumRows(); ++j)
+        for (size_t j = 0; j < m3.GetNumRows(); ++j)
         {
             NS_TEST_ASSERT_MSG_EQ(std::real(m3(i, j)),
                                   m1.GetNumCols(),
@@ -133,9 +133,9 @@
 
     // multiplication with a scalar value
     MatrixArray<T> m4 = m3 * (static_cast<T>(5.0));
-    for (auto i = 0; i < m4.GetNumCols(); ++i)
+    for (size_t i = 0; i < m4.GetNumCols(); ++i)
     {
-        for (auto j = 0; j < m4.GetNumRows(); ++j)
+        for (size_t j = 0; j < m4.GetNumRows(); ++j)
         {
             NS_TEST_ASSERT_MSG_EQ(m3(i, j) * (static_cast<T>(5.0)),
                                   m4(i, j),
@@ -147,11 +147,11 @@
     // test multiplication of arrays of matrices
     MatrixArray<T> m5 = MatrixArray<T>(2, 3, 2);
     MatrixArray<T> m6 = MatrixArray<T>(m5.GetNumCols(), m5.GetNumRows(), m5.GetNumPages());
-    for (auto p = 0; p < m5.GetNumPages(); ++p)
+    for (size_t p = 0; p < m5.GetNumPages(); ++p)
     {
-        for (auto i = 0; i < m5.GetNumRows(); ++i)
+        for (size_t i = 0; i < m5.GetNumRows(); ++i)
         {
-            for (auto j = 0; j < m5.GetNumCols(); ++j)
+            for (size_t j = 0; j < m5.GetNumCols(); ++j)
             {
                 m5(i, j, p) = 1;
                 m6(j, i, p) = 1;
@@ -169,11 +169,11 @@
                           m7.GetNumCols(),
                           "The number of rows and cols should be equal");
 
-    for (auto p = 0; p < m7.GetNumPages(); ++p)
+    for (size_t p = 0; p < m7.GetNumPages(); ++p)
     {
-        for (auto i = 0; i < m7.GetNumCols(); ++i)
+        for (size_t i = 0; i < m7.GetNumCols(); ++i)
         {
-            for (auto j = 0; j < m7.GetNumRows(); ++j)
+            for (size_t j = 0; j < m7.GetNumRows(); ++j)
             {
                 NS_TEST_ASSERT_MSG_EQ(std::real(m7(i, j, p)),
                                       m5.GetNumCols(),
@@ -191,6 +191,27 @@
     NS_TEST_ASSERT_MSG_EQ(m6, m8, "These two matrices should be equal");
     NS_LOG_INFO("m8 = m5.Transpose ()" << m8);
 
+    // test transpose using initialization arrays
+    std::valarray<int> a{0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};
+    std::valarray<int> b{0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4};
+    std::valarray<T> aCasted(a.size());
+    std::valarray<T> bCasted(b.size());
+    for (size_t i = 0; i < a.size(); ++i)
+    {
+        aCasted[i] = static_cast<T>(a[i]);
+    }
+    for (size_t i = 0; i < b.size(); ++i)
+    {
+        bCasted[i] = static_cast<T>(b[i]);
+    }
+    m5 = MatrixArray<T>(3, 5, 1, aCasted);
+    m6 = MatrixArray<T>(5, 3, 1, bCasted);
+    m8 = m5.Transpose();
+    NS_TEST_ASSERT_MSG_EQ(m6, m8, "These two matrices should be equal");
+    NS_LOG_INFO("m5 (3, 5, 1):" << m5);
+    NS_LOG_INFO("m6 (5, 3, 1):" << m6);
+    NS_LOG_INFO("m8 (5, 3, 1) = m5.Transpose ()" << m8);
+
     // test 1D array creation, i.e. vector and transposing it
     MatrixArray<T> m9 = MatrixArray<T>(std::vector<T>({0, 1, 2, 3, 4, 5, 6, 7}));
     NS_TEST_ASSERT_MSG_EQ((m9.GetNumRows() == 8) && (m9.GetNumCols() == 1) &&
@@ -217,12 +238,14 @@
     NS_TEST_ASSERT_MSG_EQ(m10, m9, "m10 and m9 should be equal");
 
     // test multiplication by using an initialization matrixArray
-    std::valarray<int> a{0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5};
-    std::valarray<int> b{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
+    // matrix dimensions in each page are 2x3, 3x2, and the resulting matrix per page is a square
+    // matrix 2x2
+    a = {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5};
+    b = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
     std::valarray<int> c{2, 3, 4, 6, 2, 3, 4, 6};
-    std::valarray<T> aCasted(a.size());
-    std::valarray<T> bCasted(b.size());
-    std::valarray<T> cCasted(c.size());
+    aCasted = std::valarray<T>(a.size());
+    bCasted = std::valarray<T>(b.size());
+    std::valarray<T> cCasted = std::valarray<T>(c.size());
 
     for (size_t i = 0; i < a.size(); ++i)
     {
@@ -247,9 +270,88 @@
                           m14.GetNumRows(),
                           "The number of rows is not as expected.");
     NS_TEST_ASSERT_MSG_EQ(m13, m14, "The values are not equal.");
-    NS_LOG_INFO("m11:" << m11);
-    NS_LOG_INFO("m12:" << m12);
-    NS_LOG_INFO("m13 = matrixArrayA * matrixArrayB:" << m13);
+    NS_LOG_INFO("m11 (2,3,2):" << m11);
+    NS_LOG_INFO("m12 (3,2,2):" << m12);
+    NS_LOG_INFO("m13 = m11 * m12:" << m13);
+
+    // test multiplication by using an initialization matrixArray
+    // matrices have different number of elements per page
+    // matrix dimensions in each page are 4x3, 3x2, and the resulting matrix
+    // dimensions are 4x2
+    a = std::valarray<int>(
+        {0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5});
+    b = std::valarray<int>({0, 1, 0, 1, 0, 1, 0, 10, 0, 10, 0, 10});
+    c = std::valarray<int>({2, 3, 2, 3, 4, 6, 4, 6, 20, 30, 20, 30, 40, 60, 40, 60});
+    aCasted = std::valarray<T>(a.size());
+    bCasted = std::valarray<T>(b.size());
+    cCasted = std::valarray<T>(c.size());
+
+    for (size_t i = 0; i < a.size(); ++i)
+    {
+        aCasted[i] = static_cast<T>(a[i]);
+    }
+    for (size_t i = 0; i < b.size(); ++i)
+    {
+        bCasted[i] = static_cast<T>(b[i]);
+    }
+    for (size_t i = 0; i < c.size(); ++i)
+    {
+        cCasted[i] = static_cast<T>(c[i]);
+    }
+
+    m11 = MatrixArray<T>(4, 3, 2, aCasted);
+    m12 = MatrixArray<T>(3, 2, 2, bCasted);
+    m13 = m11 * m12;
+    m14 = MatrixArray<T>(4, 2, 2, cCasted);
+    NS_TEST_ASSERT_MSG_EQ(m13.GetNumCols(),
+                          m14.GetNumCols(),
+                          "The number of columns is not as expected.");
+    NS_TEST_ASSERT_MSG_EQ(m13.GetNumRows(),
+                          m14.GetNumRows(),
+                          "The number of rows is not as expected.");
+    NS_TEST_ASSERT_MSG_EQ(m13, m14, "The values are not equal.");
+    NS_LOG_INFO("m11 (4,3,2):" << m11);
+    NS_LOG_INFO("m12 (3,2,2):" << m12);
+    NS_LOG_INFO("m13 = m11 * m12:" << m13);
+
+    // test multiplication by using an initialization matrixArray
+    // matrices have different number of elements per page
+    // matrix dimensions in each page are 1x3, 3x2, and the resulting matrix has
+    // dimensions 1x2
+    a = std::valarray<int>({5, 4, 5, 5, 4, 5});
+    b = std::valarray<int>({0, 1, 0, 1, 0, 1, 1, 2, 3, 10, 100, 1000});
+    c = std::valarray<int>({4, 10, 28, 5450});
+    aCasted = std::valarray<T>(a.size());
+    bCasted = std::valarray<T>(b.size());
+    cCasted = std::valarray<T>(c.size());
+
+    for (size_t i = 0; i < a.size(); ++i)
+    {
+        aCasted[i] = static_cast<T>(a[i]);
+    }
+    for (size_t i = 0; i < b.size(); ++i)
+    {
+        bCasted[i] = static_cast<T>(b[i]);
+    }
+    for (size_t i = 0; i < c.size(); ++i)
+    {
+        cCasted[i] = static_cast<T>(c[i]);
+    }
+
+    m11 = MatrixArray<T>(1, 3, 2, aCasted);
+    m12 = MatrixArray<T>(3, 2, 2, bCasted);
+    m13 = m11 * m12;
+    m14 = MatrixArray<T>(1, 2, 2, cCasted);
+    NS_TEST_ASSERT_MSG_EQ(m13.GetNumCols(),
+                          m14.GetNumCols(),
+                          "The number of columns is not as expected.");
+    NS_TEST_ASSERT_MSG_EQ(m13.GetNumRows(),
+                          m14.GetNumRows(),
+                          "The number of rows is not as expected.");
+    NS_TEST_ASSERT_MSG_EQ(m13, m14, "The values are not equal.");
+    NS_LOG_INFO("m11 (1,3,2):" << m11);
+    NS_LOG_INFO("m12 (3,2,2):" << m12);
+    NS_LOG_INFO("m13 = m11 * m12:" << m13);
 
     // test MultiplyByLeftAndRightMatrix
     std::valarray<int> d{1, 1, 1};
@@ -320,23 +422,100 @@
     NS_LOG_INFO("m24 = m20 * m22 * m21" << m24);
 
     // test initialization with moving
+    size_t lCastedSize = lCasted.size();
     NS_LOG_INFO("size() of lCasted before move: " << lCasted.size());
     MatrixArray<T> m25 = MatrixArray<T>(2, 2, 2, std::move(lCasted));
-    NS_LOG_INFO("size() of lCasted after move: " << lCasted.size());
     NS_LOG_INFO("m25.GetSize ()" << m25.GetSize());
-    NS_TEST_ASSERT_MSG_EQ(lCasted.size(), 0, "The size of lCasted should be 0.");
+    NS_TEST_ASSERT_MSG_EQ(lCastedSize, m25.GetSize(), "The number of elements are not equal.");
 
+    size_t hCastedSize = hCasted.size();
     NS_LOG_INFO("size() of hCasted before move: " << hCasted.size());
     MatrixArray<T> m26 = MatrixArray<T>(2, 3, std::move(hCasted));
-    NS_LOG_INFO("size() of hCasted after move: " << hCasted.size());
     NS_LOG_INFO("m26.GetSize ()" << m26.GetSize());
-    NS_TEST_ASSERT_MSG_EQ(hCasted.size(), 0, "The size of hCasted should be 0.");
+    NS_TEST_ASSERT_MSG_EQ(hCastedSize, m26.GetSize(), "The number of elements are not equal.");
 
+    size_t jCastedSize = jCasted.size();
     NS_LOG_INFO("size() of jCasted before move: " << jCasted.size());
     MatrixArray<T> m27 = MatrixArray<T>(std::move(jCasted));
-    NS_LOG_INFO("size() of jCasted after move: " << jCasted.size());
     NS_LOG_INFO("m27.GetSize ()" << m27.GetSize());
-    NS_TEST_ASSERT_MSG_EQ(hCasted.size(), 0, "The size of jCasted should be 0.");
+    NS_TEST_ASSERT_MSG_EQ(jCastedSize, m27.GetSize(), "The number of elements are not equal.");
+}
+
+/**
+ * \ingroup matrixArray-tests
+ *  Test for testing functions that apply to MatrixArrays that use complex numbers,
+ *  such as HermitianTranspose that is only defined for complex type
+ */
+class ComplexMatrixArrayTestCase : public TestCase
+{
+  public:
+    /** Constructor*/
+    ComplexMatrixArrayTestCase();
+    /**
+     * Constructor
+     *
+     * \param [in] name reference name
+     */
+    ComplexMatrixArrayTestCase(const std::string& name);
+    /** Destructor*/
+    ~ComplexMatrixArrayTestCase() override;
+
+  private:
+    void DoRun() override;
+};
+
+ComplexMatrixArrayTestCase::ComplexMatrixArrayTestCase()
+    : TestCase("ComplexMatrixArrayTestCase")
+{
+}
+
+ComplexMatrixArrayTestCase::ComplexMatrixArrayTestCase(const std::string& name)
+    : TestCase(name)
+{
+}
+
+ComplexMatrixArrayTestCase::~ComplexMatrixArrayTestCase()
+{
+}
+
+void
+ComplexMatrixArrayTestCase::DoRun()
+{
+    std::valarray<std::complex<double>> complexValarray1 = {
+        {1, 1},
+        {2, 2},
+        {3, 3},
+        {4, 4},
+        {5, 5},
+        {6, 6},
+        {-1, 1},
+        {-2, 2},
+        {-3, 3},
+        {-4, 4},
+        {-5, 5},
+        {-6, 6},
+    };
+    std::valarray<std::complex<double>> complexValarray2 = {
+        {1, -1},
+        {4, -4},
+        {2, -2},
+        {5, -5},
+        {3, -3},
+        {6, -6},
+        {-1, -1},
+        {-4, -4},
+        {-2, -2},
+        {-5, -5},
+        {-3, -3},
+        {-6, -6},
+    };
+    ComplexMatrixArray m1 = ComplexMatrixArray(3, 2, 2, complexValarray1);
+    ComplexMatrixArray m2 = ComplexMatrixArray(2, 3, 2, complexValarray2);
+    ComplexMatrixArray m3 = m1.HermitianTranspose();
+    NS_LOG_INFO("m1 (3, 2, 2):" << m1);
+    NS_LOG_INFO("m2 (2, 3, 2):" << m2);
+    NS_LOG_INFO("m3 (2, 3, 2):" << m3);
+    NS_TEST_ASSERT_MSG_EQ(m2, m3, "m2 and m3 matrices should be equal");
 }
 
 /**
@@ -357,6 +536,7 @@
     AddTestCase(
         new MatrixArrayTestCase<std::complex<double>>("Test MatrixArray<std::complex<double>>"));
     AddTestCase(new MatrixArrayTestCase<int>("Test MatrixArray<int>"));
+    AddTestCase(new ComplexMatrixArrayTestCase("Test ComplexMatrixArray"));
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/object-test-suite.cc ns-3.39/src/core/test/object-test-suite.cc
--- ns-3.38/src/core/test/object-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/object-test-suite.cc	2023-07-05 14:23:47.769427300 -0700
@@ -541,7 +541,7 @@
 
     //
     // Since a declared a BaseA, even if it is really a DerivedA, we should not
-    // be able to GetOBject for a DerivedA since this would break the type
+    // be able to GetObject for a DerivedA since this would break the type
     // declaration.
     //
     NS_TEST_ASSERT_MSG_EQ(!a->GetObject<DerivedA>(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/random-variable-stream-test-suite.cc ns-3.39/src/core/test/random-variable-stream-test-suite.cc
--- ns-3.38/src/core/test/random-variable-stream-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/random-variable-stream-test-suite.cc	2023-07-05 14:23:47.769427300 -0700
@@ -316,7 +316,7 @@
      */
     void SetTestSuiteSeed()
     {
-        if (m_seedSet == false)
+        if (!m_seedSet)
         {
             uint32_t seed;
             if (RngSeedManager::GetRun() == 0)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/simulator-test-suite.cc ns-3.39/src/core/test/simulator-test-suite.cc
--- ns-3.38/src/core/test/simulator-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/simulator-test-suite.cc	2023-07-05 14:23:47.773427200 -0700
@@ -115,14 +115,7 @@
 void
 SimulatorEventsTestCase::EventB(int b)
 {
-    if (b != 2 || NowUs() != 11)
-    {
-        m_b = false;
-    }
-    else
-    {
-        m_b = true;
-    }
+    m_b = !(b != 2 || NowUs() != 11);
     Simulator::Remove(m_idC);
     Simulator::Schedule(MicroSeconds(10), &SimulatorEventsTestCase::EventD, this, 4);
 }
@@ -136,14 +129,7 @@
 void
 SimulatorEventsTestCase::EventD(int d)
 {
-    if (d != 4 || NowUs() != (11 + 10))
-    {
-        m_d = false;
-    }
-    else
-    {
-        m_d = true;
-    }
+    m_d = !(d != 4 || NowUs() != (11 + 10));
 }
 
 void
@@ -185,7 +171,11 @@
     NS_TEST_EXPECT_MSG_EQ(m_d, true, "Event D did not run ?");
 
     EventId anId = Simulator::ScheduleNow(&SimulatorEventsTestCase::Eventfoo0, this);
+
+    // Test copy assignment operator
+    // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
     EventId anotherId = anId;
+
     NS_TEST_EXPECT_MSG_EQ(!(anId.IsExpired() || anotherId.IsExpired()),
                           true,
                           "Event should not have expired yet.");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/trickle-timer-test-suite.cc ns-3.39/src/core/test/trickle-timer-test-suite.cc
--- ns-3.38/src/core/test/trickle-timer-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/trickle-timer-test-suite.cc	2023-07-05 14:23:47.773427200 -0700
@@ -96,7 +96,7 @@
 void
 TrickleTimerTestCase::ExpireTimer()
 {
-    if (m_enableDataCollection == false)
+    if (!m_enableDataCollection)
     {
         return;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/type-id-test-suite.cc ns-3.39/src/core/test/type-id-test-suite.cc
--- ns-3.38/src/core/test/type-id-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/type-id-test-suite.cc	2023-07-05 14:23:47.773427200 -0700
@@ -59,10 +59,8 @@
   private:
     void DoRun() override;
 
-    enum
-    {
-        HashChainFlag = 0x80000000
-    };
+    /// Hash chaining flag, copied from type-id.cc:IidManager
+    static constexpr auto HASH_CHAIN_FLAG{0x80000000};
 };
 
 UniqueTypeIdTestCase::UniqueTypeIdTestCase()
@@ -93,7 +91,7 @@
     {
         const TypeId tid = TypeId::GetRegistered(i);
         std::cout << suite << "" << std::setw(6) << tid.GetUid();
-        if (tid.GetHash() & HashChainFlag)
+        if (tid.GetHash() & HASH_CHAIN_FLAG)
         {
             std::cout << "  chain";
         }
@@ -108,9 +106,9 @@
                               TypeId::LookupByName(tid.GetName()).GetUid(),
                               "LookupByName returned different TypeId for " << tid.GetName());
 
-        // Mask off HashChainFlag in this test, since tid might have been chained
-        NS_TEST_ASSERT_MSG_EQ((tid.GetHash() & (~HashChainFlag)),
-                              (hasher.clear().GetHash32(tid.GetName()) & (~HashChainFlag)),
+        // Mask off HASH_CHAIN_FLAG in this test, since tid might have been chained
+        NS_TEST_ASSERT_MSG_EQ((tid.GetHash() & (~HASH_CHAIN_FLAG)),
+                              (hasher.clear().GetHash32(tid.GetName()) & (~HASH_CHAIN_FLAG)),
                               "TypeId .hash and Hash32 (.name) unequal for " << tid.GetName());
 
         NS_TEST_ASSERT_MSG_EQ(tid.GetUid(),
@@ -135,10 +133,8 @@
   private:
     void DoRun() override;
 
-    enum
-    {
-        HashChainFlag = 0x80000000
-    };
+    /// Hash chaining flag, copied from type-id.cc:IidManager
+    static constexpr auto HASH_CHAIN_FLAG{0x80000000};
 };
 
 CollisionTestCase::CollisionTestCase()
@@ -167,14 +163,14 @@
     TypeId t2(t2Name);
 
     // Check that they are alphabetical: t1 name < t2 name
-    NS_TEST_ASSERT_MSG_EQ((t1.GetHash() & HashChainFlag),
+    NS_TEST_ASSERT_MSG_EQ((t1.GetHash() & HASH_CHAIN_FLAG),
                           0,
-                          "First and lesser TypeId has HashChainFlag set");
+                          "First and lesser TypeId has HASH_CHAIN_FLAG set");
     std::cout << suite << "collision: first,lesser  not chained: OK" << std::endl;
 
-    NS_TEST_ASSERT_MSG_NE((t2.GetHash() & HashChainFlag),
+    NS_TEST_ASSERT_MSG_NE((t2.GetHash() & HASH_CHAIN_FLAG),
                           0,
-                          "Second and greater TypeId does not have HashChainFlag set");
+                          "Second and greater TypeId does not have HASH_CHAIN_FLAG set");
     std::cout << suite << "collision: second,greater    chained: OK" << std::endl;
 
     // Register colliding types in reverse alphabetical order
@@ -188,14 +184,14 @@
     TypeId t4(t4Name);
 
     // Check that they are alphabetical: t3 name > t4 name
-    NS_TEST_ASSERT_MSG_NE((t3.GetHash() & HashChainFlag),
+    NS_TEST_ASSERT_MSG_NE((t3.GetHash() & HASH_CHAIN_FLAG),
                           0,
-                          "First and greater TypeId does not have HashChainFlag set");
+                          "First and greater TypeId does not have HASH_CHAIN_FLAG set");
     std::cout << suite << "collision: first,greater     chained: OK" << std::endl;
 
-    NS_TEST_ASSERT_MSG_EQ((t4.GetHash() & HashChainFlag),
+    NS_TEST_ASSERT_MSG_EQ((t4.GetHash() & HASH_CHAIN_FLAG),
                           0,
-                          "Second and lesser TypeId has HashChainFlag set");
+                          "Second and lesser TypeId has HASH_CHAIN_FLAG set");
     std::cout << suite << "collision: second,lesser not chained: OK" << std::endl;
 
     /** TODO Extra credit:  register three types whose hashes collide
@@ -321,7 +317,7 @@
     std::cerr << suite << "DeprecatedAttribute TypeId: " << tid.GetUid() << std::endl;
 
     //  Try the lookups
-    struct TypeId::AttributeInformation ainfo;
+    TypeId::AttributeInformation ainfo;
     NS_TEST_ASSERT_MSG_EQ(tid.LookupAttributeByName("attribute", &ainfo),
                           true,
                           "lookup new attribute");
@@ -334,7 +330,7 @@
     std::cerr << suite << "lookup old attribute:"
               << (ainfo.supportLevel == TypeId::DEPRECATED ? "deprecated" : "error") << std::endl;
 
-    struct TypeId::TraceSourceInformation tinfo;
+    TypeId::TraceSourceInformation tinfo;
     Ptr<const TraceSourceAccessor> acc;
     acc = tid.LookupTraceSourceByName("trace", &tinfo);
     NS_TEST_ASSERT_MSG_NE(acc, nullptr, "lookup new trace source");
@@ -368,10 +364,8 @@
      */
     void Report(const std::string how, const uint32_t delta) const;
 
-    enum
-    {
-        REPETITIONS = 100000
-    };
+    /// Number of repetitions
+    static constexpr uint32_t REPETITIONS{100000};
 };
 
 LookupTimeTestCase::LookupTimeTestCase()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/core/test/val-array-test-suite.cc ns-3.39/src/core/test/val-array-test-suite.cc
--- ns-3.38/src/core/test/val-array-test-suite.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/core/test/val-array-test-suite.cc	2023-07-05 14:23:47.773427200 -0700
@@ -48,7 +48,7 @@
      *
      * \param [in] name reference name
      */
-    ValArrayTestCase<T>(const std::string name);
+    ValArrayTestCase<T>(const std::string& name);
 
     /** Destructor. */
     ~ValArrayTestCase<T>() override;
@@ -80,7 +80,7 @@
 };
 
 template <class T>
-ValArrayTestCase<T>::ValArrayTestCase(const std::string name)
+ValArrayTestCase<T>::ValArrayTestCase(const std::string& name)
     : TestCase(name)
 {
 }
@@ -95,9 +95,9 @@
 ValArrayTestCase<T>::DoRun()
 {
     ValArray<T> v1 = ValArray<T>(2, 3);
-    for (uint16_t i = 0; i < v1.GetNumRows(); ++i)
+    for (size_t i = 0; i < v1.GetNumRows(); ++i)
     {
-        for (uint16_t j = 0; j < v1.GetNumCols(); ++j)
+        for (size_t j = 0; j < v1.GetNumCols(); ++j)
         {
             v1(i, j) = 1;
         }
@@ -108,9 +108,9 @@
     NS_TEST_ASSERT_MSG_EQ(v1.GetNumCols(), v2.GetNumCols(), "The number of cols are not equal.");
 
     // test copy constructor
-    for (uint16_t i = 0; i < v1.GetNumRows(); ++i)
+    for (size_t i = 0; i < v1.GetNumRows(); ++i)
     {
-        for (uint16_t j = 0; j < v1.GetNumCols(); ++j)
+        for (size_t j = 0; j < v1.GetNumCols(); ++j)
         {
             NS_TEST_ASSERT_MSG_EQ(v1(i, j), v2(i, j), "The elements are not equal.");
         }
@@ -120,9 +120,9 @@
     ValArray<T> v3 = v1;
     NS_TEST_ASSERT_MSG_EQ(v1.GetNumRows(), v3.GetNumRows(), "The number of rows are not equal.");
     NS_TEST_ASSERT_MSG_EQ(v1.GetNumCols(), v3.GetNumCols(), "The number of cols are not equal.");
-    for (uint16_t i = 0; i < v1.GetNumRows(); ++i)
+    for (size_t i = 0; i < v1.GetNumRows(); ++i)
     {
-        for (uint16_t j = 0; j < v1.GetNumCols(); ++j)
+        for (size_t j = 0; j < v1.GetNumCols(); ++j)
         {
             NS_TEST_ASSERT_MSG_EQ(v1(i, j), v2(i, j), "The elements are not equal.");
         }
@@ -136,9 +136,9 @@
     v4 = std::move(v1);
     NS_LOG_INFO("v4 size after move: " << v4.GetSize());
     NS_TEST_ASSERT_MSG_EQ(v1size, v4.GetSize(), "The number of elements are not equal.");
-    for (uint16_t i = 0; i < v4.GetNumRows(); ++i)
+    for (size_t i = 0; i < v4.GetNumRows(); ++i)
     {
-        for (uint16_t j = 0; j < v4.GetNumCols(); ++j)
+        for (size_t j = 0; j < v4.GetNumCols(); ++j)
         {
             // Use v3 for comparison since it hasn't moved
             NS_TEST_ASSERT_MSG_EQ(v3(i, j), v4(i, j), "The elements are not equal.");
@@ -150,9 +150,9 @@
     size_t v3size = v3.GetSize();
     ValArray<T> v5(std::move(v3));
     NS_TEST_ASSERT_MSG_EQ(v3size, v5.GetSize(), "The number of elements are not equal.");
-    for (uint16_t i = 0; i < v5.GetNumRows(); ++i)
+    for (size_t i = 0; i < v5.GetNumRows(); ++i)
     {
-        for (uint16_t j = 0; j < v5.GetNumCols(); ++j)
+        for (size_t j = 0; j < v5.GetNumCols(); ++j)
         {
             // Use v4 for comparison since it hasn't moved
             NS_TEST_ASSERT_MSG_EQ(v4(i, j), v5(i, j), "The elements are not equal.");
@@ -177,10 +177,10 @@
     // test whether column-major order was respected during the initialization and
     // also in the access operator if we iterate over rows first we should find 0, 2, 4, 6, ...
     std::valarray<int> initArray2{0, 2, 4, 6, 1, 3, 5, 7};
-    auto testIndex = 0;
-    for (uint16_t i = 0; i < v6.GetNumRows(); ++i)
+    size_t testIndex = 0;
+    for (size_t i = 0; i < v6.GetNumRows(); ++i)
     {
-        for (uint16_t j = 0; j < v6.GetNumCols(); ++j)
+        for (size_t j = 0; j < v6.GetNumCols(); ++j)
         {
             NS_TEST_ASSERT_MSG_EQ(v6(i, j),
                                   static_cast<T>(initArray2[testIndex]),
@@ -203,11 +203,11 @@
     // if we iterate over rows first we should find 0, 2, 4, 6, ...
     std::valarray<int> initArray4{0, 2, 4, 6, 1, 3, 5, 7, 0, 2, 4, 6, 1, 3, 5, 7};
     testIndex = 0;
-    for (uint16_t p = 0; p < v7.GetNumPages(); ++p)
+    for (size_t p = 0; p < v7.GetNumPages(); ++p)
     {
-        for (uint16_t i = 0; i < v7.GetNumRows(); ++i)
+        for (size_t i = 0; i < v7.GetNumRows(); ++i)
         {
-            for (uint16_t j = 0; j < v7.GetNumCols(); ++j)
+            for (size_t j = 0; j < v7.GetNumCols(); ++j)
             {
                 NS_TEST_ASSERT_MSG_EQ(v7(i, j, p),
                                       static_cast<T>(initArray4[testIndex]),
@@ -219,11 +219,11 @@
 
     // multiplication with a scalar value with 3D array
     ValArray<T> v8 = v7 * (static_cast<T>(5.0));
-    for (uint16_t p = 0; p < v8.GetNumPages(); ++p)
+    for (size_t p = 0; p < v8.GetNumPages(); ++p)
     {
-        for (uint16_t i = 0; i < v8.GetNumRows(); ++i)
+        for (size_t i = 0; i < v8.GetNumRows(); ++i)
         {
-            for (uint16_t j = 0; j < v8.GetNumCols(); ++j)
+            for (size_t j = 0; j < v8.GetNumCols(); ++j)
             {
                 NS_TEST_ASSERT_MSG_EQ(v7(i, j, p) * (static_cast<T>(5.0)),
                                       v8(i, j, p),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/csma/model/csma-channel.cc ns-3.39/src/csma/model/csma-channel.cc
--- ns-3.38/src/csma/model/csma-channel.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/csma/model/csma-channel.cc	2023-07-05 14:23:47.773427200 -0700
@@ -310,14 +310,7 @@
 bool
 CsmaChannel::IsBusy()
 {
-    if (m_state == IDLE)
-    {
-        return false;
-    }
-    else
-    {
-        return true;
-    }
+    return m_state != IDLE;
 }
 
 DataRate
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/csma/model/csma-net-device.cc ns-3.39/src/csma/model/csma-net-device.cc
--- ns-3.38/src/csma/model/csma-net-device.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/csma/model/csma-net-device.cc	2023-07-05 14:23:47.773427200 -0700
@@ -459,7 +459,7 @@
     //
     // Only transmit if the send side of net device is enabled
     //
-    if (IsSendEnabled() == false)
+    if (!IsSendEnabled())
     {
         m_phyTxDropTrace(m_currentPkt);
         m_currentPkt = nullptr;
@@ -512,7 +512,7 @@
         // The channel is free, transmit the packet
         //
         m_phyTxBeginTrace(m_currentPkt);
-        if (m_channel->TransmitStart(m_currentPkt, m_deviceId) == false)
+        if (!m_channel->TransmitStart(m_currentPkt, m_deviceId))
         {
             NS_LOG_WARN("Channel TransmitStart returns an error");
             m_phyTxDropTrace(m_currentPkt);
@@ -718,7 +718,7 @@
     //
     // Only receive if the send side of net device is enabled
     //
-    if (IsReceiveEnabled() == false)
+    if (!IsReceiveEnabled())
     {
         m_phyRxDropTrace(packet);
         return;
@@ -974,7 +974,7 @@
     //
     // Only transmit if send side of net device is enabled
     //
-    if (IsSendEnabled() == false)
+    if (!IsSendEnabled())
     {
         m_macTxDropTrace(packet);
         return false;
@@ -990,7 +990,7 @@
     // Place the packet to be sent on the send queue.  Note that the
     // queue may fire a drop trace, but we will too.
     //
-    if (m_queue->Enqueue(packet) == false)
+    if (!m_queue->Enqueue(packet))
     {
         m_macTxDropTrace(packet);
         return false;
@@ -1003,7 +1003,7 @@
     //
     if (m_txMachineState == READY)
     {
-        if (m_queue->IsEmpty() == false)
+        if (!m_queue->IsEmpty())
         {
             Ptr<Packet> packet = m_queue->Dequeue();
             NS_ASSERT_MSG(packet,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/csma/test/examples-to-run.py ns-3.39/src/csma/test/examples-to-run.py
--- ns-3.38/src/csma/test/examples-to-run.py	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/csma/test/examples-to-run.py	2023-07-05 14:23:47.773427200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsdv/examples/dsdv-manet.cc ns-3.39/src/dsdv/examples/dsdv-manet.cc
--- ns-3.38/src/dsdv/examples/dsdv-manet.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/dsdv/examples/dsdv-manet.cc	2023-07-05 14:23:47.773427200 -0700
@@ -29,7 +29,6 @@
  */
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/dsdv-helper.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsdv/model/dsdv-routing-protocol.cc ns-3.39/src/dsdv/model/dsdv-routing-protocol.cc
--- ns-3.38/src/dsdv/model/dsdv-routing-protocol.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/dsdv/model/dsdv-routing-protocol.cc	2023-07-05 14:23:47.773427200 -0700
@@ -391,10 +391,10 @@
 RoutingProtocol::RouteInput(Ptr<const Packet> p,
                             const Ipv4Header& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb)
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(m_mainAddress << " received packet " << p->GetUid() << " from "
                                   << header.GetSource() << " on interface " << idev->GetAddress()
@@ -419,7 +419,7 @@
     }
 
     // Deferred route request
-    if (EnableBuffering == true && idev == m_lo)
+    if (EnableBuffering && idev == m_lo)
     {
         DeferredRouteOutputTag tag;
         if (p->PeekPacketTag(tag))
@@ -449,7 +449,7 @@
             if (dst == iface.GetBroadcast() || dst.IsBroadcast())
             {
                 Ptr<Packet> packet = p->Copy();
-                if (lcb.IsNull() == false)
+                if (!lcb.IsNull())
                 {
                     NS_LOG_LOGIC("Broadcast local delivery to " << iface.GetLocal());
                     lcb(p, header, iif);
@@ -482,7 +482,7 @@
 
     if (m_ipv4->IsDestinationAddress(dst, iif))
     {
-        if (lcb.IsNull() == false)
+        if (!lcb.IsNull())
         {
             NS_LOG_LOGIC("Unicast local delivery to " << dst);
             lcb(p, header, iif);
@@ -497,7 +497,7 @@
     }
 
     // Check if input device supports IP forwarding
-    if (m_ipv4->IsForwarding(iif) == false)
+    if (!m_ipv4->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         ecb(p, header, Socket::ERROR_NOROUTETOHOST);
@@ -627,7 +627,7 @@
         EventId event;
         bool permanentTableVerifier =
             m_routingTable.LookupRoute(dsdvHeader.GetDst(), fwdTableEntry);
-        if (permanentTableVerifier == false)
+        if (!permanentTableVerifier)
         {
             if (dsdvHeader.GetDstSeqno() % 2 != 1)
             {
@@ -860,8 +860,8 @@
                                          << " SeqNo:" << i->second.GetSeqNo()
                                          << " HopCount:" << i->second.GetHop() + 1);
             RoutingTableEntry temp = i->second;
-            if ((i->second.GetEntriesChanged() == true) &&
-                (!m_advRoutingTable.AnyRunningEvent(temp.GetDestination())))
+            if (i->second.GetEntriesChanged() &&
+                !m_advRoutingTable.AnyRunningEvent(temp.GetDestination()))
             {
                 dsdvHeader.SetDst(i->second.GetDestination());
                 dsdvHeader.SetDstSeqno(i->second.GetSeqNo());
@@ -1287,8 +1287,8 @@
              ++i)
         {
             RoutingTableEntry advEntry = i->second;
-            if ((advEntry.GetEntriesChanged() == true) &&
-                (!m_advRoutingTable.AnyRunningEvent(advEntry.GetDestination())))
+            if (advEntry.GetEntriesChanged() &&
+                !m_advRoutingTable.AnyRunningEvent(advEntry.GetDestination()))
             {
                 if (!(advEntry.GetSeqNo() % 2))
                 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsdv/model/dsdv-routing-protocol.h ns-3.39/src/dsdv/model/dsdv-routing-protocol.h
--- ns-3.38/src/dsdv/model/dsdv-routing-protocol.h	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/dsdv/model/dsdv-routing-protocol.h	2023-07-05 14:23:47.773427200 -0700
@@ -87,10 +87,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void PrintRoutingTable(Ptr<OutputStreamWrapper> stream,
                            Time::Unit unit = Time::S) const override;
     void NotifyInterfaceUp(uint32_t interface) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsdv/model/dsdv-rtable.cc ns-3.39/src/dsdv/model/dsdv-rtable.cc
--- ns-3.38/src/dsdv/model/dsdv-rtable.cc	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/dsdv/model/dsdv-rtable.cc	2023-07-05 14:23:47.773427200 -0700
@@ -101,7 +101,7 @@
     {
         return false;
     }
-    if (forRouteInput == true && id == i->second.GetInterface().GetBroadcast())
+    if (forRouteInput && id == i->second.GetInterface().GetBroadcast())
     {
         return false;
     }
@@ -112,12 +112,7 @@
 bool
 RoutingTable::DeleteRoute(Ipv4Address dst)
 {
-    if (m_ipv4AddressEntry.erase(dst) != 0)
-    {
-        // NS_LOG_DEBUG("Route erased");
-        return true;
-    }
-    return false;
+    return m_ipv4AddressEntry.erase(dst) != 0;
 }
 
 uint32_t
@@ -329,14 +324,7 @@
         return false;
     }
     event = i->second;
-    if (event.IsRunning())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return event.IsRunning();
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsdv/model/dsdv-rtable.h ns-3.39/src/dsdv/model/dsdv-rtable.h
--- ns-3.38/src/dsdv/model/dsdv-rtable.h	2023-03-16 20:54:23.600868500 -0700
+++ ns-3.39/src/dsdv/model/dsdv-rtable.h	2023-07-05 14:23:47.773427200 -0700
@@ -353,7 +353,7 @@
      * \param id destination address
      * \param rt entry with destination address dst, if exists
      * \param forRouteInput for routing input
-     * \return true on success
+     * \return true onns3  success
      */
     bool LookupRoute(Ipv4Address id, RoutingTableEntry& rt, bool forRouteInput);
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-gratuitous-reply-table.h ns-3.39/src/dsr/model/dsr-gratuitous-reply-table.h
--- ns-3.38/src/dsr/model/dsr-gratuitous-reply-table.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-gratuitous-reply-table.h	2023-07-05 14:23:47.777427200 -0700
@@ -132,7 +132,7 @@
          * \param b GraReplyEntry entry
          * \return true if expired, false otherwise
          */
-        bool operator()(const struct GraReplyEntry& b) const
+        bool operator()(const GraReplyEntry& b) const
         {
             return (b.m_gratReplyHoldoff < Simulator::Now());
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-option-header.cc ns-3.39/src/dsr/model/dsr-option-header.cc
--- ns-3.38/src/dsr/model/dsr-option-header.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-option-header.cc	2023-07-05 14:23:47.777427200 -0700
@@ -417,14 +417,11 @@
     m_identification = i.ReadNtohU16();
     ReadFrom(i, m_target);
 
-    uint8_t index = 0;
-    for (std::vector<Ipv4Address>::iterator it = m_ipv4Address.begin(); it != m_ipv4Address.end();
-         it++)
+    for (std::size_t index = 0; index < m_ipv4Address.size(); index++)
     {
         i.Read(buff, 4);
-        m_address = it->Deserialize(buff);
-        SetNodeAddress(index, m_address);
-        ++index;
+        m_address = Ipv4Address::Deserialize(buff);
+        SetNodeAddress(static_cast<uint8_t>(index), m_address);
     }
 
     return GetSerializedSize();
@@ -555,14 +552,11 @@
     i.ReadU8();
     i.ReadU8();
 
-    uint8_t index = 0;
-    for (std::vector<Ipv4Address>::iterator it = m_ipv4Address.begin(); it != m_ipv4Address.end();
-         it++)
+    for (std::size_t index = 0; index < m_ipv4Address.size(); index++)
     {
         i.Read(buff, 4);
-        m_address = it->Deserialize(buff);
-        SetNodeAddress(index, m_address);
-        ++index;
+        m_address = Ipv4Address::Deserialize(buff);
+        SetNodeAddress(static_cast<uint8_t>(index), m_address);
     }
 
     return GetSerializedSize();
@@ -718,14 +712,11 @@
     m_salvage = i.ReadU8();
     m_segmentsLeft = i.ReadU8();
 
-    uint8_t index = 0;
-    for (std::vector<Ipv4Address>::iterator it = m_ipv4Address.begin(); it != m_ipv4Address.end();
-         it++)
+    for (std::size_t index = 0; index < m_ipv4Address.size(); index++)
     {
         i.Read(buff, 4);
-        m_address = it->Deserialize(buff);
-        SetNodeAddress(index, m_address);
-        ++index;
+        m_address = Ipv4Address::Deserialize(buff);
+        SetNodeAddress(static_cast<uint8_t>(index), m_address);
     }
 
     return GetSerializedSize();
@@ -1018,25 +1009,25 @@
     return retVal;
 }
 
-NS_OBJECT_ENSURE_REGISTERED(DsrOptionRerrUnsupportHeader);
+NS_OBJECT_ENSURE_REGISTERED(DsrOptionRerrUnsupportedHeader);
 
 TypeId
-DsrOptionRerrUnsupportHeader::GetTypeId()
+DsrOptionRerrUnsupportedHeader::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::dsr::DsrOptionRerrUnsupportHeader")
-                            .AddConstructor<DsrOptionRerrUnsupportHeader>()
+    static TypeId tid = TypeId("ns3::dsr::DsrOptionRerrUnsupportedHeader")
+                            .AddConstructor<DsrOptionRerrUnsupportedHeader>()
                             .SetParent<DsrOptionRerrHeader>()
                             .SetGroupName("Dsr");
     return tid;
 }
 
 TypeId
-DsrOptionRerrUnsupportHeader::GetInstanceTypeId() const
+DsrOptionRerrUnsupportedHeader::GetInstanceTypeId() const
 {
     return GetTypeId();
 }
 
-DsrOptionRerrUnsupportHeader::DsrOptionRerrUnsupportHeader()
+DsrOptionRerrUnsupportedHeader::DsrOptionRerrUnsupportedHeader()
     : m_salvage(0)
 {
     SetType(3);
@@ -1044,60 +1035,60 @@
     SetErrorType(3);
 }
 
-DsrOptionRerrUnsupportHeader::~DsrOptionRerrUnsupportHeader()
+DsrOptionRerrUnsupportedHeader::~DsrOptionRerrUnsupportedHeader()
 {
 }
 
 void
-DsrOptionRerrUnsupportHeader::SetSalvage(uint8_t salvage)
+DsrOptionRerrUnsupportedHeader::SetSalvage(uint8_t salvage)
 {
     m_salvage = salvage;
 }
 
 uint8_t
-DsrOptionRerrUnsupportHeader::GetSalvage() const
+DsrOptionRerrUnsupportedHeader::GetSalvage() const
 {
     return m_salvage;
 }
 
 void
-DsrOptionRerrUnsupportHeader::SetErrorSrc(Ipv4Address errorSrcAddress)
+DsrOptionRerrUnsupportedHeader::SetErrorSrc(Ipv4Address errorSrcAddress)
 {
     m_errorSrcAddress = errorSrcAddress;
 }
 
 Ipv4Address
-DsrOptionRerrUnsupportHeader::GetErrorSrc() const
+DsrOptionRerrUnsupportedHeader::GetErrorSrc() const
 {
     return m_errorSrcAddress;
 }
 
 void
-DsrOptionRerrUnsupportHeader::SetErrorDst(Ipv4Address errorDstAddress)
+DsrOptionRerrUnsupportedHeader::SetErrorDst(Ipv4Address errorDstAddress)
 {
     m_errorDstAddress = errorDstAddress;
 }
 
 Ipv4Address
-DsrOptionRerrUnsupportHeader::GetErrorDst() const
+DsrOptionRerrUnsupportedHeader::GetErrorDst() const
 {
     return m_errorDstAddress;
 }
 
 void
-DsrOptionRerrUnsupportHeader::SetUnsupported(uint16_t unsupported)
+DsrOptionRerrUnsupportedHeader::SetUnsupported(uint16_t unsupported)
 {
     m_unsupported = unsupported;
 }
 
 uint16_t
-DsrOptionRerrUnsupportHeader::GetUnsupported() const
+DsrOptionRerrUnsupportedHeader::GetUnsupported() const
 {
     return m_unsupported;
 }
 
 void
-DsrOptionRerrUnsupportHeader::Print(std::ostream& os) const
+DsrOptionRerrUnsupportedHeader::Print(std::ostream& os) const
 {
     os << "( type = " << (uint32_t)GetType() << " length = " << (uint32_t)GetLength()
        << " errorType = " << (uint32_t)m_errorType << " salvage = " << (uint32_t)m_salvage
@@ -1106,13 +1097,13 @@
 }
 
 uint32_t
-DsrOptionRerrUnsupportHeader::GetSerializedSize() const
+DsrOptionRerrUnsupportedHeader::GetSerializedSize() const
 {
     return 16;
 }
 
 void
-DsrOptionRerrUnsupportHeader::Serialize(Buffer::Iterator start) const
+DsrOptionRerrUnsupportedHeader::Serialize(Buffer::Iterator start) const
 {
     Buffer::Iterator i = start;
 
@@ -1126,7 +1117,7 @@
 }
 
 uint32_t
-DsrOptionRerrUnsupportHeader::Deserialize(Buffer::Iterator start)
+DsrOptionRerrUnsupportedHeader::Deserialize(Buffer::Iterator start)
 {
     Buffer::Iterator i = start;
 
@@ -1142,7 +1133,7 @@
 }
 
 DsrOptionHeader::Alignment
-DsrOptionRerrUnsupportHeader::GetAlignment() const
+DsrOptionRerrUnsupportedHeader::GetAlignment() const
 {
     Alignment retVal = {4, 0};
     return retVal;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-option-header.h ns-3.39/src/dsr/model/dsr-option-header.h
--- ns-3.38/src/dsr/model/dsr-option-header.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-option-header.h	2023-07-05 14:23:47.777427200 -0700
@@ -1028,7 +1028,7 @@
    \endverbatim
  */
 
-class DsrOptionRerrUnsupportHeader : public DsrOptionRerrHeader
+class DsrOptionRerrUnsupportedHeader : public DsrOptionRerrHeader
 {
   public:
     /**
@@ -1044,11 +1044,11 @@
     /**
      * \brief Constructor.
      */
-    DsrOptionRerrUnsupportHeader();
+    DsrOptionRerrUnsupportedHeader();
     /**
      * \brief Destructor.
      */
-    ~DsrOptionRerrUnsupportHeader() override;
+    ~DsrOptionRerrUnsupportedHeader() override;
     /**
      * \brief Set the route error source address
      * \param errorSrcAddress The error source address
@@ -1335,7 +1335,7 @@
     Ipv4Address m_realDstAddress;
 };
 
-static inline std::ostream&
+[[maybe_unused]] static inline std::ostream&
 operator<<(std::ostream& os, const DsrOptionSRHeader& sr)
 {
     sr.Print(os);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-options.cc ns-3.39/src/dsr/model/dsr-options.cc
--- ns-3.38/src/dsr/model/dsr-options.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-options.cc	2023-07-05 14:23:47.777427200 -0700
@@ -58,6 +58,7 @@
 #include "ns3/udp-header.h"
 #include "ns3/uinteger.h"
 
+#include <algorithm>
 #include <ctime>
 #include <list>
 #include <map>
@@ -164,18 +165,10 @@
 DsrOptions::ReverseRoutes(std::vector<Ipv4Address>& vec)
 {
     NS_LOG_FUNCTION(this);
-    std::vector<Ipv4Address> vec2(vec);
-    vec.clear(); // To ensure vec is empty before start
-    for (std::vector<Ipv4Address>::reverse_iterator ri = vec2.rbegin(); ri != vec2.rend(); ++ri)
-    {
-        vec.push_back(*ri);
-    }
 
-    if ((vec.size() == vec2.size()) && (vec.front() == vec2.back()))
-    {
-        return true;
-    }
-    return false;
+    std::reverse(vec.begin(), vec.end());
+
+    return true;
 }
 
 Ipv4Address
@@ -190,23 +183,22 @@
         nextHop = vec[1];
         return nextHop;
     }
-    else
+
+    if (ipv4Address == vec.back())
     {
-        if (ipv4Address == vec.back())
-        {
-            NS_LOG_DEBUG("We have reached to the final destination " << ipv4Address << " "
-                                                                     << vec.back());
-            return ipv4Address;
-        }
-        for (std::vector<Ipv4Address>::const_iterator i = vec.begin(); i != vec.end(); ++i)
+        NS_LOG_DEBUG("We have reached to the final destination " << ipv4Address << " "
+                                                                 << vec.back());
+        return ipv4Address;
+    }
+    for (std::vector<Ipv4Address>::const_iterator i = vec.begin(); i != vec.end(); ++i)
+    {
+        if (ipv4Address == (*i))
         {
-            if (ipv4Address == (*i))
-            {
-                nextHop = *(++i);
-                return nextHop;
-            }
+            nextHop = *(++i);
+            return nextHop;
         }
     }
+
     NS_LOG_DEBUG("next hop address not found, route corrupted");
     Ipv4Address none = "0.0.0.0";
     return none;
@@ -223,17 +215,16 @@
         nextHop = vec[0];
         return nextHop;
     }
-    else
+
+    for (std::vector<Ipv4Address>::reverse_iterator ri = vec.rbegin(); ri != vec.rend(); ++ri)
     {
-        for (std::vector<Ipv4Address>::reverse_iterator ri = vec.rbegin(); ri != vec.rend(); ++ri)
+        if (ipv4Address == (*ri))
         {
-            if (ipv4Address == (*ri))
-            {
-                nextHop = *(++ri);
-                return nextHop;
-            }
+            nextHop = *(++ri);
+            return nextHop;
         }
     }
+
     NS_LOG_DEBUG("next hop address not found, route corrupted");
     Ipv4Address none = "0.0.0.0";
     return none;
@@ -292,10 +283,6 @@
             {
                 return true;
             }
-            else
-            {
-                continue;
-            }
         }
     }
     return false;
@@ -311,10 +298,6 @@
         {
             return true;
         }
-        else
-        {
-            continue;
-        }
     }
     return false;
 }
@@ -335,31 +318,22 @@
             vec.push_back(*i);
             continue;
         }
-        else
+
+        for (std::vector<Ipv4Address>::iterator j = vec.begin(); j != vec.end(); ++j)
         {
-            for (std::vector<Ipv4Address>::iterator j = vec.begin(); j != vec.end(); ++j)
+            if ((*i) == (*j))
             {
-                if ((*i) == (*j))
-                {
-                    if ((j + 1) != vec.end())
-                    {
-                        vec.erase(j + 1, vec.end()); // Automatic shorten the route
-                        break;
-                    }
-                    else
-                    {
-                        break;
-                    }
-                }
-                else if (j == (vec.end() - 1))
-                {
-                    vec.push_back(*i);
-                    break;
-                }
-                else
+                if ((j + 1) != vec.end())
                 {
-                    continue;
+                    vec.erase(j + 1, vec.end()); // Automatic shorten the route
                 }
+
+                break;
+            }
+            else if (j == (vec.end() - 1))
+            {
+                vec.push_back(*i);
+                break;
             }
         }
     }
@@ -1634,12 +1608,12 @@
         /*
          * Remove the route error header from the packet, and get the error type
          */
-        DsrOptionRerrUnsupportHeader rerrUnsupport;
-        p->RemoveHeader(rerrUnsupport);
+        DsrOptionRerrUnsupportedHeader rerrUnsupported;
+        p->RemoveHeader(rerrUnsupported);
 
         /// \todo This is for the other two error options, not supporting for now
-        // uint32_t rerrSize = rerrUnsupport.GetSerializedSize();
-        // uint32_t serialized = DoSendError (p, rerrUnsupport, rerrSize, ipv4Address, protocol);
+        // uint32_t rerrSize = rerrUnsupported.GetSerializedSize();
+        // uint32_t serialized = DoSendError (p, rerrUnsupported, rerrSize, ipv4Address, protocol);
         uint32_t serialized = 0;
         return serialized;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-rcache.cc ns-3.39/src/dsr/model/dsr-rcache.cc
--- ns-3.38/src/dsr/model/dsr-rcache.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-rcache.cc	2023-07-05 14:23:47.777427200 -0700
@@ -164,7 +164,6 @@
      */
     m_ntimer.SetDelay(m_delay);
     m_ntimer.SetFunction(&DsrRouteCache::PurgeMac, this);
-    m_txErrorCallback = MakeCallback(&DsrRouteCache::ProcessTxError, this);
 }
 
 DsrRouteCache::~DsrRouteCache()
@@ -220,100 +219,95 @@
     {
         return LookupRoute_Link(id, rt);
     }
-    else
+
+    Purge(); // Purge first to remove expired entries
+    if (m_sortedRoutes.empty())
     {
-        Purge(); // Purge first to remove expired entries
-        if (m_sortedRoutes.empty())
+        NS_LOG_LOGIC("Route to " << id << " not found; m_sortedRoutes is empty");
+        return false;
+    }
+    std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::const_iterator i =
+        m_sortedRoutes.find(id);
+    if (i == m_sortedRoutes.end())
+    {
+        NS_LOG_LOGIC("No Direct Route to " << id << " found");
+        for (std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::const_iterator j =
+                 m_sortedRoutes.begin();
+             j != m_sortedRoutes.end();
+             ++j)
         {
-            NS_LOG_LOGIC("Route to " << id << " not found; m_sortedRoutes is empty");
-            return false;
-        }
-        std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::const_iterator i =
-            m_sortedRoutes.find(id);
-        if (i == m_sortedRoutes.end())
-        {
-            NS_LOG_LOGIC("No Direct Route to " << id << " found");
-            for (std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::const_iterator j =
-                     m_sortedRoutes.begin();
-                 j != m_sortedRoutes.end();
-                 ++j)
+            std::list<DsrRouteCacheEntry> rtVector =
+                j->second; // The route cache vector linked with destination address
+            /*
+             * Loop through the possibly multiple routes within the route vector
+             */
+            for (std::list<DsrRouteCacheEntry>::const_iterator k = rtVector.begin();
+                 k != rtVector.end();
+                 ++k)
             {
-                std::list<DsrRouteCacheEntry> rtVector =
-                    j->second; // The route cache vector linked with destination address
-                /*
-                 * Loop through the possibly multiple routes within the route vector
-                 */
-                for (std::list<DsrRouteCacheEntry>::const_iterator k = rtVector.begin();
-                     k != rtVector.end();
-                     ++k)
-                {
-                    // return the first route in the route vector
-                    DsrRouteCacheEntry::IP_VECTOR routeVector = k->GetVector();
-                    DsrRouteCacheEntry::IP_VECTOR changeVector;
-
-                    for (DsrRouteCacheEntry::IP_VECTOR::iterator l = routeVector.begin();
-                         l != routeVector.end();
-                         ++l)
-                    {
-                        if (*l != id)
-                        {
-                            changeVector.push_back(*l);
-                        }
-                        else
-                        {
-                            changeVector.push_back(*l);
-                            break;
-                        }
-                    }
-                    /*
-                     * When the changed vector is smaller in size and larger than 1, which means we
-                     * have found a route with the destination address we are looking for
-                     */
-                    if ((changeVector.size() < routeVector.size()) && (changeVector.size() > 1))
+                // return the first route in the route vector
+                DsrRouteCacheEntry::IP_VECTOR routeVector = k->GetVector();
+                DsrRouteCacheEntry::IP_VECTOR changeVector;
+
+                for (DsrRouteCacheEntry::IP_VECTOR::iterator l = routeVector.begin();
+                     l != routeVector.end();
+                     ++l)
+                {
+                    changeVector.push_back(*l);
+
+                    if (*l == id)
                     {
-                        DsrRouteCacheEntry changeEntry; // Create the route entry
-                        changeEntry.SetVector(changeVector);
-                        changeEntry.SetDestination(id);
-                        // Use the expire time from original route entry
-                        changeEntry.SetExpireTime(k->GetExpireTime());
-                        // We need to add new route entry here
-                        std::list<DsrRouteCacheEntry> newVector;
-                        newVector.push_back(changeEntry);
-                        newVector.sort(CompareRoutesExpire); // sort the route vector first
-                        m_sortedRoutes[id] =
-                            newVector; // Only get the first sub route and add it in route cache
-                        NS_LOG_INFO("We have a sub-route to " << id << " add it in route cache");
+                        break;
                     }
                 }
+                /*
+                 * When the changed vector is smaller in size and larger than 1, which means we
+                 * have found a route with the destination address we are looking for
+                 */
+                if ((changeVector.size() < routeVector.size()) && (changeVector.size() > 1))
+                {
+                    DsrRouteCacheEntry changeEntry; // Create the route entry
+                    changeEntry.SetVector(changeVector);
+                    changeEntry.SetDestination(id);
+                    // Use the expire time from original route entry
+                    changeEntry.SetExpireTime(k->GetExpireTime());
+                    // We need to add new route entry here
+                    std::list<DsrRouteCacheEntry> newVector;
+                    newVector.push_back(changeEntry);
+                    newVector.sort(CompareRoutesExpire); // sort the route vector first
+                    m_sortedRoutes[id] =
+                        newVector; // Only get the first sub route and add it in route cache
+                    NS_LOG_INFO("We have a sub-route to " << id << " add it in route cache");
+                }
             }
         }
-        NS_LOG_INFO("Here we check the route cache again after updated the sub routes");
-        std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::const_iterator m =
-            m_sortedRoutes.find(id);
-        if (m == m_sortedRoutes.end())
-        {
-            NS_LOG_LOGIC("No updated route till last time");
-            return false;
-        }
-        /*
-         * We have a direct route to the destination address
-         */
-        std::list<DsrRouteCacheEntry> rtVector = m->second;
-        rt = rtVector.front(); // use the first entry in the route vector
-        NS_LOG_LOGIC("Route to " << id << " with route size " << rtVector.size());
-        return true;
     }
+    NS_LOG_INFO("Here we check the route cache again after updated the sub routes");
+    std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::const_iterator m =
+        m_sortedRoutes.find(id);
+    if (m == m_sortedRoutes.end())
+    {
+        NS_LOG_LOGIC("No updated route till last time");
+        return false;
+    }
+    /*
+     * We have a direct route to the destination address
+     */
+    std::list<DsrRouteCacheEntry> rtVector = m->second;
+    rt = rtVector.front(); // use the first entry in the route vector
+    NS_LOG_LOGIC("Route to " << id << " with route size " << rtVector.size());
+    return true;
 }
 
 void
 DsrRouteCache::SetCacheType(std::string type)
 {
     NS_LOG_FUNCTION(this << type);
-    if (type == std::string("LinkCache"))
+    if (type == "LinkCache")
     {
         m_isLinkCache = true;
     }
-    else if (type == std::string("PathCache"))
+    else if (type == "PathCache")
     {
         m_isLinkCache = false;
     }
@@ -442,13 +436,7 @@
             }
             route.push_back(source);
             // Reverse the route
-            DsrRouteCacheEntry::IP_VECTOR reverseroute;
-            for (DsrRouteCacheEntry::IP_VECTOR::reverse_iterator j = route.rbegin();
-                 j != route.rend();
-                 ++j)
-            {
-                reverseroute.push_back(*j);
-            }
+            DsrRouteCacheEntry::IP_VECTOR reverseroute(route.rbegin(), route.rend());
             NS_LOG_LOGIC("Add newly calculated best routes");
             PrintVector(reverseroute);
             m_bestRoutesTable_link[i->first] = reverseroute;
@@ -469,24 +457,22 @@
         NS_LOG_INFO("No route find to " << id);
         return false;
     }
-    else
-    {
-        if (i->second.size() < 2)
-        {
-            NS_LOG_LOGIC("Route to " << id << " error");
-            return false;
-        }
 
-        DsrRouteCacheEntry newEntry; // Create the route entry
-        newEntry.SetVector(i->second);
-        newEntry.SetDestination(id);
-        newEntry.SetExpireTime(RouteCacheTimeout);
-        NS_LOG_INFO("Route to " << id << " found with the length " << i->second.size());
-        rt = newEntry;
-        std::vector<Ipv4Address> path = rt.GetVector();
-        PrintVector(path);
-        return true;
+    if (i->second.size() < 2)
+    {
+        NS_LOG_LOGIC("Route to " << id << " error");
+        return false;
     }
+
+    DsrRouteCacheEntry newEntry; // Create the route entry
+    newEntry.SetVector(i->second);
+    newEntry.SetDestination(id);
+    newEntry.SetExpireTime(RouteCacheTimeout);
+    NS_LOG_INFO("Route to " << id << " found with the length " << i->second.size());
+    rt = newEntry;
+    std::vector<Ipv4Address> path = rt.GetVector();
+    PrintVector(path);
+    return true;
 }
 
 void
@@ -710,55 +696,54 @@
             m_sortedRoutes.insert(std::make_pair(dst, rtVector));
         return result.second;
     }
-    else
+
+    rtVector = i->second;
+    NS_LOG_DEBUG("The existing route size " << rtVector.size() << " for destination address "
+                                            << dst);
+    /**
+     * \brief Drop the most aged packet when buffer reaches to max
+     */
+    if (rtVector.size() >= m_maxEntriesEachDst)
     {
-        rtVector = i->second;
-        NS_LOG_DEBUG("The existing route size " << rtVector.size() << " for destination address "
-                                                << dst);
-        /**
-         * \brief Drop the most aged packet when buffer reaches to max
-         */
-        if (rtVector.size() >= m_maxEntriesEachDst)
-        {
-            RemoveLastEntry(rtVector); // Drop the last entry for the sorted route cache, the route
-                                       // has already been sorted
-        }
+        RemoveLastEntry(rtVector); // Drop the last entry for the sorted route cache, the route
+                                   // has already been sorted
+    }
 
-        if (FindSameRoute(rt, rtVector))
+    if (FindSameRoute(rt, rtVector))
+    {
+        NS_LOG_DEBUG(
+            "Find same vector, the FindSameRoute function will update the route expire time");
+        return true;
+    }
+    else
+    {
+        // Check if the expire time for the new route has expired or not
+        if (rt.GetExpireTime() > Time(0))
         {
-            NS_LOG_DEBUG(
-                "Find same vector, the FindSameRoute function will update the route expire time");
-            return true;
+            rtVector.push_back(rt);
+            // This sort function will sort the route cache entries based on the size of route
+            // in each of the route entries
+            rtVector.sort(CompareRoutesExpire);
+            NS_LOG_DEBUG("The first time" << rtVector.front().GetExpireTime().As(Time::S)
+                                          << " The second time "
+                                          << rtVector.back().GetExpireTime().As(Time::S));
+            NS_LOG_DEBUG("The first hop" << rtVector.front().GetVector().size()
+                                         << " The second hop "
+                                         << rtVector.back().GetVector().size());
+            m_sortedRoutes.erase(dst); // erase the route entries for dst first
+            /**
+             * Save the new route cache along with the destination address in map
+             */
+            std::pair<std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::iterator, bool> result =
+                m_sortedRoutes.insert(std::make_pair(dst, rtVector));
+            return result.second;
         }
         else
         {
-            // Check if the expire time for the new route has expired or not
-            if (rt.GetExpireTime() > Time(0))
-            {
-                rtVector.push_back(rt);
-                // This sort function will sort the route cache entries based on the size of route
-                // in each of the route entries
-                rtVector.sort(CompareRoutesExpire);
-                NS_LOG_DEBUG("The first time" << rtVector.front().GetExpireTime().As(Time::S)
-                                              << " The second time "
-                                              << rtVector.back().GetExpireTime().As(Time::S));
-                NS_LOG_DEBUG("The first hop" << rtVector.front().GetVector().size()
-                                             << " The second hop "
-                                             << rtVector.back().GetVector().size());
-                m_sortedRoutes.erase(dst); // erase the route entries for dst first
-                /**
-                 * Save the new route cache along with the destination address in map
-                 */
-                std::pair<std::map<Ipv4Address, std::list<DsrRouteCacheEntry>>::iterator, bool>
-                    result = m_sortedRoutes.insert(std::make_pair(dst, rtVector));
-                return result.second;
-            }
-            else
-            {
-                NS_LOG_INFO("The newly found route is already expired");
-            }
+            NS_LOG_INFO("The newly found route is already expired");
         }
     }
+
     return false;
 }
 
@@ -897,15 +882,12 @@
                     }
                     else
                     {
+                        changeVector.push_back(*i);
+
                         if (*(i + 1) == unreachNode)
                         {
-                            changeVector.push_back(*i);
                             break;
                         }
-                        else
-                        {
-                            changeVector.push_back(*i);
-                        }
                     }
                 }
                 /*
@@ -1109,14 +1091,12 @@
         m_ackIdCache[nextHop] = 1;
         return 1;
     }
-    else
-    {
-        uint16_t ackId = m_ackIdCache[nextHop];
-        NS_LOG_LOGIC("Ack id for " << nextHop << " found in the cache has value " << ackId);
-        ackId++;
-        m_ackIdCache[nextHop] = ackId;
-        return ackId;
-    }
+
+    uint16_t ackId = m_ackIdCache[nextHop];
+    NS_LOG_LOGIC("Ack id for " << nextHop << " found in the cache has value " << ackId);
+    ackId++;
+    m_ackIdCache[nextHop] = ackId;
+    return ackId;
 }
 
 uint16_t
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-rcache.h ns-3.39/src/dsr/model/dsr-rcache.h
--- ns-3.38/src/dsr/model/dsr-rcache.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-rcache.h	2023-07-05 14:23:47.777427200 -0700
@@ -767,16 +767,6 @@
     void DelArpCache(Ptr<ArpCache>);
 
     /**
-     * \brief Get callback to ProcessTxError, this callback is trying to use the wifi mac tx error
-     * header to notify a link layer drop event, however, it is not fully supported yet
-     * \return The callback to ProcessTxError
-     */
-    Callback<void, const WifiMacHeader&> GetTxErrorCallback() const
-    {
-        return m_txErrorCallback;
-    }
-
-    /**
      * Handle link failure callback
      * \param cb the callback to be set
      */
@@ -912,8 +902,6 @@
      */
     Callback<void, Ipv4Address, uint8_t> m_handleLinkFailure; ///< link failure callback
 
-    Callback<void, const WifiMacHeader&> m_txErrorCallback; ///< TX error callback
-
     Timer m_ntimer; ///< Timer for neighbor's list. Schedule Purge().
 
     std::vector<Neighbor> m_nb; ///< vector of entries
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-routing.cc ns-3.39/src/dsr/model/dsr-routing.cc
--- ns-3.38/src/dsr/model/dsr-routing.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-routing.cc	2023-07-05 14:23:47.777427200 -0700
@@ -579,8 +579,6 @@
                 Ptr<AdhocWifiMac> adhoc = mac->GetObject<AdhocWifiMac>();
                 if (adhoc)
                 {
-                    adhoc->TraceDisconnectWithoutContext("TxErrHeader",
-                                                         m_routeCache->GetTxErrorCallback());
                     m_routeCache->DelArpCache(m_ipv4->GetInterface(i)->GetArpCache());
                 }
             }
@@ -769,23 +767,22 @@
         nextHop = vec[1];
         return nextHop;
     }
-    else
+
+    if (ipv4Address == vec.back())
+    {
+        NS_LOG_DEBUG("We have reached to the final destination " << ipv4Address << " "
+                                                                 << vec.back());
+        return ipv4Address;
+    }
+    for (std::vector<Ipv4Address>::const_iterator i = vec.begin(); i != vec.end(); ++i)
     {
-        if (ipv4Address == vec.back())
+        if (ipv4Address == (*i))
         {
-            NS_LOG_DEBUG("We have reached to the final destination " << ipv4Address << " "
-                                                                     << vec.back());
-            return ipv4Address;
-        }
-        for (std::vector<Ipv4Address>::const_iterator i = vec.begin(); i != vec.end(); ++i)
-        {
-            if (ipv4Address == (*i))
-            {
-                nextHop = *(++i);
-                return nextHop;
-            }
+            nextHop = *(++i);
+            return nextHop;
         }
     }
+
     NS_LOG_DEBUG("Next hop address not found");
     Ipv4Address none = "0.0.0.0";
     return none;
@@ -833,12 +830,10 @@
         NS_LOG_DEBUG("Exceed the node range");
         return "0.0.0.0";
     }
-    else
-    {
-        Ptr<Node> node = NodeList::GetNode(uint32_t(id));
-        Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
-        return ipv4->GetAddress(1, 0).GetLocal();
-    }
+
+    Ptr<Node> node = NodeList::GetNode(uint32_t(id));
+    Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
+    return ipv4->GetAddress(1, 0).GetLocal();
 }
 
 uint32_t
@@ -3035,7 +3030,6 @@
             SendPacketFromBuffer(sourceRoute, nextHop, protocol);
         }
         NS_LOG_LOGIC("Route to " << dst << " found");
-        return;
     }
     else
     {
@@ -3535,7 +3529,7 @@
     }
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 DsrRouting::Receive(Ptr<Packet> p, const Ipv4Header& ip, Ptr<Ipv4Interface> incomingInterface)
 {
     NS_LOG_FUNCTION(this << p << ip << incomingInterface);
@@ -3660,7 +3654,7 @@
         optionLength =
             dsrOption
                 ->Process(p, packet, m_mainAddress, source, ip, protocol, isPromisc, promiscSource);
-        segmentsLeft = *(data + 3);
+        segmentsLeft = data[3];
         if (optionLength == 0)
         {
             NS_LOG_INFO("Discard this packet");
@@ -3674,36 +3668,32 @@
                 uint8_t nextHeader = dsrRoutingHeader.GetNextHeader();
                 Ptr<Ipv4L3Protocol> l3proto = m_node->GetObject<Ipv4L3Protocol>();
                 Ptr<IpL4Protocol> nextProto = l3proto->GetProtocol(nextHeader);
-                if (nextProto)
+                if (!nextProto)
                 {
-                    // we need to make a copy in the unlikely event we hit the
-                    // RX_ENDPOINT_UNREACH code path
-                    // Here we can use the packet that has been get off whole DSR header
-                    enum IpL4Protocol::RxStatus status =
-                        nextProto->Receive(copy, ip, incomingInterface);
-                    NS_LOG_DEBUG("The receive status " << status);
-                    switch (status)
-                    {
-                    case IpL4Protocol::RX_OK:
-                    // fall through
-                    case IpL4Protocol::RX_ENDPOINT_CLOSED:
-                    // fall through
-                    case IpL4Protocol::RX_CSUM_FAILED:
-                        break;
-                    case IpL4Protocol::RX_ENDPOINT_UNREACH:
-                        if (ip.GetDestination().IsBroadcast() == true ||
-                            ip.GetDestination().IsMulticast() == true)
-                        {
-                            break; // Do not reply to broadcast or multicast
-                        }
-                        // Another case to suppress ICMP is a subnet-directed broadcast
-                    }
-                    return status;
+                    NS_FATAL_ERROR("Should not have 0 next protocol value");
                 }
-                else
+
+                // we need to make a copy in the unlikely event we hit the
+                // RX_ENDPOINT_UNREACH code path
+                // Here we can use the packet that has been get off whole DSR header
+                IpL4Protocol::RxStatus status = nextProto->Receive(copy, ip, incomingInterface);
+                NS_LOG_DEBUG("The receive status " << status);
+                switch (status)
                 {
-                    NS_FATAL_ERROR("Should not have 0 next protocol value");
+                case IpL4Protocol::RX_OK:
+                // fall through
+                case IpL4Protocol::RX_ENDPOINT_CLOSED:
+                // fall through
+                case IpL4Protocol::RX_CSUM_FAILED:
+                    break;
+                case IpL4Protocol::RX_ENDPOINT_UNREACH:
+                    if (ip.GetDestination().IsBroadcast() || ip.GetDestination().IsMulticast())
+                    {
+                        break; // Do not reply to broadcast or multicast
+                    }
+                    // Another case to suppress ICMP is a subnet-directed broadcast
                 }
+                return status;
             }
             else
             {
@@ -3720,26 +3710,26 @@
          */
         uint8_t salvage = 0;
 
-        DsrOptionRerrUnsupportHeader rerrUnsupportHeader;
-        rerrUnsupportHeader.SetErrorType(3); // The error type 3 means Option not supported
-        rerrUnsupportHeader.SetErrorSrc(
+        DsrOptionRerrUnsupportedHeader rerrUnsupportedHeader;
+        rerrUnsupportedHeader.SetErrorType(3); // The error type 3 means Option not supported
+        rerrUnsupportedHeader.SetErrorSrc(
             m_mainAddress); // The error source address is our own address
-        rerrUnsupportHeader.SetUnsupported(optionType); // The unsupported option type number
-        rerrUnsupportHeader.SetErrorDst(
+        rerrUnsupportedHeader.SetUnsupported(optionType); // The unsupported option type number
+        rerrUnsupportedHeader.SetErrorDst(
             src); // Error destination address is the destination of the data packet
-        rerrUnsupportHeader.SetSalvage(
+        rerrUnsupportedHeader.SetSalvage(
             salvage); // Set the value about whether to salvage a packet or not
 
         /*
          * The unknown option error is not supported currently in this implementation, and it's also
          * not likely to happen in simulations
          */
-        //            SendError (rerrUnsupportHeader, 0, protocol); // Send the error packet
+        //            SendError (rerrUnsupportedHeader, 0, protocol); // Send the error packet
     }
     return IpL4Protocol::RX_OK;
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 DsrRouting::Receive(Ptr<Packet> p, const Ipv6Header& ip, Ptr<Ipv6Interface> incomingInterface)
 {
     NS_LOG_FUNCTION(this << p << ip.GetSource() << ip.GetDestination() << incomingInterface);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-routing.h ns-3.39/src/dsr/model/dsr-routing.h
--- ns-3.38/src/dsr/model/dsr-routing.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-routing.h	2023-07-05 14:23:47.777427200 -0700
@@ -644,9 +644,9 @@
      * Called from lower-level layers to send the packet up
      * in the stack.
      */
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv4Header& header,
-                                        Ptr<Ipv4Interface> incomingInterface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv4Header& header,
+                                   Ptr<Ipv4Interface> incomingInterface) override;
 
     /**
      * \param p packet to forward up
@@ -657,9 +657,9 @@
      * Called from lower-level layers to send the packet up
      * in the stack.  Not implemented (IPv6).
      */
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv6Header& header,
-                                        Ptr<Ipv6Interface> incomingInterface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv6Header& header,
+                                   Ptr<Ipv6Interface> incomingInterface) override;
 
     void SetDownTarget(IpL4Protocol::DownTargetCallback callback) override;
     void SetDownTarget6(IpL4Protocol::DownTargetCallback6 callback) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-rreq-table.cc ns-3.39/src/dsr/model/dsr-rreq-table.cc
--- ns-3.38/src/dsr/model/dsr-rreq-table.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-rreq-table.cc	2023-07-05 14:23:47.777427200 -0700
@@ -145,11 +145,9 @@
         NS_LOG_LOGIC("Request table entry not found");
         return 0;
     }
-    else
-    {
-        RreqTableEntry rreqTableEntry = i->second;
-        return rreqTableEntry.m_reqNo;
-    }
+
+    RreqTableEntry rreqTableEntry = i->second;
+    return rreqTableEntry.m_reqNo;
 }
 
 // ----------------------------------------------------------------------------------------------------------
@@ -168,25 +166,23 @@
         m_rreqIdCache[dst] = 0;
         return 0;
     }
+
+    NS_LOG_LOGIC("Request id for " << dst << " found in the cache");
+    uint32_t rreqId = m_rreqIdCache[dst];
+    if (rreqId >= m_maxRreqId)
+    {
+        NS_LOG_DEBUG("The request id increase past the max value, " << m_maxRreqId
+                                                                    << " so reset it to 0");
+        rreqId = 0;
+        m_rreqIdCache[dst] = rreqId;
+    }
     else
     {
-        NS_LOG_LOGIC("Request id for " << dst << " found in the cache");
-        uint32_t rreqId = m_rreqIdCache[dst];
-        if (rreqId >= m_maxRreqId)
-        {
-            NS_LOG_DEBUG("The request id increase past the max value, " << m_maxRreqId
-                                                                        << " so reset it to 0");
-            rreqId = 0;
-            m_rreqIdCache[dst] = rreqId;
-        }
-        else
-        {
-            rreqId++;
-            m_rreqIdCache[dst] = rreqId;
-        }
-        NS_LOG_INFO("The Request id for " << dst << " is " << rreqId);
-        return rreqId;
+        rreqId++;
+        m_rreqIdCache[dst] = rreqId;
     }
+    NS_LOG_INFO("The Request id for " << dst << " is " << rreqId);
+    return rreqId;
 }
 
 uint32_t
@@ -276,35 +272,32 @@
         m_sourceRreqMap[src] = receivedRreqEntryList;
         return false;
     }
-    else
+
+    NS_LOG_LOGIC("Find the request table entry for  " << src << ", check if it is exact duplicate");
+    /*
+     * Drop the most aged packet when buffer reaches to max
+     */
+    receivedRreqEntryList = i->second;
+    if (receivedRreqEntryList.size() >= m_requestIdSize)
     {
-        NS_LOG_LOGIC("Find the request table entry for  " << src
-                                                          << ", check if it is exact duplicate");
-        /*
-         * Drop the most aged packet when buffer reaches to max
-         */
-        receivedRreqEntryList = i->second;
-        if (receivedRreqEntryList.size() >= m_requestIdSize)
-        {
-            receivedRreqEntryList.pop_front();
-        }
-        Ipv4Address src = i->first;
-        // We loop the receive rreq entry to find duplicate
-        for (std::list<DsrReceivedRreqEntry>::const_iterator j = receivedRreqEntryList.begin();
-             j != receivedRreqEntryList.end();
-             ++j)
+        receivedRreqEntryList.pop_front();
+    }
+
+    // We loop the receive rreq entry to find duplicate
+    for (std::list<DsrReceivedRreqEntry>::const_iterator j = receivedRreqEntryList.begin();
+         j != receivedRreqEntryList.end();
+         ++j)
+    {
+        if (*j == rreqEntry) /// Check if we have found one duplication entry or not
         {
-            if (*j == rreqEntry) /// Check if we have found one duplication entry or not
-            {
-                return true;
-            }
+            return true;
         }
-        /// if this entry is not found, we need to save the entry in the cache, and then return
-        /// false for the check
-        receivedRreqEntryList.push_back(rreqEntry);
-        m_sourceRreqMap[src] = receivedRreqEntryList;
-        return false;
     }
+    /// if this entry is not found, we need to save the entry in the cache, and then return
+    /// false for the check
+    receivedRreqEntryList.push_back(rreqEntry);
+    m_sourceRreqMap[src] = receivedRreqEntryList;
+    return false;
 }
 
 } // namespace dsr
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/dsr/model/dsr-rreq-table.h ns-3.39/src/dsr/model/dsr-rreq-table.h
--- ns-3.38/src/dsr/model/dsr-rreq-table.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/dsr/model/dsr-rreq-table.h	2023-07-05 14:23:47.777427200 -0700
@@ -408,7 +408,7 @@
          * \param b BlackList entry
          * \return true if expired, false otherwise
          */
-        bool operator()(const struct BlackList& b) const
+        bool operator()(const BlackList& b) const
         {
             return (b.m_expireTime < Simulator::Now());
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/energy/helper/energy-source-container.h ns-3.39/src/energy/helper/energy-source-container.h
--- ns-3.38/src/energy/helper/energy-source-container.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/energy/helper/energy-source-container.h	2023-07-05 14:23:47.785427300 -0700
@@ -63,7 +63,7 @@
     /**
      * \param source Pointer to an EnergySource.
      *
-     * Creates an EnergySourceContianer with exactly one EnergySource previously
+     * Creates an EnergySourceContainer with exactly one EnergySource previously
      * instantiated.
      */
     EnergySourceContainer(Ptr<EnergySource> source);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/energy/model/energy-source.cc ns-3.39/src/energy/model/energy-source.cc
--- ns-3.38/src/energy/model/energy-source.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/energy/model/energy-source.cc	2023-07-05 14:23:47.789427300 -0700
@@ -22,9 +22,8 @@
  * Modifications made by: Cristiano Tapparello <cristiano.tapparello@rochester.edu>
  */
 
-#include "energy-source.h"
-
-#include "ns3/log.h"
+#include <ns3/energy-source.h>
+#include <ns3/log.h>
 
 namespace ns3
 {
@@ -168,22 +167,27 @@
         totalCurrentA += (*i)->GetCurrentA();
     }
 
-    double totalHarvestedPower = 0.0;
-
-    std::vector<Ptr<EnergyHarvester>>::const_iterator harvester;
-    for (harvester = m_harvesters.begin(); harvester != m_harvesters.end(); harvester++)
+    if (!m_harvesters.empty())
     {
-        totalHarvestedPower += (*harvester)->GetPower();
-    }
+        double totalHarvestedPower = 0.0;
 
-    NS_LOG_DEBUG("EnergySource(" << GetNode()->GetId()
-                                 << "): Total harvested power = " << totalHarvestedPower);
+        std::vector<Ptr<EnergyHarvester>>::const_iterator harvester;
+        for (harvester = m_harvesters.begin(); harvester != m_harvesters.end(); harvester++)
+        {
+            totalHarvestedPower += (*harvester)->GetPower();
+        }
 
-    double currentHarvestersA = totalHarvestedPower / GetSupplyVoltage();
-    NS_LOG_DEBUG("EnergySource(" << GetNode()->GetId()
-                                 << "): Current from harvesters = " << currentHarvestersA);
+        double supplyVoltage = GetSupplyVoltage();
 
-    totalCurrentA -= currentHarvestersA;
+        if (supplyVoltage != 0)
+        {
+            double currentHarvestersA = totalHarvestedPower / supplyVoltage;
+            NS_LOG_DEBUG(" Total harvested power: " << totalHarvestedPower
+                                                    << "| Current from harvesters: "
+                                                    << currentHarvestersA);
+            totalCurrentA -= currentHarvestersA;
+        }
+    }
 
     return totalCurrentA;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/energy/model/li-ion-energy-source.h ns-3.39/src/energy/model/li-ion-energy-source.h
--- ns-3.38/src/energy/model/li-ion-energy-source.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/energy/model/li-ion-energy-source.h	2023-07-05 14:23:47.789427300 -0700
@@ -46,7 +46,7 @@
  *
  *
  * The model requires several parameters to approximates the discharge curves:
- * - IntialCellVoltage, maximum voltage of the fully charged cell
+ * - InitialCellVoltage, maximum voltage of the fully charged cell
  * - NominalCellVoltage, nominal cell's voltage, is used to determine the end of the
  *   nominal zone.
  * - ExpCellVoltage, cell's voltage at the end of the exponential zone
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/energy/model/simple-device-energy-model.h ns-3.39/src/energy/model/simple-device-energy-model.h
--- ns-3.38/src/energy/model/simple-device-energy-model.h	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/energy/model/simple-device-energy-model.h	2023-07-05 14:23:47.789427300 -0700
@@ -65,7 +65,7 @@
     virtual Ptr<Node> GetNode() const;
 
     /**
-     * \brief Sets pointer to EnergySouce installed on node.
+     * \brief Sets pointer to EnergySource installed on node.
      *
      * \param source Pointer to EnergySource installed on node.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/energy/test/examples-to-run.py ns-3.39/src/energy/test/examples-to-run.py
--- ns-3.38/src/energy/test/examples-to-run.py	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/energy/test/examples-to-run.py	2023-07-05 14:23:47.789427300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/CMakeLists.txt ns-3.39/src/fd-net-device/CMakeLists.txt
--- ns-3.38/src/fd-net-device/CMakeLists.txt	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/fd-net-device/CMakeLists.txt	2023-07-05 14:23:47.789427300 -0700
@@ -1,9 +1,3 @@
-option(
-  NS3_DPDK
-  "Enable fd-net-device DPDK features"
-  OFF
-)
-
 set(module_enabled_features) # modulegen_customizations consumes this list
 
 check_include_file(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/examples/fd-emu-onoff.cc ns-3.39/src/fd-net-device/examples/fd-emu-onoff.cc
--- ns-3.38/src/fd-net-device/examples/fd-emu-onoff.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/fd-net-device/examples/fd-emu-onoff.cc	2023-07-05 14:23:47.793427200 -0700
@@ -103,7 +103,6 @@
 //
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/fd-net-device-module.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/examples/fd-emu-ping.cc ns-3.39/src/fd-net-device/examples/fd-emu-ping.cc
--- ns-3.38/src/fd-net-device/examples/fd-emu-ping.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/fd-net-device/examples/fd-emu-ping.cc	2023-07-05 14:23:47.793427200 -0700
@@ -50,7 +50,7 @@
 //  1') If you run emulation in dpdk mode, use device address (eg. 0000:00.1f.6)
 //      as device name. This address can be obtained by running `lspci`
 //  2) The host device must be set to promiscuous mode
-//     (e.g. "sudo ifconfig eth0 promisc")
+//     (e.g. "sudo ip link set eth0 promisc on")
 //  2') If you run emulation in netmap or dpdk mode, you need before to load
 //      the netmap.ko or dpdk modules. The user is in charge to configure and
 //      build netmap/dpdk separately.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/examples/fd-emu-send.cc ns-3.39/src/fd-net-device/examples/fd-emu-send.cc
--- ns-3.38/src/fd-net-device/examples/fd-emu-send.cc	2023-03-16 20:54:23.604868400 -0700
+++ ns-3.39/src/fd-net-device/examples/fd-emu-send.cc	2023-07-05 14:23:47.793427200 -0700
@@ -99,7 +99,7 @@
 
         if (level == 1)
         {
-            if (device->SendFrom(packet, sender, receiver, 0) == false)
+            if (!device->SendFrom(packet, sender, receiver, 0))
             {
                 failed++;
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/helper/dpdk-net-device-helper.h ns-3.39/src/fd-net-device/helper/dpdk-net-device-helper.h
--- ns-3.38/src/fd-net-device/helper/dpdk-net-device-helper.h	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/fd-net-device/helper/dpdk-net-device-helper.h	2023-07-05 14:23:47.793427200 -0700
@@ -40,7 +40,7 @@
      */
     DpdkNetDeviceHelper();
 
-    virtual ~DpdkNetDeviceHelper()
+    ~DpdkNetDeviceHelper() override
     {
     }
 
@@ -73,7 +73,7 @@
      * \param node The node to install the device in
      * \returns A container holding the added net device.
      */
-    Ptr<NetDevice> InstallPriv(Ptr<Node> node) const;
+    Ptr<NetDevice> InstallPriv(Ptr<Node> node) const override;
 
     /**
      * Logical cores to use
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/helper/encode-decode.h ns-3.39/src/fd-net-device/helper/encode-decode.h
--- ns-3.38/src/fd-net-device/helper/encode-decode.h	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/fd-net-device/helper/encode-decode.h	2023-07-05 14:23:47.793427200 -0700
@@ -18,6 +18,7 @@
 #ifndef ENCODE_DECODE_H
 #define ENCODE_DECODE_H
 
+#include <cstdint>
 #include <string>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/model/dpdk-net-device.h ns-3.39/src/fd-net-device/model/dpdk-net-device.h
--- ns-3.38/src/fd-net-device/model/dpdk-net-device.h	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/fd-net-device/model/dpdk-net-device.h	2023-07-05 14:23:47.797427200 -0700
@@ -61,7 +61,7 @@
     /**
      * Destructor for the DpdkNetDevice.
      */
-    ~DpdkNetDevice();
+    ~DpdkNetDevice() override;
 
     /**
      * Check the link status of all ports in up to 9s
@@ -114,20 +114,20 @@
      * Check the status of the link.
      * \return Status of the link - up/down as true/false.
      */
-    bool IsLinkUp() const;
+    bool IsLinkUp() const override;
 
     /**
      * Free the given packet buffer.
      * \param buf the pointer to the buffer to be freed
      */
-    virtual void FreeBuffer(uint8_t* buf);
+    void FreeBuffer(uint8_t* buf) override;
 
     /**
      * Allocate packet buffer.
      * \param len the length of the buffer
      * \return A pointer to the newly created buffer.
      */
-    virtual uint8_t* AllocateBuffer(size_t len);
+    uint8_t* AllocateBuffer(size_t len) override;
 
   protected:
     /**
@@ -136,7 +136,7 @@
      * \param length The data length.
      * \return The size of data written.
      */
-    ssize_t Write(uint8_t* buffer, size_t length);
+    ssize_t Write(uint8_t* buffer, size_t length) override;
 
     /**
      * The port number of the device to be used.
@@ -149,7 +149,7 @@
     std::string m_deviceName;
 
   private:
-    void DoFinishStoppingDevice();
+    void DoFinishStoppingDevice() override;
     /**
      * Condition variable for Dpdk to stop
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/model/fd-net-device.cc ns-3.39/src/fd-net-device/model/fd-net-device.cc
--- ns-3.38/src/fd-net-device/model/fd-net-device.cc	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/fd-net-device/model/fd-net-device.cc	2023-07-05 14:23:47.797427200 -0700
@@ -570,7 +570,7 @@
     NS_LOG_FUNCTION(this << packet << src << dest << protocolNumber);
     NS_LOG_LOGIC("packet: " << packet << " UID: " << packet->GetUid());
 
-    if (IsLinkUp() == false)
+    if (!IsLinkUp())
     {
         m_macTxDropTrace(packet);
         return false;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/fd-net-device/test/examples-to-run.py ns-3.39/src/fd-net-device/test/examples-to-run.py
--- ns-3.38/src/fd-net-device/test/examples-to-run.py	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/fd-net-device/test/examples-to-run.py	2023-07-05 14:23:47.797427200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/flow-monitor/doc/flow-monitor.rst ns-3.39/src/flow-monitor/doc/flow-monitor.rst
--- ns-3.38/src/flow-monitor/doc/flow-monitor.rst	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/flow-monitor/doc/flow-monitor.rst	2023-07-05 14:23:47.825427300 -0700
@@ -13,7 +13,7 @@
 Model Description
 *****************
 
-The source code for the new module lives in the directory ``src/flow-monitor``.
+The source code for the module lives in the directory ``src/flow-monitor``.
 
 The Flow Monitor module goal is to provide a flexible system to measure the
 performance of network protocols. The module uses probes, installed in network
@@ -86,6 +86,9 @@
 
 These stats will be written in XML form upon request (see the Usage section).
 
+Due to the above design, FlowMonitor can not generate statistics when used with DSR routing
+protocol (because DSR forwards packets using broadcast addresses)
+
 The "lost" packets problem
 ##########################
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/flow-monitor/model/flow-monitor.cc ns-3.39/src/flow-monitor/model/flow-monitor.cc
--- ns-3.38/src/flow-monitor/model/flow-monitor.cc	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/flow-monitor/model/flow-monitor.cc	2023-07-05 14:23:47.825427300 -0700
@@ -534,4 +534,31 @@
     os.close();
 }
 
+void
+FlowMonitor::ResetAllStats()
+{
+    NS_LOG_FUNCTION(this);
+
+    for (auto& iter : m_flowStats)
+    {
+        auto& flowStat = iter.second;
+        flowStat.delaySum = Seconds(0);
+        flowStat.jitterSum = Seconds(0);
+        flowStat.lastDelay = Seconds(0);
+        flowStat.txBytes = 0;
+        flowStat.rxBytes = 0;
+        flowStat.txPackets = 0;
+        flowStat.rxPackets = 0;
+        flowStat.lostPackets = 0;
+        flowStat.timesForwarded = 0;
+        flowStat.bytesDropped.clear();
+        flowStat.packetsDropped.clear();
+
+        flowStat.delayHistogram.Clear();
+        flowStat.jitterHistogram.Clear();
+        flowStat.packetSizeHistogram.Clear();
+        flowStat.flowInterruptionsHistogram.Clear();
+    }
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/flow-monitor/model/flow-monitor.h ns-3.39/src/flow-monitor/model/flow-monitor.h
--- ns-3.38/src/flow-monitor/model/flow-monitor.h	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/flow-monitor/model/flow-monitor.h	2023-07-05 14:23:47.825427300 -0700
@@ -273,6 +273,9 @@
     /// \param enableProbes if true, include also the per-probe/flow pair statistics in the output
     void SerializeToXmlFile(std::string fileName, bool enableHistograms, bool enableProbes);
 
+    /// Reset all the statistics
+    void ResetAllStats();
+
   protected:
     void NotifyConstructionCompleted() override;
     void DoDispose() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/flow-monitor/test/examples-to-run.py ns-3.39/src/flow-monitor/test/examples-to-run.py
--- ns-3.38/src/flow-monitor/test/examples-to-run.py	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/flow-monitor/test/examples-to-run.py	2023-07-05 14:23:47.825427300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/CMakeLists.txt ns-3.39/src/internet/CMakeLists.txt
--- ns-3.38/src/internet/CMakeLists.txt	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/CMakeLists.txt	2023-07-05 14:23:47.829427500 -0700
@@ -262,6 +262,7 @@
 set(test_sources
     test/global-route-manager-impl-test-suite.cc
     test/icmp-test.cc
+    test/internet-stack-helper-test-suite.cc
     test/ipv4-address-generator-test-suite.cc
     test/ipv4-address-helper-test-suite.cc
     test/ipv4-deduplication-test.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/examples/neighbor-cache-example.cc ns-3.39/src/internet/examples/neighbor-cache-example.cc
--- ns-3.38/src/internet/examples/neighbor-cache-example.cc	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/examples/neighbor-cache-example.cc	2023-07-05 14:23:47.837427400 -0700
@@ -283,7 +283,6 @@
 #include "ns3/csma-module.h"
 #include "ns3/internet-module.h"
 #include "ns3/network-module.h"
-#include "ns3/point-to-point-module.h"
 
 using namespace ns3;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/helper/internet-stack-helper.cc ns-3.39/src/internet/helper/internet-stack-helper.cc
--- ns-3.38/src/internet/helper/internet-stack-helper.cc	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/helper/internet-stack-helper.cc	2023-07-05 14:23:47.837427400 -0700
@@ -20,22 +20,22 @@
 
 #include "internet-stack-helper.h"
 
+#include "ipv4-global-routing-helper.h"
+#include "ipv4-list-routing-helper.h"
+#include "ipv4-static-routing-helper.h"
+#include "ipv6-static-routing-helper.h"
+
 #include "ns3/arp-l3-protocol.h"
 #include "ns3/assert.h"
 #include "ns3/callback.h"
 #include "ns3/config.h"
-#include "ns3/core-config.h"
 #include "ns3/global-router-interface.h"
 #include "ns3/icmpv6-l4-protocol.h"
-#include "ns3/ipv4-global-routing-helper.h"
 #include "ns3/ipv4-global-routing.h"
-#include "ns3/ipv4-list-routing-helper.h"
-#include "ns3/ipv4-static-routing-helper.h"
 #include "ns3/ipv4.h"
 #include "ns3/ipv6-extension-demux.h"
 #include "ns3/ipv6-extension-header.h"
 #include "ns3/ipv6-extension.h"
-#include "ns3/ipv6-static-routing-helper.h"
 #include "ns3/ipv6.h"
 #include "ns3/log.h"
 #include "ns3/names.h"
@@ -129,7 +129,6 @@
 void
 InternetStackHelper::Initialize()
 {
-    SetTcp("ns3::TcpL4Protocol");
     Ipv4StaticRoutingHelper staticRouting;
     Ipv4GlobalRoutingHelper globalRouting;
     Ipv4ListRoutingHelper listRouting;
@@ -152,7 +151,6 @@
     m_routingv6 = o.m_routingv6->Copy();
     m_ipv4Enabled = o.m_ipv4Enabled;
     m_ipv6Enabled = o.m_ipv6Enabled;
-    m_tcpFactory = o.m_tcpFactory;
     m_ipv4ArpJitterEnabled = o.m_ipv4ArpJitterEnabled;
     m_ipv6NsRsJitterEnabled = o.m_ipv6NsRsJitterEnabled;
 }
@@ -267,12 +265,6 @@
 }
 
 void
-InternetStackHelper::SetTcp(const std::string tid)
-{
-    m_tcpFactory.SetTypeId(tid);
-}
-
-void
 InternetStackHelper::Install(NodeContainer c) const
 {
     for (NodeContainer::Iterator i = c.Begin(); i != c.End(); ++i)
@@ -290,6 +282,12 @@
 void
 InternetStackHelper::CreateAndAggregateObjectFromTypeId(Ptr<Node> node, const std::string typeId)
 {
+    TypeId tid = TypeId::LookupByName(typeId);
+    if (node->GetObject<Object>(tid))
+    {
+        return;
+    }
+
     ObjectFactory factory;
     factory.SetTypeId(typeId);
     Ptr<Object> protocol = factory.Create<Object>();
@@ -301,42 +299,33 @@
 {
     if (m_ipv4Enabled)
     {
-        if (node->GetObject<Ipv4>())
-        {
-            NS_FATAL_ERROR("InternetStackHelper::Install (): Aggregating "
-                           "an InternetStack to a node with an existing Ipv4 object");
-            return;
-        }
-
+        /* IPv4 stack */
         CreateAndAggregateObjectFromTypeId(node, "ns3::ArpL3Protocol");
         CreateAndAggregateObjectFromTypeId(node, "ns3::Ipv4L3Protocol");
         CreateAndAggregateObjectFromTypeId(node, "ns3::Icmpv4L4Protocol");
-        if (m_ipv4ArpJitterEnabled == false)
+        if (!m_ipv4ArpJitterEnabled)
         {
             Ptr<ArpL3Protocol> arp = node->GetObject<ArpL3Protocol>();
             NS_ASSERT(arp);
             arp->SetAttribute("RequestJitter",
                               StringValue("ns3::ConstantRandomVariable[Constant=0.0]"));
         }
+
         // Set routing
         Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
-        Ptr<Ipv4RoutingProtocol> ipv4Routing = m_routing->Create(node);
-        ipv4->SetRoutingProtocol(ipv4Routing);
+        if (!ipv4->GetRoutingProtocol())
+        {
+            Ptr<Ipv4RoutingProtocol> ipv4Routing = m_routing->Create(node);
+            ipv4->SetRoutingProtocol(ipv4Routing);
+        }
     }
 
     if (m_ipv6Enabled)
     {
         /* IPv6 stack */
-        if (node->GetObject<Ipv6>())
-        {
-            NS_FATAL_ERROR("InternetStackHelper::Install (): Aggregating "
-                           "an InternetStack to a node with an existing Ipv6 object");
-            return;
-        }
-
         CreateAndAggregateObjectFromTypeId(node, "ns3::Ipv6L3Protocol");
         CreateAndAggregateObjectFromTypeId(node, "ns3::Icmpv6L4Protocol");
-        if (m_ipv6NsRsJitterEnabled == false)
+        if (!m_ipv6NsRsJitterEnabled)
         {
             Ptr<Icmpv6L4Protocol> icmpv6l4 = node->GetObject<Icmpv6L4Protocol>();
             NS_ASSERT(icmpv6l4);
@@ -345,9 +334,11 @@
         }
         // Set routing
         Ptr<Ipv6> ipv6 = node->GetObject<Ipv6>();
-        Ptr<Ipv6RoutingProtocol> ipv6Routing = m_routingv6->Create(node);
-        ipv6->SetRoutingProtocol(ipv6Routing);
-
+        if (!ipv6->GetRoutingProtocol())
+        {
+            Ptr<Ipv6RoutingProtocol> ipv6Routing = m_routingv6->Create(node);
+            ipv6->SetRoutingProtocol(ipv6Routing);
+        }
         /* register IPv6 extensions and options */
         ipv6->RegisterExtensions();
         ipv6->RegisterOptions();
@@ -357,9 +348,12 @@
     {
         CreateAndAggregateObjectFromTypeId(node, "ns3::TrafficControlLayer");
         CreateAndAggregateObjectFromTypeId(node, "ns3::UdpL4Protocol");
-        node->AggregateObject(m_tcpFactory.Create<Object>());
-        Ptr<PacketSocketFactory> factory = CreateObject<PacketSocketFactory>();
-        node->AggregateObject(factory);
+        CreateAndAggregateObjectFromTypeId(node, "ns3::TcpL4Protocol");
+        if (!node->GetObject<PacketSocketFactory>())
+        {
+            Ptr<PacketSocketFactory> factory = CreateObject<PacketSocketFactory>();
+            node->AggregateObject(factory);
+        }
     }
 
     if (m_ipv4Enabled)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/helper/internet-stack-helper.h ns-3.39/src/internet/helper/internet-stack-helper.h
--- ns-3.38/src/internet/helper/internet-stack-helper.h	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/helper/internet-stack-helper.h	2023-07-05 14:23:47.837427400 -0700
@@ -144,8 +144,8 @@
 
     /**
      * Aggregate implementations of the ns3::Ipv4, ns3::Ipv6, ns3::Udp, and ns3::Tcp classes
-     * onto the provided node.  This method will assert if called on a node that
-     * already has an Ipv4 object aggregated to it.
+     * onto the provided node.  This method will do nothing if the stacks are already installed,
+     * and will not overwrite existing stacks parameters.
      *
      * \param nodeName The name of the node on which to install the stack.
      */
@@ -153,8 +153,8 @@
 
     /**
      * Aggregate implementations of the ns3::Ipv4, ns3::Ipv6, ns3::Udp, and ns3::Tcp classes
-     * onto the provided node.  This method will assert if called on a node that
-     * already has an Ipv4 object aggregated to it.
+     * onto the provided node.  This method will do nothing if the stacks are already installed,
+     * and will not overwrite existing stacks parameters.
      *
      * \param node The node on which to install the stack.
      */
@@ -162,9 +162,8 @@
 
     /**
      * For each node in the input container, aggregate implementations of the
-     * ns3::Ipv4, ns3::Ipv6, ns3::Udp, and, ns3::Tcp classes.  The program will assert
-     * if this method is called on a container with a node that already has
-     * an Ipv4 object aggregated to it.
+     * ns3::Ipv4, ns3::Ipv6, ns3::Udp, and, ns3::Tcp classes.  This method will do nothing if the
+     * stacks are already installed, and will not overwrite existing stacks parameters.
      *
      * \param c NodeContainer that holds the set of nodes on which to install the
      * new stacks.
@@ -177,19 +176,6 @@
     void InstallAll() const;
 
     /**
-     * \brief set the Tcp stack which will not need any other parameter.
-     *
-     * This function sets up the tcp stack to the given TypeId. It should not be
-     * used for NSC stack setup because the nsc stack needs the Library attribute
-     * to be setup, please use instead the version that requires an attribute
-     * and a value. If you choose to use this function anyways to set nsc stack
-     * the default value for the linux library will be used: "liblinux2.6.26.so".
-     *
-     * \param tid the type id, typically it is set to  "ns3::TcpL4Protocol"
-     */
-    void SetTcp(std::string tid);
-
-    /**
      * \brief Enable/disable IPv4 stack install.
      * \param enable enable state
      */
@@ -291,11 +277,6 @@
     void Initialize();
 
     /**
-     * \brief TCP objects factory
-     */
-    ObjectFactory m_tcpFactory;
-
-    /**
      * \brief IPv4 routing helper.
      */
     const Ipv4RoutingHelper* m_routing;
@@ -306,7 +287,8 @@
     const Ipv6RoutingHelper* m_routingv6;
 
     /**
-     * \brief create an object from its TypeId and aggregates it to the node
+     * \brief create an object from its TypeId and aggregates it to the node. Does nothing if
+     * an object of the same type is already aggregated to the node.
      * \param node the node
      * \param typeId the object TypeId
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/helper/ipv6-interface-container.cc ns-3.39/src/internet/helper/ipv6-interface-container.cc
--- ns-3.38/src/internet/helper/ipv6-interface-container.cc	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/helper/ipv6-interface-container.cc	2023-07-05 14:23:47.837427400 -0700
@@ -21,7 +21,8 @@
 
 #include "ipv6-interface-container.h"
 
-#include "ns3/ipv6-static-routing-helper.h"
+#include "ipv6-static-routing-helper.h"
+
 #include "ns3/names.h"
 #include "ns3/node-list.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/helper/neighbor-cache-helper.cc ns-3.39/src/internet/helper/neighbor-cache-helper.cc
--- ns-3.38/src/internet/helper/neighbor-cache-helper.cc	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/helper/neighbor-cache-helper.cc	2023-07-05 14:23:47.841427300 -0700
@@ -17,7 +17,7 @@
  * Author: Zhiheng Dong <dzh2077@gmail.com>
  */
 
-#include "ns3/neighbor-cache-helper.h"
+#include "neighbor-cache-helper.h"
 
 #include "ns3/assert.h"
 #include "ns3/channel-list.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/helper/ripng-helper.cc ns-3.39/src/internet/helper/ripng-helper.cc
--- ns-3.38/src/internet/helper/ripng-helper.cc	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/helper/ripng-helper.cc	2023-07-05 14:23:47.841427300 -0700
@@ -24,6 +24,8 @@
 #include "ns3/node.h"
 #include "ns3/ripng.h"
 
+#include <map>
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/helper/ripng-helper.h ns-3.39/src/internet/helper/ripng-helper.h
--- ns-3.38/src/internet/helper/ripng-helper.h	2023-03-16 20:54:23.612868500 -0700
+++ ns-3.39/src/internet/helper/ripng-helper.h	2023-07-05 14:23:47.841427300 -0700
@@ -25,6 +25,8 @@
 #include "ns3/node.h"
 #include "ns3/object-factory.h"
 
+#include <map>
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/arp-cache.cc ns-3.39/src/internet/model/arp-cache.cc
--- ns-3.38/src/internet/model/arp-cache.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/arp-cache.cc	2023-07-05 14:23:47.841427300 -0700
@@ -18,7 +18,6 @@
  */
 #include "arp-cache.h"
 
-#include "arp-header.h"
 #include "ipv4-header.h"
 #include "ipv4-interface.h"
 
@@ -551,11 +550,7 @@
     Time timeout = GetTimeout();
     Time delta = Simulator::Now() - m_lastSeen;
     NS_LOG_DEBUG("delta=" << delta.GetSeconds() << "s");
-    if (delta > timeout)
-    {
-        return true;
-    }
-    return false;
+    return delta > timeout;
 }
 
 ArpCache::Ipv4PayloadHeaderPair
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/arp-l3-protocol.cc ns-3.39/src/internet/model/arp-l3-protocol.cc
--- ns-3.38/src/internet/model/arp-l3-protocol.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/arp-l3-protocol.cc	2023-07-05 14:23:47.841427300 -0700
@@ -276,7 +276,7 @@
             break;
         }
     }
-    if (found == false)
+    if (!found)
     {
         NS_LOG_LOGIC("node=" << m_node->GetId() << ", got request from "
                              << arp.GetSourceIpv4Address() << " for unknown address "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/global-route-manager-impl.cc ns-3.39/src/internet/model/global-route-manager-impl.cc
--- ns-3.38/src/internet/model/global-route-manager-impl.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/global-route-manager-impl.cc	2023-07-05 14:23:47.841427300 -0700
@@ -26,14 +26,13 @@
 #include "candidate-queue.h"
 #include "global-router-interface.h"
 #include "ipv4-global-routing.h"
+#include "ipv4.h"
 
 #include "ns3/assert.h"
 #include "ns3/fatal-error.h"
-#include "ns3/ipv4-list-routing.h"
-#include "ns3/ipv4-routing-protocol.h"
-#include "ns3/ipv4.h"
 #include "ns3/log.h"
 #include "ns3/node-list.h"
+#include "ns3/simulator.h"
 
 #include <algorithm>
 #include <iostream>
@@ -1216,7 +1215,7 @@
             // skipped a previous link and moved forward to the next (which is then the
             // one we want).
             //
-            if (skip == false)
+            if (!skip)
             {
                 NS_LOG_LOGIC("Returning the found link");
                 return l;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/global-router-interface.cc ns-3.39/src/internet/model/global-router-interface.cc
--- ns-3.38/src/internet/model/global-router-interface.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/global-router-interface.cc	2023-07-05 14:23:47.845427500 -0700
@@ -20,13 +20,13 @@
 #include "global-router-interface.h"
 
 #include "ipv4-global-routing.h"
+#include "ipv4.h"
 #include "loopback-net-device.h"
 
 #include "ns3/abort.h"
 #include "ns3/assert.h"
 #include "ns3/bridge-net-device.h"
 #include "ns3/channel.h"
-#include "ns3/ipv4.h"
 #include "ns3/log.h"
 #include "ns3/net-device.h"
 #include "ns3/node-list.h"
@@ -791,7 +791,7 @@
     // is a transit network.
     //
     ClearBridgesVisited();
-    if (AnotherRouterOnLink(nd) == false)
+    if (!AnotherRouterOnLink(nd))
     {
         //
         // This is a net device connected to a stub network
@@ -831,29 +831,29 @@
         // case.
         //
         ClearBridgesVisited();
-        Ipv4Address desigRtr;
-        desigRtr = FindDesignatedRouterForLink(nd);
+        Ipv4Address designatedRtr;
+        designatedRtr = FindDesignatedRouterForLink(nd);
 
         //
         // Let's double-check that any designated router we find out on our
         // network is really on our network.
         //
-        if (desigRtr != "255.255.255.255")
+        if (designatedRtr != "255.255.255.255")
         {
             Ipv4Address networkHere = addrLocal.CombineMask(maskLocal);
-            Ipv4Address networkThere = desigRtr.CombineMask(maskLocal);
+            Ipv4Address networkThere = designatedRtr.CombineMask(maskLocal);
             NS_ABORT_MSG_UNLESS(
                 networkHere == networkThere,
                 "GlobalRouter::ProcessSingleBroadcastLink(): Network number confusion ("
-                    << addrLocal << "/" << maskLocal.GetPrefixLength() << ", " << desigRtr << "/"
-                    << maskLocal.GetPrefixLength() << ")");
+                    << addrLocal << "/" << maskLocal.GetPrefixLength() << ", " << designatedRtr
+                    << "/" << maskLocal.GetPrefixLength() << ")");
         }
-        if (desigRtr == addrLocal)
+        if (designatedRtr == addrLocal)
         {
             c.Add(nd);
             NS_LOG_LOGIC("Node " << node->GetId() << " elected a designated router");
         }
-        plr->SetLinkId(desigRtr);
+        plr->SetLinkId(designatedRtr);
 
         //
         // OSPF says that the Link Data is this router's own IP address.
@@ -921,7 +921,7 @@
   //
 
   bool areTransitNetwork = false;
-  Ipv4Address desigRtr ("255.255.255.255");
+  Ipv4Address designatedRtr ("255.255.255.255");
 
   for (uint32_t i = 0; i < bnd->GetNBridgePorts (); ++i)
     {
@@ -945,24 +945,24 @@
           // all.
           //
           ClearBridgesVisited ();
-          Ipv4Address desigRtrTemp = FindDesignatedRouterForLink (ndTemp);
+          Ipv4Address designatedRtrTemp = FindDesignatedRouterForLink (ndTemp);
 
           //
           // Let's double-check that any designated router we find out on our
           // network is really on our network.
           //
-          if (desigRtrTemp != "255.255.255.255")
+          if (designatedRtrTemp != "255.255.255.255")
             {
               Ipv4Address networkHere = addrLocal.CombineMask (maskLocal);
-              Ipv4Address networkThere = desigRtrTemp.CombineMask (maskLocal);
+              Ipv4Address networkThere = designatedRtrTemp.CombineMask (maskLocal);
               NS_ABORT_MSG_UNLESS (networkHere == networkThere,
                                    "GlobalRouter::ProcessSingleBroadcastLink(): Network number confusion (" <<
                                    addrLocal << "/" << maskLocal.GetPrefixLength () << ", " <<
-                                   desigRtrTemp << "/" << maskLocal.GetPrefixLength () << ")");
+                                   designatedRtrTemp << "/" << maskLocal.GetPrefixLength () << ")");
             }
-          if (desigRtrTemp < desigRtr)
+          if (designatedRtrTemp < designatedRtr)
             {
-              desigRtr = desigRtrTemp;
+              designatedRtr = designatedRtrTemp;
             }
         }
     }
@@ -1013,12 +1013,12 @@
       // gets the IP interface address of the designated router in this
       // case.
       //
-      if (desigRtr == addrLocal)
+      if (designatedRtr == addrLocal)
         {
           c.Add (nd);
           NS_LOG_LOGIC ("Node " << node->GetId () << " elected a designated router");
         }
-      plr->SetLinkId (desigRtr);
+      plr->SetLinkId (designatedRtr);
 
       //
       // OSPF says that the Link Data is this router's own IP address.
@@ -1290,7 +1290,7 @@
         Ptr<NetDevice> nd = ch->GetDevice(i);
         NS_LOG_LOGIC("checking to see if the device " << nd << " is bridged");
         Ptr<BridgeNetDevice> bnd = NetDeviceIsBridged(nd);
-        if (bnd && BridgeHasAlreadyBeenVisited(bnd) == false)
+        if (bnd && !BridgeHasAlreadyBeenVisited(bnd))
         {
             NS_LOG_LOGIC("Device is bridged by BridgeNetDevice "
                          << bnd << " with " << bnd->GetNBridgePorts() << " ports");
@@ -1337,7 +1337,7 @@
     NS_LOG_LOGIC("Looking for designated router off of net device " << ndLocal << " on node "
                                                                     << ndLocal->GetNode()->GetId());
 
-    Ipv4Address desigRtr("255.255.255.255");
+    Ipv4Address designatedRtr("255.255.255.255");
 
     //
     // Look through all of the devices on the channel to which the net device
@@ -1402,8 +1402,8 @@
                                     "primary one");
                     }
                     Ipv4Address addrOther = ipv4->GetAddress(interfaceOther, 0).GetLocal();
-                    desigRtr = addrOther < desigRtr ? addrOther : desigRtr;
-                    NS_LOG_LOGIC("designated router now " << desigRtr);
+                    designatedRtr = addrOther < designatedRtr ? addrOther : designatedRtr;
+                    NS_LOG_LOGIC("designated router now " << designatedRtr);
                 }
             }
 
@@ -1433,8 +1433,8 @@
 
                 NS_LOG_LOGIC("Recursively looking for routers down bridge port " << ndBridged);
                 Ipv4Address addrOther = FindDesignatedRouterForLink(ndBridged);
-                desigRtr = addrOther < desigRtr ? addrOther : desigRtr;
-                NS_LOG_LOGIC("designated router now " << desigRtr);
+                designatedRtr = addrOther < designatedRtr ? addrOther : designatedRtr;
+                NS_LOG_LOGIC("designated router now " << designatedRtr);
             }
         }
         else
@@ -1466,13 +1466,13 @@
                                     "primary one");
                     }
                     Ipv4Address addrOther = ipv4->GetAddress(interfaceOther, 0).GetLocal();
-                    desigRtr = addrOther < desigRtr ? addrOther : desigRtr;
-                    NS_LOG_LOGIC("designated router now " << desigRtr);
+                    designatedRtr = addrOther < designatedRtr ? addrOther : designatedRtr;
+                    NS_LOG_LOGIC("designated router now " << designatedRtr);
                 }
             }
         }
     }
-    return desigRtr;
+    return designatedRtr;
 }
 
 //
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/icmpv4-l4-protocol.cc ns-3.39/src/internet/model/icmpv4-l4-protocol.cc
--- ns-3.38/src/internet/model/icmpv4-l4-protocol.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/icmpv4-l4-protocol.cc	2023-07-05 14:23:47.845427500 -0700
@@ -20,13 +20,14 @@
 #include "icmpv4-l4-protocol.h"
 
 #include "ipv4-interface.h"
-#include "ipv4-l3-protocol.h"
 #include "ipv4-raw-socket-factory-impl.h"
+#include "ipv4-route.h"
+#include "ipv4-routing-protocol.h"
+#include "ipv4.h"
+#include "ipv6-interface.h"
 
 #include "ns3/assert.h"
 #include "ns3/boolean.h"
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv6-interface.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/packet.h"
@@ -289,7 +290,7 @@
     Forward(source, icmp, 0, ipHeader, payload);
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 Icmpv4L4Protocol::Receive(Ptr<Packet> p,
                           const Ipv4Header& header,
                           Ptr<Ipv4Interface> incomingInterface)
@@ -342,7 +343,7 @@
     return IpL4Protocol::RX_OK;
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 Icmpv4L4Protocol::Receive(Ptr<Packet> p,
                           const Ipv6Header& header,
                           Ptr<Ipv6Interface> incomingInterface)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/icmpv4-l4-protocol.h ns-3.39/src/internet/model/icmpv4-l4-protocol.h
--- ns-3.38/src/internet/model/icmpv4-l4-protocol.h	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/icmpv4-l4-protocol.h	2023-07-05 14:23:47.845427500 -0700
@@ -81,9 +81,9 @@
      * \param incomingInterface the interface from which the packet is coming
      * \returns the receive status
      */
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv4Header& header,
-                                        Ptr<Ipv4Interface> incomingInterface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv4Header& header,
+                                   Ptr<Ipv4Interface> incomingInterface) override;
 
     /**
      * \brief Receive method.
@@ -92,9 +92,9 @@
      * \param incomingInterface the interface from which the packet is coming
      * \returns the receive status
      */
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv6Header& header,
-                                        Ptr<Ipv6Interface> incomingInterface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv6Header& header,
+                                   Ptr<Ipv6Interface> incomingInterface) override;
 
     /**
      * \brief Send a Destination Unreachable - Fragmentation needed ICMP error
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/icmpv6-l4-protocol.cc ns-3.39/src/internet/model/icmpv6-l4-protocol.cc
--- ns-3.38/src/internet/model/icmpv6-l4-protocol.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/icmpv6-l4-protocol.cc	2023-07-05 14:23:47.845427500 -0700
@@ -24,13 +24,13 @@
 
 #include "ipv6-interface.h"
 #include "ipv6-l3-protocol.h"
+#include "ipv6-route.h"
+#include "ipv6-routing-protocol.h"
 
 #include "ns3/assert.h"
 #include "ns3/boolean.h"
 #include "ns3/double.h"
 #include "ns3/integer.h"
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6-routing-protocol.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/packet.h"
@@ -284,7 +284,7 @@
                         Ipv6Address::MakeSolicitedAddress(target));
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 Icmpv6L4Protocol::Receive(Ptr<Packet> packet,
                           const Ipv4Header& header,
                           Ptr<Ipv4Interface> interface)
@@ -293,7 +293,7 @@
     return IpL4Protocol::RX_ENDPOINT_UNREACH;
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 Icmpv6L4Protocol::Receive(Ptr<Packet> packet,
                           const Ipv6Header& header,
                           Ptr<Ipv6Interface> interface)
@@ -446,7 +446,7 @@
         defaultRouter = src;
     }
 
-    while (next == true)
+    while (next)
     {
         uint8_t type = 0;
         p->CopyData(&type, sizeof(type));
@@ -688,7 +688,7 @@
     bool next = true;
     bool hasSllao = false;
 
-    while (next == true)
+    while (next)
     {
         uint8_t type;
         packet->CopyData(&type, sizeof(type));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/icmpv6-l4-protocol.h ns-3.39/src/internet/model/icmpv6-l4-protocol.h
--- ns-3.38/src/internet/model/icmpv6-l4-protocol.h	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/icmpv6-l4-protocol.h	2023-07-05 14:23:47.845427500 -0700
@@ -328,9 +328,9 @@
      * \param interface the interface from which the packet is coming
      * \returns the receive status
      */
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv4Header& header,
-                                        Ptr<Ipv4Interface> interface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv4Header& header,
+                                   Ptr<Ipv4Interface> interface) override;
 
     /**
      * \brief Receive method.
@@ -339,9 +339,9 @@
      * \param interface the interface from which the packet is coming
      * \returns the receive status
      */
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv6Header& header,
-                                        Ptr<Ipv6Interface> interface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv6Header& header,
+                                   Ptr<Ipv6Interface> interface) override;
 
     /**
      * \brief Function called when DAD timeout.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-end-point.cc ns-3.39/src/internet/model/ipv4-end-point.cc
--- ns-3.38/src/internet/model/ipv4-end-point.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-end-point.cc	2023-07-05 14:23:47.845427500 -0700
@@ -51,7 +51,7 @@
 }
 
 Ipv4Address
-Ipv4EndPoint::GetLocalAddress()
+Ipv4EndPoint::GetLocalAddress() const
 {
     NS_LOG_FUNCTION(this);
     return m_localAddr;
@@ -65,21 +65,21 @@
 }
 
 uint16_t
-Ipv4EndPoint::GetLocalPort()
+Ipv4EndPoint::GetLocalPort() const
 {
     NS_LOG_FUNCTION(this);
     return m_localPort;
 }
 
 Ipv4Address
-Ipv4EndPoint::GetPeerAddress()
+Ipv4EndPoint::GetPeerAddress() const
 {
     NS_LOG_FUNCTION(this);
     return m_peerAddr;
 }
 
 uint16_t
-Ipv4EndPoint::GetPeerPort()
+Ipv4EndPoint::GetPeerPort() const
 {
     NS_LOG_FUNCTION(this);
     return m_peerPort;
@@ -101,7 +101,7 @@
 }
 
 Ptr<NetDevice>
-Ipv4EndPoint::GetBoundNetDevice()
+Ipv4EndPoint::GetBoundNetDevice() const
 {
     NS_LOG_FUNCTION(this);
     return m_boundnetdevice;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-end-point.h ns-3.39/src/internet/model/ipv4-end-point.h
--- ns-3.38/src/internet/model/ipv4-end-point.h	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-end-point.h	2023-07-05 14:23:47.845427500 -0700
@@ -62,7 +62,7 @@
      * \brief Get the local address.
      * \return the local address
      */
-    Ipv4Address GetLocalAddress();
+    Ipv4Address GetLocalAddress() const;
 
     /**
      * \brief Set the local address.
@@ -74,19 +74,19 @@
      * \brief Get the local port.
      * \return the local port
      */
-    uint16_t GetLocalPort();
+    uint16_t GetLocalPort() const;
 
     /**
      * \brief Get the peer address.
      * \return the peer address
      */
-    Ipv4Address GetPeerAddress();
+    Ipv4Address GetPeerAddress() const;
 
     /**
      * \brief Get the peer port.
      * \return the peer port
      */
-    uint16_t GetPeerPort();
+    uint16_t GetPeerPort() const;
 
     /**
      * \brief Set the peer information (address and port).
@@ -124,7 +124,7 @@
      *
      * \returns Pointer to interface.
      */
-    Ptr<NetDevice> GetBoundNetDevice();
+    Ptr<NetDevice> GetBoundNetDevice() const;
 
     // Called from socket implementations to get notified about important events.
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-global-routing.cc ns-3.39/src/internet/model/ipv4-global-routing.cc
--- ns-3.38/src/internet/model/ipv4-global-routing.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-global-routing.cc	2023-07-05 14:23:47.845427500 -0700
@@ -18,10 +18,10 @@
 #include "ipv4-global-routing.h"
 
 #include "global-route-manager.h"
+#include "ipv4-route.h"
+#include "ipv4-routing-table-entry.h"
 
 #include "ns3/boolean.h"
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv4-routing-table-entry.h"
 #include "ns3/log.h"
 #include "ns3/names.h"
 #include "ns3/net-device.h"
@@ -473,10 +473,10 @@
 Ipv4GlobalRouting::RouteInput(Ptr<const Packet> p,
                               const Ipv4Header& header,
                               Ptr<const NetDevice> idev,
-                              UnicastForwardCallback ucb,
-                              MulticastForwardCallback mcb,
-                              LocalDeliverCallback lcb,
-                              ErrorCallback ecb)
+                              const UnicastForwardCallback& ucb,
+                              const MulticastForwardCallback& mcb,
+                              const LocalDeliverCallback& lcb,
+                              const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p << header << header.GetSource() << header.GetDestination() << idev
                          << &lcb << &ecb);
@@ -504,7 +504,7 @@
     }
 
     // Check if input device supports IP forwarding
-    if (m_ipv4->IsForwarding(iif) == false)
+    if (!m_ipv4->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         ecb(p, header, Socket::ERROR_NOROUTETOHOST);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-global-routing.h ns-3.39/src/internet/model/ipv4-global-routing.h
--- ns-3.38/src/internet/model/ipv4-global-routing.h	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-global-routing.h	2023-07-05 14:23:47.845427500 -0700
@@ -96,10 +96,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
     void NotifyAddAddress(uint32_t interface, Ipv4InterfaceAddress address) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-l3-protocol.cc ns-3.39/src/internet/model/ipv4-l3-protocol.cc
--- ns-3.38/src/internet/model/ipv4-l3-protocol.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-l3-protocol.cc	2023-07-05 14:23:47.849427500 -0700
@@ -22,16 +22,15 @@
 #include "arp-cache.h"
 #include "arp-l3-protocol.h"
 #include "icmpv4-l4-protocol.h"
+#include "ipv4-header.h"
 #include "ipv4-interface.h"
 #include "ipv4-raw-socket-impl.h"
+#include "ipv4-route.h"
 #include "loopback-net-device.h"
 
 #include "ns3/boolean.h"
 #include "ns3/callback.h"
 #include "ns3/ipv4-address.h"
-#include "ns3/ipv4-header.h"
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv4-routing-table-entry.h"
 #include "ns3/log.h"
 #include "ns3/net-device.h"
 #include "ns3/node.h"
@@ -134,6 +133,10 @@
 Ipv4L3Protocol::Ipv4L3Protocol()
 {
     NS_LOG_FUNCTION(this);
+    m_ucb = MakeCallback(&Ipv4L3Protocol::IpForward, this);
+    m_mcb = MakeCallback(&Ipv4L3Protocol::IpMulticastForward, this);
+    m_lcb = MakeCallback(&Ipv4L3Protocol::LocalDeliver, this);
+    m_ecb = MakeCallback(&Ipv4L3Protocol::RouteInputError, this);
 }
 
 Ipv4L3Protocol::~Ipv4L3Protocol()
@@ -535,11 +538,10 @@
 #ifdef NOTYET
         if (MulticastCheckGroup(iif, address))
 #endif
-            if (true)
-            {
-                NS_LOG_LOGIC("For me (Ipv4Addr multicast address)");
-                return true;
-            }
+        {
+            NS_LOG_LOGIC("For me (Ipv4Addr multicast address)");
+            return true;
+        }
     }
 
     if (address.IsBroadcast())
@@ -674,13 +676,7 @@
     }
 
     NS_ASSERT_MSG(m_routingProtocol, "Need a routing protocol object to process packets");
-    if (!m_routingProtocol->RouteInput(packet,
-                                       ipHeader,
-                                       device,
-                                       MakeCallback(&Ipv4L3Protocol::IpForward, this),
-                                       MakeCallback(&Ipv4L3Protocol::IpMulticastForward, this),
-                                       MakeCallback(&Ipv4L3Protocol::LocalDeliver, this),
-                                       MakeCallback(&Ipv4L3Protocol::RouteInputError, this)))
+    if (!m_routingProtocol->RouteInput(packet, ipHeader, device, m_ucb, m_mcb, m_lcb, m_ecb))
     {
         NS_LOG_WARN("No route found for forwarding packet.  Drop.");
         m_dropTrace(ipHeader, packet, DROP_NO_ROUTE, this, interface);
@@ -963,7 +959,7 @@
     uint64_t srcDst = dst | (src << 32);
     std::pair<uint64_t, uint8_t> key = std::make_pair(srcDst, protocol);
 
-    if (mayFragment == true)
+    if (mayFragment)
     {
         ipHeader.SetMayFragment();
         ipHeader.SetIdentification(m_identification[key]);
@@ -1092,8 +1088,7 @@
     if (ipHeader.GetTtl() == 0)
     {
         // Do not reply to multicast/broadcast IP address
-        if (ipHeader.GetDestination().IsBroadcast() == false &&
-            ipHeader.GetDestination().IsMulticast() == false)
+        if (!ipHeader.GetDestination().IsBroadcast() && !ipHeader.GetDestination().IsMulticast())
         {
             Ptr<Icmpv4L4Protocol> icmp = GetIcmp();
             icmp->SendTimeExceededTtl(ipHeader, packet, false);
@@ -1129,7 +1124,7 @@
         NS_LOG_LOGIC("Received a fragment, processing " << *p);
         bool isPacketComplete;
         isPacketComplete = ProcessFragment(p, ipHeader, iif);
-        if (isPacketComplete == false)
+        if (!isPacketComplete)
         {
             return;
         }
@@ -1146,7 +1141,7 @@
         // we need to make a copy in the unlikely event we hit the
         // RX_ENDPOINT_UNREACH codepath
         Ptr<Packet> copy = p->Copy();
-        enum IpL4Protocol::RxStatus status = protocol->Receive(p, ipHeader, GetInterface(iif));
+        IpL4Protocol::RxStatus status = protocol->Receive(p, ipHeader, GetInterface(iif));
         switch (status)
         {
         case IpL4Protocol::RX_OK:
@@ -1156,8 +1151,7 @@
         case IpL4Protocol::RX_CSUM_FAILED:
             break;
         case IpL4Protocol::RX_ENDPOINT_UNREACH:
-            if (ipHeader.GetDestination().IsBroadcast() == true ||
-                ipHeader.GetDestination().IsMulticast() == true)
+            if (ipHeader.GetDestination().IsBroadcast() || ipHeader.GetDestination().IsMulticast())
             {
                 break; // Do not reply to broadcast or multicast
             }
@@ -1173,7 +1167,7 @@
                     subnetDirected = true;
                 }
             }
-            if (subnetDirected == false)
+            if (!subnetDirected)
             {
                 GetIcmp()->SendDestUnreachPort(ipHeader, copy);
             }
@@ -1268,7 +1262,7 @@
         Ipv4InterfaceAddress test = GetAddress(interfaceIdx, i);
         if (test.GetLocal().CombineMask(test.GetMask()) == dest.CombineMask(test.GetMask()))
         {
-            if (test.IsSecondary() == false)
+            if (!test.IsSecondary())
             {
                 return test.GetLocal();
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-l3-protocol.h ns-3.39/src/internet/model/ipv4-l3-protocol.h
--- ns-3.38/src/internet/model/ipv4-l3-protocol.h	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-l3-protocol.h	2023-07-05 14:23:47.849427500 -0700
@@ -630,6 +630,11 @@
     Time m_expire;      //!< duplicate entry expiration delay
     Time m_purge;       //!< time between purging expired duplicate entries
     EventId m_cleanDpd; //!< event to cleanup expired duplicate entries
+
+    Ipv4RoutingProtocol::UnicastForwardCallback m_ucb;   ///< Unicast forward callback
+    Ipv4RoutingProtocol::MulticastForwardCallback m_mcb; ///< Multicast forward callback
+    Ipv4RoutingProtocol::LocalDeliverCallback m_lcb;     ///< Local delivery callback
+    Ipv4RoutingProtocol::ErrorCallback m_ecb;            ///< Error callback
 };
 
 } // Namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-list-routing.cc ns-3.39/src/internet/model/ipv4-list-routing.cc
--- ns-3.38/src/internet/model/ipv4-list-routing.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-list-routing.cc	2023-07-05 14:23:47.849427500 -0700
@@ -18,9 +18,9 @@
 
 #include "ipv4-list-routing.h"
 
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv4-static-routing.h"
-#include "ns3/ipv4.h"
+#include "ipv4-route.h"
+#include "ipv4.h"
+
 #include "ns3/log.h"
 #include "ns3/node.h"
 
@@ -105,7 +105,7 @@
 Ipv4ListRouting::RouteOutput(Ptr<Packet> p,
                              const Ipv4Header& header,
                              Ptr<NetDevice> oif,
-                             enum Socket::SocketErrno& sockerr)
+                             Socket::SocketErrno& sockerr)
 {
     NS_LOG_FUNCTION(this << p << header.GetDestination() << header.GetSource() << oif << sockerr);
     Ptr<Ipv4Route> route;
@@ -136,10 +136,10 @@
 Ipv4ListRouting::RouteInput(Ptr<const Packet> p,
                             const Ipv4Header& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb)
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p << header << idev << &ucb << &mcb << &lcb << &ecb);
     bool retVal = false;
@@ -151,7 +151,7 @@
     uint32_t iif = m_ipv4->GetInterfaceForDevice(idev);
 
     retVal = m_ipv4->IsDestinationAddress(header.GetDestination(), iif);
-    if (retVal == true)
+    if (retVal)
     {
         NS_LOG_LOGIC("Address " << header.GetDestination() << " is a match for local delivery");
         if (header.GetDestination().IsMulticast())
@@ -168,7 +168,7 @@
         }
     }
     // Check if input device supports IP forwarding
-    if (m_ipv4->IsForwarding(iif) == false)
+    if (!m_ipv4->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         ecb(p, header, Socket::ERROR_NOROUTETOHOST);
@@ -178,7 +178,7 @@
     // If we have already delivered a packet locally (e.g. multicast)
     // we suppress further downstream local delivery by nulling the callback
     LocalDeliverCallback downstreamLcb = lcb;
-    if (retVal == true)
+    if (retVal)
     {
         downstreamLcb = MakeNullCallback<void, Ptr<const Packet>, const Ipv4Header&, uint32_t>();
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-list-routing.h ns-3.39/src/internet/model/ipv4-list-routing.h
--- ns-3.38/src/internet/model/ipv4-list-routing.h	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-list-routing.h	2023-07-05 14:23:47.849427500 -0700
@@ -88,10 +88,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
     void NotifyAddAddress(uint32_t interface, Ipv4InterfaceAddress address) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-packet-filter.cc ns-3.39/src/internet/model/ipv4-packet-filter.cc
--- ns-3.38/src/internet/model/ipv4-packet-filter.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-packet-filter.cc	2023-07-05 14:23:47.849427500 -0700
@@ -26,8 +26,6 @@
 
 #include "ns3/enum.h"
 #include "ns3/log.h"
-#include "ns3/tcp-header.h"
-#include "ns3/udp-header.h"
 #include "ns3/uinteger.h"
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-packet-probe.cc ns-3.39/src/internet/model/ipv4-packet-probe.cc
--- ns-3.38/src/internet/model/ipv4-packet-probe.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-packet-probe.cc	2023-07-05 14:23:47.849427500 -0700
@@ -20,7 +20,7 @@
  * Modified by: Mitch Watrous (watrous@u.washington.edu)
  */
 
-#include "ns3/ipv4-packet-probe.h"
+#include "ipv4-packet-probe.h"
 
 #include "ns3/config.h"
 #include "ns3/log.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-queue-disc-item.cc ns-3.39/src/internet/model/ipv4-queue-disc-item.cc
--- ns-3.38/src/internet/model/ipv4-queue-disc-item.cc	2023-03-16 20:54:23.616868500 -0700
+++ ns-3.39/src/internet/model/ipv4-queue-disc-item.cc	2023-07-05 14:23:47.849427500 -0700
@@ -17,9 +17,10 @@
 
 #include "ipv4-queue-disc-item.h"
 
+#include "tcp-header.h"
+#include "udp-header.h"
+
 #include "ns3/log.h"
-#include "ns3/tcp-header.h"
-#include "ns3/udp-header.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-raw-socket-impl.cc ns-3.39/src/internet/model/ipv4-raw-socket-impl.cc
--- ns-3.38/src/internet/model/ipv4-raw-socket-impl.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv4-raw-socket-impl.cc	2023-07-05 14:23:47.849427500 -0700
@@ -1,18 +1,18 @@
 #include "ipv4-raw-socket-impl.h"
 
 #include "icmpv4.h"
-#include "ipv4-l3-protocol.h"
+#include "ipv4-packet-info-tag.h"
+#include "ipv4-routing-protocol.h"
 
 #include "ns3/boolean.h"
 #include "ns3/inet-socket-address.h"
-#include "ns3/ipv4-packet-info-tag.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/packet.h"
 #include "ns3/uinteger.h"
 
 #ifdef __WIN32__
-#include "ns3/win32-internet.h"
+#include "win32-internet.h"
 #else
 #include <netinet/in.h>
 #include <sys/socket.h>
@@ -88,14 +88,14 @@
     Socket::DoDispose();
 }
 
-enum Socket::SocketErrno
+Socket::SocketErrno
 Ipv4RawSocketImpl::GetErrno() const
 {
     NS_LOG_FUNCTION(this);
     return m_err;
 }
 
-enum Socket::SocketType
+Socket::SocketType
 Ipv4RawSocketImpl::GetSocketType() const
 {
     NS_LOG_FUNCTION(this);
@@ -526,11 +526,7 @@
 Ipv4RawSocketImpl::SetAllowBroadcast(bool allowBroadcast)
 {
     NS_LOG_FUNCTION(this << allowBroadcast);
-    if (!allowBroadcast)
-    {
-        return false;
-    }
-    return true;
+    return allowBroadcast;
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-raw-socket-impl.h ns-3.39/src/internet/model/ipv4-raw-socket-impl.h
--- ns-3.38/src/internet/model/ipv4-raw-socket-impl.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv4-raw-socket-impl.h	2023-07-05 14:23:47.849427500 -0700
@@ -42,13 +42,13 @@
      */
     void SetNode(Ptr<Node> node);
 
-    enum Socket::SocketErrno GetErrno() const override;
+    Socket::SocketErrno GetErrno() const override;
 
     /**
      * \brief Get socket type (NS3_SOCK_RAW)
      * \return socket type
      */
-    enum Socket::SocketType GetSocketType() const override;
+    Socket::SocketType GetSocketType() const override;
 
     Ptr<Node> GetNode() const override;
     int Bind(const Address& address) override;
@@ -99,15 +99,15 @@
         uint16_t fromProtocol; /**< Protocol used */
     };
 
-    mutable enum Socket::SocketErrno m_err; //!< Last error number.
-    Ptr<Node> m_node;                       //!< Node
-    Ipv4Address m_src;                      //!< Source address.
-    Ipv4Address m_dst;                      //!< Destination address.
-    uint16_t m_protocol;                    //!< Protocol.
-    std::list<struct Data> m_recv;          //!< Packet waiting to be processed.
-    bool m_shutdownSend;                    //!< Flag to shutdown send capability.
-    bool m_shutdownRecv;                    //!< Flag to shutdown receive capability.
-    uint32_t m_icmpFilter;                  //!< ICMPv4 filter specification
+    mutable Socket::SocketErrno m_err; //!< Last error number.
+    Ptr<Node> m_node;                  //!< Node
+    Ipv4Address m_src;                 //!< Source address.
+    Ipv4Address m_dst;                 //!< Destination address.
+    uint16_t m_protocol;               //!< Protocol.
+    std::list<Data> m_recv;            //!< Packet waiting to be processed.
+    bool m_shutdownSend;               //!< Flag to shutdown send capability.
+    bool m_shutdownRecv;               //!< Flag to shutdown receive capability.
+    uint32_t m_icmpFilter;             //!< ICMPv4 filter specification
     bool m_iphdrincl; //!< Include IP Header information (a.k.a setsockopt (IP_HDRINCL))
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-routing-protocol.cc ns-3.39/src/internet/model/ipv4-routing-protocol.cc
--- ns-3.38/src/internet/model/ipv4-routing-protocol.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv4-routing-protocol.cc	2023-07-05 14:23:47.849427500 -0700
@@ -17,8 +17,6 @@
 
 #include "ipv4-routing-protocol.h"
 
-#include "ipv4-route.h"
-
 #include "ns3/assert.h"
 #include "ns3/log.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-routing-protocol.h ns-3.39/src/internet/model/ipv4-routing-protocol.h
--- ns-3.38/src/internet/model/ipv4-routing-protocol.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv4-routing-protocol.h	2023-07-05 14:23:47.849427500 -0700
@@ -126,10 +126,10 @@
     virtual bool RouteInput(Ptr<const Packet> p,
                             const Ipv4Header& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb) = 0;
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb) = 0;
 
     /**
      * \param interface the index of the interface we are being notified about
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-routing-table-entry.cc ns-3.39/src/internet/model/ipv4-routing-table-entry.cc
--- ns-3.38/src/internet/model/ipv4-routing-table-entry.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv4-routing-table-entry.cc	2023-07-05 14:23:47.849427500 -0700
@@ -99,14 +99,7 @@
 Ipv4RoutingTableEntry::IsHost() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_destNetworkMask == Ipv4Mask::GetOnes())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return m_destNetworkMask == Ipv4Mask::GetOnes();
 }
 
 Ipv4Address
@@ -127,14 +120,7 @@
 Ipv4RoutingTableEntry::IsDefault() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_dest == Ipv4Address::GetZero())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return m_dest == Ipv4Address::GetZero();
 }
 
 Ipv4Address
@@ -155,14 +141,7 @@
 Ipv4RoutingTableEntry::IsGateway() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_gateway == Ipv4Address::GetZero())
-    {
-        return false;
-    }
-    else
-    {
-        return true;
-    }
+    return m_gateway != Ipv4Address::GetZero();
 }
 
 Ipv4Address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-static-routing.cc ns-3.39/src/internet/model/ipv4-static-routing.cc
--- ns-3.38/src/internet/model/ipv4-static-routing.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv4-static-routing.cc	2023-07-05 14:23:47.853427400 -0700
@@ -26,9 +26,9 @@
 
 #include "ipv4-static-routing.h"
 
+#include "ipv4-route.h"
 #include "ipv4-routing-table-entry.h"
 
-#include "ns3/ipv4-route.h"
 #include "ns3/log.h"
 #include "ns3/names.h"
 #include "ns3/node.h"
@@ -503,10 +503,10 @@
 Ipv4StaticRouting::RouteInput(Ptr<const Packet> p,
                               const Ipv4Header& ipHeader,
                               Ptr<const NetDevice> idev,
-                              UnicastForwardCallback ucb,
-                              MulticastForwardCallback mcb,
-                              LocalDeliverCallback lcb,
-                              ErrorCallback ecb)
+                              const UnicastForwardCallback& ucb,
+                              const MulticastForwardCallback& mcb,
+                              const LocalDeliverCallback& lcb,
+                              const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p << ipHeader << ipHeader.GetSource() << ipHeader.GetDestination()
                          << idev << &ucb << &mcb << &lcb << &ecb);
@@ -558,7 +558,7 @@
     }
 
     // Check if input device supports IP forwarding
-    if (m_ipv4->IsForwarding(iif) == false)
+    if (!m_ipv4->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         ecb(p, ipHeader, Socket::ERROR_NOROUTETOHOST);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv4-static-routing.h ns-3.39/src/internet/model/ipv4-static-routing.h
--- ns-3.38/src/internet/model/ipv4-static-routing.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv4-static-routing.h	2023-07-05 14:23:47.853427400 -0700
@@ -83,10 +83,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
 
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-address-generator.cc ns-3.39/src/internet/model/ipv6-address-generator.cc
--- ns-3.38/src/internet/model/ipv6-address-generator.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-address-generator.cc	2023-07-05 14:23:47.853427400 -0700
@@ -605,9 +605,8 @@
 {
     NS_LOG_FUNCTION(this << address << prefix);
 
-    Ipv6Address addr = address;
     NS_ABORT_MSG_UNLESS(
-        address == addr.CombinePrefix(prefix),
+        address == address.CombinePrefix(prefix),
         "Ipv6AddressGeneratorImpl::IsNetworkAllocated(): network address and mask don't match "
             << address << " " << prefix);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-end-point.cc ns-3.39/src/internet/model/ipv6-end-point.cc
--- ns-3.38/src/internet/model/ipv6-end-point.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-end-point.cc	2023-07-05 14:23:47.853427400 -0700
@@ -49,7 +49,7 @@
 }
 
 Ipv6Address
-Ipv6EndPoint::GetLocalAddress()
+Ipv6EndPoint::GetLocalAddress() const
 {
     return m_localAddr;
 }
@@ -73,7 +73,7 @@
 }
 
 Ipv6Address
-Ipv6EndPoint::GetPeerAddress()
+Ipv6EndPoint::GetPeerAddress() const
 {
     return m_peerAddr;
 }
@@ -91,7 +91,7 @@
 }
 
 Ptr<NetDevice>
-Ipv6EndPoint::GetBoundNetDevice()
+Ipv6EndPoint::GetBoundNetDevice() const
 {
     return m_boundnetdevice;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-end-point.h ns-3.39/src/internet/model/ipv6-end-point.h
--- ns-3.38/src/internet/model/ipv6-end-point.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-end-point.h	2023-07-05 14:23:47.853427400 -0700
@@ -62,7 +62,7 @@
      * \brief Get the local address.
      * \return the local address
      */
-    Ipv6Address GetLocalAddress();
+    Ipv6Address GetLocalAddress() const;
 
     /**
      * \brief Set the local address.
@@ -86,7 +86,7 @@
      * \brief Get the peer address.
      * \return the peer address
      */
-    Ipv6Address GetPeerAddress();
+    Ipv6Address GetPeerAddress() const;
 
     /**
      * \brief Get the peer port.
@@ -130,7 +130,7 @@
      *
      * \returns Pointer to interface.
      */
-    Ptr<NetDevice> GetBoundNetDevice();
+    Ptr<NetDevice> GetBoundNetDevice() const;
 
     /**
      * \brief Set the reception callback.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-extension.cc ns-3.39/src/internet/model/ipv6-extension.cc
--- ns-3.38/src/internet/model/ipv6-extension.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-extension.cc	2023-07-05 14:23:47.853427400 -0700
@@ -22,17 +22,14 @@
 #include "icmpv6-l4-protocol.h"
 #include "ipv6-extension-demux.h"
 #include "ipv6-extension-header.h"
+#include "ipv6-header.h"
+#include "ipv6-l3-protocol.h"
 #include "ipv6-option-demux.h"
 #include "ipv6-option.h"
-#include "udp-header.h"
+#include "ipv6-route.h"
 
 #include "ns3/assert.h"
 #include "ns3/ipv6-address.h"
-#include "ns3/ipv6-header.h"
-#include "ns3/ipv6-l3-protocol.h"
-#include "ns3/ipv6-list-routing.h"
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6-static-routing.h"
 #include "ns3/log.h"
 #include "ns3/object-vector.h"
 #include "ns3/trace-source-accessor.h"
@@ -513,11 +510,16 @@
         }
         else if (nextHeader == Ipv6Header::IPV6_EXT_ROUTING)
         {
-            uint8_t buf[2];
+            Ptr<Ipv6ExtensionRoutingDemux> ipv6ExtensionRoutingDemux =
+                GetNode()->GetObject<Ipv6ExtensionRoutingDemux>();
+
+            uint8_t buf[4];
             p->CopyData(buf, sizeof(buf));
-            uint8_t numberAddress = buf[1] / 2;
-            Ipv6ExtensionLooseRoutingHeader* routingHeader = new Ipv6ExtensionLooseRoutingHeader();
-            routingHeader->SetNumberAddress(numberAddress);
+            uint8_t routingType = buf[2];
+
+            Ipv6ExtensionRoutingHeader* routingHeader =
+                ipv6ExtensionRoutingDemux->GetExtensionRoutingHeaderPtr(routingType);
+
             p->RemoveHeader(*routingHeader);
 
             nextHeader = routingHeader->GetNextHeader();
@@ -885,6 +887,12 @@
     return 0;
 }
 
+Ipv6ExtensionRoutingHeader*
+Ipv6ExtensionRouting::GetExtensionRoutingHeaderPtr()
+{
+    return nullptr;
+}
+
 uint8_t
 Ipv6ExtensionRouting::Process(Ptr<Packet>& packet,
                               uint8_t offset,
@@ -1014,15 +1022,30 @@
 Ptr<Ipv6ExtensionRouting>
 Ipv6ExtensionRoutingDemux::GetExtensionRouting(uint8_t typeRouting)
 {
-    for (Ipv6ExtensionRoutingList_t::iterator i = m_extensionsRouting.begin();
-         i != m_extensionsRouting.end();
-         i++)
+    for (const auto& extRouting : m_extensionsRouting)
     {
-        if ((*i)->GetTypeRouting() == typeRouting)
+        if (extRouting->GetTypeRouting() == typeRouting)
         {
-            return *i;
+            return extRouting;
         }
     }
+
+    return nullptr;
+}
+
+Ipv6ExtensionRoutingHeader*
+Ipv6ExtensionRoutingDemux::GetExtensionRoutingHeaderPtr(uint8_t typeRouting)
+{
+    NS_LOG_FUNCTION(this << typeRouting);
+
+    for (const auto& extRouting : m_extensionsRouting)
+    {
+        if (extRouting->GetTypeRouting() == typeRouting)
+        {
+            return extRouting->GetExtensionRoutingHeaderPtr();
+        }
+    }
+
     return nullptr;
 }
 
@@ -1059,6 +1082,12 @@
     return TYPE_ROUTING;
 }
 
+Ipv6ExtensionRoutingHeader*
+Ipv6ExtensionLooseRouting::GetExtensionRoutingHeaderPtr()
+{
+    return new Ipv6ExtensionLooseRoutingHeader();
+}
+
 uint8_t
 Ipv6ExtensionLooseRouting::Process(Ptr<Packet>& packet,
                                    uint8_t offset,
@@ -1089,9 +1118,7 @@
     // Get the number of routers' address field
     uint8_t buf[2];
     p->CopyData(buf, sizeof(buf));
-    uint8_t numberAddress = buf[1] / 2;
     Ipv6ExtensionLooseRoutingHeader routingHeader;
-    routingHeader.SetNumberAddress(numberAddress);
     p->RemoveHeader(routingHeader);
 
     if (nextHeader)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-extension.h ns-3.39/src/internet/model/ipv6-extension.h
--- ns-3.38/src/internet/model/ipv6-extension.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-extension.h	2023-07-05 14:23:47.853427400 -0700
@@ -20,6 +20,7 @@
 #ifndef IPV6_EXTENSION_H
 #define IPV6_EXTENSION_H
 
+#include "ipv6-extension-header.h"
 #include "ipv6-interface.h"
 
 #include "ns3/buffer.h"
@@ -515,6 +516,13 @@
      */
     virtual uint8_t GetTypeRouting() const;
 
+    /**
+     * \brief Get a pointer to a new routing extension header.
+     * The ownership is transferred to the caller.
+     * \return a pointer to a new routing extension header.
+     */
+    virtual Ipv6ExtensionRoutingHeader* GetExtensionRoutingHeaderPtr();
+
     uint8_t Process(Ptr<Packet>& packet,
                     uint8_t offset,
                     const Ipv6Header& ipv6Header,
@@ -569,6 +577,14 @@
     Ptr<Ipv6ExtensionRouting> GetExtensionRouting(uint8_t typeRouting);
 
     /**
+     * \brief Get a pointer to a new routing extension header corresponding
+     * to typeRouting. The ownership is transferred to the caller.
+     * \param typeRouting the number of the routing extension to retrieve
+     * \return a pointer to a new routing extension header matching IPv6 routing extension
+     */
+    Ipv6ExtensionRoutingHeader* GetExtensionRoutingHeaderPtr(uint8_t typeRouting);
+
+    /**
      * \brief Remove a routing extension from this demux.
      * \param extensionRouting pointer on the extension to remove
      */
@@ -632,6 +648,8 @@
      */
     uint8_t GetTypeRouting() const override;
 
+    Ipv6ExtensionRoutingHeader* GetExtensionRoutingHeaderPtr() override;
+
     uint8_t Process(Ptr<Packet>& packet,
                     uint8_t offset,
                     const Ipv6Header& ipv6Header,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6.h ns-3.39/src/internet/model/ipv6.h
--- ns-3.38/src/internet/model/ipv6.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6.h	2023-07-05 14:23:47.857427600 -0700
@@ -383,12 +383,14 @@
                       Ptr<Ipv6Route> route) = 0;
 
     /**
-     * \brief Register the IPv6 Extensions.
+     * \brief Register the IPv6 Extensions. Does nothing if the Extensions have been already
+     * registered.
      */
     virtual void RegisterExtensions() = 0;
 
     /**
-     * \brief Register the IPv6 Options.
+     * \brief Register the IPv6 Options. Does nothing if the Options have been already
+     * registered.
      */
     virtual void RegisterOptions() = 0;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-header.h ns-3.39/src/internet/model/ipv6-header.h
--- ns-3.38/src/internet/model/ipv6-header.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/internet/model/ipv6-header.h	2023-07-05 14:23:47.853427400 -0700
@@ -20,7 +20,6 @@
 #ifndef IPV6_HEADER_H
 #define IPV6_HEADER_H
 
-#include "ns3/deprecated.h"
 #include "ns3/header.h"
 #include "ns3/ipv6-address.h"
 
@@ -89,7 +88,7 @@
         IPV6_EXT_ROUTING = 43,
         IPV6_EXT_FRAGMENTATION = 44,
         IPV6_EXT_CONFIDENTIALITY = 50,
-        IPV6_EXT_AUTHENTIFICATION = 51,
+        IPV6_EXT_AUTHENTICATION = 51,
         IPV6_ICMPV6 = 58,
         IPV6_EXT_END = 59,
         IPV6_EXT_DESTINATION = 60,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-l3-protocol.cc ns-3.39/src/internet/model/ipv6-l3-protocol.cc
--- ns-3.38/src/internet/model/ipv6-l3-protocol.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-l3-protocol.cc	2023-07-05 14:23:47.853427400 -0700
@@ -22,20 +22,19 @@
 #include "icmpv6-l4-protocol.h"
 #include "ipv6-autoconfigured-prefix.h"
 #include "ipv6-extension-demux.h"
-#include "ipv6-extension-header.h"
 #include "ipv6-extension.h"
 #include "ipv6-interface.h"
 #include "ipv6-option-demux.h"
 #include "ipv6-option.h"
 #include "ipv6-raw-socket-factory-impl.h"
 #include "ipv6-raw-socket-impl.h"
+#include "ipv6-route.h"
+#include "ipv6-routing-protocol.h"
 #include "loopback-net-device.h"
 #include "ndisc-cache.h"
 
 #include "ns3/boolean.h"
 #include "ns3/callback.h"
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6-routing-protocol.h"
 #include "ns3/log.h"
 #include "ns3/mac16-address.h"
 #include "ns3/mac64-address.h"
@@ -91,7 +90,7 @@
                           MakeBooleanChecker())
             .AddAttribute("StrongEndSystemModel",
                           "Reject packets for an address not configured on the interface they're "
-                          "coming from (RFC1222).",
+                          "coming from (RFC1122, section 3.3.4.2).",
                           BooleanValue(true),
                           MakeBooleanAccessor(&Ipv6L3Protocol::m_strongEndSystemModel),
                           MakeBooleanChecker())
@@ -134,6 +133,10 @@
 
     Ptr<Ipv6RawSocketFactoryImpl> rawFactoryImpl = CreateObject<Ipv6RawSocketFactoryImpl>();
     AggregateObject(rawFactoryImpl);
+    m_ucb = MakeCallback(&Ipv6L3Protocol::IpForward, this);
+    m_mcb = MakeCallback(&Ipv6L3Protocol::IpMulticastForward, this);
+    m_lcb = MakeCallback(&Ipv6L3Protocol::LocalDeliver, this);
+    m_ecb = MakeCallback(&Ipv6L3Protocol::RouteInputError, this);
 }
 
 Ipv6L3Protocol::~Ipv6L3Protocol()
@@ -1160,39 +1163,41 @@
 
     for (uint32_t j = 0; j < GetNInterfaces(); j++)
     {
-        if (j == interface || !m_strongEndSystemModel)
+        for (uint32_t i = 0; i < GetNAddresses(j); i++)
         {
-            for (uint32_t i = 0; i < GetNAddresses(j); i++)
+            Ipv6InterfaceAddress iaddr = GetAddress(j, i);
+            Ipv6Address addr = iaddr.GetAddress();
+            if (addr == hdr.GetDestination())
             {
-                Ipv6InterfaceAddress iaddr = GetAddress(j, i);
-                Ipv6Address addr = iaddr.GetAddress();
-                if (addr == hdr.GetDestination())
+                if (j == interface)
                 {
-                    if (j == interface)
-                    {
-                        NS_LOG_LOGIC("For me (destination " << addr << " match)");
-                    }
-                    else
-                    {
-                        NS_LOG_LOGIC("For me (destination " << addr
-                                                            << " match) on another interface "
-                                                            << hdr.GetDestination());
-                    }
+                    NS_LOG_LOGIC("For me (destination " << addr << " match)");
+                    LocalDeliver(packet, hdr, interface);
+                    return;
+                }
+                else if (!m_strongEndSystemModel)
+                {
+                    NS_LOG_LOGIC("For me (destination "
+                                 << addr << " match) on another interface with Weak ES Model"
+                                 << hdr.GetDestination());
                     LocalDeliver(packet, hdr, interface);
                     return;
                 }
-                NS_LOG_LOGIC("Address " << addr << " not a match");
+                else
+                {
+                    NS_LOG_LOGIC("For me (destination "
+                                 << addr
+                                 << " match) on another interface with Strong ES Model - discarding"
+                                 << hdr.GetDestination());
+                    m_dropTrace(hdr, packet, DROP_NO_ROUTE, this, interface);
+                    return;
+                }
             }
+            NS_LOG_LOGIC("Address " << addr << " not a match");
         }
     }
 
-    if (!m_routingProtocol->RouteInput(packet,
-                                       hdr,
-                                       device,
-                                       MakeCallback(&Ipv6L3Protocol::IpForward, this),
-                                       MakeCallback(&Ipv6L3Protocol::IpMulticastForward, this),
-                                       MakeCallback(&Ipv6L3Protocol::LocalDeliver, this),
-                                       MakeCallback(&Ipv6L3Protocol::RouteInputError, this)))
+    if (!m_routingProtocol->RouteInput(packet, hdr, device, m_ucb, m_mcb, m_lcb, m_ecb))
     {
         NS_LOG_WARN("No route found for forwarding packet.  Drop.");
         // Drop trace and ICMPs are courtesy of RouteInputError
@@ -1383,7 +1388,7 @@
         NS_LOG_WARN("TTL exceeded.  Drop.");
         m_dropTrace(ipHeader, packet, DROP_TTL_EXPIRED, this, 0);
         // Do not reply to multicast IPv6 address
-        if (ipHeader.GetDestination().IsMulticast() == false)
+        if (!ipHeader.GetDestination().IsMulticast())
         {
             packet->AddHeader(ipHeader);
             GetIcmpv6()->SendErrorTimeExceeded(packet,
@@ -1579,7 +1584,7 @@
 
                 m_localDeliverTrace(ip, p, iif);
 
-                enum IpL4Protocol::RxStatus status = protocol->Receive(p, ip, GetInterface(iif));
+                IpL4Protocol::RxStatus status = protocol->Receive(p, ip, GetInterface(iif));
 
                 switch (status)
                 {
@@ -1652,6 +1657,10 @@
 void
 Ipv6L3Protocol::RegisterExtensions()
 {
+    if (m_node->GetObject<Ipv6ExtensionDemux>())
+    {
+        return;
+    }
     Ptr<Ipv6ExtensionDemux> ipv6ExtensionDemux = CreateObject<Ipv6ExtensionDemux>();
     ipv6ExtensionDemux->SetNode(m_node);
 
@@ -1688,6 +1697,10 @@
 void
 Ipv6L3Protocol::RegisterOptions()
 {
+    if (m_node->GetObject<Ipv6OptionDemux>())
+    {
+        return;
+    }
     Ptr<Ipv6OptionDemux> ipv6OptionDemux = CreateObject<Ipv6OptionDemux>();
     ipv6OptionDemux->SetNode(m_node);
 
@@ -1777,11 +1790,7 @@
     Ipv6RegisteredMulticastAddressKey_t key = std::make_pair(address, interface);
     Ipv6RegisteredMulticastAddressCIter_t iter = m_multicastAddresses.find(key);
 
-    if (iter == m_multicastAddresses.end())
-    {
-        return false;
-    }
-    return true;
+    return iter != m_multicastAddresses.end();
 }
 
 bool
@@ -1792,11 +1801,7 @@
     Ipv6RegisteredMulticastAddressNoInterfaceCIter_t iter =
         m_multicastAddressesNoInterface.find(address);
 
-    if (iter == m_multicastAddressesNoInterface.end())
-    {
-        return false;
-    }
-    return true;
+    return iter != m_multicastAddressesNoInterface.end();
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-l3-protocol.h ns-3.39/src/internet/model/ipv6-l3-protocol.h
--- ns-3.38/src/internet/model/ipv6-l3-protocol.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-l3-protocol.h	2023-07-05 14:23:47.853427400 -0700
@@ -23,6 +23,7 @@
 #include "ns3/ipv6-address.h"
 #include "ns3/ipv6-header.h"
 #include "ns3/ipv6-pmtu-cache.h"
+#include "ns3/ipv6-routing-protocol.h"
 #include "ns3/ipv6.h"
 #include "ns3/net-device.h"
 #include "ns3/traced-callback.h"
@@ -367,14 +368,7 @@
                                      Ipv6Prefix mask,
                                      Ipv6Address defaultRouter);
 
-    /**
-     * \brief Register the IPv6 Extensions.
-     */
     void RegisterExtensions() override;
-
-    /**
-     * \brief Register the IPv6 Options.
-     */
     void RegisterOptions() override;
 
     /**
@@ -836,6 +830,11 @@
      * \brief List of multicast IP addresses of interest for all the interfaces.
      */
     Ipv6RegisteredMulticastAddressNoInterface_t m_multicastAddressesNoInterface;
+
+    Ipv6RoutingProtocol::UnicastForwardCallback m_ucb;   ///< Unicast forward callback
+    Ipv6RoutingProtocol::MulticastForwardCallback m_mcb; ///< Multicast forward callback
+    Ipv6RoutingProtocol::LocalDeliverCallback m_lcb;     ///< Local delivery callback
+    Ipv6RoutingProtocol::ErrorCallback m_ecb;            ///< Error callback
 };
 
 } /* namespace ns3 */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-list-routing.cc ns-3.39/src/internet/model/ipv6-list-routing.cc
--- ns-3.38/src/internet/model/ipv6-list-routing.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-list-routing.cc	2023-07-05 14:23:47.853427400 -0700
@@ -18,9 +18,9 @@
 
 #include "ipv6-list-routing.h"
 
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6-static-routing.h"
-#include "ns3/ipv6.h"
+#include "ipv6-route.h"
+#include "ipv6.h"
+
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/simulator.h"
@@ -74,7 +74,7 @@
 Ipv6ListRouting::RouteOutput(Ptr<Packet> p,
                              const Ipv6Header& header,
                              Ptr<NetDevice> oif,
-                             enum Socket::SocketErrno& sockerr)
+                             Socket::SocketErrno& sockerr)
 {
     NS_LOG_FUNCTION(this << header.GetDestination() << header.GetSource() << oif);
     Ptr<Ipv6Route> route;
@@ -105,10 +105,10 @@
 Ipv6ListRouting::RouteInput(Ptr<const Packet> p,
                             const Ipv6Header& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb)
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(p << header << idev);
     NS_LOG_LOGIC("RouteInput logic for node: " << m_ipv6->GetObject<Node>()->GetId());
@@ -120,7 +120,7 @@
 
     // Check if input device supports IP forwarding
     uint32_t iif = m_ipv6->GetInterfaceForDevice(idev);
-    if (m_ipv6->IsForwarding(iif) == false)
+    if (!m_ipv6->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         ecb(p, header, Socket::ERROR_NOROUTETOHOST);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-list-routing.h ns-3.39/src/internet/model/ipv6-list-routing.h
--- ns-3.38/src/internet/model/ipv6-list-routing.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-list-routing.h	2023-07-05 14:23:47.853427400 -0700
@@ -95,10 +95,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv6Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
     void NotifyAddAddress(uint32_t interface, Ipv6InterfaceAddress address) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-packet-filter.cc ns-3.39/src/internet/model/ipv6-packet-filter.cc
--- ns-3.38/src/internet/model/ipv6-packet-filter.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-packet-filter.cc	2023-07-05 14:23:47.853427400 -0700
@@ -26,8 +26,6 @@
 
 #include "ns3/enum.h"
 #include "ns3/log.h"
-#include "ns3/tcp-header.h"
-#include "ns3/udp-header.h"
 #include "ns3/uinteger.h"
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-packet-probe.cc ns-3.39/src/internet/model/ipv6-packet-probe.cc
--- ns-3.38/src/internet/model/ipv6-packet-probe.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-packet-probe.cc	2023-07-05 14:23:47.853427400 -0700
@@ -21,7 +21,7 @@
  * Adapted to Ipv6 by: Tommaso Pecorella (tommaso.pecorella@unifi.it)
  */
 
-#include "ns3/ipv6-packet-probe.h"
+#include "ipv6-packet-probe.h"
 
 #include "ns3/config.h"
 #include "ns3/log.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-queue-disc-item.cc ns-3.39/src/internet/model/ipv6-queue-disc-item.cc
--- ns-3.38/src/internet/model/ipv6-queue-disc-item.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-queue-disc-item.cc	2023-07-05 14:23:47.853427400 -0700
@@ -17,9 +17,10 @@
 
 #include "ipv6-queue-disc-item.h"
 
+#include "tcp-header.h"
+#include "udp-header.h"
+
 #include "ns3/log.h"
-#include "ns3/tcp-header.h"
-#include "ns3/udp-header.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-raw-socket-impl.cc ns-3.39/src/internet/model/ipv6-raw-socket-impl.cc
--- ns-3.38/src/internet/model/ipv6-raw-socket-impl.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-raw-socket-impl.cc	2023-07-05 14:23:47.853427400 -0700
@@ -22,18 +22,18 @@
 #include "icmpv6-header.h"
 #include "icmpv6-l4-protocol.h"
 #include "ipv6-l3-protocol.h"
+#include "ipv6-packet-info-tag.h"
+#include "ipv6-route.h"
+#include "ipv6-routing-protocol.h"
 
 #include "ns3/inet6-socket-address.h"
-#include "ns3/ipv6-packet-info-tag.h"
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6-routing-protocol.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/packet.h"
 #include "ns3/uinteger.h"
 
 #ifdef __WIN32__
-#include "ns3/win32-internet.h"
+#include "win32-internet.h"
 #else
 #include <netinet/in.h>
 #include <sys/socket.h>
@@ -100,14 +100,14 @@
     return m_node;
 }
 
-enum Socket::SocketErrno
+Socket::SocketErrno
 Ipv6RawSocketImpl::GetErrno() const
 {
     NS_LOG_FUNCTION(this);
     return m_err;
 }
 
-enum Socket::SocketType
+Socket::SocketType
 Ipv6RawSocketImpl::GetSocketType() const
 {
     return NS3_SOCK_RAW;
@@ -510,11 +510,7 @@
 bool
 Ipv6RawSocketImpl::SetAllowBroadcast(bool allowBroadcast)
 {
-    if (!allowBroadcast)
-    {
-        return false;
-    }
-    return true;
+    return allowBroadcast;
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-raw-socket-impl.h ns-3.39/src/internet/model/ipv6-raw-socket-impl.h
--- ns-3.38/src/internet/model/ipv6-raw-socket-impl.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-raw-socket-impl.h	2023-07-05 14:23:47.853427400 -0700
@@ -80,13 +80,13 @@
      */
     void SetNode(Ptr<Node> node);
 
-    enum Socket::SocketErrno GetErrno() const override;
+    Socket::SocketErrno GetErrno() const override;
 
     /**
      * \brief Get socket type (NS3_SOCK_RAW)
      * \return socket type
      */
-    enum Socket::SocketType GetSocketType() const override;
+    Socket::SocketType GetSocketType() const override;
 
     Ptr<Node> GetNode() const override;
 
@@ -185,7 +185,7 @@
     /**
      * \brief Last error number.
      */
-    mutable enum Socket::SocketErrno m_err;
+    mutable Socket::SocketErrno m_err;
 
     /**
      * \brief Node.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-routing-protocol.cc ns-3.39/src/internet/model/ipv6-routing-protocol.cc
--- ns-3.38/src/internet/model/ipv6-routing-protocol.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-routing-protocol.cc	2023-07-05 14:23:47.853427400 -0700
@@ -19,8 +19,6 @@
 
 #include "ipv6-routing-protocol.h"
 
-#include "ipv6-route.h"
-
 #include "ns3/assert.h"
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-routing-protocol.h ns-3.39/src/internet/model/ipv6-routing-protocol.h
--- ns-3.38/src/internet/model/ipv6-routing-protocol.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-routing-protocol.h	2023-07-05 14:23:47.853427400 -0700
@@ -132,10 +132,10 @@
     virtual bool RouteInput(Ptr<const Packet> p,
                             const Ipv6Header& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb) = 0;
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb) = 0;
 
     /**
      * \brief Notify when specified interface goes UP.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-routing-table-entry.cc ns-3.39/src/internet/model/ipv6-routing-table-entry.cc
--- ns-3.38/src/internet/model/ipv6-routing-table-entry.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-routing-table-entry.cc	2023-07-05 14:23:47.853427400 -0700
@@ -121,11 +121,7 @@
 bool
 Ipv6RoutingTableEntry::IsHost() const
 {
-    if (m_destNetworkPrefix == Ipv6Prefix::GetOnes())
-    {
-        return true;
-    }
-    return false;
+    return m_destNetworkPrefix == Ipv6Prefix::GetOnes();
 }
 
 Ipv6Address
@@ -155,11 +151,7 @@
 bool
 Ipv6RoutingTableEntry::IsDefault() const
 {
-    if (m_dest == Ipv6Address::GetZero())
-    {
-        return true;
-    }
-    return false;
+    return m_dest == Ipv6Address::GetZero();
 }
 
 Ipv6Address
@@ -177,11 +169,7 @@
 bool
 Ipv6RoutingTableEntry::IsGateway() const
 {
-    if (m_gateway == Ipv6Address::GetZero())
-    {
-        return false;
-    }
-    return true;
+    return m_gateway != Ipv6Address::GetZero();
 }
 
 Ipv6Address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-static-routing.cc ns-3.39/src/internet/model/ipv6-static-routing.cc
--- ns-3.38/src/internet/model/ipv6-static-routing.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-static-routing.cc	2023-07-05 14:23:47.853427400 -0700
@@ -19,9 +19,9 @@
 
 #include "ipv6-static-routing.h"
 
+#include "ipv6-route.h"
 #include "ipv6-routing-table-entry.h"
 
-#include "ns3/ipv6-route.h"
 #include "ns3/log.h"
 #include "ns3/names.h"
 #include "ns3/net-device.h"
@@ -715,10 +715,10 @@
 Ipv6StaticRouting::RouteInput(Ptr<const Packet> p,
                               const Ipv6Header& header,
                               Ptr<const NetDevice> idev,
-                              UnicastForwardCallback ucb,
-                              MulticastForwardCallback mcb,
-                              LocalDeliverCallback lcb,
-                              ErrorCallback ecb)
+                              const UnicastForwardCallback& ucb,
+                              const MulticastForwardCallback& mcb,
+                              const LocalDeliverCallback& lcb,
+                              const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p << header << header.GetSource() << header.GetDestination() << idev);
     NS_ASSERT(m_ipv6);
@@ -750,7 +750,7 @@
     }
 
     // Check if input device supports IP forwarding
-    if (m_ipv6->IsForwarding(iif) == false)
+    if (!m_ipv6->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         if (!ecb.IsNull())
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ipv6-static-routing.h ns-3.39/src/internet/model/ipv6-static-routing.h
--- ns-3.38/src/internet/model/ipv6-static-routing.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ipv6-static-routing.h	2023-07-05 14:23:47.853427400 -0700
@@ -257,10 +257,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv6Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
 
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/rip.cc ns-3.39/src/internet/model/rip.cc
--- ns-3.38/src/internet/model/rip.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/rip.cc	2023-07-05 14:23:47.857427600 -0700
@@ -19,18 +19,19 @@
 
 #include "rip.h"
 
+#include "ipv4-packet-info-tag.h"
+#include "ipv4-route.h"
+#include "loopback-net-device.h"
+#include "rip-header.h"
+#include "udp-header.h"
+
 #include "ns3/abort.h"
 #include "ns3/assert.h"
 #include "ns3/enum.h"
-#include "ns3/ipv4-packet-info-tag.h"
-#include "ns3/ipv4-route.h"
 #include "ns3/log.h"
-#include "ns3/loopback-net-device.h"
 #include "ns3/names.h"
 #include "ns3/node.h"
 #include "ns3/random-variable-stream.h"
-#include "ns3/rip-header.h"
-#include "ns3/udp-header.h"
 #include "ns3/uinteger.h"
 
 #include <iomanip>
@@ -153,7 +154,7 @@
         for (uint32_t j = 0; j < m_ipv4->GetNAddresses(i); j++)
         {
             Ipv4InterfaceAddress address = m_ipv4->GetAddress(i, j);
-            if (address.GetScope() != Ipv4InterfaceAddress::HOST && activeInterface == true)
+            if (address.GetScope() != Ipv4InterfaceAddress::HOST && activeInterface)
             {
                 NS_LOG_LOGIC("RIP: adding socket to " << address.GetLocal());
                 TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
@@ -241,10 +242,10 @@
 Rip::RouteInput(Ptr<const Packet> p,
                 const Ipv4Header& header,
                 Ptr<const NetDevice> idev,
-                UnicastForwardCallback ucb,
-                MulticastForwardCallback mcb,
-                LocalDeliverCallback lcb,
-                ErrorCallback ecb)
+                const UnicastForwardCallback& ucb,
+                const MulticastForwardCallback& mcb,
+                const LocalDeliverCallback& lcb,
+                const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p << header << header.GetSource() << header.GetDestination() << idev);
 
@@ -290,7 +291,7 @@
     }
 
     // Check if input device supports IP forwarding
-    if (m_ipv4->IsForwarding(iif) == false)
+    if (!m_ipv4->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         if (!ecb.IsNull())
@@ -365,8 +366,7 @@
     {
         Ipv4InterfaceAddress address = m_ipv4->GetAddress(i, j);
 
-        if (address.GetScope() != Ipv4InterfaceAddress::HOST && sendSocketFound == false &&
-            activeInterface == true)
+        if (address.GetScope() != Ipv4InterfaceAddress::HOST && !sendSocketFound && activeInterface)
         {
             NS_LOG_LOGIC("RIP: adding sending socket to " << address.GetLocal());
             TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/rip.h ns-3.39/src/internet/model/rip.h
--- ns-3.38/src/internet/model/rip.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/rip.h	2023-07-05 14:23:47.857427600 -0700
@@ -192,10 +192,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
     void NotifyAddAddress(uint32_t interface, Ipv4InterfaceAddress address) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ripng.cc ns-3.39/src/internet/model/ripng.cc
--- ns-3.38/src/internet/model/ripng.cc	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ripng.cc	2023-07-05 14:23:47.857427600 -0700
@@ -19,18 +19,19 @@
 
 #include "ripng.h"
 
+#include "ipv6-packet-info-tag.h"
+#include "ipv6-route.h"
+#include "ripng-header.h"
+#include "udp-header.h"
+
 #include "ns3/abort.h"
 #include "ns3/assert.h"
 #include "ns3/enum.h"
-#include "ns3/ipv6-packet-info-tag.h"
-#include "ns3/ipv6-route.h"
 #include "ns3/log.h"
 #include "ns3/names.h"
 #include "ns3/node.h"
 #include "ns3/random-variable-stream.h"
-#include "ns3/ripng-header.h"
 #include "ns3/simulator.h"
-#include "ns3/udp-header.h"
 #include "ns3/uinteger.h"
 
 #include <iomanip>
@@ -147,7 +148,7 @@
         for (uint32_t j = 0; j < m_ipv6->GetNAddresses(i); j++)
         {
             Ipv6InterfaceAddress address = m_ipv6->GetAddress(i, j);
-            if (address.GetScope() == Ipv6InterfaceAddress::LINKLOCAL && activeInterface == true)
+            if (address.GetScope() == Ipv6InterfaceAddress::LINKLOCAL && activeInterface)
             {
                 NS_LOG_LOGIC("RIPng: adding socket to " << address.GetAddress());
                 TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
@@ -233,10 +234,10 @@
 RipNg::RouteInput(Ptr<const Packet> p,
                   const Ipv6Header& header,
                   Ptr<const NetDevice> idev,
-                  UnicastForwardCallback ucb,
-                  MulticastForwardCallback mcb,
-                  LocalDeliverCallback lcb,
-                  ErrorCallback ecb)
+                  const UnicastForwardCallback& ucb,
+                  const MulticastForwardCallback& mcb,
+                  const LocalDeliverCallback& lcb,
+                  const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p << header << header.GetSource() << header.GetDestination() << idev);
 
@@ -263,7 +264,7 @@
     }
 
     // Check if input device supports IP forwarding
-    if (m_ipv6->IsForwarding(iif) == false)
+    if (!m_ipv6->IsForwarding(iif))
     {
         NS_LOG_LOGIC("Forwarding disabled for this interface");
         if (!ecb.IsNull())
@@ -332,8 +333,8 @@
     {
         Ipv6InterfaceAddress address = m_ipv6->GetAddress(i, j);
 
-        if (address.GetScope() == Ipv6InterfaceAddress::LINKLOCAL && sendSocketFound == false &&
-            activeInterface == true)
+        if (address.GetScope() == Ipv6InterfaceAddress::LINKLOCAL && !sendSocketFound &&
+            activeInterface)
         {
             NS_LOG_LOGIC("RIPng: adding sending socket to " << address.GetAddress());
             TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/ripng.h ns-3.39/src/internet/model/ripng.h
--- ns-3.38/src/internet/model/ripng.h	2023-03-16 20:54:23.620868400 -0700
+++ ns-3.39/src/internet/model/ripng.h	2023-07-05 14:23:47.857427600 -0700
@@ -194,10 +194,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv6Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void NotifyInterfaceUp(uint32_t interface) override;
     void NotifyInterfaceDown(uint32_t interface) override;
     void NotifyAddAddress(uint32_t interface, Ipv6InterfaceAddress address) override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-dctcp.cc ns-3.39/src/internet/model/tcp-dctcp.cc
--- ns-3.38/src/internet/model/tcp-dctcp.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-dctcp.cc	2023-07-05 14:23:47.857427600 -0700
@@ -20,9 +20,10 @@
 
 #include "tcp-dctcp.h"
 
+#include "tcp-socket-state.h"
+
 #include "ns3/abort.h"
 #include "ns3/log.h"
-#include "ns3/tcp-socket-state.h"
 
 namespace ns3
 {
@@ -142,7 +143,7 @@
     {
         m_ackedBytesEcn += segmentsAcked * tcb->m_segmentSize;
     }
-    if (m_nextSeqFlag == false)
+    if (!m_nextSeqFlag)
     {
         m_nextSeq = tcb->m_nextTxSequence;
         m_nextSeqFlag = true;
@@ -196,7 +197,7 @@
         tcb->m_rxBuffer->SetNextRxSequence(tmpRcvNxt);
     }
 
-    if (m_priorRcvNxtFlag == false)
+    if (!m_priorRcvNxtFlag)
     {
         m_priorRcvNxtFlag = true;
     }
@@ -223,7 +224,7 @@
         tcb->m_rxBuffer->SetNextRxSequence(tmpRcvNxt);
     }
 
-    if (m_priorRcvNxtFlag == false)
+    if (!m_priorRcvNxtFlag)
     {
         m_priorRcvNxtFlag = true;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-l4-protocol.cc ns-3.39/src/internet/model/tcp-l4-protocol.cc
--- ns-3.38/src/internet/model/tcp-l4-protocol.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-l4-protocol.cc	2023-07-05 14:23:47.857427600 -0700
@@ -21,10 +21,12 @@
 
 #include "ipv4-end-point-demux.h"
 #include "ipv4-end-point.h"
-#include "ipv4-l3-protocol.h"
+#include "ipv4-route.h"
+#include "ipv4-routing-protocol.h"
 #include "ipv6-end-point-demux.h"
 #include "ipv6-end-point.h"
 #include "ipv6-l3-protocol.h"
+#include "ipv6-route.h"
 #include "ipv6-routing-protocol.h"
 #include "rtt-estimator.h"
 #include "tcp-congestion-ops.h"
@@ -37,17 +39,16 @@
 
 #include "ns3/assert.h"
 #include "ns3/boolean.h"
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv6-route.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/nstime.h"
-#include "ns3/object-vector.h"
+#include "ns3/object-map.h"
 #include "ns3/packet.h"
 #include "ns3/simulator.h"
 
 #include <iomanip>
 #include <sstream>
+#include <unordered_map>
 #include <vector>
 
 namespace ns3
@@ -72,30 +73,33 @@
 TypeId
 TcpL4Protocol::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::TcpL4Protocol")
-                            .SetParent<IpL4Protocol>()
-                            .SetGroupName("Internet")
-                            .AddConstructor<TcpL4Protocol>()
-                            .AddAttribute("RttEstimatorType",
-                                          "Type of RttEstimator objects.",
-                                          TypeIdValue(RttMeanDeviation::GetTypeId()),
-                                          MakeTypeIdAccessor(&TcpL4Protocol::m_rttTypeId),
-                                          MakeTypeIdChecker())
-                            .AddAttribute("SocketType",
-                                          "Socket type of TCP objects.",
-                                          TypeIdValue(TcpCubic::GetTypeId()),
-                                          MakeTypeIdAccessor(&TcpL4Protocol::m_congestionTypeId),
-                                          MakeTypeIdChecker())
-                            .AddAttribute("RecoveryType",
-                                          "Recovery type of TCP objects.",
-                                          TypeIdValue(TcpPrrRecovery::GetTypeId()),
-                                          MakeTypeIdAccessor(&TcpL4Protocol::m_recoveryTypeId),
-                                          MakeTypeIdChecker())
-                            .AddAttribute("SocketList",
-                                          "The list of sockets associated to this protocol.",
-                                          ObjectVectorValue(),
-                                          MakeObjectVectorAccessor(&TcpL4Protocol::m_sockets),
-                                          MakeObjectVectorChecker<TcpSocketBase>());
+    static TypeId tid =
+        TypeId("ns3::TcpL4Protocol")
+            .SetParent<IpL4Protocol>()
+            .SetGroupName("Internet")
+            .AddConstructor<TcpL4Protocol>()
+            .AddAttribute("RttEstimatorType",
+                          "Type of RttEstimator objects.",
+                          TypeIdValue(RttMeanDeviation::GetTypeId()),
+                          MakeTypeIdAccessor(&TcpL4Protocol::m_rttTypeId),
+                          MakeTypeIdChecker())
+            .AddAttribute("SocketType",
+                          "Socket type of TCP objects.",
+                          TypeIdValue(TcpCubic::GetTypeId()),
+                          MakeTypeIdAccessor(&TcpL4Protocol::m_congestionTypeId),
+                          MakeTypeIdChecker())
+            .AddAttribute("RecoveryType",
+                          "Recovery type of TCP objects.",
+                          TypeIdValue(TcpPrrRecovery::GetTypeId()),
+                          MakeTypeIdAccessor(&TcpL4Protocol::m_recoveryTypeId),
+                          MakeTypeIdChecker())
+            .AddAttribute("SocketList",
+                          "A container of sockets associated to this protocol. "
+                          "The underlying type is an unordered map, the attribute name "
+                          "is kept for backward compatibility.",
+                          ObjectMapValue(),
+                          MakeObjectMapAccessor(&TcpL4Protocol::m_sockets),
+                          MakeObjectMapChecker<TcpSocketBase>());
     return tid;
 }
 
@@ -213,7 +217,7 @@
     socket->SetCongestionControlAlgorithm(algo);
     socket->SetRecoveryAlgorithm(recovery);
 
-    m_sockets.push_back(socket);
+    m_sockets[m_socketIndex++] = socket;
     return socket;
 }
 
@@ -380,7 +384,7 @@
     }
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 TcpL4Protocol::PacketReceived(Ptr<Packet> packet,
                               TcpHeader& incomingTcpHeader,
                               const Address& source,
@@ -448,7 +452,7 @@
     }
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 TcpL4Protocol::Receive(Ptr<Packet> packet,
                        const Ipv4Header& incomingIpHeader,
                        Ptr<Ipv4Interface> incomingInterface)
@@ -519,7 +523,7 @@
     return IpL4Protocol::RX_OK;
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 TcpL4Protocol::Receive(Ptr<Packet> packet,
                        const Ipv6Header& incomingIpHeader,
                        Ptr<Ipv6Interface> interface)
@@ -747,36 +751,30 @@
 TcpL4Protocol::AddSocket(Ptr<TcpSocketBase> socket)
 {
     NS_LOG_FUNCTION(this << socket);
-    std::vector<Ptr<TcpSocketBase>>::iterator it = m_sockets.begin();
 
-    while (it != m_sockets.end())
+    for (auto& socketItem : m_sockets)
     {
-        if (*it == socket)
+        if (socketItem.second == socket)
         {
             return;
         }
-
-        ++it;
     }
-
-    m_sockets.push_back(socket);
+    m_sockets[m_socketIndex++] = socket;
 }
 
 bool
 TcpL4Protocol::RemoveSocket(Ptr<TcpSocketBase> socket)
 {
     NS_LOG_FUNCTION(this << socket);
-    std::vector<Ptr<TcpSocketBase>>::iterator it = m_sockets.begin();
 
-    while (it != m_sockets.end())
+    for (auto& socketItem : m_sockets)
     {
-        if (*it == socket)
+        if (socketItem.second == socket)
         {
-            m_sockets.erase(it);
+            socketItem.second = nullptr;
+            m_sockets.erase(socketItem.first);
             return true;
         }
-
-        ++it;
     }
 
     return false;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-l4-protocol.h ns-3.39/src/internet/model/tcp-l4-protocol.h
--- ns-3.38/src/internet/model/tcp-l4-protocol.h	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-l4-protocol.h	2023-07-05 14:23:47.857427600 -0700
@@ -27,6 +27,7 @@
 #include "ns3/sequence-number.h"
 
 #include <stdint.h>
+#include <unordered_map>
 
 namespace ns3
 {
@@ -257,12 +258,12 @@
     void DeAllocate(Ipv6EndPoint* endPoint);
 
     // From IpL4Protocol
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv4Header& incomingIpHeader,
-                                        Ptr<Ipv4Interface> incomingInterface) override;
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv6Header& incomingIpHeader,
-                                        Ptr<Ipv6Interface> incomingInterface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv4Header& incomingIpHeader,
+                                   Ptr<Ipv4Interface> incomingInterface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv6Header& incomingIpHeader,
+                                   Ptr<Ipv6Interface> incomingInterface) override;
 
     void ReceiveIcmp(Ipv4Address icmpSource,
                      uint8_t icmpTtl,
@@ -311,10 +312,10 @@
      *
      * \return RX_CSUM_FAILED if the checksum check fails, RX_OK otherwise
      */
-    enum IpL4Protocol::RxStatus PacketReceived(Ptr<Packet> packet,
-                                               TcpHeader& incomingTcpHeader,
-                                               const Address& source,
-                                               const Address& destination);
+    IpL4Protocol::RxStatus PacketReceived(Ptr<Packet> packet,
+                                          TcpHeader& incomingTcpHeader,
+                                          const Address& source,
+                                          const Address& destination);
 
     /**
      * \brief Check if RST packet should be sent, and in case, send it
@@ -334,13 +335,15 @@
                           const Address& incomingDAddr);
 
   private:
-    Ptr<Node> m_node;                                //!< the node this stack is associated with
-    Ipv4EndPointDemux* m_endPoints;                  //!< A list of IPv4 end points.
-    Ipv6EndPointDemux* m_endPoints6;                 //!< A list of IPv6 end points.
-    TypeId m_rttTypeId;                              //!< The RTT Estimator TypeId
-    TypeId m_congestionTypeId;                       //!< The socket TypeId
-    TypeId m_recoveryTypeId;                         //!< The recovery TypeId
-    std::vector<Ptr<TcpSocketBase>> m_sockets;       //!< list of sockets
+    Ptr<Node> m_node;                //!< the node this stack is associated with
+    Ipv4EndPointDemux* m_endPoints;  //!< A list of IPv4 end points.
+    Ipv6EndPointDemux* m_endPoints6; //!< A list of IPv6 end points.
+    TypeId m_rttTypeId;              //!< The RTT Estimator TypeId
+    TypeId m_congestionTypeId;       //!< The socket TypeId
+    TypeId m_recoveryTypeId;         //!< The recovery TypeId
+    std::unordered_map<uint64_t, Ptr<TcpSocketBase>>
+        m_sockets;             //!< Unordered map of socket IDs and corresponding sockets
+    uint64_t m_socketIndex{0}; //!< index of the next socket to be created
     IpL4Protocol::DownTargetCallback m_downTarget;   //!< Callback to send packets over IPv4
     IpL4Protocol::DownTargetCallback6 m_downTarget6; //!< Callback to send packets over IPv6
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-ledbat.cc ns-3.39/src/internet/model/tcp-ledbat.cc
--- ns-3.38/src/internet/model/tcp-ledbat.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-ledbat.cc	2023-07-05 14:23:47.857427600 -0700
@@ -105,7 +105,7 @@
 };
 
 void
-TcpLedbat::InitCircBuf(struct OwdCircBuf& buffer)
+TcpLedbat::InitCircBuf(OwdCircBuf& buffer)
 {
     NS_LOG_FUNCTION(this);
     buffer.buffer.clear();
@@ -147,7 +147,7 @@
 }
 
 uint32_t
-TcpLedbat::MinCircBuf(struct OwdCircBuf& b)
+TcpLedbat::MinCircBuf(OwdCircBuf& b)
 {
     NS_LOG_FUNCTION_NOARGS();
     if (b.buffer.empty())
@@ -239,7 +239,7 @@
 }
 
 void
-TcpLedbat::AddDelay(struct OwdCircBuf& cb, uint32_t owd, uint32_t maxlen)
+TcpLedbat::AddDelay(OwdCircBuf& cb, uint32_t owd, uint32_t maxlen)
 {
     NS_LOG_FUNCTION(this << owd << maxlen << cb.buffer.size());
     if (cb.buffer.empty())
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-ledbat.h ns-3.39/src/internet/model/tcp-ledbat.h
--- ns-3.38/src/internet/model/tcp-ledbat.h	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-ledbat.h	2023-07-05 14:23:47.857427600 -0700
@@ -139,10 +139,10 @@
      *
      * \param buffer The buffer to be initialised
      */
-    void InitCircBuf(struct OwdCircBuf& buffer);
+    void InitCircBuf(OwdCircBuf& buffer);
 
     /// Filter function used by LEDBAT for current delay
-    typedef uint32_t (*FilterFunction)(struct OwdCircBuf&);
+    typedef uint32_t (*FilterFunction)(OwdCircBuf&);
 
     /**
      * \brief Return the minimum delay of the buffer
@@ -150,7 +150,7 @@
      * \param b The buffer
      * \return The minimum delay
      */
-    static uint32_t MinCircBuf(struct OwdCircBuf& b);
+    static uint32_t MinCircBuf(OwdCircBuf& b);
 
     /**
      * \brief Return the value of current delay
@@ -174,7 +174,7 @@
      * \param owd The new delay
      * \param maxlen The maximum permitted length
      */
-    void AddDelay(struct OwdCircBuf& cb, uint32_t owd, uint32_t maxlen);
+    void AddDelay(OwdCircBuf& cb, uint32_t owd, uint32_t maxlen);
 
     /**
      * \brief Update the base delay buffer
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-option-sack-permitted.cc ns-3.39/src/internet/model/tcp-option-sack-permitted.cc
--- ns-3.38/src/internet/model/tcp-option-sack-permitted.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-option-sack-permitted.cc	2023-07-05 14:23:47.857427600 -0700
@@ -24,7 +24,6 @@
 #include "tcp-option-sack-permitted.h"
 
 #include "ns3/log.h"
-#include "ns3/tcp-header.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-socket-base.cc ns-3.39/src/internet/model/tcp-socket-base.cc
--- ns-3.38/src/internet/model/tcp-socket-base.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-socket-base.cc	2023-07-05 14:23:47.861427500 -0700
@@ -27,8 +27,13 @@
 #include "tcp-socket-base.h"
 
 #include "ipv4-end-point.h"
+#include "ipv4-route.h"
+#include "ipv4-routing-protocol.h"
+#include "ipv4.h"
 #include "ipv6-end-point.h"
 #include "ipv6-l3-protocol.h"
+#include "ipv6-route.h"
+#include "ipv6-routing-protocol.h"
 #include "rtt-estimator.h"
 #include "tcp-congestion-ops.h"
 #include "tcp-header.h"
@@ -37,6 +42,7 @@
 #include "tcp-option-sack.h"
 #include "tcp-option-ts.h"
 #include "tcp-option-winscale.h"
+#include "tcp-rate-ops.h"
 #include "tcp-recovery-ops.h"
 #include "tcp-rx-buffer.h"
 #include "tcp-tx-buffer.h"
@@ -46,13 +52,6 @@
 #include "ns3/double.h"
 #include "ns3/inet-socket-address.h"
 #include "ns3/inet6-socket-address.h"
-#include "ns3/ipv4-interface-address.h"
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv4-routing-protocol.h"
-#include "ns3/ipv4.h"
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6-routing-protocol.h"
-#include "ns3/ipv6.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/object.h"
@@ -60,7 +59,6 @@
 #include "ns3/pointer.h"
 #include "ns3/simulation-singleton.h"
 #include "ns3/simulator.h"
-#include "ns3/tcp-rate-ops.h"
 #include "ns3/trace-source-accessor.h"
 #include "ns3/uinteger.h"
 
@@ -513,14 +511,14 @@
 }
 
 /* Inherit from Socket class: Returns error code */
-enum Socket::SocketErrno
+Socket::SocketErrno
 TcpSocketBase::GetErrno() const
 {
     return m_errno;
 }
 
 /* Inherit from Socket class: Returns socket type, NS3_SOCK_STREAM */
-enum Socket::SocketType
+Socket::SocketType
 TcpSocketBase::GetSocketType() const
 {
     return NS3_SOCK_STREAM;
@@ -709,7 +707,7 @@
         // a v4 address and re-call this function
         Inet6SocketAddress transport = Inet6SocketAddress::ConvertFrom(address);
         Ipv6Address v6Addr = transport.GetIpv6();
-        if (v6Addr.IsIpv4MappedAddress() == true)
+        if (v6Addr.IsIpv4MappedAddress())
         {
             Ipv4Address v4Addr = v6Addr.GetIpv4MappedAddress();
             return Connect(InetSocketAddress(v4Addr, transport.GetPort()));
@@ -786,7 +784,7 @@
 
     if (m_txBuffer->SizeFromSequence(m_tcb->m_nextTxSequence) > 0)
     { // App close with pending data must wait until all data transmitted
-        if (m_closeOnEmpty == false)
+        if (!m_closeOnEmpty)
         {
             m_closeOnEmpty = true;
             NS_LOG_INFO("Socket " << this << " deferring close, state " << TcpStateName[m_state]);
@@ -1596,13 +1594,9 @@
 TcpSocketBase::ReadOptions(const TcpHeader& tcpHeader, uint32_t* bytesSacked)
 {
     NS_LOG_FUNCTION(this << tcpHeader);
-    TcpHeader::TcpOptionList::const_iterator it;
-    const TcpHeader::TcpOptionList options = tcpHeader.GetOptionList();
 
-    for (it = options.begin(); it != options.end(); ++it)
+    for (const auto& option : tcpHeader.GetOptionList())
     {
-        const Ptr<const TcpOption> option = (*it);
-
         // Check only for ACK options here
         switch (option->GetKind())
         {
@@ -2881,6 +2875,11 @@
 void
 TcpSocketBase::DeallocateEndPoint()
 {
+    // note: it shouldn't be necessary to invalidate the callback and manually call
+    // TcpL4Protocol::RemoveSocket. Alas, if one relies on the endpoint destruction
+    // callback, there's a weird memory access to a free'd area. Harmless, but valgrind
+    // considers it an error.
+
     if (m_endPoint != nullptr)
     {
         CancelAllTimers();
@@ -3267,7 +3266,7 @@
     NS_LOG_FUNCTION(this);
 
     // update the history of sequence numbers used to calculate the RTT
-    if (isRetransmission == false)
+    if (!isRetransmission)
     { // This is the next expected one, just log at end
         m_history.emplace_back(seq, sz, Simulator::Now());
     }
@@ -4410,7 +4409,7 @@
         m_highRxMark = header.GetSequenceNumber();
         update = true;
     }
-    if (update == true)
+    if (update)
     {
         m_rWnd = receivedWindow;
         NS_LOG_LOGIC("updating rWnd to " << m_rWnd);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-tx-buffer.cc ns-3.39/src/internet/model/tcp-tx-buffer.cc
--- ns-3.38/src/internet/model/tcp-tx-buffer.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-tx-buffer.cc	2023-07-05 14:23:47.861427500 -0700
@@ -23,7 +23,7 @@
 #include "ns3/abort.h"
 #include "ns3/log.h"
 #include "ns3/packet.h"
-#include "ns3/tcp-option-ts.h"
+#include "ns3/simulator.h"
 
 #include <algorithm>
 #include <iostream>
@@ -943,13 +943,13 @@
         // Search for the right iterator before calling IsLost()
         if (beginOfCurrentPacket >= seq)
         {
-            if ((*it)->m_lost == true)
+            if ((*it)->m_lost)
             {
                 NS_LOG_INFO("seq=" << seq << " is lost because of lost flag");
                 return true;
             }
 
-            if ((*it)->m_sacked == true)
+            if ((*it)->m_sacked)
             {
                 NS_LOG_INFO("seq=" << seq << " is not lost because of sacked flag");
                 return false;
@@ -991,7 +991,7 @@
         item = *it;
 
         // Condition 1.a , 1.b , and 1.c
-        if (item->m_retrans == false && item->m_sacked == false)
+        if (!item->m_retrans && !item->m_sacked)
         {
             if (item->m_lost)
             {
@@ -1166,7 +1166,7 @@
     Ptr<const Packet> current;
     SequenceNumber32 beginOfCurrentPacket = seq;
 
-    if ((*segment)->m_sacked == true)
+    if ((*segment)->m_sacked)
     {
         return false;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/tcp-westwood-plus.cc ns-3.39/src/internet/model/tcp-westwood-plus.cc
--- ns-3.38/src/internet/model/tcp-westwood-plus.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/tcp-westwood-plus.cc	2023-07-05 14:23:47.861427500 -0700
@@ -32,9 +32,6 @@
 
 #include "tcp-westwood-plus.h"
 
-#include "rtt-estimator.h"
-#include "tcp-socket-base.h"
-
 #include "ns3/log.h"
 #include "ns3/simulator.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/udp-l4-protocol.cc ns-3.39/src/internet/model/udp-l4-protocol.cc
--- ns-3.38/src/internet/model/udp-l4-protocol.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/udp-l4-protocol.cc	2023-07-05 14:23:47.861427500 -0700
@@ -21,25 +21,25 @@
 
 #include "ipv4-end-point-demux.h"
 #include "ipv4-end-point.h"
-#include "ipv4-l3-protocol.h"
+#include "ipv4-route.h"
+#include "ipv4.h"
 #include "ipv6-end-point-demux.h"
 #include "ipv6-end-point.h"
-#include "ipv6-l3-protocol.h"
+#include "ipv6-route.h"
+#include "ipv6.h"
 #include "udp-header.h"
 #include "udp-socket-factory-impl.h"
 #include "udp-socket-impl.h"
 
 #include "ns3/assert.h"
 #include "ns3/boolean.h"
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv6-header.h"
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
-#include "ns3/object-vector.h"
+#include "ns3/object-map.h"
 #include "ns3/packet.h"
 
+#include <unordered_map>
+
 namespace ns3
 {
 
@@ -53,15 +53,18 @@
 TypeId
 UdpL4Protocol::GetTypeId()
 {
-    static TypeId tid = TypeId("ns3::UdpL4Protocol")
-                            .SetParent<IpL4Protocol>()
-                            .SetGroupName("Internet")
-                            .AddConstructor<UdpL4Protocol>()
-                            .AddAttribute("SocketList",
-                                          "The list of sockets associated to this protocol.",
-                                          ObjectVectorValue(),
-                                          MakeObjectVectorAccessor(&UdpL4Protocol::m_sockets),
-                                          MakeObjectVectorChecker<UdpSocketImpl>());
+    static TypeId tid =
+        TypeId("ns3::UdpL4Protocol")
+            .SetParent<IpL4Protocol>()
+            .SetGroupName("Internet")
+            .AddConstructor<UdpL4Protocol>()
+            .AddAttribute("SocketList",
+                          "A container of sockets associated to this protocol. "
+                          "The underlying type is an unordered map, the attribute name "
+                          "is kept for backward compatibility.",
+                          ObjectMapValue(),
+                          MakeObjectMapAccessor(&UdpL4Protocol::m_sockets),
+                          MakeObjectMapChecker<UdpSocketImpl>());
     return tid;
 }
 
@@ -135,9 +138,9 @@
 UdpL4Protocol::DoDispose()
 {
     NS_LOG_FUNCTION(this);
-    for (std::vector<Ptr<UdpSocketImpl>>::iterator i = m_sockets.begin(); i != m_sockets.end(); i++)
+    for (auto i = m_sockets.begin(); i != m_sockets.end(); i++)
     {
-        *i = nullptr;
+        i->second = nullptr;
     }
     m_sockets.clear();
 
@@ -167,7 +170,7 @@
     Ptr<UdpSocketImpl> socket = CreateObject<UdpSocketImpl>();
     socket->SetNode(m_node);
     socket->SetUdp(this);
-    m_sockets.push_back(socket);
+    m_sockets[m_socketIndex++] = socket;
     return socket;
 }
 
@@ -328,7 +331,7 @@
     }
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 UdpL4Protocol::Receive(Ptr<Packet> packet, const Ipv4Header& header, Ptr<Ipv4Interface> interface)
 {
     NS_LOG_FUNCTION(this << packet << header);
@@ -362,7 +365,7 @@
                                                                  interface);
     if (endPoints.empty())
     {
-        if (this->GetObject<Ipv6L3Protocol>())
+        if (this->GetObject<Ipv6>())
         {
             NS_LOG_LOGIC("  No Ipv4 endpoints matched on UdpL4Protocol, trying Ipv6 " << this);
             Ptr<Ipv6Interface> fakeInterface;
@@ -387,7 +390,7 @@
     return IpL4Protocol::RX_OK;
 }
 
-enum IpL4Protocol::RxStatus
+IpL4Protocol::RxStatus
 UdpL4Protocol::Receive(Ptr<Packet> packet, const Ipv6Header& header, Ptr<Ipv6Interface> interface)
 {
     NS_LOG_FUNCTION(this << packet << header.GetSource() << header.GetDestination());
@@ -547,4 +550,21 @@
     return m_downTarget6;
 }
 
+bool
+UdpL4Protocol::RemoveSocket(Ptr<UdpSocketImpl> socket)
+{
+    NS_LOG_FUNCTION(this << socket);
+
+    for (auto& socketItem : m_sockets)
+    {
+        if (socketItem.second == socket)
+        {
+            socketItem.second = nullptr;
+            m_sockets.erase(socketItem.first);
+            return true;
+        }
+    }
+    return false;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/udp-l4-protocol.h ns-3.39/src/internet/model/udp-l4-protocol.h
--- ns-3.38/src/internet/model/udp-l4-protocol.h	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/udp-l4-protocol.h	2023-07-05 14:23:47.861427500 -0700
@@ -26,6 +26,7 @@
 #include "ns3/ptr.h"
 
 #include <stdint.h>
+#include <unordered_map>
 
 namespace ns3
 {
@@ -183,6 +184,14 @@
      */
     void DeAllocate(Ipv6EndPoint* endPoint);
 
+    /**
+     * \brief Remove a socket from the internal list
+     *
+     * \param socket socket to remove
+     * \return true if the socket has been removed
+     */
+    bool RemoveSocket(Ptr<UdpSocketImpl> socket);
+
     // called by UdpSocket.
     /**
      * \brief Send a packet via UDP (IPv4)
@@ -242,12 +251,12 @@
               Ptr<Ipv6Route> route);
 
     // inherited from Ipv4L4Protocol
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv4Header& header,
-                                        Ptr<Ipv4Interface> interface) override;
-    enum IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
-                                        const Ipv6Header& header,
-                                        Ptr<Ipv6Interface> interface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv4Header& header,
+                                   Ptr<Ipv4Interface> interface) override;
+    IpL4Protocol::RxStatus Receive(Ptr<Packet> p,
+                                   const Ipv6Header& header,
+                                   Ptr<Ipv6Interface> interface) override;
 
     void ReceiveIcmp(Ipv4Address icmpSource,
                      uint8_t icmpTtl,
@@ -283,11 +292,13 @@
     void NotifyNewAggregate() override;
 
   private:
-    Ptr<Node> m_node;                //!< the node this stack is associated with
+    Ptr<Node> m_node;                //!< The node this stack is associated with
     Ipv4EndPointDemux* m_endPoints;  //!< A list of IPv4 end points.
     Ipv6EndPointDemux* m_endPoints6; //!< A list of IPv6 end points.
 
-    std::vector<Ptr<UdpSocketImpl>> m_sockets;       //!< list of sockets
+    std::unordered_map<uint64_t, Ptr<UdpSocketImpl>>
+        m_sockets;             //!< Unordered map of socket IDs and corresponding sockets
+    uint64_t m_socketIndex{0}; //!< Index of the next socket to be created
     IpL4Protocol::DownTargetCallback m_downTarget;   //!< Callback to send packets over IPv4
     IpL4Protocol::DownTargetCallback6 m_downTarget6; //!< Callback to send packets over IPv6
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/model/udp-socket-impl.cc ns-3.39/src/internet/model/udp-socket-impl.cc
--- ns-3.38/src/internet/model/udp-socket-impl.cc	2023-03-16 20:54:23.624868600 -0700
+++ ns-3.39/src/internet/model/udp-socket-impl.cc	2023-07-05 14:23:47.861427500 -0700
@@ -20,25 +20,24 @@
 #include "udp-socket-impl.h"
 
 #include "ipv4-end-point.h"
+#include "ipv4-header.h"
+#include "ipv4-packet-info-tag.h"
+#include "ipv4-route.h"
+#include "ipv4-routing-protocol.h"
+#include "ipv4.h"
 #include "ipv6-end-point.h"
+#include "ipv6-l3-protocol.h"
+#include "ipv6-packet-info-tag.h"
+#include "ipv6-route.h"
+#include "ipv6-routing-protocol.h"
+#include "ipv6.h"
 #include "udp-l4-protocol.h"
 
 #include "ns3/inet-socket-address.h"
 #include "ns3/inet6-socket-address.h"
-#include "ns3/ipv4-header.h"
-#include "ns3/ipv4-packet-info-tag.h"
-#include "ns3/ipv4-route.h"
-#include "ns3/ipv4-routing-protocol.h"
-#include "ns3/ipv4.h"
-#include "ns3/ipv6-l3-protocol.h"
-#include "ns3/ipv6-packet-info-tag.h"
-#include "ns3/ipv6-route.h"
-#include "ns3/ipv6-routing-protocol.h"
-#include "ns3/ipv6.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/trace-source-accessor.h"
-#include "ns3/udp-socket-factory.h"
 
 #include <limits>
 
@@ -153,14 +152,14 @@
     m_udp = udp;
 }
 
-enum Socket::SocketErrno
+Socket::SocketErrno
 UdpSocketImpl::GetErrno() const
 {
     NS_LOG_FUNCTION(this);
     return m_errno;
 }
 
-enum Socket::SocketType
+Socket::SocketType
 UdpSocketImpl::GetSocketType() const
 {
     return NS3_SOCK_DGRAM;
@@ -177,6 +176,10 @@
 UdpSocketImpl::Destroy()
 {
     NS_LOG_FUNCTION(this);
+    if (m_udp)
+    {
+        m_udp->RemoveSocket(this);
+    }
     m_endPoint = nullptr;
 }
 
@@ -184,6 +187,10 @@
 UdpSocketImpl::Destroy6()
 {
     NS_LOG_FUNCTION(this);
+    if (m_udp)
+    {
+        m_udp->RemoveSocket(this);
+    }
     m_endPoint6 = nullptr;
 }
 
@@ -193,13 +200,11 @@
 {
     if (m_endPoint != nullptr)
     {
-        m_endPoint->SetDestroyCallback(MakeNullCallback<void>());
         m_udp->DeAllocate(m_endPoint);
         m_endPoint = nullptr;
     }
     if (m_endPoint6 != nullptr)
     {
-        m_endPoint6->SetDestroyCallback(MakeNullCallback<void>());
         m_udp->DeAllocate(m_endPoint6);
         m_endPoint6 = nullptr;
     }
@@ -390,7 +395,7 @@
 UdpSocketImpl::Close()
 {
     NS_LOG_FUNCTION(this);
-    if (m_shutdownRecv == true && m_shutdownSend == true)
+    if (m_shutdownRecv && m_shutdownSend)
     {
         m_errno = Socket::ERROR_BADF;
         return -1;
@@ -406,7 +411,7 @@
 UdpSocketImpl::Connect(const Address& address)
 {
     NS_LOG_FUNCTION(this << address);
-    if (InetSocketAddress::IsMatchingType(address) == true)
+    if (InetSocketAddress::IsMatchingType(address))
     {
         InetSocketAddress transport = InetSocketAddress::ConvertFrom(address);
         m_defaultAddress = Address(transport.GetIpv4());
@@ -415,7 +420,7 @@
         m_connected = true;
         NotifyConnectionSucceeded();
     }
-    else if (Inet6SocketAddress::IsMatchingType(address) == true)
+    else if (Inet6SocketAddress::IsMatchingType(address))
     {
         Inet6SocketAddress transport = Inet6SocketAddress::ConvertFrom(address);
         m_defaultAddress = Address(transport.GetIpv6());
@@ -457,7 +462,7 @@
 UdpSocketImpl::DoSend(Ptr<Packet> p)
 {
     NS_LOG_FUNCTION(this << p);
-    if ((m_endPoint == nullptr) && (Ipv4Address::IsMatchingType(m_defaultAddress) == true))
+    if (m_endPoint == nullptr && Ipv4Address::IsMatchingType(m_defaultAddress))
     {
         if (Bind() == -1)
         {
@@ -466,7 +471,7 @@
         }
         NS_ASSERT(m_endPoint != nullptr);
     }
-    else if ((m_endPoint6 == nullptr) && (Ipv6Address::IsMatchingType(m_defaultAddress) == true))
+    else if (m_endPoint6 == nullptr && Ipv6Address::IsMatchingType(m_defaultAddress))
     {
         if (Bind6() == -1)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/examples-to-run.py ns-3.39/src/internet/test/examples-to-run.py
--- ns-3.38/src/internet/test/examples-to-run.py	2023-03-16 20:54:23.628868600 -0700
+++ ns-3.39/src/internet/test/examples-to-run.py	2023-07-05 14:23:47.861427500 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/internet-stack-helper-test-suite.cc ns-3.39/src/internet/test/internet-stack-helper-test-suite.cc
--- ns-3.38/src/internet/test/internet-stack-helper-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/internet/test/internet-stack-helper-test-suite.cc	2023-07-05 14:23:47.861427500 -0700
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2023 Universita' di Firenze
+ *
+ * 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: Tommaso Pecorella <tommaso.pecorella@unifi.it>
+ */
+
+#include "ns3/internet-stack-helper.h"
+#include "ns3/log.h"
+#include "ns3/node.h"
+#include "ns3/test.h"
+
+#include <string>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("InternetStackHelperTestSuite");
+
+/**
+ * \ingroup internet-test
+ *
+ * \brief InternetStackHelper Test
+ */
+class InternetStackHelperTestCase : public TestCase
+{
+  public:
+    InternetStackHelperTestCase();
+
+  private:
+    void DoRun() override;
+    void DoTeardown() override;
+};
+
+InternetStackHelperTestCase::InternetStackHelperTestCase()
+    : TestCase("InternetStackHelperTestCase")
+{
+}
+
+void
+InternetStackHelperTestCase::DoRun()
+{
+    // Checks:
+    // 1. IPv4 only, add IPv4 + IPv6 (result, IPv4 + IPv6)
+    // 2. IPv6 only, add IPv4 + IPv6 (result, IPv4 + IPv6)
+    // 3. IPv4 + IPv6, add IPv4 + IPv6 (result, IPv4 + IPv6)
+
+    Ptr<Node> nodeIpv4Only = CreateObject<Node>();
+    Ptr<Node> nodeIpv6Only = CreateObject<Node>();
+    Ptr<Node> nodeIpv46 = CreateObject<Node>();
+
+    InternetStackHelper internet;
+
+    internet.SetIpv4StackInstall(true);
+    internet.SetIpv6StackInstall(false);
+    internet.Install(nodeIpv4Only);
+
+    internet.SetIpv4StackInstall(false);
+    internet.SetIpv6StackInstall(true);
+    internet.Install(nodeIpv6Only);
+
+    internet.SetIpv4StackInstall(true);
+    internet.SetIpv6StackInstall(true);
+    internet.Install(nodeIpv46);
+
+    // Check that the three nodes have only the intended IP stack.
+    NS_TEST_EXPECT_MSG_NE(nodeIpv4Only->GetObject<Ipv4>(),
+                          nullptr,
+                          "IPv4 not found on IPv4-only node (should have been there)");
+    NS_TEST_EXPECT_MSG_EQ(nodeIpv4Only->GetObject<Ipv6>(),
+                          nullptr,
+                          "IPv6 found on IPv4-only node (should not have been there)");
+
+    NS_TEST_EXPECT_MSG_EQ(nodeIpv6Only->GetObject<Ipv4>(),
+                          nullptr,
+                          "IPv4 found on IPv6-only node (should not have been there)");
+    NS_TEST_EXPECT_MSG_NE(nodeIpv6Only->GetObject<Ipv6>(),
+                          nullptr,
+                          "IPv6 not found on IPv6-only node (should have been there)");
+
+    NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject<Ipv4>(),
+                          nullptr,
+                          "IPv4 not found on dual stack node (should have been there)");
+    NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject<Ipv6>(),
+                          nullptr,
+                          "IPv6 not found on dual stack node (should have been there)");
+
+    // Now we install IPv4 and IPv6 on the IPv4-only node
+    // IPv4 is already there, no error should happen.
+    internet.Install(nodeIpv4Only);
+    // Now we install IPv4 and IPv6 on the IPv6-only node,
+    // IPv6 is already there, no error should happen.
+    internet.Install(nodeIpv6Only);
+    // Now we install IPv4 and IPv6 on the dual stack node
+    // IPv4 and IPv6 are already there, no error should happen.
+    internet.Install(nodeIpv46);
+
+    // Check that the three nodes have both IPv4 and IPv6.
+    NS_TEST_EXPECT_MSG_NE(
+        nodeIpv4Only->GetObject<Ipv4>(),
+        nullptr,
+        "IPv4 not found on IPv4-only, now dual stack node (should have been there)");
+    NS_TEST_EXPECT_MSG_NE(
+        nodeIpv4Only->GetObject<Ipv6>(),
+        nullptr,
+        "IPv6 not found on IPv4-only, now dual stack node (should have been there)");
+
+    NS_TEST_EXPECT_MSG_NE(
+        nodeIpv6Only->GetObject<Ipv4>(),
+        nullptr,
+        "IPv4 not found on IPv6-only, now dual stack node (should have been there)");
+    NS_TEST_EXPECT_MSG_NE(
+        nodeIpv6Only->GetObject<Ipv6>(),
+        nullptr,
+        "IPv6 not found on IPv6-only, now dual stack node (should have been there)");
+
+    NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject<Ipv4>(),
+                          nullptr,
+                          "IPv4 not found on dual stack node (should have been there)");
+    NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject<Ipv6>(),
+                          nullptr,
+                          "IPv6 not found on dual stack node (should have been there)");
+}
+
+void
+InternetStackHelperTestCase::DoTeardown()
+{
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup internet-test
+ *
+ * \brief InternetStackHelper TestSuite
+ */
+class InternetStackHelperTestSuite : public TestSuite
+{
+  public:
+    InternetStackHelperTestSuite()
+        : TestSuite("internet-stack-helper", UNIT)
+    {
+        AddTestCase(new InternetStackHelperTestCase(), TestCase::QUICK);
+    }
+};
+
+static InternetStackHelperTestSuite
+    g_internetStackHelperTestSuite; //!< Static variable for test initialization
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/ipv4-global-routing-test-suite.cc ns-3.39/src/internet/test/ipv4-global-routing-test-suite.cc
--- ns-3.38/src/internet/test/ipv4-global-routing-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
+++ ns-3.39/src/internet/test/ipv4-global-routing-test-suite.cc	2023-07-05 14:23:47.865427500 -0700
@@ -940,7 +940,7 @@
 void
 Ipv4DynamicGlobalRoutingTestCase::SendData(uint8_t index)
 {
-    if (m_sendSocks[index].second == false)
+    if (!m_sendSocks[index].second)
     {
         return;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/ipv4-list-routing-test-suite.cc ns-3.39/src/internet/test/ipv4-list-routing-test-suite.cc
--- ns-3.38/src/internet/test/ipv4-list-routing-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
+++ ns-3.39/src/internet/test/ipv4-list-routing-test-suite.cc	2023-07-05 14:23:47.865427500 -0700
@@ -20,7 +20,8 @@
 #include "ns3/ipv4-routing-protocol.h"
 #include "ns3/test.h"
 
-using namespace ns3;
+namespace ns3
+{
 
 /**
  * \ingroup internet-test
@@ -41,10 +42,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override
     {
         return false;
     }
@@ -93,10 +94,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override
     {
         return false;
     }
@@ -217,3 +218,5 @@
 
 static Ipv4ListRoutingTestSuite
     g_ipv4ListRoutingTestSuite; //!< Static variable for test initialization
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/ipv6-list-routing-test-suite.cc ns-3.39/src/internet/test/ipv6-list-routing-test-suite.cc
--- ns-3.38/src/internet/test/ipv6-list-routing-test-suite.cc	2023-03-16 20:54:23.628868600 -0700
+++ ns-3.39/src/internet/test/ipv6-list-routing-test-suite.cc	2023-07-05 14:23:47.865427500 -0700
@@ -21,7 +21,8 @@
 #include "ns3/ipv6-routing-protocol.h"
 #include "ns3/test.h"
 
-using namespace ns3;
+namespace ns3
+{
 
 /**
  * \ingroup internet-test
@@ -42,10 +43,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv6Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override
     {
         return false;
     }
@@ -108,10 +109,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv6Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override
     {
         return false;
     }
@@ -246,3 +247,5 @@
 
 static Ipv6ListRoutingTestSuite
     g_ipv6ListRoutingTestSuite; //!< Static variable for test initialization
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/tcp-endpoint-bug2211.cc ns-3.39/src/internet/test/tcp-endpoint-bug2211.cc
--- ns-3.38/src/internet/test/tcp-endpoint-bug2211.cc	2023-03-16 20:54:23.628868600 -0700
+++ ns-3.39/src/internet/test/tcp-endpoint-bug2211.cc	2023-07-05 14:23:47.865427500 -0700
@@ -119,7 +119,7 @@
 
     TypeId tid = TcpSocketFactory::GetTypeId();
     Ptr<Socket> sink = Socket::CreateSocket(node, tid);
-    if (m_v6 == false)
+    if (!m_v6)
     {
         sink->Bind(InetSocketAddress(Ipv4Address::GetAny(), 9));
     }
@@ -135,7 +135,7 @@
     source->Bind();
     source->SetConnectCallback(MakeCallback(&TcpEndPointBug2211Test::HandleConnect, this),
                                MakeNullCallback<void, Ptr<Socket>>());
-    if (m_v6 == false)
+    if (!m_v6)
     {
         source->Connect(InetSocketAddress(Ipv4Address::GetLoopback(), 9));
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/tcp-pacing-test.cc ns-3.39/src/internet/test/tcp-pacing-test.cc
--- ns-3.38/src/internet/test/tcp-pacing-test.cc	2023-03-16 20:54:23.628868600 -0700
+++ ns-3.39/src/internet/test/tcp-pacing-test.cc	2023-07-05 14:23:47.865427500 -0700
@@ -336,14 +336,7 @@
         // so add an additional m_segmentSize to bytesInFlight
         uint32_t soonBytesInFlight = m_bytesInFlight + m_segmentSize;
         bool canPacketBeSent = ((m_curCwnd - soonBytesInFlight) >= m_segmentSize);
-        if (!canPacketBeSent || (m_curCwnd == 0))
-        {
-            m_isFullCwndSent = true;
-        }
-        else
-        {
-            m_isFullCwndSent = false;
-        }
+        m_isFullCwndSent = (!canPacketBeSent || m_curCwnd == 0);
         m_nextPacketInterval = m_expectedInterval;
         NS_LOG_DEBUG("Next expected interval (s): " << m_nextPacketInterval.GetSeconds());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet/test/tcp-test.cc ns-3.39/src/internet/test/tcp-test.cc
--- ns-3.38/src/internet/test/tcp-test.cc	2023-03-16 20:54:23.628868600 -0700
+++ ns-3.39/src/internet/test/tcp-test.cc	2023-07-05 14:23:47.865427500 -0700
@@ -226,7 +226,7 @@
     memset(m_sourceRxPayload, 0, m_totalBytes);
     memset(m_serverRxPayload, 0, m_totalBytes);
 
-    if (m_useIpv6 == true)
+    if (m_useIpv6)
     {
         SetupDefaultSim6();
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet-apps/model/dhcp-client.cc ns-3.39/src/internet-apps/model/dhcp-client.cc
--- ns-3.38/src/internet-apps/model/dhcp-client.cc	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/internet-apps/model/dhcp-client.cc	2023-07-05 14:23:47.829427500 -0700
@@ -369,7 +369,7 @@
     NS_LOG_FUNCTION(this << header);
 
     m_offerList.push_back(header);
-    if (m_offered == false)
+    if (!m_offered)
     {
         m_discoverEvent.Cancel();
         m_offered = true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet-apps/model/dhcp-header.cc ns-3.39/src/internet-apps/model/dhcp-header.cc
--- ns-3.38/src/internet-apps/model/dhcp-header.cc	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/internet-apps/model/dhcp-header.cc	2023-07-05 14:23:47.829427500 -0700
@@ -75,7 +75,7 @@
 void
 DhcpHeader::SetType(uint8_t type)
 {
-    if (m_opt[OP_MSGTYPE] == false)
+    if (!m_opt[OP_MSGTYPE])
     {
         m_len += 3;
         m_opt[OP_MSGTYPE] = true;
@@ -154,7 +154,7 @@
 void
 DhcpHeader::SetDhcps(Ipv4Address addr)
 {
-    if (m_opt[OP_SERVID] == false)
+    if (!m_opt[OP_SERVID])
     {
         m_len += 6;
         m_opt[OP_SERVID] = true;
@@ -171,7 +171,7 @@
 void
 DhcpHeader::SetReq(Ipv4Address addr)
 {
-    if (m_opt[OP_ADDREQ] == false)
+    if (!m_opt[OP_ADDREQ])
     {
         m_len += 6;
         m_opt[OP_ADDREQ] = true;
@@ -188,7 +188,7 @@
 void
 DhcpHeader::SetMask(uint32_t addr)
 {
-    if (m_opt[OP_MASK] == false)
+    if (!m_opt[OP_MASK])
     {
         m_len += 6;
         m_opt[OP_MASK] = true;
@@ -205,7 +205,7 @@
 void
 DhcpHeader::SetRouter(Ipv4Address addr)
 {
-    if (m_opt[OP_ROUTE] == false)
+    if (!m_opt[OP_ROUTE])
     {
         m_len += 6;
         m_opt[OP_ROUTE] = true;
@@ -222,7 +222,7 @@
 void
 DhcpHeader::SetLease(uint32_t time)
 {
-    if (m_opt[OP_LEASE] == false)
+    if (!m_opt[OP_LEASE])
     {
         m_len += 6;
         m_opt[OP_LEASE] = true;
@@ -239,7 +239,7 @@
 void
 DhcpHeader::SetRenew(uint32_t time)
 {
-    if (m_opt[OP_RENEW] == false)
+    if (!m_opt[OP_RENEW])
     {
         m_len += 6;
         m_opt[OP_RENEW] = true;
@@ -256,7 +256,7 @@
 void
 DhcpHeader::SetRebind(uint32_t time)
 {
-    if (m_opt[OP_REBIND] == false)
+    if (!m_opt[OP_REBIND])
     {
         m_len += 6;
         m_opt[OP_REBIND] = true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet-apps/model/ping.h ns-3.39/src/internet-apps/model/ping.h
--- ns-3.38/src/internet-apps/model/ping.h	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/internet-apps/model/ping.h	2023-07-05 14:23:47.829427500 -0700
@@ -145,7 +145,7 @@
      *
      * \param [in] report The report information
      */
-    typedef void (*ReportTrace)(const struct PingReport& report);
+    typedef void (*ReportTrace)(const PingReport& report);
 
   private:
     /**
@@ -222,7 +222,7 @@
     /// TracedCallback for drop events
     TracedCallback<uint16_t, DropReason> m_dropTrace;
     /// TracedCallback for final ping report
-    TracedCallback<const struct PingReport&> m_reportTrace;
+    TracedCallback<const PingReport&> m_reportTrace;
     /// Variable to stor verbose mode
     VerboseMode m_verbose{VerboseMode::VERBOSE};
     /// Received packets counter
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/internet-apps/model/radvd.cc ns-3.39/src/internet-apps/model/radvd.cc
--- ns-3.38/src/internet-apps/model/radvd.cc	2023-03-16 20:54:23.608868600 -0700
+++ ns-3.39/src/internet-apps/model/radvd.cc	2023-07-05 14:23:47.829427500 -0700
@@ -192,7 +192,7 @@
 {
     NS_LOG_FUNCTION(this << dst << reschedule);
 
-    if (reschedule == true)
+    if (reschedule)
     {
         config->SetLastRaTxTime(Simulator::Now());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/doc/lr-wpan.rst ns-3.39/src/lr-wpan/doc/lr-wpan.rst
--- ns-3.38/src/lr-wpan/doc/lr-wpan.rst	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/doc/lr-wpan.rst	2023-07-05 14:23:47.869427400 -0700
@@ -155,11 +155,11 @@
 
 * *Energy Detection (ED) Scan:* In an energy scan, a device or a coordinator scan a set number of channels looking for traces of energy. The maximum energy registered during a given amount of time is stored. Energy scan is typically used to measure the quality of a channel at any given time. For this reason, coordinators often use this scan before initiating a PAN on a channel.
 
-* *Active Scan:* A device sends beacon requests on a set number of channels looking for a PAN coordinator. The receiving coordinator must be configured on non-beacon mode. Coordinators on beacon-mode ignore these requests. The coordinators who accept the request, respond with a beacon. After an active scan take place, during the association process devices extract the information in the PAN descriptors from the collected beacons and based on this information (e.g. channel, LQI level), choose a coordinator to associate with.
+* *Active Scan:* A device sends ``beacon request commands`` on a set number of channels looking for a PAN coordinator. The receiving coordinator must be configured on non-beacon mode. Coordinators on beacon-mode ignore these requests. The coordinators who accept the request, respond with a beacon. After an active scan take place, during the association process devices extract the information in the PAN descriptors from the collected beacons and based on this information (e.g. channel, LQI level), choose a coordinator to associate with.
 
-* *Passive Scan:* In a passive scan, no beacon requests are sent. Devices scan a set number of channels looking for beacons currently being transmitted (coordinators in beacon-mode). Like in the active scan, the information from beacons is stored in PAN descriptors and used by the device to choose a coordinator to associate with.
+* *Passive Scan:* In a passive scan, no ``beacon requests commands`` are sent. Devices scan a set number of channels looking for beacons currently being transmitted (coordinators in beacon-mode). Like in the active scan, the information from beacons is stored in PAN descriptors and used by the device to choose a coordinator to associate with.
 
-* *Orphan Scan:* <Not supported by ns-3>
+* *Orphan Scan:* Orphan scan is used typically by device as a result of repeated communication failure attempts with a coordinator. In other words, an orphan scan represents the intent of a device to relocate its coordinator. In some situations, it can be used by devices higher layers to not only rejoin a network but also join a network for the first time. In an orphan scan, a device send a ``orphan notification command`` to a given list of channels. If a coordinator receives this notification, it responds to the device with a ``coordinator realignment command``.
 
 In active and passive scans, the link quality indicator (LQI) is the main parameter used to
 determine the optimal coordinator. LQI values range from 0 to 255. Where 255 is the highest quality link value and 0 the lowest. Typically, a link lower than 127 is considered a link with poor quality.
@@ -315,7 +315,7 @@
 - The standard describes the support of multiple PHY band-modulations but currently, only 250kbps O-QPSK (channel page 0) is supported.
 - Active and passive MAC scans are able to obtain a LQI value from a beacon frame, however, the scan primitives assumes LQI is correctly implemented and does not check the validity of its value.
 - Configuration of the ED thresholds are currently not supported.
-- Orphan scans are not supported.
+- Coordinator realignment command is only supported in orphan scans.
 - Disassociation primitives are not supported.
 - Security is not supported.
 - Beacon enabled mode GTS are not supported.
@@ -324,8 +324,10 @@
 ==========
 
 * Wireless Medium Access Control (MAC) and Physical Layer (PHY) Specifications for Low-Rate Wireless Personal Area Networks (WPANs), IEEE Computer Society, IEEE Std 802.15.4-2006, 8 September 2006.
+* IEEE Standard for Local and metropolitan area networks--Part 15.4: Low-Rate Wireless Personal Area Networks (LR-WPANs)," in IEEE Std 802.15.4-2011 (Revision of IEEE Std 802.15.4-2006) , vol., no., pp.1-314, 5 Sept. 2011, doi: 10.1109/IEEESTD.2011.6012487.
 * J. Zheng and Myung J. Lee, "A comprehensive performance study of IEEE 802.15.4," Sensor Network Operations, IEEE Press, Wiley Interscience, Chapter 4, pp. 218-237, 2006.
 * Alberto Gallegos Ramonet and Taku Noguchi. 2020. LR-WPAN: Beacon Enabled Direct Transmissions on Ns-3. In 2020 the 6th International Conference on Communication and Information Processing (ICCIP 2020). Association for Computing Machinery, New York, NY, USA, 115â€“122. https://doi.org/10.1145/3442555.3442574.
+* Gallegos Ramonet, A.; Noguchi, T. Performance Analysis of IEEE 802.15.4 Bootstrap Process. Electronics 2022, 11, 4090. https://doi.org/10.3390/electronics11244090.
 
 Usage
 *****
@@ -362,6 +364,7 @@
 * ``lr-wpan-active-scan.cc``:  A simple example showing the use of an active scan in the MAC.
 * ``lr-wpan-mlme.cc``: Demonstrates the use of lr-wpan beacon mode. Nodes use a manual association (i.e. No bootstrap) in this example.
 * ``lr-wpan-bootstrap.cc``:  Demonstrates the use of scanning and association working together to initiate a PAN.
+* ``lr-wpan-orphan-scan.cc``: Demonstrates the use of an orphan scanning in a simple network joining procedure.
 
 
 In particular, the module enables a very simplified end-to-end data
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/examples/CMakeLists.txt ns-3.39/src/lr-wpan/examples/CMakeLists.txt
--- ns-3.38/src/lr-wpan/examples/CMakeLists.txt	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/examples/CMakeLists.txt	2023-07-05 14:23:47.869427400 -0700
@@ -5,6 +5,7 @@
     lr-wpan-phy-test
     lr-wpan-ed-scan
     lr-wpan-active-scan
+    lr-wpan-orphan-scan
 )
 
 foreach(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/examples/lr-wpan-orphan-scan.cc ns-3.39/src/lr-wpan/examples/lr-wpan-orphan-scan.cc
--- ns-3.38/src/lr-wpan/examples/lr-wpan-orphan-scan.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/lr-wpan/examples/lr-wpan-orphan-scan.cc	2023-07-05 14:23:47.869427400 -0700
@@ -0,0 +1,215 @@
+/*
+ * 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>
+ */
+
+/*
+ * [00:00:00:00:00:00:00:01 | 00:01]     [00:00:00:00:00:00:00:02 | ff:ff]
+ *  PAN Coordinator 1 (PAN: 5)                      End Device
+ *       |---------------------100m-----------------------|
+ *  Channel 12                                (Orphan Scan channels 11-14)
+ *
+ *
+ * This example demonstrate the usage of the MAC MLME-SCAN.request (ORPHAN scan) primitive as
+ * described by IEEE 802.15.4-2011 (See Figures 14 and 15).
+ *
+ * Orphan scan is used typically on devices as a result of repeated communication failures
+ * (For example, lossing too many ACK). An orphan scan represents the attempt of a device to
+ * relocate its coordinator. In some situations, it can be used by devices higher layers to not only
+ * rejoin a network but also join a network for the first time (Like in the joining through
+ * orphaning mechanism described in Zigbee networks).
+ *
+ * In this example, the end device is set to scan 4 channels (11~14) for a period of
+ * macResponseWaitTime until it finally gets in contact with the coordinator.
+ * On contact, the coordinator responds to the device (via coordinator realignment command)
+ * an assign it a short address. The detailed sequence of events is as following:
+ *
+ * 1) [Time 2s] The coordinator start a network in channel 12.
+ * 2) [Time 3s] The end device start orphan scan and transmits a orphan
+ *    notification cmd on channel 11.
+ * 3) No response is received in channel 11, therefore, the device ends scanning on
+ *    channel 11 after macResponseWaitTime and repeats step 2 in channel 12.
+ * 4) [Time 3.00269s] The orphan notification command is received by the coordinator in
+ *    channel 12. The coordinator verify the requesting device and replies to the device
+ *    with a coordinator realignment command containing the assigned short address [DE:AF].
+ * 5) [Time 3.00646s] The device receives the coordinator realignment command, update its
+ *    macPanId, macShortAddress, macCoordShortAddress and macCoordExtAddress.
+ * 6) Scanning of the remaining channels 13 and 14 is cancelled.
+ */
+
+#include <ns3/constant-position-mobility-model.h>
+#include <ns3/core-module.h>
+#include <ns3/log.h>
+#include <ns3/lr-wpan-module.h>
+#include <ns3/packet.h>
+#include <ns3/propagation-delay-model.h>
+#include <ns3/propagation-loss-model.h>
+#include <ns3/simulator.h>
+#include <ns3/single-model-spectrum-channel.h>
+
+#include <iostream>
+
+using namespace ns3;
+
+static void
+ScanConfirm(Ptr<LrWpanNetDevice> device, MlmeScanConfirmParams params)
+{
+    if (params.m_status == MLMESCAN_SUCCESS)
+    {
+        std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
+                  << device->GetMac()->GetShortAddress() << " | "
+                  << device->GetMac()->GetExtendedAddress()
+                  << "] MLME-SCAN.confirm: Active scan status SUCCESSFUL "
+                  << "(Coordinator found and address assigned) \n";
+    }
+    else if (params.m_status == MLMESCAN_NO_BEACON)
+    {
+        std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
+                  << device->GetMac()->GetShortAddress() << " | "
+                  << device->GetMac()->GetExtendedAddress()
+                  << "] MLME-SCAN.confirm: Could not locate coordinator "
+                  << "(Coord realignment command not received) "
+                  << "status: " << params.m_status << "\n";
+    }
+    else
+    {
+        std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
+                  << device->GetMac()->GetShortAddress() << " | "
+                  << device->GetMac()->GetExtendedAddress()
+                  << "] MLME-SCAN.confirm: An error occurred during scanning, "
+                  << "status: " << params.m_status << "\n";
+    }
+}
+
+static void
+OrphanIndication(Ptr<LrWpanNetDevice> device, MlmeOrphanIndicationParams params)
+{
+    // The steps taken by the coordinator on the event of an orphan indication
+    // are meant to be implemented by the next higher layer and are out of the scope of the
+    // standard. In this example, we simply accept the request , assign a fixed short address
+    // [DE:AF] and respond to the requesting device using a MLME-ORPHAN.response.
+
+    std::cout << Simulator::Now().As(Time::S) << " Node " << device->GetNode()->GetId() << " ["
+              << device->GetMac()->GetShortAddress() << " | "
+              << device->GetMac()->GetExtendedAddress()
+              << "] MLME-ORPHAN.indication: Orphan Notification received, processing...\n";
+
+    MlmeOrphanResponseParams respParams;
+    respParams.m_assocMember = true;
+    respParams.m_orphanAddr = params.m_orphanAddr;
+    respParams.m_shortAddr = Mac16Address("DE:AF");
+
+    Simulator::ScheduleNow(&LrWpanMac::MlmeOrphanResponse, device->GetMac(), respParams);
+}
+
+int
+main(int argc, char* argv[])
+{
+    LogComponentEnableAll(LogLevel(LOG_PREFIX_TIME | LOG_PREFIX_FUNC));
+
+    // Create 2 PAN coordinator nodes, and 1 end device
+    Ptr<Node> coord1 = CreateObject<Node>();
+    Ptr<Node> endNode = CreateObject<Node>();
+
+    Ptr<LrWpanNetDevice> coord1NetDevice = CreateObject<LrWpanNetDevice>();
+    Ptr<LrWpanNetDevice> endNodeNetDevice = CreateObject<LrWpanNetDevice>();
+
+    // PAN Coordinators configurations require to set both, the EUI-64 (extended address)
+    // and to assign their own short address.
+    coord1NetDevice->GetMac()->SetExtendedAddress(Mac64Address("00:00:00:00:00:00:00:01"));
+    coord1NetDevice->GetMac()->SetShortAddress(Mac16Address("00:01"));
+
+    // Other devices must have only its EUI-64 and later on, their short address is
+    // potentially assigned by the coordinator.
+    endNodeNetDevice->GetMac()->SetExtendedAddress(Mac64Address("00:00:00:00:00:00:00:02"));
+
+    // Configure Spectrum channel
+    Ptr<SingleModelSpectrumChannel> channel = CreateObject<SingleModelSpectrumChannel>();
+    Ptr<LogDistancePropagationLossModel> propModel =
+        CreateObject<LogDistancePropagationLossModel>();
+    Ptr<ConstantSpeedPropagationDelayModel> delayModel =
+        CreateObject<ConstantSpeedPropagationDelayModel>();
+    channel->AddPropagationLossModel(propModel);
+    channel->SetPropagationDelayModel(delayModel);
+
+    coord1NetDevice->SetChannel(channel);
+    endNodeNetDevice->SetChannel(channel);
+
+    coord1->AddDevice(coord1NetDevice);
+    endNode->AddDevice(endNodeNetDevice);
+
+    // Mobility
+    Ptr<ConstantPositionMobilityModel> coord1Mobility =
+        CreateObject<ConstantPositionMobilityModel>();
+    coord1Mobility->SetPosition(Vector(0, 0, 0));
+    coord1NetDevice->GetPhy()->SetMobility(coord1Mobility);
+
+    Ptr<ConstantPositionMobilityModel> endNodeMobility =
+        CreateObject<ConstantPositionMobilityModel>();
+    endNodeMobility->SetPosition(Vector(100, 0, 0));
+    endNodeNetDevice->GetPhy()->SetMobility(endNodeMobility);
+
+    // MAC layer Callbacks hooks
+    endNodeNetDevice->GetMac()->SetMlmeScanConfirmCallback(
+        MakeBoundCallback(&ScanConfirm, endNodeNetDevice));
+
+    coord1NetDevice->GetMac()->SetMlmeOrphanIndicationCallback(
+        MakeBoundCallback(&OrphanIndication, coord1NetDevice));
+
+    /////////////////
+    // ORPHAN SCAN //
+    /////////////////
+
+    // PAN coordinator N0 (PAN 5) is set to channel 12 in non-beacon mode
+    // but answer to beacon request and orphan notification commands.
+    MlmeStartRequestParams params;
+    params.m_panCoor = true;
+    params.m_PanId = 5;
+    params.m_bcnOrd = 15;
+    params.m_sfrmOrd = 15;
+    params.m_logCh = 12;
+    Simulator::ScheduleWithContext(1,
+                                   Seconds(2.0),
+                                   &LrWpanMac::MlmeStartRequest,
+                                   coord1NetDevice->GetMac(),
+                                   params);
+
+    // End device N1 is set to scan 4 channels looking for the presence of a coordinator.
+    // On each channel, a single orphan notification command is sent and a response is
+    // waited for a maximum time of macResponseWaitTime. If a reply is received from a
+    // coordinator within this time (coordinator realignment command), the programmed scans on
+    // other channels is suspended.
+    // Scan Channels are represented by bits 0-26  (27 LSB)
+    //                       ch 14  ch 11
+    //                           |  |
+    // 0x7800  = 0000000000000000111100000000000
+    MlmeScanRequestParams scanParams;
+    scanParams.m_chPage = 0;
+    scanParams.m_scanChannels = 0x7800;
+    scanParams.m_scanType = MLMESCAN_ORPHAN;
+    Simulator::ScheduleWithContext(1,
+                                   Seconds(3.0),
+                                   &LrWpanMac::MlmeScanRequest,
+                                   endNodeNetDevice->GetMac(),
+                                   scanParams);
+
+    Simulator::Stop(Seconds(2000));
+    Simulator::Run();
+
+    Simulator::Destroy();
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/examples/lr-wpan-per-plot.cc ns-3.39/src/lr-wpan/examples/lr-wpan-per-plot.cc
--- ns-3.38/src/lr-wpan/examples/lr-wpan-per-plot.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/examples/lr-wpan-per-plot.cc	2023-07-05 14:23:47.869427400 -0700
@@ -86,7 +86,7 @@
 
     Gnuplot perplot = Gnuplot("802.15.4-per-vs-rxSignal.eps");
     Gnuplot2dDataset perdatasetExperimental("Experimental");
-    Gnuplot2dDataset perdatasetTheorical("Theoretical");
+    Gnuplot2dDataset perdatasetTheoretical("Theoretical");
 
     Ptr<Node> n0 = CreateObject<Node>();
     Ptr<Node> n1 = CreateObject<Node>();
@@ -214,7 +214,7 @@
             sensThreshold = false;
         }
 
-        perdatasetTheorical.Add(j, perTheoretical);
+        perdatasetTheoretical.Add(j, perTheoretical);
     }
 
     std::cout << "_____________________________________________________________________________\n";
@@ -229,7 +229,7 @@
        << "Rx Sensitivity (Theo) = " << sensitivityTheo << " dBm";
 
     perplot.AddDataset(perdatasetExperimental);
-    perplot.AddDataset(perdatasetTheorical);
+    perplot.AddDataset(perdatasetTheoretical);
 
     perplot.SetTitle(os.str());
     perplot.SetTerminal("postscript eps color enh \"Times-BoldItalic\"");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/helper/lr-wpan-helper.cc ns-3.39/src/lr-wpan/helper/lr-wpan-helper.cc
--- ns-3.38/src/lr-wpan/helper/lr-wpan-helper.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/helper/lr-wpan-helper.cc	2023-07-05 14:23:47.869427400 -0700
@@ -361,7 +361,7 @@
     Ptr<PcapFileWrapper> file =
         pcapHelper.CreateFile(filename, std::ios::out, PcapHelper::DLT_IEEE802_15_4);
 
-    if (promiscuous == true)
+    if (promiscuous)
     {
         device->GetMac()->TraceConnectWithoutContext("PromiscSniffer",
                                                      MakeBoundCallback(&PcapSniffLrWpan, file));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-constants.h ns-3.39/src/lr-wpan/model/lr-wpan-constants.h
--- ns-3.38/src/lr-wpan/model/lr-wpan-constants.h	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-constants.h	2023-07-05 14:23:47.869427400 -0700
@@ -119,7 +119,7 @@
  * The maximum size, in octets, of a beacon payload.
  * See IEEE 802.15.4-2011, section 6.4.1, Table 51.
  */
-constexpr uint32_t aMaxBeaconPayloadLenght{aMaxPhyPacketSize - aMaxBeaconOverhead};
+constexpr uint32_t aMaxBeaconPayloadLength{aMaxPhyPacketSize - aMaxBeaconOverhead};
 
 /** @} */
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-csmaca.cc ns-3.39/src/lr-wpan/model/lr-wpan-csmaca.cc
--- ns-3.38/src/lr-wpan/model/lr-wpan-csmaca.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-csmaca.cc	2023-07-05 14:23:47.869427400 -0700
@@ -31,7 +31,9 @@
 #include <algorithm>
 
 #undef NS_LOG_APPEND_CONTEXT
-#define NS_LOG_APPEND_CONTEXT std::clog << "[address " << m_mac->GetShortAddress() << "] ";
+#define NS_LOG_APPEND_CONTEXT                                                                      \
+    std::clog << "[address " << m_mac->GetShortAddress() << " | " << m_mac->GetExtendedAddress()   \
+              << "] ";
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-fields.cc ns-3.39/src/lr-wpan/model/lr-wpan-fields.cc
--- ns-3.38/src/lr-wpan/model/lr-wpan-fields.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-fields.cc	2023-07-05 14:23:47.869427400 -0700
@@ -268,18 +268,18 @@
 
     if (m_gtsSpecDescCount > 0)
     {
-        uint8_t gtsDescStartAndLenght;
+        uint8_t gtsDescStartAndLength;
         i.WriteU8(GetGtsDirectionField());
 
         for (int j = 0; j < m_gtsSpecDescCount; j++)
         {
             WriteTo(i, m_gtsList[j].m_gtsDescDevShortAddr);
 
-            gtsDescStartAndLenght =
+            gtsDescStartAndLength =
                 (m_gtsList[j].m_gtsDescStartSlot & 0x0F) | // GTS descriptor bits 16-19
                 (m_gtsList[j].m_gtsDescLength & 0xF0);     // GTS descriptor bits 20-23
 
-            i.WriteU8(gtsDescStartAndLenght);
+            i.WriteU8(gtsDescStartAndLength);
         }
     }
     return i;
@@ -296,14 +296,14 @@
         uint8_t gtsDirectionField = i.ReadU8();
         SetGtsDirectionField(gtsDirectionField);
 
-        uint8_t gtsDescStartAndLenght;
+        uint8_t gtsDescStartAndLength;
         for (int j = 0; j < m_gtsSpecDescCount; j++)
         {
             ReadFrom(i, m_gtsList[j].m_gtsDescDevShortAddr);
 
-            gtsDescStartAndLenght = i.ReadU8();
-            m_gtsList[j].m_gtsDescStartSlot = (gtsDescStartAndLenght) & (0x0F);
-            m_gtsList[j].m_gtsDescLength = (gtsDescStartAndLenght >> 4) & (0x0F);
+            gtsDescStartAndLength = i.ReadU8();
+            m_gtsList[j].m_gtsDescStartSlot = (gtsDescStartAndLength) & (0x0F);
+            m_gtsList[j].m_gtsDescLength = (gtsDescStartAndLength >> 4) & (0x0F);
         }
     }
     return i;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-mac.cc ns-3.39/src/lr-wpan/model/lr-wpan-mac.cc
--- ns-3.38/src/lr-wpan/model/lr-wpan-mac.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-mac.cc	2023-07-05 14:23:47.869427400 -0700
@@ -39,7 +39,8 @@
 #include <ns3/uinteger.h>
 
 #undef NS_LOG_APPEND_CONTEXT
-#define NS_LOG_APPEND_CONTEXT std::clog << "[address " << m_shortAddress << "] ";
+#define NS_LOG_APPEND_CONTEXT                                                                      \
+    std::clog << "[address " << m_shortAddress << " | " << m_selfExt << "] ";
 
 namespace ns3
 {
@@ -188,6 +189,7 @@
     m_macSIFSPeriod = 12;
 
     m_panCoor = false;
+    m_coor = false;
     m_macBeaconOrder = 15;
     m_macSuperframeOrder = 15;
     m_macTransactionPersistenceTime = 500; // 0x01F5
@@ -276,7 +278,15 @@
     m_mlmeAssociateConfirmCallback = MakeNullCallback<void, MlmeAssociateConfirmParams>();
     m_mlmeAssociateIndicationCallback = MakeNullCallback<void, MlmeAssociateIndicationParams>();
     m_mlmeCommStatusIndicationCallback = MakeNullCallback<void, MlmeCommStatusIndicationParams>();
+    m_mlmeOrphanIndicationCallback = MakeNullCallback<void, MlmeOrphanIndicationParams>();
 
+    m_panDescriptorList.clear();
+    m_energyDetectList.clear();
+    m_unscannedChannels.clear();
+
+    m_scanEvent.Cancel();
+    m_scanEnergyEvent.Cancel();
+    m_scanOrphanEvent.Cancel();
     m_beaconEvent.Cancel();
 
     Object::DoDispose();
@@ -489,13 +499,12 @@
         // A DEVICE must be tracking beacons (MLME-SYNC.request is running) before attempting
         // request data from the coordinator.
 
-        // TODO: Check if the current device is coordinator (not just pan coordinator)
         //  Indirect Transmission can only be done by PAN coordinator or coordinators.
-        NS_ASSERT(m_panCoor);
+        NS_ASSERT(m_coor);
         p->AddHeader(macHdr);
 
         LrWpanMacTrailer macTrailer;
-        // Calculate FCS if the global attribute ChecksumEnable is set.
+        // Calculate FCS if the global attribute ChecksumEnabled is set.
         if (Node::ChecksumEnabled())
         {
             macTrailer.EnableFcs(true);
@@ -520,7 +529,7 @@
         p->AddHeader(macHdr);
 
         LrWpanMacTrailer macTrailer;
-        // Calculate FCS if the global attribute ChecksumEnable is set.
+        // Calculate FCS if the global attribute ChecksumEnabled is set.
         if (Node::ChecksumEnabled())
         {
             macTrailer.EnableFcs(true);
@@ -570,9 +579,9 @@
     m_pendPrimitive = MLME_START_REQ;
     m_startParams = params;
 
-    LrWpanPhyPibAttributes pibAttr;
-    pibAttr.phyCurrentPage = m_startParams.m_logChPage;
-    m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentPage, &pibAttr);
+    Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+    pibAttr->phyCurrentPage = m_startParams.m_logChPage;
+    m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentPage, pibAttr);
 }
 
 void
@@ -584,7 +593,7 @@
     confirmParams.m_scanType = params.m_scanType;
     confirmParams.m_chPage = params.m_chPage;
 
-    if (m_scanEvent.IsRunning() || m_scanEnergyEvent.IsRunning())
+    if ((m_scanEvent.IsRunning() || m_scanEnergyEvent.IsRunning()) || m_scanOrphanEvent.IsRunning())
     {
         if (!m_mlmeScanConfirmCallback.IsNull())
         {
@@ -595,7 +604,7 @@
         return;
     }
 
-    if (params.m_scanDuration > 14 || params.m_scanType > MLMESCAN_PASSIVE)
+    if (params.m_scanDuration > 14 || params.m_scanType > MLMESCAN_ORPHAN)
     {
         if (!m_mlmeScanConfirmCallback.IsNull())
         {
@@ -611,6 +620,7 @@
 
     m_panDescriptorList.clear();
     m_energyDetectList.clear();
+    m_unscannedChannels.clear();
 
     // TODO: stop beacon transmission
 
@@ -629,9 +639,9 @@
     m_scanParams = params;
     m_pendPrimitive = MLME_SCAN_REQ;
 
-    LrWpanPhyPibAttributes pibAttr;
-    pibAttr.phyCurrentPage = params.m_chPage;
-    m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentPage, &pibAttr);
+    Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+    pibAttr->phyCurrentPage = params.m_chPage;
+    m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentPage, pibAttr);
 }
 
 void
@@ -683,9 +693,9 @@
     }
     else
     {
-        LrWpanPhyPibAttributes pibAttr;
-        pibAttr.phyCurrentPage = params.m_chPage;
-        m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentPage, &pibAttr);
+        Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+        pibAttr->phyCurrentPage = params.m_chPage;
+        m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentPage, pibAttr);
     }
 }
 
@@ -758,7 +768,7 @@
     commandPacket->AddHeader(macPayload);
     commandPacket->AddHeader(macHdr);
 
-    // Calculate FCS if the global attribute ChecksumEnable is set.
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
     if (Node::ChecksumEnabled())
     {
         macTrailer.EnableFcs(true);
@@ -772,6 +782,66 @@
 }
 
 void
+LrWpanMac::MlmeOrphanResponse(MlmeOrphanResponseParams params)
+{
+    NS_LOG_FUNCTION(this);
+    // Mac header Coordinator realigment Command
+    // See 802.15.4-2011 (Section 6.2.7.2)
+    LrWpanMacHeader macHdr(LrWpanMacHeader::LRWPAN_MAC_COMMAND, m_macDsn.GetValue());
+    m_macDsn++;
+    LrWpanMacTrailer macTrailer;
+    Ptr<Packet> commandPacket = Create<Packet>();
+    macHdr.SetPanIdComp();
+    macHdr.SetDstAddrMode(LrWpanMacHeader::EXTADDR);
+    macHdr.SetDstAddrFields(0xffff, params.m_orphanAddr);
+
+    macHdr.SetSrcAddrMode(LrWpanMacHeader::EXTADDR);
+    macHdr.SetSrcAddrFields(m_macPanId, GetExtendedAddress());
+    macHdr.SetSrcAddrFields(m_macPanId, Mac16Address("FF:FF"));
+
+    macHdr.SetFrameVer(0x01);
+    macHdr.SetSecDisable();
+    macHdr.SetAckReq();
+
+    CommandPayloadHeader macPayload(CommandPayloadHeader::COOR_REALIGN);
+    macPayload.SetPanId(m_macPanId);
+    macPayload.SetCoordShortAddr(GetShortAddress());
+    macPayload.SetChannel(m_phy->GetCurrentChannelNum());
+    macPayload.SetPage(m_phy->GetCurrentPage());
+
+    if (params.m_assocMember)
+    {
+        // The orphan device was associated with the coordinator
+
+        // Either FF:FE for extended address mode
+        // or the short address assigned by the coord.
+        macPayload.SetShortAddr(params.m_shortAddr);
+    }
+    else
+    {
+        // The orphan device was NOT associated with the coordinator
+        macPayload.SetShortAddr(Mac16Address("FF:FF"));
+    }
+
+    commandPacket->AddHeader(macPayload);
+    commandPacket->AddHeader(macHdr);
+
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
+    if (Node::ChecksumEnabled())
+    {
+        macTrailer.EnableFcs(true);
+        macTrailer.SetFcs(commandPacket);
+    }
+
+    commandPacket->AddTrailer(macTrailer);
+
+    Ptr<TxQueueElement> txQElement = Create<TxQueueElement>();
+    txQElement->txQPkt = commandPacket;
+    EnqueueTxQElement(txQElement);
+    CheckQueue();
+}
+
+void
 LrWpanMac::MlmeSyncRequest(MlmeSyncRequestParams params)
 {
     NS_LOG_FUNCTION(this);
@@ -779,9 +849,9 @@
 
     uint64_t symbolRate = (uint64_t)m_phy->GetDataOrSymbolRate(false); // symbols per second
     // change phy current logical channel
-    LrWpanPhyPibAttributes pibAttr;
-    pibAttr.phyCurrentChannel = params.m_logCh;
-    m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel, &pibAttr);
+    Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+    pibAttr->phyCurrentChannel = params.m_logCh;
+    m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel, pibAttr);
 
     // Enable Phy receiver
     m_phy->PlmeSetTRXStateRequest(IEEE_802_15_4_PHY_RX_ON);
@@ -830,16 +900,17 @@
 LrWpanMac::MlmeSetRequest(LrWpanMacPibAttributeIdentifier id, Ptr<LrWpanMacPibAttributes> attribute)
 {
     MlmeSetConfirmParams confirmParams;
+    confirmParams.m_status = MLMESET_SUCCESS;
+
     switch (id)
     {
     case macBeaconPayload:
-        if (attribute->macBeaconPayload->GetSize() > lrwpan::aMaxBeaconPayloadLenght)
+        if (attribute->macBeaconPayload->GetSize() > lrwpan::aMaxBeaconPayloadLength)
         {
             confirmParams.m_status = MLMESET_INVALID_PARAMETER;
         }
         else
         {
-            confirmParams.m_status = MLMESET_SUCCESS;
             m_macBeaconPayload = attribute->macBeaconPayload;
             m_macBeaconPayloadLength = attribute->macBeaconPayload->GetSize();
         }
@@ -847,6 +918,15 @@
     case macBeaconPayloadLength:
         confirmParams.m_status = MLMESET_INVALID_PARAMETER;
         break;
+    case macShortAddress:
+        m_shortAddress = attribute->macShortAddress;
+        break;
+    case macExtendedAddress:
+        confirmParams.m_status = MLMESET_READ_ONLY;
+        break;
+    case macPanId:
+        m_macPanId = macPanId;
+        break;
     default:
         // TODO: Add support for setting other attributes
         confirmParams.m_status = MLMESET_UNSUPPORTED_ATTRIBUTE;
@@ -861,6 +941,40 @@
 }
 
 void
+LrWpanMac::MlmeGetRequest(LrWpanMacPibAttributeIdentifier id)
+{
+    LrWpanMlmeGetConfirmStatus status = MLMEGET_SUCCESS;
+    Ptr<LrWpanMacPibAttributes> attributes = Create<LrWpanMacPibAttributes>();
+
+    switch (id)
+    {
+    case macBeaconPayload:
+        attributes->macBeaconPayload = m_macBeaconPayload;
+        break;
+    case macBeaconPayloadLength:
+        attributes->macBeaconPayloadLength = m_macBeaconPayloadLength;
+        break;
+    case macShortAddress:
+        attributes->macShortAddress = m_shortAddress;
+        break;
+    case macExtendedAddress:
+        attributes->macExtendedAddress = m_selfExt;
+        break;
+    case macPanId:
+        attributes->macPanId = m_macPanId;
+        break;
+    default:
+        status = MLMEGET_UNSUPPORTED_ATTRIBUTE;
+        break;
+    }
+
+    if (!m_mlmeGetConfirmCallback.IsNull())
+    {
+        m_mlmeGetConfirmCallback(status, id, attributes);
+    }
+}
+
+void
 LrWpanMac::SendOneBeacon()
 {
     NS_LOG_FUNCTION(this);
@@ -906,7 +1020,7 @@
     beaconPacket->AddHeader(macPayload);
     beaconPacket->AddHeader(macHdr);
 
-    // Calculate FCS if the global attribute ChecksumEnable is set.
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
     if (Node::ChecksumEnabled())
     {
         macTrailer.EnableFcs(true);
@@ -944,8 +1058,8 @@
     macHdr.SetDstAddrMode(LrWpanMacHeader::SHORTADDR);
     macHdr.SetSrcAddrMode(LrWpanMacHeader::NOADDR);
 
-    macHdr.SetDstAddrFields(0xFFFF,
-                            Mac16Address("FF:FF")); // Not associated PAN, broadcast dst address
+    // Not associated PAN, broadcast dst address
+    macHdr.SetDstAddrFields(0xFFFF, Mac16Address("FF:FF"));
 
     macHdr.SetSecDisable();
     macHdr.SetNoAckReq();
@@ -956,7 +1070,48 @@
     commandPacket->AddHeader(macPayload);
     commandPacket->AddHeader(macHdr);
 
-    // Calculate FCS if the global attribute ChecksumEnable is set.
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
+    if (Node::ChecksumEnabled())
+    {
+        macTrailer.EnableFcs(true);
+        macTrailer.SetFcs(commandPacket);
+    }
+
+    commandPacket->AddTrailer(macTrailer);
+
+    Ptr<TxQueueElement> txQElement = Create<TxQueueElement>();
+    txQElement->txQPkt = commandPacket;
+    EnqueueTxQElement(txQElement);
+    CheckQueue();
+}
+
+void
+LrWpanMac::SendOrphanNotificationCommand()
+{
+    LrWpanMacHeader macHdr(LrWpanMacHeader::LRWPAN_MAC_COMMAND, m_macDsn.GetValue());
+    m_macDsn++;
+    LrWpanMacTrailer macTrailer;
+    Ptr<Packet> commandPacket = Create<Packet>();
+
+    // See IEEE 802.15.4-2011 (5.3.6)
+    macHdr.SetPanIdComp();
+
+    macHdr.SetSrcAddrMode(LrWpanMacHeader::EXTADDR);
+    macHdr.SetSrcAddrFields(0xFFFF, GetExtendedAddress());
+
+    macHdr.SetDstAddrMode(LrWpanMacHeader::SHORTADDR);
+    macHdr.SetDstAddrFields(0xFFFF, Mac16Address("FF:FF"));
+
+    macHdr.SetSecDisable();
+    macHdr.SetNoAckReq();
+
+    CommandPayloadHeader macPayload;
+    macPayload.SetCommandFrameType(CommandPayloadHeader::ORPHAN_NOTIF);
+
+    commandPacket->AddHeader(macPayload);
+    commandPacket->AddHeader(macHdr);
+
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
     if (Node::ChecksumEnabled())
     {
         macTrailer.EnableFcs(true);
@@ -1005,7 +1160,7 @@
     commandPacket->AddHeader(macPayload);
     commandPacket->AddHeader(macHdr);
 
-    // Calculate FCS if the global attribute ChecksumEnable is set.
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
     if (Node::ChecksumEnabled())
     {
         macTrailer.EnableFcs(true);
@@ -1063,7 +1218,7 @@
     commandPacket->AddHeader(macPayload);
     commandPacket->AddHeader(macHdr);
 
-    // Calculate FCS if the global attribute ChecksumEnable is set.
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
     if (Node::ChecksumEnabled())
     {
         macTrailer.EnableFcs(true);
@@ -1139,9 +1294,11 @@
         if (m_startParams.m_panCoor)
         {
             m_panCoor = true;
-            m_macPanId = m_startParams.m_PanId;
         }
 
+        m_coor = true;
+        m_macPanId = m_startParams.m_PanId;
+
         NS_ASSERT(m_startParams.m_PanId != 0xffff);
 
         m_macBeaconOrder = m_startParams.m_bcnOrd;
@@ -1160,6 +1317,7 @@
             m_incCfpEvent.Cancel();
             m_trackingEvent.Cancel();
             m_scanEvent.Cancel();
+            m_scanOrphanEvent.Cancel();
             m_scanEnergyEvent.Cancel();
 
             m_csmaCa->SetUnSlottedCsmaCa();
@@ -1221,36 +1379,74 @@
     if (channelFound)
     {
         // Switch to the next channel in the list and restart scan
-        LrWpanPhyPibAttributes pibAttr;
-        pibAttr.phyCurrentChannel = m_channelScanIndex;
-        m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel, &pibAttr);
+        Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+        pibAttr->phyCurrentChannel = m_channelScanIndex;
+        m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel, pibAttr);
     }
     else
     {
-        // All scans on the channel list completed
-        // Return to the MAC values previous to start of the first scan.
+        // All channels in the list scan completed.
+        // Return variables to the values before the scan and return the status to the next layer.
         m_macPanId = m_macPanIdScan;
         m_macPanIdScan = 0;
 
         // TODO: restart beacon transmissions that were active before the beginning of the scan
         // (i.e when a coordinator perform a scan and it was already transmitting beacons)
-
-        // All channels scanned, report success
         MlmeScanConfirmParams confirmParams;
-        confirmParams.m_status = MLMESCAN_SUCCESS;
         confirmParams.m_chPage = m_scanParams.m_chPage;
         confirmParams.m_scanType = m_scanParams.m_scanType;
         confirmParams.m_energyDetList = {};
-        confirmParams.m_panDescList = m_panDescriptorList;
+        confirmParams.m_unscannedCh = m_unscannedChannels;
+        confirmParams.m_resultListSize = m_panDescriptorList.size();
 
-        if (!m_mlmeScanConfirmCallback.IsNull())
+        // See IEEE 802.15.4-2011, Table 31 (panDescriptorList value on macAutoRequest)
+        // and Section 6.2.10.2
+        switch (confirmParams.m_scanType)
         {
-            m_mlmeScanConfirmCallback(confirmParams);
+        case MLMESCAN_PASSIVE:
+            if (m_macAutoRequest)
+            {
+                confirmParams.m_panDescList = m_panDescriptorList;
+            }
+            confirmParams.m_status = MLMESCAN_SUCCESS;
+            break;
+        case MLMESCAN_ACTIVE:
+            if (m_panDescriptorList.empty())
+            {
+                confirmParams.m_status = MLMESCAN_NO_BEACON;
+            }
+            else
+            {
+                if (m_macAutoRequest)
+                {
+                    confirmParams.m_panDescList = m_panDescriptorList;
+                }
+                confirmParams.m_status = MLMESCAN_SUCCESS;
+            }
+            break;
+        case MLMESCAN_ORPHAN:
+            confirmParams.m_panDescList = {};
+            confirmParams.m_status = MLMESCAN_NO_BEACON;
+            confirmParams.m_resultListSize = 0;
+            // The device lost track of the coordinator and was unable
+            // to locate it, disassociate from the network.
+            m_macPanId = 0xffff;
+            m_shortAddress = Mac16Address("FF:FF");
+            m_macCoordShortAddress = Mac16Address("ff:ff");
+            m_macCoordExtendedAddress = Mac64Address("ff:ff:ff:ff:ff:ff:ff:ed");
+            break;
+        default:
+            NS_LOG_ERROR(this << " Invalid scan type");
         }
 
         m_pendPrimitive = MLME_NONE;
         m_channelScanIndex = 0;
         m_scanParams = {};
+
+        if (!m_mlmeScanConfirmCallback.IsNull())
+        {
+            m_mlmeScanConfirmCallback(confirmParams);
+        }
     }
 }
 
@@ -1259,7 +1455,7 @@
 {
     NS_LOG_FUNCTION(this);
     // Add the results of channel energy scan to the detectList
-    m_energyDetectList.push_back(m_maxEnergyLevel);
+    m_energyDetectList.emplace_back(m_maxEnergyLevel);
     m_maxEnergyLevel = 0;
 
     m_channelScanIndex++;
@@ -1278,9 +1474,9 @@
     if (channelFound)
     {
         // switch to the next channel in the list and restart scan
-        LrWpanPhyPibAttributes pibAttr;
-        pibAttr.phyCurrentChannel = m_channelScanIndex;
-        m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel, &pibAttr);
+        Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+        pibAttr->phyCurrentChannel = m_channelScanIndex;
+        m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel, pibAttr);
     }
     else
     {
@@ -1293,18 +1489,21 @@
         // (i.e when a coordinator perform a scan and it was already transmitting beacons)
 
         // All channels scanned, report success
+        MlmeScanConfirmParams confirmParams;
+        confirmParams.m_status = MLMESCAN_SUCCESS;
+        confirmParams.m_chPage = m_phy->GetCurrentPage();
+        confirmParams.m_scanType = m_scanParams.m_scanType;
+        confirmParams.m_energyDetList = m_energyDetectList;
+        confirmParams.m_resultListSize = m_energyDetectList.size();
+
+        m_pendPrimitive = MLME_NONE;
+        m_channelScanIndex = 0;
+        m_scanParams = {};
+
         if (!m_mlmeScanConfirmCallback.IsNull())
         {
-            MlmeScanConfirmParams confirmParams;
-            confirmParams.m_status = MLMESCAN_SUCCESS;
-            confirmParams.m_chPage = m_phy->GetCurrentPage();
-            confirmParams.m_scanType = m_scanParams.m_scanType;
-            confirmParams.m_energyDetList = m_energyDetectList;
             m_mlmeScanConfirmCallback(confirmParams);
         }
-        m_pendPrimitive = MLME_NONE;
-        m_channelScanIndex = 0;
-        m_scanParams = {};
     }
 }
 
@@ -1495,9 +1694,7 @@
     // Pull a packet from the queue and start sending if we are not already sending.
     if (m_lrWpanMacState == MAC_IDLE && !m_txQueue.empty() && !m_setMacState.IsRunning())
     {
-        // TODO: this should check if the node is a coordinator and using the outcoming superframe
-        // not just the PAN coordinator
-        if (m_csmaCa->IsUnSlottedCsmaCa() || (m_outSuperframeStatus == CAP && m_panCoor) ||
+        if (m_csmaCa->IsUnSlottedCsmaCa() || (m_outSuperframeStatus == CAP && m_coor) ||
             m_incSuperframeStatus == CAP)
         {
             // check MAC is not in a IFS
@@ -1597,6 +1794,12 @@
 }
 
 void
+LrWpanMac::SetMlmeOrphanIndicationCallback(MlmeOrphanIndicationCallback c)
+{
+    m_mlmeOrphanIndicationCallback = c;
+}
+
+void
 LrWpanMac::SetMcpsDataConfirmCallback(McpsDataConfirmCallback c)
 {
     m_mcpsDataConfirmCallback = c;
@@ -1645,6 +1848,12 @@
 }
 
 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 ||
@@ -1754,11 +1963,11 @@
 
             if (acceptFrame && (receivedMacHdr.GetDstAddrMode() > 1))
             {
-                // Accept frame if:
+                // Accept frame if one of the following is true:
 
                 // 1) Have the same macPanId
-                // 2) Or is Message to all PANs
-                // 3) Or Is a beacon and the macPanId is not present (bootstrap)
+                // 2) Is Message to all PANs
+                // 3) Is a beacon or command frame and the macPanId is not present (bootstrap)
                 acceptFrame = ((receivedMacHdr.GetDstPanId() == m_macPanId ||
                                 receivedMacHdr.GetDstPanId() == 0xffff) ||
                                (m_macPanId == 0xffff && receivedMacHdr.IsBeacon())) ||
@@ -1767,10 +1976,17 @@
 
             if (acceptFrame && (receivedMacHdr.GetShortDstAddr() == Mac16Address("FF:FF")))
             {
-                // A broadcast message (e.g. beacons) should not be received by the device who
-                // issues it.
-                acceptFrame = (receivedMacHdr.GetShortSrcAddr() != GetShortAddress());
                 // TODO: shouldn't this be filtered by the PHY?
+                // A broadcast message (e.g. beacons, orphan notifications) should not be received
+                // by the device who issues it.
+                if (receivedMacHdr.GetSrcAddrMode() == EXT_ADDR)
+                {
+                    acceptFrame = (receivedMacHdr.GetExtSrcAddr() != GetExtendedAddress());
+                }
+                else
+                {
+                    acceptFrame = (receivedMacHdr.GetShortSrcAddr() != GetShortAddress());
+                }
             }
 
             if (acceptFrame && (receivedMacHdr.GetDstAddrMode() == SHORT_ADDR))
@@ -1783,16 +1999,9 @@
                 else if (receivedMacHdr.GetShortDstAddr().IsBroadcast() ||
                          receivedMacHdr.GetShortDstAddr().IsMulticast())
                 {
-                    // broadcast or multicast
-                    if (receivedMacHdr.IsAckReq())
-                    {
-                        // discard broadcast/multicast with the ACK bit set
-                        acceptFrame = false;
-                    }
-                    else
-                    {
-                        acceptFrame = true;
-                    }
+                    // Broadcast or multicast.
+                    // Discard broadcast/multicast with the ACK bit set.
+                    acceptFrame = !receivedMacHdr.IsAckReq();
                 }
                 else
                 {
@@ -1805,15 +2014,23 @@
                 acceptFrame = (receivedMacHdr.GetExtDstAddr() == m_selfExt);
             }
 
-            // When PASSIVE or ACTIVE scan is running, reject any frames other than BEACON frames
-            if (acceptFrame && (!receivedMacHdr.IsBeacon() && m_scanEvent.IsRunning()))
+            if (acceptFrame && m_scanEvent.IsRunning())
             {
-                acceptFrame = false;
+                if (!receivedMacHdr.IsBeacon())
+                {
+                    acceptFrame = false;
+                }
             }
-
-            // Energy Scan is running, reject any frames
-            if (m_scanEnergyEvent.IsRunning())
+            else if (acceptFrame && m_scanOrphanEvent.IsRunning())
+            {
+                if (!receivedMacHdr.IsCommand())
+                {
+                    acceptFrame = false;
+                }
+            }
+            else if (m_scanEnergyEvent.IsRunning())
             {
+                // Reject any frames if energy scan is running
                 acceptFrame = false;
             }
 
@@ -1828,7 +2045,7 @@
 
                 if (receivedMacPayload.GetCommandFrameType() ==
                         CommandPayloadHeader::ASSOCIATION_REQ &&
-                    !(m_macAssociationPermit == true && m_panCoor == true))
+                    !(m_macAssociationPermit && m_coor))
                 {
                     acceptFrame = false;
                 }
@@ -2073,7 +2290,7 @@
 
                             if (!descriptorExists)
                             {
-                                m_panDescriptorList.push_back(panDescriptor);
+                                m_panDescriptorList.emplace_back(panDescriptor);
                             }
                             return;
                         }
@@ -2130,15 +2347,15 @@
                 }
                 else if (receivedMacHdr.IsCommand())
                 {
-                    // Handle the reception of frame commands that do not require ACK (i.e. Beacon
-                    // Request Command)
+                    // Handle the reception of frame commands that do not require ACK
+                    // (i.e. Beacon Request, Orphan notification, Coordinator Realigment)
                     CommandPayloadHeader receivedMacPayload;
                     p->PeekHeader(receivedMacPayload);
-                    if (receivedMacPayload.GetCommandFrameType() ==
-                        CommandPayloadHeader::BEACON_REQ)
+
+                    switch (receivedMacPayload.GetCommandFrameType())
                     {
-                        // TODO: check that node is any coordinator not just pan coordinator
-                        if (m_csmaCa->IsUnSlottedCsmaCa() && m_panCoor)
+                    case CommandPayloadHeader::BEACON_REQ:
+                        if (m_csmaCa->IsUnSlottedCsmaCa() && m_coor)
                         {
                             SendOneBeacon();
                         }
@@ -2146,6 +2363,51 @@
                         {
                             m_macRxDropTrace(originalPkt);
                         }
+                        break;
+                    case CommandPayloadHeader::ORPHAN_NOTIF:
+                        if (!m_mlmeOrphanIndicationCallback.IsNull())
+                        {
+                            if (m_coor)
+                            {
+                                MlmeOrphanIndicationParams orphanParams;
+                                orphanParams.m_orphanAddr = receivedMacHdr.GetExtSrcAddr();
+                                m_mlmeOrphanIndicationCallback(orphanParams);
+                            }
+                        }
+                        break;
+                    case CommandPayloadHeader::COOR_REALIGN:
+                        if (m_scanOrphanEvent.IsRunning())
+                        {
+                            // Coordinator located, no need to keep scanning other channels
+                            m_scanOrphanEvent.Cancel();
+
+                            m_macPanIdScan = 0;
+                            m_pendPrimitive = MLME_NONE;
+                            m_channelScanIndex = 0;
+
+                            // Update the device information with the received information
+                            // from the Coordinator Realigment command.
+                            m_macPanId = receivedMacPayload.GetPanId();
+                            m_shortAddress = receivedMacPayload.GetShortAddr();
+                            m_macCoordExtendedAddress = receivedMacHdr.GetExtSrcAddr();
+                            m_macCoordShortAddress = receivedMacPayload.GetCoordShortAddr();
+
+                            if (!m_mlmeScanConfirmCallback.IsNull())
+                            {
+                                MlmeScanConfirmParams confirmParams;
+                                confirmParams.m_scanType = m_scanParams.m_scanType;
+                                confirmParams.m_chPage = m_scanParams.m_chPage;
+                                confirmParams.m_status = MLMESCAN_SUCCESS;
+                                m_mlmeScanConfirmCallback(confirmParams);
+                            }
+                            m_scanParams = {};
+                        }
+                        // TODO: handle Coordinator realignment when not
+                        //       used during an orphan scan.
+                        break;
+                    default:
+                        m_macRxDropTrace(originalPkt);
+                        break;
                     }
                 }
                 else if (receivedMacHdr.IsData() && !m_mcpsDataIndicationCallback.IsNull())
@@ -2245,6 +2507,24 @@
                                 break;
                             }
 
+                            case CommandPayloadHeader::COOR_REALIGN: {
+                                // ACK of coordinator realigment commands is not specified in the
+                                // standard, in here, we assume they are required as in other
+                                // commands.
+                                if (!m_mlmeCommStatusIndicationCallback.IsNull())
+                                {
+                                    MlmeCommStatusIndicationParams commStatusParams;
+                                    commStatusParams.m_panId = m_macPanId;
+                                    commStatusParams.m_srcAddrMode = LrWpanMacHeader::EXTADDR;
+                                    commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
+                                    commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
+                                    commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
+                                    commStatusParams.m_status =
+                                        LrWpanMlmeCommStatus::MLMECOMMSTATUS_SUCCESS;
+                                    m_mlmeCommStatusIndicationCallback(commStatusParams);
+                                }
+                            }
+
                             default: {
                                 // TODO: add response to other request commands (e.g. Orphan)
                                 break;
@@ -2316,7 +2596,7 @@
     LrWpanMacTrailer macTrailer;
     Ptr<Packet> ackPacket = Create<Packet>(0);
     ackPacket->AddHeader(macHdr);
-    // Calculate FCS if the global attribute ChecksumEnable is set.
+    // Calculate FCS if the global attribute ChecksumEnabled is set.
     if (Node::ChecksumEnabled())
     {
         macTrailer.EnableFcs(true);
@@ -2834,8 +3114,38 @@
             }
             else if (macHdr.IsCommand())
             {
-                // We handle commands that do not require ACK (e.g. BeaconReq command)
-                // other command are handle by the previous if statement.
+                // We handle commands that do not require ACK
+                // (e.g. Coordinator realigment command in an orphan response)
+                // Other command with ACK required are handle by the previous if statement.
+
+                // Check the transmitted packet command type and issue the appropriate indication.
+                Ptr<Packet> txOriginalPkt = m_txPkt->Copy();
+                LrWpanMacHeader txMacHdr;
+                txOriginalPkt->RemoveHeader(txMacHdr);
+                CommandPayloadHeader txMacPayload;
+                txOriginalPkt->RemoveHeader(txMacPayload);
+
+                if (txMacPayload.GetCommandFrameType() == CommandPayloadHeader::COOR_REALIGN)
+                {
+                    if (!m_mlmeCommStatusIndicationCallback.IsNull())
+                    {
+                        MlmeCommStatusIndicationParams commStatusParams;
+                        commStatusParams.m_panId = m_macPanId;
+
+                        commStatusParams.m_srcAddrMode = macHdr.GetSrcAddrMode();
+                        commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
+                        commStatusParams.m_srcShortAddr = macHdr.GetShortSrcAddr();
+
+                        commStatusParams.m_dstAddrMode = macHdr.GetDstAddrMode();
+                        commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
+                        commStatusParams.m_dstShortAddr = macHdr.GetShortDstAddr();
+
+                        commStatusParams.m_status = LrWpanMlmeCommStatus::MLMECOMMSTATUS_SUCCESS;
+                        m_mlmeCommStatusIndicationCallback(commStatusParams);
+                    }
+                }
+
+                ifsWaitTime = Seconds(static_cast<double>(GetIfsSize()) / symbolRate);
                 RemoveFirstTxQElement();
             }
             else
@@ -3012,7 +3322,7 @@
 void
 LrWpanMac::PlmeGetAttributeConfirm(LrWpanPhyEnumeration status,
                                    LrWpanPibAttributeIdentifier id,
-                                   LrWpanPhyPibAttributes* attribute)
+                                   Ptr<LrWpanPhyPibAttributes> attribute)
 {
     NS_LOG_FUNCTION(this << status << id << attribute);
 }
@@ -3090,10 +3400,10 @@
 
             if (channelFound)
             {
-                LrWpanPhyPibAttributes pibAttr;
-                pibAttr.phyCurrentChannel = m_channelScanIndex;
+                Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+                pibAttr->phyCurrentChannel = m_channelScanIndex;
                 m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel,
-                                               &pibAttr);
+                                               pibAttr);
             }
         }
         else
@@ -3115,9 +3425,19 @@
         if (status == LrWpanPhyEnumeration::IEEE_802_15_4_PHY_SUCCESS)
         {
             uint64_t symbolRate = static_cast<uint64_t>(m_phy->GetDataOrSymbolRate(false));
-            uint64_t scanDuration = lrwpan::aBaseSuperframeDuration *
-                                    ((static_cast<uint32_t>(1 << m_scanParams.m_scanDuration)) + 1);
-            Time nextScanTime = Seconds(static_cast<double>(scanDuration / symbolRate));
+            Time nextScanTime;
+
+            if (m_scanParams.m_scanType == MLMESCAN_ORPHAN)
+            {
+                nextScanTime = Seconds(static_cast<double>(m_macResponseWaitTime) / symbolRate);
+            }
+            else
+            {
+                uint64_t scanDurationSym =
+                    lrwpan::aBaseSuperframeDuration * (pow(2, m_scanParams.m_scanDuration) + 1);
+
+                nextScanTime = Seconds(static_cast<double>(scanDurationSym) / symbolRate);
+            }
 
             switch (m_scanParams.m_scanType)
             {
@@ -3138,8 +3458,9 @@
                 m_phy->PlmeSetTRXStateRequest(IEEE_802_15_4_PHY_RX_ON);
                 break;
             case MLMESCAN_ORPHAN:
-                // TODO: add orphan scan support
-                NS_LOG_ERROR("Scan Type currently not supported");
+                m_scanOrphanEvent =
+                    Simulator::Schedule(nextScanTime, &LrWpanMac::EndChannelScan, this);
+                SendOrphanNotificationCommand();
                 break;
 
             default:
@@ -3174,10 +3495,10 @@
     {
         if (status == LrWpanPhyEnumeration::IEEE_802_15_4_PHY_SUCCESS)
         {
-            LrWpanPhyPibAttributes pibAttr;
-            pibAttr.phyCurrentChannel = m_startParams.m_logCh;
+            Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+            pibAttr->phyCurrentChannel = m_startParams.m_logCh;
             m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel,
-                                           &pibAttr);
+                                           pibAttr);
         }
         else
         {
@@ -3213,10 +3534,10 @@
     {
         if (status == LrWpanPhyEnumeration::IEEE_802_15_4_PHY_SUCCESS)
         {
-            LrWpanPhyPibAttributes pibAttr;
-            pibAttr.phyCurrentChannel = m_associateParams.m_chNum;
+            Ptr<LrWpanPhyPibAttributes> pibAttr = Create<LrWpanPhyPibAttributes>();
+            pibAttr->phyCurrentChannel = m_associateParams.m_chNum;
             m_phy->PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier::phyCurrentChannel,
-                                           &pibAttr);
+                                           pibAttr);
         }
         else
         {
@@ -3378,8 +3699,41 @@
                 }
                 break;
             }
+            case CommandPayloadHeader::COOR_REALIGN: {
+                if (!m_mlmeCommStatusIndicationCallback.IsNull())
+                {
+                    MlmeCommStatusIndicationParams commStatusParams;
+                    commStatusParams.m_panId = m_macPanId;
+                    commStatusParams.m_srcAddrMode = LrWpanMacHeader::EXTADDR;
+                    commStatusParams.m_srcExtAddr = macHdr.GetExtSrcAddr();
+                    commStatusParams.m_dstAddrMode = LrWpanMacHeader::EXTADDR;
+                    commStatusParams.m_dstExtAddr = macHdr.GetExtDstAddr();
+                    commStatusParams.m_status =
+                        LrWpanMlmeCommStatus::MLMECOMMSTATUS_CHANNEL_ACCESS_FAILURE;
+                    m_mlmeCommStatusIndicationCallback(commStatusParams);
+                }
+                break;
+            }
+            case CommandPayloadHeader::ORPHAN_NOTIF: {
+                if (m_scanOrphanEvent.IsRunning())
+                {
+                    m_unscannedChannels.emplace_back(m_phy->GetCurrentChannelNum());
+                }
+                // TODO: Handle orphan notification command during a
+                //       channel access failure when not is not scanning.
+                break;
+            }
+            case CommandPayloadHeader::BEACON_REQ: {
+                if (m_scanEvent.IsRunning())
+                {
+                    m_unscannedChannels.emplace_back(m_phy->GetCurrentChannelNum());
+                }
+                // TODO: Handle beacon request command during a
+                //       channel access failure when not scanning.
+                break;
+            }
             default: {
-                // TODO: Other commands(e.g. Orphan Request)
+                // TODO: Other commands(e.g. Disassociation notification, etc)
                 break;
             }
             }
@@ -3517,9 +3871,9 @@
     LrWpanMacHeader macHdr;
     m_txPkt->PeekHeader(macHdr);
 
-    if (m_panCoor)
+    if (m_coor)
     {
-        // The device is the PAN coordinator and the packet is not to itself
+        // The device is its coordinator and the packet is not to itself
         return false;
     }
     else if (m_macCoordShortAddress == macHdr.GetShortDstAddr() ||
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-mac.h ns-3.39/src/lr-wpan/model/lr-wpan-mac.h
--- ns-3.38/src/lr-wpan/model/lr-wpan-mac.h	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-mac.h	2023-07-05 14:23:47.869427400 -0700
@@ -326,13 +326,28 @@
 /**
  * \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
+    macBeaconPayloadLength = 1,
+    macShortAddress = 2,
+    macExtendedAddress = 3,
+    macPanId = 4,
+    unsupported = 255
     // TODO: complete other MAC pib attributes
 };
 
@@ -345,6 +360,9 @@
 {
     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
 };
 
@@ -455,11 +473,11 @@
  */
 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.
+    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
@@ -507,9 +525,13 @@
 {
     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{0x7FFFFFF};              //!< The channel numbers to be scanned.
-    uint8_t m_scanDuration{14}; //!< A value used to calculate the length of time to spend scanning
-                                //!< [aBaseSuperframeDuration * (2^m_scanDuration +)].
+    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.
 };
 
@@ -520,16 +542,17 @@
  */
 struct MlmeScanConfirmParams
 {
-    LrWpanMlmeScanConfirmStatus m_status{
-        MLMESCAN_INVALID_PARAMETER}; //!< The status of the scan 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).
-    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)
+    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).
 };
@@ -620,16 +643,38 @@
     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.
+    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
@@ -646,9 +691,9 @@
  */
 struct MlmeSetConfirmParams
 {
-    LrWpanMlmeSetConfirmStatus m_status{
-        MLMESET_UNSUPPORTED_ATTRIBUTE}; //!< The result of the request to write
-                                        //!< the PIB attribute.
+    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.
 };
 
@@ -754,6 +799,17 @@
 /**
  * \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.
@@ -763,6 +819,19 @@
 /**
  * \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
@@ -902,6 +971,16 @@
     void MlmeAssociateResponse(MlmeAssociateResponseParams params);
 
     /**
+     * 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);
+
+    /**
      * IEEE 802.15.4-2011, section 6.2.13.1
      * MLME-SYNC.request
      * Request to synchronize with the coordinator by acquiring and,
@@ -931,6 +1010,15 @@
     void MlmeSetRequest(LrWpanMacPibAttributeIdentifier id, Ptr<LrWpanMacPibAttributes> attribute);
 
     /**
+     * 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);
+
+    /**
      * Set the CSMA/CA implementation to be used by the MAC.
      *
      * \param csmaCa the CSMA/CA implementation
@@ -979,6 +1067,15 @@
     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.
@@ -1050,6 +1147,15 @@
      */
     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
 
     /**
@@ -1095,7 +1201,7 @@
      */
     void PlmeGetAttributeConfirm(LrWpanPhyEnumeration status,
                                  LrWpanPibAttributeIdentifier id,
-                                 LrWpanPhyPibAttributes* attribute);
+                                 Ptr<LrWpanPhyPibAttributes> attribute);
 
     /**
      * IEEE 802.15.4-2006 section 6.2.2.8
@@ -1363,6 +1469,11 @@
     bool m_panCoor;
 
     /**
+     * Indicates if the current device is a coordinator type
+     */
+    bool m_coor;
+
+    /**
      * Indication of the Interval used by the coordinator to transmit beacon frames
      * expressed in symbols.
      */
@@ -1564,6 +1675,13 @@
     void SendBeaconRequestCommand();
 
     /**
+     * Called to send a orphan notification command. This is used by an associated device that
+     * has lost synchronization with its coordinator.
+     * As described in IEEE 802.15.4-2011 (Section 5.3.6)
+     */
+    void SendOrphanNotificationCommand();
+
+    /**
      * Called to end a MLME-START.request after changing the page and channel number.
      */
     void EndStartRequest();
@@ -1903,6 +2021,13 @@
     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.
      */
@@ -1961,6 +2086,12 @@
     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.
@@ -2043,8 +2174,13 @@
     std::vector<uint8_t> m_energyDetectList;
 
     /**
+     * The list of unscanned channels during a scan operation.
+     */
+    std::vector<uint8_t> m_unscannedChannels;
+
+    /**
      * The parameters used during a MLME-SCAN.request. These parameters are stored here while
-     * PLME-SET operations (set channel page, set channel number) and multiple ed scans take place.
+     * PLME-SET (set channel page, set channel number) and other operations take place.
      */
     MlmeScanRequestParams m_scanParams;
 
@@ -2139,11 +2275,16 @@
     EventId m_trackingEvent;
 
     /**
-     * Scheduler event for the end of a channel scan.
+     * Scheduler event for the end of an ACTIVE or PASSIVE channel scan.
      */
     EventId m_scanEvent;
 
     /**
+     * Scheduler event for the end of an ORPHAN channel scan.
+     */
+    EventId m_scanOrphanEvent;
+
+    /**
      * Scheduler event for the end of a ED channel scan.
      */
     EventId m_scanEnergyEvent;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-mac-header.cc ns-3.39/src/lr-wpan/model/lr-wpan-mac-header.cc
--- ns-3.38/src/lr-wpan/model/lr-wpan-mac-header.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-mac-header.cc	2023-07-05 14:23:47.869427400 -0700
@@ -58,23 +58,19 @@
 {
 }
 
-enum LrWpanMacHeader::LrWpanMacType
+LrWpanMacHeader::LrWpanMacType
 LrWpanMacHeader::GetType() const
 {
     switch (m_fctrlFrmType)
     {
     case 0:
         return LRWPAN_MAC_BEACON;
-        break;
     case 1:
         return LRWPAN_MAC_DATA;
-        break;
     case 2:
         return LRWPAN_MAC_ACKNOWLEDGMENT;
-        break;
     case 3:
         return LRWPAN_MAC_COMMAND;
-        break;
     default:
         return LRWPAN_MAC_RESERVED;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc ns-3.39/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc
--- ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-mac-pl-headers.cc	2023-07-05 14:23:47.869427400 -0700
@@ -182,6 +182,7 @@
     case BEACON_REQ:
         break;
     case COOR_REALIGN:
+        size += 8;
         break;
     case GTS_REQ:
         break;
@@ -217,6 +218,11 @@
     case BEACON_REQ:
         break;
     case COOR_REALIGN:
+        i.WriteU16(m_panid);
+        WriteTo(i, m_coordShortAddr);
+        i.WriteU8(m_logCh);
+        WriteTo(i, m_shortAddr);
+        i.WriteU8(m_logChPage);
         break;
     case GTS_REQ:
         break;
@@ -251,6 +257,11 @@
     case BEACON_REQ:
         break;
     case COOR_REALIGN:
+        m_panid = i.ReadU16();
+        ReadFrom(i, m_coordShortAddr);
+        m_logCh = i.ReadU8();
+        ReadFrom(i, m_shortAddr);
+        m_logChPage = i.ReadU8();
         break;
     case GTS_REQ:
         break;
@@ -264,7 +275,7 @@
 void
 CommandPayloadHeader::Print(std::ostream& os) const
 {
-    os << "| MAC Command Frame ID | = " << (uint32_t)m_cmdFrameId;
+    os << "| MAC Command Frame ID | = " << static_cast<uint32_t>(m_cmdFrameId);
     switch (m_cmdFrameId)
     {
     case ASSOCIATION_REQ:
@@ -289,6 +300,11 @@
     case BEACON_REQ:
         break;
     case COOR_REALIGN:
+        os << "| PAN identifier| = " << m_panid
+           << "| PAN Coord Short address| = " << m_coordShortAddr
+           << "| Channel Num.| = " << static_cast<uint32_t>(m_logCh)
+           << "| Short address| = " << m_shortAddr
+           << "| Page Num.| = " << static_cast<uint32_t>(m_logChPage);
         break;
     case GTS_REQ:
         break;
@@ -310,6 +326,34 @@
     m_capabilityInfo = cap;
 }
 
+void
+CommandPayloadHeader::SetCoordShortAddr(Mac16Address addr)
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    m_coordShortAddr = addr;
+}
+
+void
+CommandPayloadHeader::SetChannel(uint8_t channel)
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    m_logCh = channel;
+}
+
+void
+CommandPayloadHeader::SetPage(uint8_t page)
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    m_logChPage = page;
+}
+
+void
+CommandPayloadHeader::SetPanId(uint16_t id)
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    m_panid = id;
+}
+
 CommandPayloadHeader::MacCommand
 CommandPayloadHeader::GetCommandFrameType() const
 {
@@ -317,31 +361,22 @@
     {
     case 0x01:
         return ASSOCIATION_REQ;
-        break;
     case 0x02:
         return ASSOCIATION_RESP;
-        break;
     case 0x03:
         return DISASSOCIATION_NOTIF;
-        break;
     case 0x04:
         return DATA_REQ;
-        break;
     case 0x05:
         return PANID_CONFLICT;
-        break;
     case 0x06:
         return ORPHAN_NOTIF;
-        break;
     case 0x07:
         return BEACON_REQ;
-        break;
     case 0x08:
         return COOR_REALIGN;
-        break;
     case 0x09:
         return GTS_REQ;
-        break;
     default:
         return CMD_RESERVED;
     }
@@ -350,7 +385,7 @@
 void
 CommandPayloadHeader::SetShortAddr(Mac16Address shortAddr)
 {
-    NS_ASSERT(m_cmdFrameId == ASSOCIATION_RESP);
+    NS_ASSERT(m_cmdFrameId == ASSOCIATION_RESP || m_cmdFrameId == COOR_REALIGN);
     m_shortAddr = shortAddr;
 }
 
@@ -364,7 +399,6 @@
 Mac16Address
 CommandPayloadHeader::GetShortAddr() const
 {
-    NS_ASSERT(m_cmdFrameId == ASSOCIATION_RESP);
     return m_shortAddr;
 }
 
@@ -382,4 +416,32 @@
     return m_capabilityInfo;
 }
 
+Mac16Address
+CommandPayloadHeader::GetCoordShortAddr() const
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    return m_coordShortAddr;
+}
+
+uint8_t
+CommandPayloadHeader::GetChannel() const
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    return m_logCh;
+}
+
+uint8_t
+CommandPayloadHeader::GetPage() const
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    return m_logChPage;
+}
+
+uint16_t
+CommandPayloadHeader::GetPanId() const
+{
+    NS_ASSERT(m_cmdFrameId == COOR_REALIGN);
+    return m_panid;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.h ns-3.39/src/lr-wpan/model/lr-wpan-mac-pl-headers.h
--- ns-3.38/src/lr-wpan/model/lr-wpan-mac-pl-headers.h	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-mac-pl-headers.h	2023-07-05 14:23:47.869427400 -0700
@@ -98,6 +98,9 @@
  * \ingroup lr-wpan
  * Implements the header for the MAC payload command frame according to
  * the IEEE 802.15.4-2011 Std.
+ * - Association Response Command (See 5.3.2.2.)
+ * - Coordinator Realigment Command (See 5.3.8.)
+ * - Association Request Command (See 5.3.1.)
  */
 class CommandPayloadHeader : public Header
 {
@@ -160,7 +163,28 @@
      */
     void SetCapabilityField(CapabilityField cap);
     /**
-     * Set the Short Address Assigned by the coordinator (Association Response Command).
+     *  Set the coordinator short address (16 bit address).
+     * \param addr The coordinator short address.
+     */
+    void SetCoordShortAddr(Mac16Address addr);
+    /**
+     *  Set the logical channel number.
+     * \param channel The channel number.
+     */
+    void SetChannel(uint8_t channel);
+    /**
+     *  Set the logical channel page number.
+     * \param page The page number.
+     */
+    void SetPage(uint8_t page);
+    /**
+     * Get the PAN identifier.
+     * \param id The PAN identifier.
+     */
+    void SetPanId(uint16_t id);
+    /**
+     * Set the Short Address Assigned by the coordinator
+     * (Association Response and Coordinator Realigment Commands).
      * \param shortAddr The short address assigned by the coordinator
      */
     void SetShortAddr(Mac16Address shortAddr);
@@ -170,7 +194,8 @@
      */
     void SetAssociationStatus(AssocStatus status);
     /**
-     * Get the Short address assigned by the coordinator (Association Response Command).
+     * Get the Short address assigned by the coordinator
+     * (Association Response and Coordinator Realigment commands).
      * \return The Mac16Address assigned by the coordinator
      */
     Mac16Address GetShortAddr() const;
@@ -185,19 +210,44 @@
      */
     MacCommand GetCommandFrameType() const;
     /**
-     * Get the Capability Information Field from the command payload header. (Association Request
-     * Command)
+     * Get the Capability Information Field from the command payload header.
+     * (Association Request Command)
      * \return The Capability Information Field
      */
     CapabilityField GetCapabilityField() const;
+    /**
+     *  Get the coordinator short address.
+     * \return The coordinator short address (16 bit address)
+     */
+    Mac16Address GetCoordShortAddr() const;
+    /**
+     *  Get the logical channel number.
+     * \return The channel number
+     */
+    uint8_t GetChannel() const;
+    /**
+     *  Get the logical channel page number.
+     * \return The page number.
+     */
+    uint8_t GetPage() const;
+    /**
+     * Get the PAN identifier.
+     * \return The PAN Identifier
+     */
+    uint16_t GetPanId() const;
 
   private:
-    MacCommand m_cmdFrameId; //!< The command Frame Identifier
-    CapabilityField
-        m_capabilityInfo;      //!< Capability Information Field (Association Request Command)
-    Mac16Address m_shortAddr;  //!< Contains the short address assigned by the coordinator
-                               //!< (Association Response Command) See IEEE 802.15.4-2011 5.3.2.2.
-    AssocStatus m_assocStatus; //!< Association Status (Association Response Command)
+    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)
+    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)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-net-device.h ns-3.39/src/lr-wpan/model/lr-wpan-net-device.h
--- ns-3.38/src/lr-wpan/model/lr-wpan-net-device.h	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-net-device.h	2023-07-05 14:23:47.869427400 -0700
@@ -250,7 +250,7 @@
      *
      * \param panId The PanID
      * \param shortAddr The Short MAC address
-     * \return a Pseudo-Mac48Adress
+     * \return a Pseudo-Mac48Address
      */
     Mac48Address BuildPseudoMacAddress(uint16_t panId, Mac16Address shortAddr) const;
 
@@ -309,8 +309,8 @@
     /**
      * How the pseudo MAC address is created.
      *
-     * According to \RFC{4944} the psudo-MAC is YYYY:0000:XXXX (with U/L bit set to local)
-     * According to \RFC{6282} the psudo-MAC is 0200:0000:XXXX
+     * According to \RFC{4944} the pseudo-MAC is YYYY:0000:XXXX (with U/L bit set to local)
+     * According to \RFC{6282} the pseudo-MAC is 0200:0000:XXXX
      */
     PseudoMacAddressMode_e m_pseudoMacMode;
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-phy.cc ns-3.39/src/lr-wpan/model/lr-wpan-phy.cc
--- ns-3.38/src/lr-wpan/model/lr-wpan-phy.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-phy.cc	2023-07-05 14:23:47.869427400 -0700
@@ -230,7 +230,7 @@
     m_plmeGetAttributeConfirmCallback = MakeNullCallback<void,
                                                          LrWpanPhyEnumeration,
                                                          LrWpanPibAttributeIdentifier,
-                                                         LrWpanPhyPibAttributes*>();
+                                                         Ptr<LrWpanPhyPibAttributes>>();
     m_plmeSetTRXStateConfirmCallback = MakeNullCallback<void, LrWpanPhyEnumeration>();
     m_plmeSetAttributeConfirmCallback =
         MakeNullCallback<void, LrWpanPhyEnumeration, LrWpanPibAttributeIdentifier>();
@@ -737,31 +737,31 @@
 LrWpanPhy::PlmeGetAttributeRequest(LrWpanPibAttributeIdentifier id)
 {
     NS_LOG_FUNCTION(this << id);
-    LrWpanPhyEnumeration status;
+    LrWpanPhyEnumeration status = IEEE_802_15_4_PHY_SUCCESS;
+    Ptr<LrWpanPhyPibAttributes> attributes = Create<LrWpanPhyPibAttributes>();
 
     switch (id)
     {
     case phyCurrentChannel:
-    case phyChannelsSupported:
-    case phyTransmitPower:
-    case phyCCAMode:
+        attributes->phyCurrentChannel = m_phyPIBAttributes.phyCurrentChannel;
+        break;
     case phyCurrentPage:
-    case phyMaxFrameDuration:
+        attributes->phyCurrentPage = m_phyPIBAttributes.phyCurrentPage;
+        break;
     case phySHRDuration:
-    case phySymbolsPerOctet: {
-        status = IEEE_802_15_4_PHY_SUCCESS;
+        attributes->phySHRDuration = GetPhySHRDuration();
         break;
-    }
-    default: {
+    case phySymbolsPerOctet:
+        attributes->phySymbolsPerOctet = GetPhySymbolsPerOctet();
+        break;
+    default:
         status = IEEE_802_15_4_PHY_UNSUPPORTED_ATTRIBUTE;
         break;
     }
-    }
+
     if (!m_plmeGetAttributeConfirmCallback.IsNull())
     {
-        LrWpanPhyPibAttributes retValue;
-        memcpy(&retValue, &m_phyPIBAttributes, sizeof(LrWpanPhyPibAttributes));
-        m_plmeGetAttributeConfirmCallback(status, id, &retValue);
+        m_plmeGetAttributeConfirmCallback(status, id, attributes);
     }
 }
 
@@ -996,7 +996,7 @@
 
 void
 LrWpanPhy::PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier id,
-                                   LrWpanPhyPibAttributes* attribute)
+                                   Ptr<LrWpanPhyPibAttributes> attribute)
 {
     NS_LOG_FUNCTION(this << id << attribute);
     NS_ASSERT(attribute);
@@ -1484,7 +1484,7 @@
     NS_ABORT_IF((m_trxState != IEEE_802_15_4_PHY_BUSY_TX) &&
                 (m_trxState != IEEE_802_15_4_PHY_TRX_OFF));
 
-    if (m_currentTxPacket.second == false)
+    if (!m_currentTxPacket.second)
     {
         NS_LOG_DEBUG("Packet successfully transmitted");
         m_phyTxEndTrace(m_currentTxPacket.first);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/model/lr-wpan-phy.h ns-3.39/src/lr-wpan/model/lr-wpan-phy.h
--- ns-3.38/src/lr-wpan/model/lr-wpan-phy.h	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/model/lr-wpan-phy.h	2023-07-05 14:23:47.869427400 -0700
@@ -156,7 +156,7 @@
  *
  * IEEE802.15.4-2006 PHY PIB Attributes Table 23 in section 6.4.2
  */
-struct LrWpanPhyPibAttributes
+struct LrWpanPhyPibAttributes : public SimpleRefCount<LrWpanPhyPibAttributes>
 {
     uint8_t phyCurrentChannel;         //!< The RF channel to use
     uint32_t phyChannelsSupported[32]; //!< BitField representing the available channels supported
@@ -218,7 +218,10 @@
  * @param id the identifier of attribute
  * @param attribute the pointer to attribute struct
  */
-typedef Callback<void, LrWpanPhyEnumeration, LrWpanPibAttributeIdentifier, LrWpanPhyPibAttributes*>
+typedef Callback<void,
+                 LrWpanPhyEnumeration,
+                 LrWpanPibAttributeIdentifier,
+                 Ptr<LrWpanPhyPibAttributes>>
     PlmeGetAttributeConfirmCallback;
 
 /**
@@ -395,7 +398,7 @@
      * \param attribute the attribute value
      */
     void PlmeSetAttributeRequest(LrWpanPibAttributeIdentifier id,
-                                 LrWpanPhyPibAttributes* attribute);
+                                 Ptr<LrWpanPhyPibAttributes> attribute);
 
     /**
      * set the callback for the end of a RX, as part of the
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/test/examples-to-run.py ns-3.39/src/lr-wpan/test/examples-to-run.py
--- ns-3.38/src/lr-wpan/test/examples-to-run.py	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/test/examples-to-run.py	2023-07-05 14:23:47.869427400 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lr-wpan/test/lr-wpan-mac-test.cc ns-3.39/src/lr-wpan/test/lr-wpan-mac-test.cc
--- ns-3.38/src/lr-wpan/test/lr-wpan-mac-test.cc	2023-03-16 20:54:23.632868500 -0700
+++ ns-3.39/src/lr-wpan/test/lr-wpan-mac-test.cc	2023-07-05 14:23:47.869427400 -0700
@@ -488,27 +488,216 @@
     NS_TEST_EXPECT_MSG_EQ(m_panDescriptorList.size(),
                           2,
                           "Error, Beacons not received or PAN descriptors not found");
-    NS_TEST_ASSERT_MSG_LT(m_panDescriptorList[0].m_linkQuality,
-                          255,
-                          "Error, Coordinator 1 (PAN 5) LQI value should be less than 255.");
-    NS_TEST_ASSERT_MSG_LT(m_panDescriptorList[1].m_linkQuality,
-                          255,
-                          "Error, Coordinator 2 (PAN 7) LQI value should be less than 255.");
-    NS_TEST_ASSERT_MSG_GT(m_panDescriptorList[0].m_linkQuality,
-                          0,
-                          "Error, Coordinator 1 (PAN 5) LQI value should be greater than 0.");
-    NS_TEST_ASSERT_MSG_GT(m_panDescriptorList[1].m_linkQuality,
-                          0,
-                          "Error, Coordinator 2 (PAN 7) LQI value should be greater than 0.");
-
-    NS_TEST_ASSERT_MSG_LT(
-        m_panDescriptorList[1].m_linkQuality,
-        m_panDescriptorList[0].m_linkQuality,
-        "Error, Coordinator 2 (PAN 7) LQI value should be less than Coordinator 1 (PAN 5).");
-
-    NS_TEST_EXPECT_MSG_EQ(g_beaconPayloadSize,
-                          25,
-                          "Error, Beacon Payload not received or incorrect size (25 bytes)");
+
+    if (m_panDescriptorList.size() == 2)
+    {
+        NS_TEST_ASSERT_MSG_LT(m_panDescriptorList[0].m_linkQuality,
+                              255,
+                              "Error, Coordinator 1 (PAN 5) LQI value should be less than 255.");
+        NS_TEST_ASSERT_MSG_LT(m_panDescriptorList[1].m_linkQuality,
+                              255,
+                              "Error, Coordinator 2 (PAN 7) LQI value should be less than 255.");
+        NS_TEST_ASSERT_MSG_GT(m_panDescriptorList[0].m_linkQuality,
+                              0,
+                              "Error, Coordinator 1 (PAN 5) LQI value should be greater than 0.");
+        NS_TEST_ASSERT_MSG_GT(m_panDescriptorList[1].m_linkQuality,
+                              0,
+                              "Error, Coordinator 2 (PAN 7) LQI value should be greater than 0.");
+
+        NS_TEST_ASSERT_MSG_LT(m_panDescriptorList[1].m_linkQuality,
+                              m_panDescriptorList[0].m_linkQuality,
+                              "Error, Coordinator 2 (PAN 7) LQI value should"
+                              " be less than Coordinator 1 (PAN 5).");
+
+        NS_TEST_EXPECT_MSG_EQ(g_beaconPayloadSize,
+                              25,
+                              "Error, Beacon Payload not received or incorrect size (25 bytes)");
+    }
+
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup lr-wpan-test
+ * \ingroup tests
+ *
+ * \brief Test MAC Orphan Scan Coordinator Realignment command reception and its values.
+ */
+class TestOrphanScan : public TestCase
+{
+  public:
+    TestOrphanScan();
+    ~TestOrphanScan() override;
+
+  private:
+    /**
+     * Function called in response to a MAC scan request.
+     *
+     * \param params MLME scan confirm parameters
+     */
+    void ScanConfirm(MlmeScanConfirmParams params);
+
+    /**
+     * Function called as a result of receiving an orphan notification command
+     * on the coordinator
+     *
+     * \param params MLME orphan indication parameters
+     */
+    void OrphanIndicationCoord(MlmeOrphanIndicationParams params);
+
+    void DoRun() override;
+
+    Ptr<LrWpanNetDevice> coord1NetDevice;  //!< The LrWpanNetDevice of coordinator 1
+    Ptr<LrWpanNetDevice> endNodeNetDevice; //!< The LrWpanNetDevice of the end device
+    bool m_orphanScanSuccess;              //!< Indicates a successful orphan scan
+};
+
+TestOrphanScan::TestOrphanScan()
+    : TestCase("Test an orphan scan and the reception of the commands involved")
+{
+    m_orphanScanSuccess = false;
+}
+
+TestOrphanScan::~TestOrphanScan()
+{
+}
+
+void
+TestOrphanScan::ScanConfirm(MlmeScanConfirmParams params)
+{
+    if (params.m_status == MLMESCAN_SUCCESS)
+    {
+        m_orphanScanSuccess = true;
+    }
+}
+
+void
+TestOrphanScan::OrphanIndicationCoord(MlmeOrphanIndicationParams params)
+{
+    // The steps taken by the coordinator on the event of an orphan indication
+    // are meant to be implemented by the next higher layer and are out of the scope of the
+    // standard. In this test, we assume that coordinator 2 already has
+    // the endDevice [00:00:00:00:00:00:00:03] registered and therefore reply to this device
+    // a with a coordidinator realignment command.
+
+    if (params.m_orphanAddr == Mac64Address("00:00:00:00:00:00:00:02"))
+    {
+        MlmeOrphanResponseParams respParams;
+        respParams.m_assocMember = true;
+        respParams.m_orphanAddr = params.m_orphanAddr;
+        respParams.m_shortAddr = Mac16Address("00:02");
+
+        Simulator::ScheduleNow(&LrWpanMac::MlmeOrphanResponse,
+                               coord1NetDevice->GetMac(),
+                               respParams);
+    }
+}
+
+void
+TestOrphanScan::DoRun()
+{
+    // Create 2 PAN coordinator nodes, and 1 end device
+    Ptr<Node> coord1 = CreateObject<Node>();
+    Ptr<Node> endNode = CreateObject<Node>();
+
+    coord1NetDevice = CreateObject<LrWpanNetDevice>();
+    endNodeNetDevice = CreateObject<LrWpanNetDevice>();
+
+    // PAN Coordinators configurations require to set both, the EUI-64 (extended address)
+    // and to assign their own short address.
+    coord1NetDevice->GetMac()->SetExtendedAddress(Mac64Address("00:00:00:00:00:00:00:01"));
+    coord1NetDevice->GetMac()->SetShortAddress(Mac16Address("00:01"));
+
+    // Other devices must have only its EUI-64 and later on, their short address is
+    // potentially assigned by the coordinator.
+    endNodeNetDevice->GetMac()->SetExtendedAddress(Mac64Address("00:00:00:00:00:00:00:02"));
+
+    // Configure Spectrum channel
+    Ptr<SingleModelSpectrumChannel> channel = CreateObject<SingleModelSpectrumChannel>();
+    Ptr<LogDistancePropagationLossModel> propModel =
+        CreateObject<LogDistancePropagationLossModel>();
+    Ptr<ConstantSpeedPropagationDelayModel> delayModel =
+        CreateObject<ConstantSpeedPropagationDelayModel>();
+    channel->AddPropagationLossModel(propModel);
+    channel->SetPropagationDelayModel(delayModel);
+
+    coord1NetDevice->SetChannel(channel);
+    endNodeNetDevice->SetChannel(channel);
+
+    coord1->AddDevice(coord1NetDevice);
+    endNode->AddDevice(endNodeNetDevice);
+
+    // Mobility
+    Ptr<ConstantPositionMobilityModel> coord1Mobility =
+        CreateObject<ConstantPositionMobilityModel>();
+    coord1Mobility->SetPosition(Vector(0, 0, 0));
+    coord1NetDevice->GetPhy()->SetMobility(coord1Mobility);
+
+    Ptr<ConstantPositionMobilityModel> endNodeMobility =
+        CreateObject<ConstantPositionMobilityModel>();
+    endNodeMobility->SetPosition(Vector(100, 0, 0));
+    endNodeNetDevice->GetPhy()->SetMobility(endNodeMobility);
+
+    // MAC layer Callbacks hooks
+    MlmeScanConfirmCallback cb1;
+    cb1 = MakeCallback(&TestOrphanScan::ScanConfirm, this);
+    endNodeNetDevice->GetMac()->SetMlmeScanConfirmCallback(cb1);
+
+    MlmeOrphanIndicationCallback cb2;
+    cb2 = MakeCallback(&TestOrphanScan::OrphanIndicationCoord, this);
+    coord1NetDevice->GetMac()->SetMlmeOrphanIndicationCallback(cb2);
+    /////////////////
+    // ORPHAN SCAN //
+    /////////////////
+
+    // PAN coordinator N0 (PAN 5) is set to channel 12 in non-beacon mode
+    // but answer to beacon request and orphan notification commands.
+    MlmeStartRequestParams params;
+    params.m_panCoor = true;
+    params.m_PanId = 5;
+    params.m_bcnOrd = 15;
+    params.m_sfrmOrd = 15;
+    params.m_logCh = 12;
+    Simulator::ScheduleWithContext(1,
+                                   Seconds(2.0),
+                                   &LrWpanMac::MlmeStartRequest,
+                                   coord1NetDevice->GetMac(),
+                                   params);
+
+    // End device N1 is set to scan 4 channels looking for the presence of a coordinator.
+    // On each channel, a single orphan notification command is sent and a response is
+    // waited for a maximum time of macResponseWaitTime. If a reply is received from a
+    // coordinator within this time (coordinator realignment command), the programmed scans on
+    // other channels is suspended.
+    // Scan Channels are represented by bits 0-26  (27 LSB)
+    //                       ch 14  ch 11
+    //                           |  |
+    // 0x7800  = 0000000000000000111100000000000
+    MlmeScanRequestParams scanParams;
+    scanParams.m_chPage = 0;
+    scanParams.m_scanChannels = 0x7800;
+    scanParams.m_scanType = MLMESCAN_ORPHAN;
+    Simulator::ScheduleWithContext(1,
+                                   Seconds(3.0),
+                                   &LrWpanMac::MlmeScanRequest,
+                                   endNodeNetDevice->GetMac(),
+                                   scanParams);
+
+    Simulator::Stop(Seconds(4000));
+    NS_LOG_DEBUG("----------- Start of TestOrphanScan -------------------");
+    Simulator::Run();
+
+    NS_TEST_EXPECT_MSG_EQ(m_orphanScanSuccess,
+                          true,
+                          "Error, no coordinator realignment commands"
+                          " received during orphan scan");
+    if (m_orphanScanSuccess)
+    {
+        NS_TEST_EXPECT_MSG_EQ(endNodeNetDevice->GetMac()->GetShortAddress(),
+                              Mac16Address("00:02"),
+                              "Error, end device did not receive short address"
+                              " during orphan scan");
+    }
 
     Simulator::Destroy();
 }
@@ -530,6 +719,7 @@
 {
     AddTestCase(new TestRxOffWhenIdleAfterCsmaFailure, TestCase::QUICK);
     AddTestCase(new TestActiveScanPanDescriptors, TestCase::QUICK);
+    AddTestCase(new TestOrphanScan, TestCase::QUICK);
 }
 
 static LrWpanMacTestSuite g_lrWpanMacTestSuite; //!< Static variable for test initialization
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/doc/source/lte-design.rst ns-3.39/src/lte/doc/source/lte-design.rst
--- ns-3.38/src/lte/doc/source/lte-design.rst	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/doc/source/lte-design.rst	2023-07-05 14:23:47.897427600 -0700
@@ -685,7 +685,7 @@
 
   Config::SetDefault("ns3::LteSpectrumPhy::DataErrorModelEnabled", BooleanValue(false));
 
-.. _sec-control-channles-phy-error-model:
+.. _sec-control-channels-phy-error-model:
 
 Control Channels PHY Error Model
 ++++++++++++++++++++++++++++++++
@@ -2266,13 +2266,13 @@
 (see``LteUePhy::RadioLinkFailureDetection``). The Qout threshold corresponds to 10%
 block error rate (BLER) of a hypothetical PDCCH transmission taking into account
 the PCFICH errors [R4-081920]_ (also refer to
-:ref:`sec-control-channles-phy-error-model`). Once, the UE is unable to decode
+:ref:`sec-control-channels-phy-error-model`). Once, the UE is unable to decode
 20 consecutive frames, i.e., the Qout evaluation period (200ms) is reached, an
 out-of-sync indication is sent to the UE RRC layer (see ``LteUeRrc::DoNotifyOutOfSync``).
 Else, the counter for the unsuccessfuly decoded frames is reset to zero. At the
 LteUeRrc, when the number of consecutive out-of-sync indications matches with the
 value of N310 parameter, the T310 timer is started and LteUePhy is notified to start
-measuring for in-sync indications (see ``LteUePhy::DoStartInSnycDetection``). We note
+measuring for in-sync indications (see ``LteUePhy::DoStartInSyncDetection``). We note
 that, the UE RRC state is not changed till the expiration of T310 timer. If the
 resultant SINR values averaged over a downlink frame is greater than a defined
 threshold Qin (default: -3.8dB), the frame is considered to be successfully
@@ -2468,7 +2468,7 @@
  - :math:`F_{n-1}` is the old filtered measurement result, where
    :math:`F_0 = M_1` (i.e. the first measurement is not filtered); and
  - :math:`a = (\frac{1}{2})^{\frac{k}{4}}`, where :math:`k` is the configurable
-   `filterCoefficent` provided by the ``QuantityConfig``;
+   `filterCoefficient` provided by the ``QuantityConfig``;
 
 :math:`k = 4` is the default value, but can be configured by setting the
 `RsrpFilterCoefficient` and `RsrqFilterCoefficient` attributes in
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-cqi-threshold.cc ns-3.39/src/lte/examples/lena-cqi-threshold.cc
--- ns-3.38/src/lte/examples/lena-cqi-threshold.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-cqi-threshold.cc	2023-07-05 14:23:47.897427600 -0700
@@ -113,7 +113,7 @@
     Simulator::Schedule(Seconds(0.020), &ChangePosition, ueNodes.Get(0));
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-deactivate-bearer.cc ns-3.39/src/lte/examples/lena-deactivate-bearer.cc
--- ns-3.38/src/lte/examples/lena-deactivate-bearer.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-deactivate-bearer.cc	2023-07-05 14:23:47.897427600 -0700
@@ -153,7 +153,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = qos.gbrUl;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         bearer.arp.priorityLevel = 15 - (u + 1);
         bearer.arp.preemptionCapability = true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-dual-stripe.cc ns-3.39/src/lte/examples/lena-dual-stripe.cc
--- ns-3.38/src/lte/examples/lena-dual-stripe.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-dual-stripe.cc	2023-07-05 14:23:47.897427600 -0700
@@ -948,7 +948,7 @@
             Ptr<NetDevice> ueDev = ueDevs.Get(u);
             for (uint32_t b = 0; b < numBearersPerUe; ++b)
             {
-                enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+                EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
                 EpsBearer bearer(q);
                 lteHelper->ActivateDataRadioBearer(ueDev, bearer);
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-fading.cc ns-3.39/src/lte/examples/lena-fading.cc
--- ns-3.38/src/lte/examples/lena-fading.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-fading.cc	2023-07-05 14:23:47.897427600 -0700
@@ -112,7 +112,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-frequency-reuse.cc ns-3.39/src/lte/examples/lena-frequency-reuse.cc
--- ns-3.38/src/lte/examples/lena-frequency-reuse.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-frequency-reuse.cc	2023-07-05 14:23:47.897427600 -0700
@@ -325,7 +325,7 @@
     lteHelper->AttachToClosestEnb(randomUeDevs, enbDevs);
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(edgeUeDevs, bearer);
     lteHelper->ActivateDataRadioBearer(centerUeDevs, bearer);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-intercell-interference.cc ns-3.39/src/lte/examples/lena-intercell-interference.cc
--- ns-3.38/src/lte/examples/lena-intercell-interference.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-intercell-interference.cc	2023-07-05 14:23:47.897427600 -0700
@@ -126,7 +126,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate a data radio bearer each UE
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-pathloss-traces.cc ns-3.39/src/lte/examples/lena-pathloss-traces.cc
--- ns-3.38/src/lte/examples/lena-pathloss-traces.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-pathloss-traces.cc	2023-07-05 14:23:47.901427700 -0700
@@ -136,7 +136,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate an EPS bearer on all UEs
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-profiling.cc ns-3.39/src/lte/examples/lena-profiling.cc
--- ns-3.38/src/lte/examples/lena-profiling.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-profiling.cc	2023-07-05 14:23:47.901427700 -0700
@@ -184,7 +184,7 @@
         NetDeviceContainer ueDev = lteHelper->InstallUeDevice(ueNodes.at(i));
         ueDevs.push_back(ueDev);
         lteHelper->Attach(ueDev, enbDevs.Get(i));
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q);
         lteHelper->ActivateDataRadioBearer(ueDev, bearer);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-radio-link-failure.cc ns-3.39/src/lte/examples/lena-radio-link-failure.cc
--- ns-3.38/src/lte/examples/lena-radio-link-failure.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-radio-link-failure.cc	2023-07-05 14:23:47.901427700 -0700
@@ -318,7 +318,7 @@
 {
     std::ofstream output;
 
-    if (firstWrite == true)
+    if (firstWrite)
     {
         output.open(fileName, std::ofstream::out);
         firstWrite = false;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-rem.cc ns-3.39/src/lte/examples/lena-rem.cc
--- ns-3.38/src/lte/examples/lena-rem.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-rem.cc	2023-07-05 14:23:47.901427700 -0700
@@ -84,7 +84,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-rem-sector-antenna.cc ns-3.39/src/lte/examples/lena-rem-sector-antenna.cc
--- ns-3.38/src/lte/examples/lena-rem-sector-antenna.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-rem-sector-antenna.cc	2023-07-05 14:23:47.901427700 -0700
@@ -198,7 +198,7 @@
         NetDeviceContainer ueDev = lteHelper->InstallUeDevice(ueNodes.at(i));
         ueDevs.push_back(ueDev);
         lteHelper->Attach(ueDev, enbDevs.Get(i));
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q);
         lteHelper->ActivateDataRadioBearer(ueDev, bearer);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-rlc-traces.cc ns-3.39/src/lte/examples/lena-rlc-traces.cc
--- ns-3.38/src/lte/examples/lena-rlc-traces.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-rlc-traces.cc	2023-07-05 14:23:47.901427700 -0700
@@ -67,7 +67,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-simple.cc ns-3.39/src/lte/examples/lena-simple.cc
--- ns-3.38/src/lte/examples/lena-simple.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-simple.cc	2023-07-05 14:23:47.901427700 -0700
@@ -95,7 +95,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
     lteHelper->EnableTraces();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-simple-epc-backhaul.cc ns-3.39/src/lte/examples/lena-simple-epc-backhaul.cc
--- ns-3.38/src/lte/examples/lena-simple-epc-backhaul.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-simple-epc-backhaul.cc	2023-07-05 14:23:47.901427700 -0700
@@ -158,7 +158,7 @@
         for (uint16_t i = 0; i < numNodePairs; ++i)
         {
             Ptr<Node> enb = enbNodes.Get(i);
-            std::vector<uint16_t> cellIds(1, i);
+            std::vector<uint16_t> cellIds(1, i + 1);
 
             // Create a point to point link between the eNB and the SGW with
             // the corresponding new NetDevices on each side
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/examples/lena-uplink-power-control.cc ns-3.39/src/lte/examples/lena-uplink-power-control.cc
--- ns-3.38/src/lte/examples/lena-uplink-power-control.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/examples/lena-uplink-power-control.cc	2023-07-05 14:23:47.901427700 -0700
@@ -91,7 +91,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/emu-epc-helper.cc ns-3.39/src/lte/helper/emu-epc-helper.cc
--- ns-3.38/src/lte/helper/emu-epc-helper.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/helper/emu-epc-helper.cc	2023-07-05 14:23:47.901427700 -0700
@@ -19,7 +19,7 @@
  *         Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/emu-epc-helper.h"
+#include "emu-epc-helper.h"
 
 #include "ns3/emu-fd-net-device-helper.h"
 #include "ns3/epc-x2.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/emu-epc-helper.h ns-3.39/src/lte/helper/emu-epc-helper.h
--- ns-3.38/src/lte/helper/emu-epc-helper.h	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/helper/emu-epc-helper.h	2023-07-05 14:23:47.901427700 -0700
@@ -22,7 +22,7 @@
 #ifndef EMU_EPC_HELPER_H
 #define EMU_EPC_HELPER_H
 
-#include "ns3/no-backhaul-epc-helper.h"
+#include "no-backhaul-epc-helper.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/epc-helper.cc ns-3.39/src/lte/helper/epc-helper.cc
--- ns-3.38/src/lte/helper/epc-helper.cc	2023-03-16 20:54:23.660868600 -0700
+++ ns-3.39/src/lte/helper/epc-helper.cc	2023-07-05 14:23:47.901427700 -0700
@@ -19,7 +19,8 @@
  *         Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include <ns3/epc-helper.h>
+#include "epc-helper.h"
+
 #include <ns3/ipv4-address.h>
 #include <ns3/ipv6-address.h>
 #include <ns3/log.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/lte-helper.cc ns-3.39/src/lte/helper/lte-helper.cc
--- ns-3.38/src/lte/helper/lte-helper.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/lte-helper.cc	2023-07-05 14:23:47.901427700 -0700
@@ -22,11 +22,17 @@
 
 #include "lte-helper.h"
 
+#include "cc-helper.h"
+#include "epc-helper.h"
+#include "mac-stats-calculator.h"
+#include "phy-rx-stats-calculator.h"
+#include "phy-stats-calculator.h"
+#include "phy-tx-stats-calculator.h"
+
 #include <ns3/abort.h>
 #include <ns3/buildings-propagation-loss-model.h>
 #include <ns3/epc-enb-application.h>
 #include <ns3/epc-enb-s1-sap.h>
-#include <ns3/epc-helper.h>
 #include <ns3/epc-ue-nas.h>
 #include <ns3/epc-x2.h>
 #include <ns3/ff-mac-scheduler.h>
@@ -35,6 +41,7 @@
 #include <ns3/log.h>
 #include <ns3/lte-anr.h>
 #include <ns3/lte-chunk-processor.h>
+#include <ns3/lte-common.h>
 #include <ns3/lte-enb-component-carrier-manager.h>
 #include <ns3/lte-enb-mac.h>
 #include <ns3/lte-enb-net-device.h>
@@ -54,13 +61,9 @@
 #include <ns3/lte-ue-net-device.h>
 #include <ns3/lte-ue-phy.h>
 #include <ns3/lte-ue-rrc.h>
-#include <ns3/mac-stats-calculator.h>
 #include <ns3/multi-model-spectrum-channel.h>
 #include <ns3/object-factory.h>
 #include <ns3/object-map.h>
-#include <ns3/phy-rx-stats-calculator.h>
-#include <ns3/phy-stats-calculator.h>
-#include <ns3/phy-tx-stats-calculator.h>
 #include <ns3/pointer.h>
 #include <ns3/string.h>
 #include <ns3/trace-fading-loss-model.h>
@@ -739,7 +742,7 @@
             it->first,
             DynamicCast<ComponentCarrierEnb>(it->second)->GetMac()->GetLteMacSapProvider());
 
-        if (ccmTest == false)
+        if (!ccmTest)
         {
             NS_FATAL_ERROR("Error in SetComponentCarrierMacSapProviders");
         }
@@ -939,7 +942,7 @@
     // number of component carriers in UE RRC
     ccmUe->SetNumberOfComponentCarriers(m_noOfCcs);
 
-    // run intializeSap to create the proper number of MAC and PHY control sap provider/users
+    // run initializeSap to create the proper number of MAC and PHY control sap provider/users
     rrc->InitializeSap();
 
     if (m_useIdealRrc)
@@ -987,7 +990,7 @@
             ccmUe->SetComponentCarrierMacSapProviders(it->first,
                                                       it->second->GetMac()->GetLteMacSapProvider());
 
-        if (ccmTest == false)
+        if (!ccmTest)
         {
             NS_FATAL_ERROR("Error in SetComponentCarrierMacSapProviders");
         }
@@ -1572,7 +1575,7 @@
 LteHelper::AssignStreams(NetDeviceContainer c, int64_t stream)
 {
     int64_t currentStream = stream;
-    if (m_fadingModel && (m_fadingStreamsAssigned == false))
+    if (m_fadingModel && !m_fadingStreamsAssigned)
     {
         Ptr<TraceFadingLossModel> tflm = m_fadingModel->GetObject<TraceFadingLossModel>();
         if (tflm)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/lte-helper.h ns-3.39/src/lte/helper/lte-helper.h
--- ns-3.38/src/lte/helper/lte-helper.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/lte-helper.h	2023-07-05 14:23:47.901427700 -0700
@@ -22,23 +22,23 @@
 #ifndef LTE_HELPER_H
 #define LTE_HELPER_H
 
-#include <ns3/cc-helper.h>
+#include "mac-stats-calculator.h"
+#include "phy-rx-stats-calculator.h"
+#include "phy-stats-calculator.h"
+#include "phy-tx-stats-calculator.h"
+#include "radio-bearer-stats-calculator.h"
+#include "radio-bearer-stats-connector.h"
+
 #include <ns3/component-carrier-enb.h>
 #include <ns3/config.h>
 #include <ns3/epc-tft.h>
 #include <ns3/eps-bearer.h>
-#include <ns3/mac-stats-calculator.h>
 #include <ns3/mobility-model.h>
 #include <ns3/names.h>
 #include <ns3/net-device-container.h>
 #include <ns3/net-device.h>
 #include <ns3/node-container.h>
 #include <ns3/node.h>
-#include <ns3/phy-rx-stats-calculator.h>
-#include <ns3/phy-stats-calculator.h>
-#include <ns3/phy-tx-stats-calculator.h>
-#include <ns3/radio-bearer-stats-calculator.h>
-#include <ns3/radio-bearer-stats-connector.h>
 #include <ns3/simulator.h>
 
 #include <map>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/lte-hex-grid-enb-topology-helper.cc ns-3.39/src/lte/helper/lte-hex-grid-enb-topology-helper.cc
--- ns-3.38/src/lte/helper/lte-hex-grid-enb-topology-helper.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/lte-hex-grid-enb-topology-helper.cc	2023-07-05 14:23:47.901427700 -0700
@@ -19,9 +19,10 @@
 
 #include "lte-hex-grid-enb-topology-helper.h"
 
+#include "epc-helper.h"
+
 #include <ns3/abort.h>
 #include <ns3/double.h>
-#include <ns3/epc-helper.h>
 #include <ns3/log.h>
 #include <ns3/pointer.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/lte-hex-grid-enb-topology-helper.h ns-3.39/src/lte/helper/lte-hex-grid-enb-topology-helper.h
--- ns-3.38/src/lte/helper/lte-hex-grid-enb-topology-helper.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/lte-hex-grid-enb-topology-helper.h	2023-07-05 14:23:47.901427700 -0700
@@ -20,7 +20,7 @@
 #ifndef LTE_HEX_GRID_ENB_TOPOLOGY_HELPER_H
 #define LTE_HEX_GRID_ENB_TOPOLOGY_HELPER_H
 
-#include <ns3/lte-helper.h>
+#include "lte-helper.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/lte-stats-calculator.cc ns-3.39/src/lte/helper/lte-stats-calculator.cc
--- ns-3.38/src/lte/helper/lte-stats-calculator.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/lte-stats-calculator.cc	2023-07-05 14:23:47.901427700 -0700
@@ -82,14 +82,7 @@
 bool
 LteStatsCalculator::ExistsImsiPath(std::string path)
 {
-    if (m_pathImsiMap.find(path) == m_pathImsiMap.end())
-    {
-        return false;
-    }
-    else
-    {
-        return true;
-    }
+    return m_pathImsiMap.find(path) != m_pathImsiMap.end();
 }
 
 void
@@ -108,14 +101,7 @@
 bool
 LteStatsCalculator::ExistsCellIdPath(std::string path)
 {
-    if (m_pathCellIdMap.find(path) == m_pathCellIdMap.end())
-    {
-        return false;
-    }
-    else
-    {
-        return true;
-    }
+    return m_pathCellIdMap.find(path) != m_pathCellIdMap.end();
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/mac-stats-calculator.cc ns-3.39/src/lte/helper/mac-stats-calculator.cc
--- ns-3.38/src/lte/helper/mac-stats-calculator.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/mac-stats-calculator.cc	2023-07-05 14:23:47.901427700 -0700
@@ -110,7 +110,7 @@
              << (uint32_t)dlSchedulingCallbackInfo.mcsTb2 << dlSchedulingCallbackInfo.sizeTb2);
     NS_LOG_INFO("Write DL Mac Stats in " << GetDlOutputFilename());
 
-    if (m_dlFirstWrite == true)
+    if (m_dlFirstWrite)
     {
         m_dlOutFile.open(GetDlOutputFilename());
         if (!m_dlOutFile.is_open())
@@ -151,7 +151,7 @@
                          << size);
     NS_LOG_INFO("Write UL Mac Stats in " << GetUlOutputFilename());
 
-    if (m_ulFirstWrite == true)
+    if (m_ulFirstWrite)
     {
         m_ulOutFile.open(GetUlOutputFilename());
         if (!m_ulOutFile.is_open())
@@ -185,7 +185,7 @@
     std::ostringstream pathAndRnti;
     std::string pathEnb = path.substr(0, path.find("/ComponentCarrierMap"));
     pathAndRnti << pathEnb << "/LteEnbRrc/UeMap/" << dlSchedulingCallbackInfo.rnti;
-    if (macStats->ExistsImsiPath(pathAndRnti.str()) == true)
+    if (macStats->ExistsImsiPath(pathAndRnti.str()))
     {
         imsi = macStats->GetImsiPath(pathAndRnti.str());
     }
@@ -195,7 +195,7 @@
         macStats->SetImsiPath(pathAndRnti.str(), imsi);
     }
     uint16_t cellId = 0;
-    if (macStats->ExistsCellIdPath(pathAndRnti.str()) == true)
+    if (macStats->ExistsCellIdPath(pathAndRnti.str()))
     {
         cellId = macStats->GetCellIdPath(pathAndRnti.str());
     }
@@ -224,7 +224,7 @@
     std::ostringstream pathAndRnti;
     std::string pathEnb = path.substr(0, path.find("/ComponentCarrierMap"));
     pathAndRnti << pathEnb << "/LteEnbRrc/UeMap/" << rnti;
-    if (macStats->ExistsImsiPath(pathAndRnti.str()) == true)
+    if (macStats->ExistsImsiPath(pathAndRnti.str()))
     {
         imsi = macStats->GetImsiPath(pathAndRnti.str());
     }
@@ -234,7 +234,7 @@
         macStats->SetImsiPath(pathAndRnti.str(), imsi);
     }
     uint16_t cellId = 0;
-    if (macStats->ExistsCellIdPath(pathAndRnti.str()) == true)
+    if (macStats->ExistsCellIdPath(pathAndRnti.str()))
     {
         cellId = macStats->GetCellIdPath(pathAndRnti.str());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/mac-stats-calculator.h ns-3.39/src/lte/helper/mac-stats-calculator.h
--- ns-3.38/src/lte/helper/mac-stats-calculator.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/mac-stats-calculator.h	2023-07-05 14:23:47.901427700 -0700
@@ -22,8 +22,9 @@
 #ifndef MAC_STATS_CALCULATOR_H_
 #define MAC_STATS_CALCULATOR_H_
 
+#include "lte-stats-calculator.h"
+
 #include "ns3/lte-enb-mac.h"
-#include "ns3/lte-stats-calculator.h"
 #include "ns3/nstime.h"
 #include "ns3/uinteger.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/no-backhaul-epc-helper.cc ns-3.39/src/lte/helper/no-backhaul-epc-helper.cc
--- ns-3.38/src/lte/helper/no-backhaul-epc-helper.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/no-backhaul-epc-helper.cc	2023-07-05 14:23:47.901427700 -0700
@@ -18,7 +18,7 @@
  *         (based on the original point-to-point-epc-helper.cc)
  */
 
-#include "ns3/no-backhaul-epc-helper.h"
+#include "no-backhaul-epc-helper.h"
 
 #include "ns3/boolean.h"
 #include "ns3/epc-enb-application.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/no-backhaul-epc-helper.h ns-3.39/src/lte/helper/no-backhaul-epc-helper.h
--- ns-3.38/src/lte/helper/no-backhaul-epc-helper.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/no-backhaul-epc-helper.h	2023-07-05 14:23:47.901427700 -0700
@@ -21,7 +21,7 @@
 #ifndef NO_BACKHAUL_EPC_HELPER_H
 #define NO_BACKHAUL_EPC_HELPER_H
 
-#include "ns3/epc-helper.h"
+#include "epc-helper.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/phy-rx-stats-calculator.cc ns-3.39/src/lte/helper/phy-rx-stats-calculator.cc
--- ns-3.38/src/lte/helper/phy-rx-stats-calculator.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/phy-rx-stats-calculator.cc	2023-07-05 14:23:47.901427700 -0700
@@ -106,7 +106,7 @@
                          << params.m_ndi << params.m_correctness);
     NS_LOG_INFO("Write DL Rx Phy Stats in " << GetDlRxOutputFilename());
 
-    if (m_dlRxFirstWrite == true)
+    if (m_dlRxFirstWrite)
     {
         m_dlRxOutFile.open(GetDlRxOutputFilename());
         if (!m_dlRxOutFile.is_open())
@@ -142,7 +142,7 @@
                          << params.m_ndi << params.m_correctness);
     NS_LOG_INFO("Write UL Rx Phy Stats in " << GetUlRxOutputFilename());
 
-    if (m_ulRxFirstWrite == true)
+    if (m_ulRxFirstWrite)
     {
         m_ulRxOutFile.open(GetUlRxOutputFilename());
         if (!m_ulRxOutFile.is_open())
@@ -178,7 +178,7 @@
     std::ostringstream pathAndRnti;
     pathAndRnti << path << "/" << params.m_rnti;
     std::string pathUePhy = path.substr(0, path.find("/ComponentCarrierMapUe"));
-    if (phyRxStats->ExistsImsiPath(pathAndRnti.str()) == true)
+    if (phyRxStats->ExistsImsiPath(pathAndRnti.str()))
     {
         imsi = phyRxStats->GetImsiPath(pathAndRnti.str());
     }
@@ -202,7 +202,7 @@
     std::ostringstream pathAndRnti;
     std::string pathEnb = path.substr(0, path.find("/ComponentCarrierMap"));
     pathAndRnti << pathEnb << "/LteEnbRrc/UeMap/" << params.m_rnti;
-    if (phyRxStats->ExistsImsiPath(pathAndRnti.str()) == true)
+    if (phyRxStats->ExistsImsiPath(pathAndRnti.str()))
     {
         imsi = phyRxStats->GetImsiPath(pathAndRnti.str());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/phy-rx-stats-calculator.h ns-3.39/src/lte/helper/phy-rx-stats-calculator.h
--- ns-3.38/src/lte/helper/phy-rx-stats-calculator.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/phy-rx-stats-calculator.h	2023-07-05 14:23:47.901427700 -0700
@@ -22,7 +22,8 @@
 #ifndef PHY_RX_STATS_CALCULATOR_H_
 #define PHY_RX_STATS_CALCULATOR_H_
 
-#include "ns3/lte-stats-calculator.h"
+#include "lte-stats-calculator.h"
+
 #include "ns3/nstime.h"
 #include "ns3/uinteger.h"
 #include <ns3/lte-common.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/phy-stats-calculator.cc ns-3.39/src/lte/helper/phy-stats-calculator.cc
--- ns-3.38/src/lte/helper/phy-stats-calculator.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/phy-stats-calculator.cc	2023-07-05 14:23:47.901427700 -0700
@@ -132,7 +132,7 @@
     NS_LOG_FUNCTION(this << cellId << imsi << rnti << rsrp << sinr);
     NS_LOG_INFO("Write RSRP/SINR Phy Stats in " << GetCurrentCellRsrpSinrFilename());
 
-    if (m_RsrpSinrFirstWrite == true)
+    if (m_RsrpSinrFirstWrite)
     {
         m_rsrpOutFile.open(GetCurrentCellRsrpSinrFilename());
         if (!m_rsrpOutFile.is_open())
@@ -164,7 +164,7 @@
     NS_LOG_FUNCTION(this << cellId << imsi << rnti << sinrLinear);
     NS_LOG_INFO("Write SINR Linear Phy Stats in " << GetUeSinrFilename());
 
-    if (m_UeSinrFirstWrite == true)
+    if (m_UeSinrFirstWrite)
     {
         m_ueSinrOutFile.open(GetUeSinrFilename());
         if (!m_ueSinrOutFile.is_open())
@@ -190,7 +190,7 @@
     NS_LOG_FUNCTION(this << cellId << interference);
     NS_LOG_INFO("Write Interference Phy Stats in " << GetInterferenceFilename());
 
-    if (m_InterferenceFirstWrite == true)
+    if (m_InterferenceFirstWrite)
     {
         m_interferenceOutFile.open(GetInterferenceFilename());
         if (!m_interferenceOutFile.is_open())
@@ -220,7 +220,7 @@
     NS_LOG_FUNCTION(phyStats << path);
     uint64_t imsi = 0;
     std::string pathUePhy = path.substr(0, path.find("/ComponentCarrierMapUe"));
-    if (phyStats->ExistsImsiPath(pathUePhy) == true)
+    if (phyStats->ExistsImsiPath(pathUePhy))
     {
         imsi = phyStats->GetImsiPath(pathUePhy);
     }
@@ -248,7 +248,7 @@
     pathAndRnti << path << "/" << rnti;
     std::string pathEnbMac = path.substr(0, path.find("/ComponentCarrierMap"));
     pathEnbMac += "/LteEnbMac/DlScheduling";
-    if (phyStats->ExistsImsiPath(pathAndRnti.str()) == true)
+    if (phyStats->ExistsImsiPath(pathAndRnti.str()))
     {
         imsi = phyStats->GetImsiPath(pathAndRnti.str());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/phy-stats-calculator.h ns-3.39/src/lte/helper/phy-stats-calculator.h
--- ns-3.38/src/lte/helper/phy-stats-calculator.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/phy-stats-calculator.h	2023-07-05 14:23:47.901427700 -0700
@@ -20,7 +20,8 @@
 #ifndef PHY_STATS_CALCULATOR_H_
 #define PHY_STATS_CALCULATOR_H_
 
-#include "ns3/lte-stats-calculator.h"
+#include "lte-stats-calculator.h"
+
 #include "ns3/nstime.h"
 #include "ns3/spectrum-value.h"
 #include "ns3/uinteger.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/phy-tx-stats-calculator.cc ns-3.39/src/lte/helper/phy-tx-stats-calculator.cc
--- ns-3.38/src/lte/helper/phy-tx-stats-calculator.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/phy-tx-stats-calculator.cc	2023-07-05 14:23:47.901427700 -0700
@@ -106,7 +106,7 @@
                          << params.m_ndi);
     NS_LOG_INFO("Write DL Tx Phy Stats in " << GetDlTxOutputFilename());
 
-    if (m_dlTxFirstWrite == true)
+    if (m_dlTxFirstWrite)
     {
         m_dlTxOutFile.open(GetDlOutputFilename());
         if (!m_dlTxOutFile.is_open())
@@ -140,7 +140,7 @@
                          << params.m_ndi);
     NS_LOG_INFO("Write UL Tx Phy Stats in " << GetUlTxOutputFilename());
 
-    if (m_ulTxFirstWrite == true)
+    if (m_ulTxFirstWrite)
     {
         m_ulTxOutFile.open(GetUlTxOutputFilename());
         if (!m_ulTxOutFile.is_open())
@@ -177,7 +177,7 @@
     std::ostringstream pathAndRnti;
     std::string pathEnb = path.substr(0, path.find("/ComponentCarrierMap"));
     pathAndRnti << pathEnb << "/LteEnbRrc/UeMap/" << params.m_rnti;
-    if (phyTxStats->ExistsImsiPath(pathAndRnti.str()) == true)
+    if (phyTxStats->ExistsImsiPath(pathAndRnti.str()))
     {
         imsi = phyTxStats->GetImsiPath(pathAndRnti.str());
     }
@@ -201,7 +201,7 @@
     std::ostringstream pathAndRnti;
     pathAndRnti << path << "/" << params.m_rnti;
     std::string pathUePhy = path.substr(0, path.find("/ComponentCarrierMapUe"));
-    if (phyTxStats->ExistsImsiPath(pathAndRnti.str()) == true)
+    if (phyTxStats->ExistsImsiPath(pathAndRnti.str()))
     {
         imsi = phyTxStats->GetImsiPath(pathAndRnti.str());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/phy-tx-stats-calculator.h ns-3.39/src/lte/helper/phy-tx-stats-calculator.h
--- ns-3.38/src/lte/helper/phy-tx-stats-calculator.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/phy-tx-stats-calculator.h	2023-07-05 14:23:47.901427700 -0700
@@ -22,7 +22,8 @@
 #ifndef PHY_TX_STATS_CALCULATOR_H_
 #define PHY_TX_STATS_CALCULATOR_H_
 
-#include "ns3/lte-stats-calculator.h"
+#include "lte-stats-calculator.h"
+
 #include "ns3/nstime.h"
 #include "ns3/uinteger.h"
 #include <ns3/lte-common.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/point-to-point-epc-helper.cc ns-3.39/src/lte/helper/point-to-point-epc-helper.cc
--- ns-3.38/src/lte/helper/point-to-point-epc-helper.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/point-to-point-epc-helper.cc	2023-07-05 14:23:47.901427700 -0700
@@ -20,7 +20,7 @@
  *         (most of the code refactored to no-backhaul-epc-helper.cc)
  */
 
-#include "ns3/point-to-point-epc-helper.h"
+#include "point-to-point-epc-helper.h"
 
 #include "ns3/boolean.h"
 #include "ns3/log.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/point-to-point-epc-helper.h ns-3.39/src/lte/helper/point-to-point-epc-helper.h
--- ns-3.38/src/lte/helper/point-to-point-epc-helper.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/point-to-point-epc-helper.h	2023-07-05 14:23:47.901427700 -0700
@@ -23,7 +23,7 @@
 #ifndef POINT_TO_POINT_EPC_HELPER_H
 #define POINT_TO_POINT_EPC_HELPER_H
 
-#include "ns3/no-backhaul-epc-helper.h"
+#include "no-backhaul-epc-helper.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/radio-bearer-stats-calculator.cc ns-3.39/src/lte/helper/radio-bearer-stats-calculator.cc
--- ns-3.38/src/lte/helper/radio-bearer-stats-calculator.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/radio-bearer-stats-calculator.cc	2023-07-05 14:23:47.901427700 -0700
@@ -244,7 +244,7 @@
     std::ofstream ulOutFile;
     std::ofstream dlOutFile;
 
-    if (m_firstWrite == true)
+    if (m_firstWrite)
     {
         ulOutFile.open(GetUlOutputFilename());
         if (!ulOutFile.is_open())
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/helper/radio-bearer-stats-calculator.h ns-3.39/src/lte/helper/radio-bearer-stats-calculator.h
--- ns-3.38/src/lte/helper/radio-bearer-stats-calculator.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/helper/radio-bearer-stats-calculator.h	2023-07-05 14:23:47.901427700 -0700
@@ -21,9 +21,10 @@
 #ifndef RADIO_BEARER_STATS_CALCULATOR_H_
 #define RADIO_BEARER_STATS_CALCULATOR_H_
 
+#include "lte-stats-calculator.h"
+
 #include "ns3/basic-data-calculators.h"
 #include "ns3/lte-common.h"
-#include "ns3/lte-stats-calculator.h"
 #include "ns3/object.h"
 #include "ns3/uinteger.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/a2-a4-rsrq-handover-algorithm.h ns-3.39/src/lte/model/a2-a4-rsrq-handover-algorithm.h
--- ns-3.38/src/lte/model/a2-a4-rsrq-handover-algorithm.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/a2-a4-rsrq-handover-algorithm.h	2023-07-05 14:23:47.901427700 -0700
@@ -27,9 +27,10 @@
 #ifndef A2_A4_RSRQ_HANDOVER_ALGORITHM_H
 #define A2_A4_RSRQ_HANDOVER_ALGORITHM_H
 
-#include <ns3/lte-handover-algorithm.h>
-#include <ns3/lte-handover-management-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-handover-algorithm.h"
+#include "lte-handover-management-sap.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/ptr.h>
 #include <ns3/simple-ref-count.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/a3-rsrp-handover-algorithm.cc ns-3.39/src/lte/model/a3-rsrp-handover-algorithm.cc
--- ns-3.38/src/lte/model/a3-rsrp-handover-algorithm.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/a3-rsrp-handover-algorithm.cc	2023-07-05 14:23:47.901427700 -0700
@@ -20,9 +20,10 @@
 
 #include "a3-rsrp-handover-algorithm.h"
 
+#include "lte-common.h"
+
 #include <ns3/double.h>
 #include <ns3/log.h>
-#include <ns3/lte-common.h>
 
 #include <algorithm>
 #include <list>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/a3-rsrp-handover-algorithm.h ns-3.39/src/lte/model/a3-rsrp-handover-algorithm.h
--- ns-3.38/src/lte/model/a3-rsrp-handover-algorithm.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/a3-rsrp-handover-algorithm.h	2023-07-05 14:23:47.901427700 -0700
@@ -21,9 +21,10 @@
 #ifndef A3_RSRP_HANDOVER_ALGORITHM_H
 #define A3_RSRP_HANDOVER_ALGORITHM_H
 
-#include <ns3/lte-handover-algorithm.h>
-#include <ns3/lte-handover-management-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-handover-algorithm.h"
+#include "lte-handover-management-sap.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/nstime.h>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/component-carrier-enb.cc ns-3.39/src/lte/model/component-carrier-enb.cc
--- ns-3.38/src/lte/model/component-carrier-enb.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/component-carrier-enb.cc	2023-07-05 14:23:47.901427700 -0700
@@ -19,13 +19,14 @@
 
 #include "component-carrier-enb.h"
 
+#include "ff-mac-scheduler.h"
+#include "lte-enb-mac.h"
+#include "lte-enb-phy.h"
+#include "lte-ffr-algorithm.h"
+
 #include <ns3/abort.h>
 #include <ns3/boolean.h>
-#include <ns3/ff-mac-scheduler.h>
 #include <ns3/log.h>
-#include <ns3/lte-enb-mac.h>
-#include <ns3/lte-enb-phy.h>
-#include <ns3/lte-ffr-algorithm.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
 #include <ns3/uinteger.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/component-carrier-enb.h ns-3.39/src/lte/model/component-carrier-enb.h
--- ns-3.38/src/lte/model/component-carrier-enb.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/component-carrier-enb.h	2023-07-05 14:23:47.901427700 -0700
@@ -21,9 +21,8 @@
 #define COMPONENT_CARRIER_ENB_H
 
 #include "component-carrier.h"
+#include "lte-enb-phy.h"
 
-#include "ns3/lte-phy.h"
-#include <ns3/lte-enb-phy.h>
 #include <ns3/nstime.h>
 #include <ns3/object.h>
 #include <ns3/packet.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/component-carrier-ue.cc ns-3.39/src/lte/model/component-carrier-ue.cc
--- ns-3.38/src/lte/model/component-carrier-ue.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/component-carrier-ue.cc	2023-07-05 14:23:47.901427700 -0700
@@ -19,11 +19,12 @@
 
 #include "component-carrier-ue.h"
 
+#include "lte-ue-mac.h"
+#include "lte-ue-phy.h"
+
 #include <ns3/abort.h>
 #include <ns3/boolean.h>
 #include <ns3/log.h>
-#include <ns3/lte-ue-mac.h>
-#include <ns3/lte-ue-phy.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
 #include <ns3/uinteger.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/component-carrier-ue.h ns-3.39/src/lte/model/component-carrier-ue.h
--- ns-3.38/src/lte/model/component-carrier-ue.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/component-carrier-ue.h	2023-07-05 14:23:47.901427700 -0700
@@ -20,9 +20,10 @@
 #ifndef COMPONENT_CARRIER_UE_H
 #define COMPONENT_CARRIER_UE_H
 
-#include "ns3/lte-phy.h"
-#include <ns3/component-carrier.h>
-#include <ns3/lte-ue-phy.h>
+#include "component-carrier.h"
+#include "lte-phy.h"
+#include "lte-ue-phy.h"
+
 #include <ns3/nstime.h>
 #include <ns3/object.h>
 #include <ns3/packet.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/cqa-ff-mac-scheduler.cc ns-3.39/src/lte/model/cqa-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/cqa-ff-mac-scheduler.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/cqa-ff-mac-scheduler.cc	2023-07-05 14:23:47.901427700 -0700
@@ -22,13 +22,15 @@
  * Proportional Fair and Round Robin schedulers implementations.
  */
 
+#include "cqa-ff-mac-scheduler.h"
+
+#include "ff-mac-common.h"
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
-#include <ns3/cqa-ff-mac-scheduler.h>
-#include <ns3/ff-mac-common.h>
 #include <ns3/integer.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
@@ -229,7 +231,7 @@
 
 void
 CqaFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -242,7 +244,7 @@
 
 void
 CqaFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -288,7 +290,7 @@
 
 void
 CqaFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -298,7 +300,7 @@
     // LTE MAC scheduler specification
     if (params.m_reconfigureFlag)
     {
-        std::vector<struct LogicalChannelConfigListElement_s>::const_iterator lcit;
+        std::vector<LogicalChannelConfigListElement_s>::const_iterator lcit;
 
         for (lcit = params.m_logicalChannelConfigList.begin();
              lcit != params.m_logicalChannelConfigList.end();
@@ -320,7 +322,7 @@
     } // else new UE is added
     else
     {
-        std::vector<struct LogicalChannelConfigListElement_s>::const_iterator lcit;
+        std::vector<LogicalChannelConfigListElement_s>::const_iterator lcit;
 
         for (lcit = params.m_logicalChannelConfigList.begin();
              lcit != params.m_logicalChannelConfigList.end();
@@ -377,7 +379,7 @@
 
 void
 CqaFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     std::vector<uint8_t>::const_iterator it;
@@ -424,7 +426,7 @@
 
 void
 CqaFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -475,7 +477,7 @@
 
 void
 CqaFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -500,7 +502,7 @@
 
 void
 CqaFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -508,7 +510,7 @@
 
 void
 CqaFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -570,14 +572,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -585,7 +581,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -656,7 +652,7 @@
 
 void
 CqaFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -687,7 +683,7 @@
     rbgMap = m_ffrSapProvider->GetAvailableDlRbg();
     for (std::vector<bool>::iterator it = rbgMap.begin(); it != rbgMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbgAllocatedNum++;
         }
@@ -715,7 +711,7 @@
 
     for (std::vector<bool>::iterator it = ulRbMap.begin(); it != ulRbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             if (tmpMinBandwidth > maxContinuousUlBandwidth)
             {
@@ -744,7 +740,7 @@
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
     rbStart = ffrRbStartOffset;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -786,7 +782,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -851,12 +847,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -947,7 +943,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -975,7 +971,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -1046,7 +1042,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -1131,7 +1127,7 @@
         return;
     }
 
-    std::map<LteFlowId_t, struct LogicalChannelConfigListElement_s>::iterator itLogicalChannels;
+    std::map<LteFlowId_t, LogicalChannelConfigListElement_s>::iterator itLogicalChannels;
 
     for (itLogicalChannels = m_ueLogicalChannelsConfigList.begin();
          itLogicalChannels != m_ueLogicalChannelsConfigList.end();
@@ -1182,7 +1178,8 @@
 
         UEtoHOL.insert(std::pair<LteFlowId_t, int>(itLogicalChannels->first, delay));
 
-        if (itLogicalChannels->second.m_qosBearerType == itLogicalChannels->second.QBT_NON_GBR)
+        if (itLogicalChannels->second.m_qosBearerType ==
+            LogicalChannelConfigListElement_s::QBT_NON_GBR)
         {
             if (map_nonGBRHOLgroupToUE.count(group) == 0)
             {
@@ -1195,7 +1192,10 @@
                 map_nonGBRHOLgroupToUE.find(group)->second.insert(itRlcBufferReq->first);
             }
         }
-        else if (itLogicalChannels->second.m_qosBearerType == itLogicalChannels->second.QBT_GBR)
+        else if (itLogicalChannels->second.m_qosBearerType ==
+                     LogicalChannelConfigListElement_s::QBT_GBR ||
+                 itLogicalChannels->second.m_qosBearerType ==
+                     LogicalChannelConfigListElement_s::QBT_DGBR)
         {
             if (map_GBRHOLgroupToUE.count(group) == 0)
             {
@@ -1326,7 +1326,7 @@
     std::set<int> availableRBGs;
     for (int i = 0; i < numberOfRBGs; i++)
     {
-        if (rbgMap.at(i) == false)
+        if (!rbgMap.at(i))
         {
             availableRBGs.insert(i);
         }
@@ -1398,7 +1398,7 @@
 
                 std::map<uint16_t, CqasFlowPerf_t>::iterator itStats;
 
-                if ((m_ffrSapProvider->IsDlRbgAvailableForUe(currentRB, flowId.m_rnti)) == false)
+                if (!m_ffrSapProvider->IsDlRbgAvailableForUe(currentRB, flowId.m_rnti))
                 {
                     continue;
                 }
@@ -1618,7 +1618,7 @@
         NS_LOG_INFO("Scheduled RNTI:" << newEl.m_rnti);
         // create the DlDciListElement_s
         DlDciListElement_s newDci;
-        std::vector<struct RlcPduListElement_s> newRlcPduLe;
+        std::vector<RlcPduListElement_s> newRlcPduLe;
         newDci.m_rnti = (*itMap).first;
         newDci.m_harqProcess = UpdateHarqProcessId((*itMap).first);
         uint16_t lcActives = LcActivePerFlow(itMap->first);
@@ -1673,7 +1673,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 // for (uint8_t j = 0; j < nLayer; j++)
                 //{
                 RlcPduListElement_s newRlcEl;
@@ -1686,7 +1686,7 @@
                 UpdateDlRlcBufferInfo(newDci.m_rnti,
                                       newRlcEl.m_logicalChannelIdentity,
                                       newRlcEl.m_size);
-                if (m_harqOn == true)
+                if (m_harqOn)
                 {
                     // store RLC PDU list for HARQ
                     std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1717,7 +1717,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1794,7 +1794,7 @@
 
 void
 CqaFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1803,7 +1803,7 @@
 
 void
 CqaFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     m_ffrSapProvider->ReportDlCqiInfo(params);
@@ -1898,7 +1898,7 @@
 
 void
 CqaFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1924,7 +1924,7 @@
 
     for (std::vector<bool>::iterator it = rbMap.begin(); it != rbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbAllocatedNum++;
         }
@@ -1943,7 +1943,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1984,7 +1984,7 @@
 
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -2125,12 +2125,12 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
                 }
-                if ((m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first)) == false)
+                if (!m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first))
                 {
                     free = false;
                     break;
@@ -2247,7 +2247,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -2327,21 +2327,21 @@
 
 void
 CqaFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 CqaFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 CqaFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -2384,7 +2384,7 @@
 
 void
 CqaFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/cqa-ff-mac-scheduler.h ns-3.39/src/lte/model/cqa-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/cqa-ff-mac-scheduler.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/cqa-ff-mac-scheduler.h	2023-07-05 14:23:47.901427700 -0700
@@ -23,43 +23,22 @@
 #ifndef CQA_FF_MAC_SCHEDULER_H
 #define CQA_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-
-#define NO_SINR -5000
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-/// DL HARQ process status vector typedef
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-/// DL HARQ process timer vector typedef
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-/// DL HARQ process DCI buffer vector typedef
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-/// vector of the LCs and layers per UE
-typedef std::vector<std::vector<struct RlcPduListElement_s>> RlcPduList_t;
-/// vector of the 8 HARQ processes per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t;
-/// UL HARQ process DCI buffer vector
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-/// UL HARQ process status vector
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /// CGA Flow Performance structure
 struct CqasFlowPerf_t
 {
@@ -133,36 +112,31 @@
      * Csched Cell Config Request
      * \param params CschedCellConfigReqParameters&
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * Csched UE Config Request
      * \param params CschedUeConfigReqParameters&
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * Csched LC Config Request
      * \param params CschedLcConfigReqParameters&
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * Csched LC Release Request
      * \param params CschedLcReleaseReqParameters&
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * Csched UE Release Request
      * \param params CschedUeReleaseReqParameters&
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -173,77 +147,70 @@
      * Sched DL RLC Buffer Request
      * \param params SchedDlRlcBufferReqParameters&
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * Sched DL Paging Buffer Request
      * \param params SchedDlPagingBufferReqParameters&
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * Sched DL MAC Buffer Request
      * \param params SchedDlMacBufferReqParameters&
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * Sched DL RLC Buffer Request
      * \param params SchedDlTriggerReqParameters&
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * Sched DL RACH Info Request
      * \param params SchedDlRachInfoReqParameters&
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * Sched DL CGI Info Request
      * \param params SchedDlCqiInfoReqParameters&
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * Sched UL Trigger Request
      * \param params SchedUlTriggerReqParameters&
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * Sched UL Noise InterferenceRequest
      * \param params SchedUlNoiseInterferenceReqParameters&
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * Sched UL Sr Info Request
      * \param params SchedUlSrInfoReqParameters&
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * Sched UL MAC Control Info Request
      * \param params SchedUlMacCtrlInfoReqParameters&
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * Sched UL CGI Info Request
      * \param params SchedUlCqiInfoReqParameters&
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * Get RGB Size
@@ -328,7 +295,7 @@
     /**
      * Map of UE logical channel config list
      */
-    std::map<LteFlowId_t, struct LogicalChannelConfigListElement_s> m_ueLogicalChannelsConfigList;
+    std::map<LteFlowId_t, LogicalChannelConfigListElement_s> m_ueLogicalChannelsConfigList;
 
     /**
      * Map of UE's DL CQI P01 received
@@ -416,9 +383,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 
     std::string m_CqaMetric; ///< CQA metric name
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-enb-application.cc ns-3.39/src/lte/model/epc-enb-application.cc
--- ns-3.38/src/lte/model/epc-enb-application.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-enb-application.cc	2023-07-05 14:23:47.901427700 -0700
@@ -227,7 +227,7 @@
          ++erabIt)
     {
         // request the RRC to setup a radio bearer
-        struct EpcEnbS1SapUser::DataRadioBearerSetupRequestParameters params;
+        EpcEnbS1SapUser::DataRadioBearerSetupRequestParameters params;
         params.rnti = rnti;
         params.bearer = erabIt->erabLevelQosParameters;
         params.bearerId = erabIt->erabId;
@@ -241,7 +241,7 @@
     }
 
     // Send Initial Context Setup Request to RRC
-    struct EpcEnbS1SapUser::InitialContextSetupRequestParameters params;
+    EpcEnbS1SapUser::InitialContextSetupRequestParameters params;
     params.rnti = rnti;
     m_s1SapUser->InitialContextSetupRequest(params);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-enb-application.h ns-3.39/src/lte/model/epc-enb-application.h
--- ns-3.38/src/lte/model/epc-enb-application.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-enb-application.h	2023-07-05 14:23:47.901427700 -0700
@@ -21,13 +21,12 @@
 #ifndef EPC_ENB_APPLICATION_H
 #define EPC_ENB_APPLICATION_H
 
+#include "epc-enb-s1-sap.h"
+#include "epc-s1ap-sap.h"
+
 #include <ns3/address.h>
 #include <ns3/application.h>
 #include <ns3/callback.h>
-#include <ns3/epc-enb-s1-sap.h>
-#include <ns3/epc-s1ap-sap.h>
-#include <ns3/eps-bearer.h>
-#include <ns3/lte-common.h>
 #include <ns3/object.h>
 #include <ns3/ptr.h>
 #include <ns3/socket.h>
@@ -150,7 +149,7 @@
     struct EpsFlowId_t
     {
         uint16_t m_rnti; ///< RNTI
-        uint8_t m_bid;   ///< Bid, the EPS Bearer IDentifier
+        uint8_t m_bid;   ///< Bid, the EPS Bearer Identifier
 
       public:
         EpsFlowId_t();
@@ -237,7 +236,7 @@
      *
      * \param packet t
      * \param rnti maps to enbUeS1Id
-     * \param bid the EPS Bearer IDentifier
+     * \param bid the EPS Bearer Identifier
      */
     void SendToLteSocket(Ptr<Packet> packet, uint16_t rnti, uint8_t bid);
 
@@ -245,16 +244,16 @@
      * Send a packet to the SGW via the S1-U interface
      *
      * \param packet packet to be sent
-     * \param teid the Tunnel Endpoint IDentifier
+     * \param teid the Tunnel Endpoint Identifier
      */
     void SendToS1uSocket(Ptr<Packet> packet, uint32_t teid);
 
     /**
      * internal method used for the actual setup of the S1 Bearer
      *
-     * \param teid the Tunnel Endpoint IDentifier
+     * \param teid the Tunnel Endpoint Identifier
      * \param rnti maps to enbUeS1Id
-     * \param bid the S1-U Bearer IDentifier
+     * \param bid the S1-U Bearer Identifier
      */
     void SetupS1Bearer(uint32_t teid, uint16_t rnti, uint8_t bid);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-enb-s1-sap.cc ns-3.39/src/lte/model/epc-enb-s1-sap.cc
--- ns-3.38/src/lte/model/epc-enb-s1-sap.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-enb-s1-sap.cc	2023-07-05 14:23:47.901427700 -0700
@@ -17,7 +17,7 @@
  * Author: Nicola Baldo <nbaldo@cttc.es>
  */
 
-#include "ns3/epc-enb-s1-sap.h"
+#include "epc-enb-s1-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-enb-s1-sap.h ns-3.39/src/lte/model/epc-enb-s1-sap.h
--- ns-3.38/src/lte/model/epc-enb-s1-sap.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-enb-s1-sap.h	2023-07-05 14:23:47.901427700 -0700
@@ -20,7 +20,8 @@
 #ifndef EPC_ENB_S1_SAP_H
 #define EPC_ENB_S1_SAP_H
 
-#include <ns3/eps-bearer.h>
+#include "eps-bearer.h"
+
 #include <ns3/ipv4-address.h>
 
 #include <list>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-gtpc-header.cc ns-3.39/src/lte/model/epc-gtpc-header.cc
--- ns-3.38/src/lte/model/epc-gtpc-header.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-gtpc-header.cc	2023-07-05 14:23:47.901427700 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/epc-gtpc-header.h"
+#include "epc-gtpc-header.h"
 
 #include "ns3/log.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-gtpc-header.h ns-3.39/src/lte/model/epc-gtpc-header.h
--- ns-3.38/src/lte/model/epc-gtpc-header.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-gtpc-header.h	2023-07-05 14:23:47.901427700 -0700
@@ -20,8 +20,9 @@
 #ifndef EPC_GTPC_HEADER_H
 #define EPC_GTPC_HEADER_H
 
-#include "ns3/epc-tft.h"
-#include "ns3/eps-bearer.h"
+#include "epc-tft.h"
+#include "eps-bearer.h"
+
 #include "ns3/header.h"
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-gtpu-header.cc ns-3.39/src/lte/model/epc-gtpu-header.cc
--- ns-3.38/src/lte/model/epc-gtpu-header.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-gtpu-header.cc	2023-07-05 14:23:47.901427700 -0700
@@ -270,16 +270,12 @@
 bool
 GtpuHeader::operator==(const GtpuHeader& b) const
 {
-    if (m_version == b.m_version && m_protocolType == b.m_protocolType &&
-        m_extensionHeaderFlag == b.m_extensionHeaderFlag &&
-        m_sequenceNumberFlag == b.m_sequenceNumberFlag && m_nPduNumberFlag == b.m_nPduNumberFlag &&
-        m_messageType == b.m_messageType && m_length == b.m_length && m_teid == b.m_teid &&
-        m_sequenceNumber == b.m_sequenceNumber && m_nPduNumber == b.m_nPduNumber &&
-        m_nextExtensionType == b.m_nextExtensionType)
-    {
-        return true;
-    }
-    return false;
+    return m_version == b.m_version && m_protocolType == b.m_protocolType &&
+           m_extensionHeaderFlag == b.m_extensionHeaderFlag &&
+           m_sequenceNumberFlag == b.m_sequenceNumberFlag &&
+           m_nPduNumberFlag == b.m_nPduNumberFlag && m_messageType == b.m_messageType &&
+           m_length == b.m_length && m_teid == b.m_teid && m_sequenceNumber == b.m_sequenceNumber &&
+           m_nPduNumber == b.m_nPduNumber && m_nextExtensionType == b.m_nextExtensionType;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-mme-application.cc ns-3.39/src/lte/model/epc-mme-application.cc
--- ns-3.38/src/lte/model/epc-mme-application.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-mme-application.cc	2023-07-05 14:23:47.905427700 -0700
@@ -17,11 +17,14 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/epc-mme-application.h"
+#include "epc-mme-application.h"
+
+#include "epc-gtpc-header.h"
 
-#include "ns3/epc-gtpc-header.h"
 #include "ns3/log.h"
 
+#include <map>
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-mme-application.h ns-3.39/src/lte/model/epc-mme-application.h
--- ns-3.38/src/lte/model/epc-mme-application.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-mme-application.h	2023-07-05 14:23:47.905427700 -0700
@@ -26,6 +26,8 @@
 #include "ns3/application.h"
 #include "ns3/socket.h"
 
+#include <map>
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-pgw-application.cc ns-3.39/src/lte/model/epc-pgw-application.cc
--- ns-3.38/src/lte/model/epc-pgw-application.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-pgw-application.cc	2023-07-05 14:23:47.905427700 -0700
@@ -18,10 +18,11 @@
  *         (based on epc-sgw-pgw-application.cc)
  */
 
-#include "ns3/epc-pgw-application.h"
+#include "epc-pgw-application.h"
+
+#include "epc-gtpu-header.h"
 
 #include "ns3/abort.h"
-#include "ns3/epc-gtpu-header.h"
 #include "ns3/inet-socket-address.h"
 #include "ns3/ipv4-l3-protocol.h"
 #include "ns3/ipv4.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-pgw-application.h ns-3.39/src/lte/model/epc-pgw-application.h
--- ns-3.38/src/lte/model/epc-pgw-application.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-pgw-application.h	2023-07-05 14:23:47.905427700 -0700
@@ -21,9 +21,10 @@
 #ifndef EPC_PGW_APPLICATION_H
 #define EPC_PGW_APPLICATION_H
 
+#include "epc-gtpc-header.h"
+#include "epc-tft-classifier.h"
+
 #include "ns3/application.h"
-#include "ns3/epc-gtpc-header.h"
-#include "ns3/epc-tft-classifier.h"
 #include "ns3/socket.h"
 #include "ns3/virtual-net-device.h"
 
@@ -123,7 +124,7 @@
      *
      * \param packet packet to be sent
      * \param sgwS5uAddress the address of the SGW
-     * \param teid the Tunnel Endpoint IDentifier
+     * \param teid the Tunnel Endpoint Identifier
      */
     void SendToS5uSocket(Ptr<Packet> packet, Ipv4Address sgwS5uAddress, uint32_t teid);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-s11-sap.h ns-3.39/src/lte/model/epc-s11-sap.h
--- ns-3.38/src/lte/model/epc-s11-sap.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-s11-sap.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,9 +20,10 @@
 #ifndef EPC_S11_SAP_H
 #define EPC_S11_SAP_H
 
+#include "epc-tft.h"
+#include "eps-bearer.h"
+
 #include <ns3/address.h>
-#include <ns3/epc-tft.h>
-#include <ns3/eps-bearer.h>
 #include <ns3/object.h>
 #include <ns3/ptr.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-s1ap-sap.h ns-3.39/src/lte/model/epc-s1ap-sap.h
--- ns-3.38/src/lte/model/epc-s1ap-sap.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-s1ap-sap.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,9 +20,10 @@
 #ifndef EPC_S1AP_SAP_H
 #define EPC_S1AP_SAP_H
 
+#include "eps-bearer.h"
+
 #include <ns3/address.h>
-#include <ns3/epc-tft.h>
-#include <ns3/eps-bearer.h>
+#include <ns3/ipv4-address.h>
 #include <ns3/object.h>
 #include <ns3/ptr.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-sgw-application.cc ns-3.39/src/lte/model/epc-sgw-application.cc
--- ns-3.38/src/lte/model/epc-sgw-application.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-sgw-application.cc	2023-07-05 14:23:47.905427700 -0700
@@ -17,11 +17,14 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/epc-sgw-application.h"
+#include "epc-sgw-application.h"
+
+#include "epc-gtpu-header.h"
 
-#include "ns3/epc-gtpu-header.h"
 #include "ns3/log.h"
 
+#include <map>
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-sgw-application.h ns-3.39/src/lte/model/epc-sgw-application.h
--- ns-3.38/src/lte/model/epc-sgw-application.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-sgw-application.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,11 +20,14 @@
 #ifndef EPC_SGW_APPLICATION_H
 #define EPC_SGW_APPLICATION_H
 
+#include "epc-gtpc-header.h"
+
 #include "ns3/address.h"
 #include "ns3/application.h"
-#include "ns3/epc-gtpc-header.h"
 #include "ns3/socket.h"
 
+#include <map>
+
 namespace ns3
 {
 
@@ -144,7 +147,7 @@
      *
      * \param packet packet to be sent
      * \param enbS1uAddress the address of the eNB
-     * \param teid the Tunnel Endpoint IDentifier
+     * \param teid the Tunnel Endpoint Identifier
      */
     void SendToS1uSocket(Ptr<Packet> packet, Ipv4Address enbS1uAddress, uint32_t teid);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-tft-classifier.h ns-3.39/src/lte/model/epc-tft-classifier.h
--- ns-3.38/src/lte/model/epc-tft-classifier.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-tft-classifier.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,7 +20,8 @@
 #ifndef EPC_TFT_CLASSIFIER_H
 #define EPC_TFT_CLASSIFIER_H
 
-#include "ns3/epc-tft.h"
+#include "epc-tft.h"
+
 #include "ns3/ptr.h"
 #include "ns3/simple-ref-count.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-ue-nas.cc ns-3.39/src/lte/model/epc-ue-nas.cc
--- ns-3.38/src/lte/model/epc-ue-nas.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-ue-nas.cc	2023-07-05 14:23:47.905427700 -0700
@@ -20,11 +20,11 @@
 #include "epc-ue-nas.h"
 
 #include "lte-as-sap.h"
-#include "lte-enb-net-device.h"
 
 #include <ns3/epc-helper.h>
 #include <ns3/fatal-error.h>
 #include <ns3/log.h>
+#include <ns3/simulator.h>
 
 namespace ns3
 {
@@ -222,7 +222,6 @@
     default:
         NS_LOG_WARN(this << " NAS OFF, discarding packet");
         return false;
-        break;
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-ue-nas.h ns-3.39/src/lte/model/epc-ue-nas.h
--- ns-3.38/src/lte/model/epc-ue-nas.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-ue-nas.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,10 +20,10 @@
 #ifndef EPC_UE_NAS_H
 #define EPC_UE_NAS_H
 
+#include "epc-tft-classifier.h"
 #include "eps-bearer.h"
+#include "lte-as-sap.h"
 
-#include <ns3/epc-tft-classifier.h>
-#include <ns3/lte-as-sap.h>
 #include <ns3/object.h>
 #include <ns3/traced-callback.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-x2.cc ns-3.39/src/lte/model/epc-x2.cc
--- ns-3.38/src/lte/model/epc-x2.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-x2.cc	2023-07-05 14:23:47.905427700 -0700
@@ -17,10 +17,11 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/epc-x2.h"
+#include "epc-x2.h"
+
+#include "epc-gtpu-header.h"
+#include "epc-x2-header.h"
 
-#include "ns3/epc-gtpu-header.h"
-#include "ns3/epc-x2-header.h"
 #include "ns3/inet-socket-address.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-x2.h ns-3.39/src/lte/model/epc-x2.h
--- ns-3.38/src/lte/model/epc-x2.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-x2.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,8 +20,9 @@
 #ifndef EPC_X2_H
 #define EPC_X2_H
 
+#include "epc-x2-sap.h"
+
 #include "ns3/callback.h"
-#include "ns3/epc-x2-sap.h"
 #include "ns3/object.h"
 #include "ns3/ptr.h"
 #include "ns3/socket.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-x2-header.cc ns-3.39/src/lte/model/epc-x2-header.cc
--- ns-3.38/src/lte/model/epc-x2-header.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-x2-header.cc	2023-07-05 14:23:47.905427700 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/epc-x2-header.h"
+#include "epc-x2-header.h"
 
 #include "ns3/log.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-x2-header.h ns-3.39/src/lte/model/epc-x2-header.h
--- ns-3.38/src/lte/model/epc-x2-header.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-x2-header.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,7 +20,8 @@
 #ifndef EPC_X2_HEADER_H
 #define EPC_X2_HEADER_H
 
-#include "ns3/epc-x2-sap.h"
+#include "epc-x2-sap.h"
+
 #include "ns3/header.h"
 
 #include <vector>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-x2-sap.cc ns-3.39/src/lte/model/epc-x2-sap.cc
--- ns-3.38/src/lte/model/epc-x2-sap.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-x2-sap.cc	2023-07-05 14:23:47.905427700 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/epc-x2-sap.h"
+#include "epc-x2-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/epc-x2-sap.h ns-3.39/src/lte/model/epc-x2-sap.h
--- ns-3.38/src/lte/model/epc-x2-sap.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/epc-x2-sap.h	2023-07-05 14:23:47.905427700 -0700
@@ -20,7 +20,8 @@
 #ifndef EPC_X2_SAP_H
 #define EPC_X2_SAP_H
 
-#include "ns3/eps-bearer.h"
+#include "eps-bearer.h"
+
 #include "ns3/ipv4-address.h"
 #include "ns3/packet.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/eps-bearer.cc ns-3.39/src/lte/model/eps-bearer.cc
--- ns-3.38/src/lte/model/eps-bearer.cc	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/eps-bearer.cc	2023-07-05 14:23:47.905427700 -0700
@@ -52,7 +52,7 @@
             .AddConstructor<EpsBearer>()
             .AddAttribute(
                 "Release",
-                "Change from 11 to 15 if you need bearer definition as per Release 15."
+                "Change from 11 to 18 if you need bearer definition as per newer Releases."
                 " Reference document: TS 23.203. The change does not impact other LTE code than "
                 " bearers definition.",
                 UintegerValue(11),
@@ -111,38 +111,42 @@
     case 15:
         m_requirements = GetRequirementsRel15();
         break;
+    case 18:
+        m_requirements = GetRequirementsRel18();
+        break;
     default:
         NS_FATAL_ERROR("Not recognized release " << static_cast<uint32_t>(release)
-                                                 << " please use a value between 8 and 11, or 15");
+                                                 << " please choose a value between"
+                                                    " 8 and 11, or 15 or 18");
     }
     m_release = release;
 }
 
-bool
-EpsBearer::IsGbr() const
+uint8_t
+EpsBearer::GetResourceType() const
 {
-    return IsGbr(*m_requirements, qci);
+    return GetResourceType(m_requirements, qci);
 }
 
 uint8_t
 EpsBearer::GetPriority() const
 {
-    return GetPriority(*m_requirements, qci);
+    return GetPriority(m_requirements, qci);
 }
 
 uint16_t
 EpsBearer::GetPacketDelayBudgetMs() const
 {
-    return GetPacketDelayBudgetMs(*m_requirements, qci);
+    return GetPacketDelayBudgetMs(m_requirements, qci);
 }
 
 double
 EpsBearer::GetPacketErrorLossRate() const
 {
-    return GetPacketErrorLossRate(*m_requirements, qci);
+    return GetPacketErrorLossRate(m_requirements, qci);
 }
 
-EpsBearer::BearerRequirementsMap*
+const EpsBearer::BearerRequirementsMap&
 EpsBearer::GetRequirementsRel11()
 {
     /* Needed to support GCC 4.9. Otherwise, use list constructors, for example:
@@ -151,74 +155,93 @@
      * {
      *   return
      *     {
-     *       { GBR_CONV_VOICE          , { true,  20, 100, 1.0e-2,    0, 2000} },
+     *       { GBR_CONV_VOICE          , { 1,  20, 100, 1.0e-2,    0, 2000} },
      *       ...
      *     };
      * }
      */
-    static EpsBearer::BearerRequirementsMap ret;
-
-    if (ret.empty())
-    {
-        ret.insert(std::make_pair(GBR_CONV_VOICE, std::make_tuple(true, 2, 100, 1.0e-2, 0, 0)));
-        ret.insert(std::make_pair(GBR_CONV_VIDEO, std::make_tuple(true, 4, 150, 1.0e-3, 0, 0)));
-        ret.insert(std::make_pair(GBR_GAMING, std::make_tuple(true, 3, 50, 1.0e-3, 0, 0)));
-        ret.insert(std::make_pair(GBR_NON_CONV_VIDEO, std::make_tuple(true, 5, 300, 1.0e-6, 0, 0)));
-        ret.insert(std::make_pair(NGBR_IMS, std::make_tuple(false, 1, 100, 1.0e-6, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VIDEO_TCP_OPERATOR, std::make_tuple(false, 6, 300, 1.0e-6, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VOICE_VIDEO_GAMING, std::make_tuple(false, 7, 100, 1.0e-3, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VIDEO_TCP_PREMIUM, std::make_tuple(false, 8, 300, 1.0e-6, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VIDEO_TCP_DEFAULT, std::make_tuple(false, 9, 300, 1.0e-6, 0, 0)));
-    }
-    return &ret;
+    static EpsBearer::BearerRequirementsMap ret{
+        {GBR_CONV_VOICE, std::make_tuple(1, 2, 100, 1.0e-2, 0, 0)},
+        {GBR_CONV_VIDEO, std::make_tuple(1, 4, 150, 1.0e-3, 0, 0)},
+        {GBR_GAMING, std::make_tuple(1, 3, 50, 1.0e-3, 0, 0)},
+        {GBR_NON_CONV_VIDEO, std::make_tuple(1, 5, 300, 1.0e-6, 0, 0)},
+        {NGBR_IMS, std::make_tuple(0, 1, 100, 1.0e-6, 0, 0)},
+        {NGBR_VIDEO_TCP_OPERATOR, std::make_tuple(0, 6, 300, 1.0e-6, 0, 0)},
+        {NGBR_VOICE_VIDEO_GAMING, std::make_tuple(0, 7, 100, 1.0e-3, 0, 0)},
+        {NGBR_VIDEO_TCP_PREMIUM, std::make_tuple(0, 8, 300, 1.0e-6, 0, 0)},
+        {NGBR_VIDEO_TCP_DEFAULT, std::make_tuple(0, 9, 300, 1.0e-6, 0, 0)},
+    };
+    return ret;
 }
 
-EpsBearer::BearerRequirementsMap*
+const EpsBearer::BearerRequirementsMap&
 EpsBearer::GetRequirementsRel15()
 {
     // Needed to support GCC 4.9. Otherwise, use list constructors (see GetRequirementsRel10)
-    static EpsBearer::BearerRequirementsMap ret;
+    static EpsBearer::BearerRequirementsMap ret{
+        {GBR_CONV_VOICE, std::make_tuple(1, 20, 100, 1.0e-2, 0, 2000)},
+        {GBR_CONV_VIDEO, std::make_tuple(1, 40, 150, 1.0e-3, 0, 2000)},
+        {GBR_GAMING, std::make_tuple(1, 30, 50, 1.0e-3, 0, 2000)},
+        {GBR_NON_CONV_VIDEO, std::make_tuple(1, 50, 300, 1.0e-6, 0, 2000)},
+        {GBR_MC_PUSH_TO_TALK, std::make_tuple(1, 7, 75, 1.0e-2, 0, 2000)},
+        {GBR_NMC_PUSH_TO_TALK, std::make_tuple(1, 20, 100, 1.0e-2, 0, 2000)},
+        {GBR_MC_VIDEO, std::make_tuple(1, 15, 100, 1.0e-3, 0, 2000)},
+        {GBR_V2X, std::make_tuple(1, 25, 50, 1.0e-2, 0, 2000)},
+        {NGBR_IMS, std::make_tuple(0, 10, 100, 1.0e-6, 0, 0)},
+        {NGBR_VIDEO_TCP_OPERATOR, std::make_tuple(0, 60, 300, 1.0e-6, 0, 0)},
+        {NGBR_VOICE_VIDEO_GAMING, std::make_tuple(0, 70, 100, 1.0e-3, 0, 0)},
+        {NGBR_VIDEO_TCP_PREMIUM, std::make_tuple(0, 80, 300, 1.0e-6, 0, 0)},
+        {NGBR_VIDEO_TCP_DEFAULT, std::make_tuple(0, 90, 300, 1.0e-6, 0, 0)},
+        {NGBR_MC_DELAY_SIGNAL, std::make_tuple(0, 5, 60, 1.0e-6, 0, 0)},
+        {NGBR_MC_DATA, std::make_tuple(0, 55, 200, 1.0e-6, 0, 0)},
+        {NGBR_V2X, std::make_tuple(0, 65, 5, 1.0e-2, 0, 0)},
+        {NGBR_LOW_LAT_EMBB, std::make_tuple(0, 68, 10, 1.0e-6, 0, 0)},
+        {DGBR_DISCRETE_AUT_SMALL, std::make_tuple(2, 19, 10, 1.0e-4, 255, 2000)},
+        {DGBR_DISCRETE_AUT_LARGE, std::make_tuple(2, 22, 10, 1.0e-4, 1358, 2000)},
+        {DGBR_ITS, std::make_tuple(2, 24, 30, 1.0e-5, 1354, 2000)},
+        {DGBR_ELECTRICITY, std::make_tuple(2, 21, 5, 1.0e-5, 255, 2000)},
+    };
+    return ret;
+}
 
-    if (ret.empty())
-    {
-        ret.insert(std::make_pair(GBR_CONV_VOICE, std::make_tuple(true, 20, 100, 1.0e-2, 0, 2000)));
-        ret.insert(std::make_pair(GBR_CONV_VIDEO, std::make_tuple(true, 40, 150, 1.0e-3, 0, 2000)));
-        ret.insert(std::make_pair(GBR_GAMING, std::make_tuple(true, 30, 50, 1.0e-3, 0, 2000)));
-        ret.insert(
-            std::make_pair(GBR_NON_CONV_VIDEO, std::make_tuple(true, 50, 300, 1.0e-6, 0, 2000)));
-        ret.insert(
-            std::make_pair(GBR_MC_PUSH_TO_TALK, std::make_tuple(true, 7, 75, 1.0e-2, 0, 2000)));
-        ret.insert(
-            std::make_pair(GBR_NMC_PUSH_TO_TALK, std::make_tuple(true, 20, 100, 1.0e-2, 0, 2000)));
-        ret.insert(std::make_pair(GBR_MC_VIDEO, std::make_tuple(true, 15, 100, 1.0e-3, 0, 2000)));
-        ret.insert(std::make_pair(GBR_V2X, std::make_tuple(true, 25, 50, 1.0e-2, 0, 2000)));
-        ret.insert(std::make_pair(NGBR_IMS, std::make_tuple(false, 10, 100, 1.0e-6, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VIDEO_TCP_OPERATOR, std::make_tuple(false, 60, 300, 1.0e-6, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VOICE_VIDEO_GAMING, std::make_tuple(false, 70, 100, 1.0e-3, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VIDEO_TCP_PREMIUM, std::make_tuple(false, 80, 300, 1.0e-6, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_VIDEO_TCP_DEFAULT, std::make_tuple(false, 90, 300, 1.0e-6, 0, 0)));
-        ret.insert(
-            std::make_pair(NGBR_MC_DELAY_SIGNAL, std::make_tuple(false, 5, 60, 1.0e-6, 0, 0)));
-        ret.insert(std::make_pair(NGBR_MC_DATA, std::make_tuple(false, 55, 200, 1.0e-6, 0, 0)));
-        ret.insert(std::make_pair(NGBR_V2X, std::make_tuple(false, 65, 5, 1.0e-2, 0, 0)));
-        ret.insert(std::make_pair(NGBR_LOW_LAT_EMBB, std::make_tuple(false, 68, 10, 1.0e-6, 0, 0)));
-        ret.insert(std::make_pair(DGBR_DISCRETE_AUT_SMALL,
-                                  std::make_tuple(false, 19, 10, 1.0e-4, 255, 2000)));
-        ret.insert(std::make_pair(DGBR_DISCRETE_AUT_LARGE,
-                                  std::make_tuple(false, 22, 10, 1.0e-4, 1358, 2000)));
-        ret.insert(std::make_pair(DGBR_ITS, std::make_tuple(false, 24, 30, 1.0e-5, 1354, 2000)));
-        ret.insert(
-            std::make_pair(DGBR_ELECTRICITY, std::make_tuple(false, 21, 5, 1.0e-5, 255, 2000)));
-    }
-    return &ret;
+const EpsBearer::BearerRequirementsMap&
+EpsBearer::GetRequirementsRel18()
+{
+    // Needed to support GCC 4.9. Otherwise, use list constructors (see GetRequirementsRel10)
+    static EpsBearer::BearerRequirementsMap ret{
+        {GBR_CONV_VOICE, std::make_tuple(1, 20, 100, 1.0e-2, 0, 2000)},
+        {GBR_CONV_VIDEO, std::make_tuple(1, 40, 150, 1.0e-3, 0, 2000)},
+        {GBR_GAMING, std::make_tuple(1, 30, 50, 1.0e-3, 0, 2000)},
+        {GBR_NON_CONV_VIDEO, std::make_tuple(1, 50, 300, 1.0e-6, 0, 2000)},
+        {GBR_MC_PUSH_TO_TALK, std::make_tuple(1, 7, 75, 1.0e-2, 0, 2000)},
+        {GBR_NMC_PUSH_TO_TALK, std::make_tuple(1, 20, 100, 1.0e-2, 0, 2000)},
+        {GBR_MC_VIDEO, std::make_tuple(1, 15, 100, 1.0e-3, 0, 2000)},
+        {GBR_V2X, std::make_tuple(1, 25, 50, 1.0e-2, 0, 2000)},
+        {NGBR_IMS, std::make_tuple(0, 10, 100, 1.0e-6, 0, 0)},
+        {NGBR_VIDEO_TCP_OPERATOR, std::make_tuple(0, 60, 300, 1.0e-6, 0, 0)},
+        {NGBR_VOICE_VIDEO_GAMING, std::make_tuple(0, 70, 100, 1.0e-3, 0, 0)},
+        {NGBR_VIDEO_TCP_PREMIUM, std::make_tuple(0, 80, 300, 1.0e-6, 0, 0)},
+        {NGBR_VIDEO_TCP_DEFAULT, std::make_tuple(0, 90, 300, 1.0e-6, 0, 0)},
+        {NGBR_MC_DELAY_SIGNAL, std::make_tuple(0, 5, 60, 1.0e-6, 0, 0)},
+        {NGBR_MC_DATA, std::make_tuple(0, 55, 200, 1.0e-6, 0, 0)},
+        {NGBR_V2X, std::make_tuple(0, 65, 5, 1.0e-2, 0, 0)},
+        {NGBR_LOW_LAT_EMBB, std::make_tuple(0, 68, 10, 1.0e-6, 0, 0)},
+        {GBR_LIVE_UL_71, std::make_tuple(1, 56, 150, 1.0e-6, 0, 0)},
+        {GBR_LIVE_UL_72, std::make_tuple(1, 56, 300, 1.0e-4, 0, 0)},
+        {GBR_LIVE_UL_73, std::make_tuple(1, 56, 300, 1.0e-8, 0, 0)},
+        {GBR_LIVE_UL_74, std::make_tuple(1, 56, 500, 1.0e-8, 0, 0)},
+        {GBR_LIVE_UL_76, std::make_tuple(1, 56, 500, 1.0e-4, 0, 0)},
+        {DGBR_DISCRETE_AUT_SMALL, std::make_tuple(2, 19, 10, 1.0e-4, 255, 2000)},
+        {DGBR_DISCRETE_AUT_LARGE, std::make_tuple(2, 22, 10, 1.0e-4, 1358, 2000)},
+        {DGBR_ITS, std::make_tuple(2, 24, 30, 1.0e-5, 1354, 2000)},
+        {DGBR_ELECTRICITY, std::make_tuple(2, 21, 5, 1.0e-5, 255, 2000)},
+        {DGBR_V2X, std::make_tuple(2, 18, 5, 1.0e-4, 1354, 2000)},
+        {DGBR_INTER_SERV_87, std::make_tuple(2, 25, 5, 1.0e-3, 500, 2000)},
+        {DGBR_INTER_SERV_88, std::make_tuple(2, 25, 10, 1.0e-3, 1125, 2000)},
+        {DGBR_VISUAL_CONTENT_89, std::make_tuple(2, 25, 15, 1.0e-4, 17000, 2000)},
+        {DGBR_VISUAL_CONTENT_90, std::make_tuple(2, 25, 20, 1.0e-4, 63000, 2000)},
+    };
+    return ret;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/eps-bearer.h ns-3.39/src/lte/model/eps-bearer.h
--- ns-3.38/src/lte/model/eps-bearer.h	2023-03-16 20:54:23.664868600 -0700
+++ ns-3.39/src/lte/model/eps-bearer.h	2023-07-05 14:23:47.905427700 -0700
@@ -79,10 +79,10 @@
  *
  * Please note that from Release 8 (the latest when the LENA project finished)
  * to Release 11, the bearers ID and requirements are the same. From Release 12,
- * they started to change, and the latest version is now Release 15. However,
- * we do not support intermediate types: in other words, you can select from
- * Release 8 to Release 11, or Release 15. Any other value will result in a
- * program crash.
+ * they started to change, and the latest version is now Release 18. However,
+ * we do not support intermediate types between releases 11 and 15: in other words,
+ * you can select from Release 8 to Release 11, or Release 15 or 18.
+ * Any other value will result in a program crash.
  *
  * The release version only affect Bearer definitions. Other part of the LTE
  * module are not affected when changing the Release attribute.
@@ -100,7 +100,7 @@
 
     /**
      * QoS Class Indicator. See 3GPP 23.203 Section 6.1.7.2 for standard values.
-     * Updated to Release 15.
+     * Updated to Release 18.
      */
     enum Qci : uint8_t
     {
@@ -112,6 +112,11 @@
         GBR_NMC_PUSH_TO_TALK = 66, ///< GBR Non-Mission-Critical User Plane Push To Talk voice
         GBR_MC_VIDEO = 67,         ///< GBR Mission Critical Video User Plane
         GBR_V2X = 75,              ///< GBR V2X Messages
+        GBR_LIVE_UL_71 = 71,       ///< GBR Live UL streaming
+        GBR_LIVE_UL_72 = 72,       ///< GBR Live UL streaming
+        GBR_LIVE_UL_73 = 73,       ///< GBR Live UL streaming
+        GBR_LIVE_UL_74 = 74,       ///< GBR Live UL streaming
+        GBR_LIVE_UL_76 = 76,       ///< GBR Live UL streaming
         NGBR_IMS = 5,              ///< Non-GBR IMS Signalling
         NGBR_VIDEO_TCP_OPERATOR =
             6, ///< Non-GBR TCP-based Video (Buffered Streaming, e.g., www, e-mail...)
@@ -131,7 +136,16 @@
             83,        ///< Delay-Critical GBR Discrete Automation Large Packets (TS 22.261)
         DGBR_ITS = 84, ///< Delay-Critical GBR Intelligent Transport Systems (TS 22.261)
         DGBR_ELECTRICITY =
-            85, ///< Delay-Critical GBR Electricity Distribution High Voltage (TS 22.261)
+            85,        ///< Delay-Critical GBR Electricity Distribution High Voltage (TS 22.261)
+        DGBR_V2X = 86, ///< Delay-Critical GBR V2X Messages (TS 23.501)
+        DGBR_INTER_SERV_87 =
+            87, ///< Delay-Critical GBR Interactive Service - Motion tracking data (TS 23.501)
+        DGBR_INTER_SERV_88 =
+            88, ///< Delay-Critical GBR Interactive Service - Motion tracking data (TS 23.501)
+        DGBR_VISUAL_CONTENT_89 =
+            89, ///< Delay-Critical GBR Visual Content for cloud/edge/split rendering (TS 23.501)
+        DGBR_VISUAL_CONTENT_90 =
+            90, ///< Delay-Critical GBR Visual Content for cloud/edge/split rendering (TS 23.501)
     };
 
     Qci qci; ///< Qos class indicator
@@ -198,9 +212,9 @@
 
     /**
      *
-     * @return true if the EPS Bearer is a Guaranteed Bit Rate bearer, false otherwise
+     * @return the resource type (NON-GBR, GBR, DC-GBR) of the selected QCI
      */
-    bool IsGbr() const;
+    uint8_t GetResourceType() const;
 
     /**
      *
@@ -250,20 +264,21 @@
     /**
      * \brief Map between QCI and requirements
      *
-     * The tuple is formed by: isGbr, priority, packet delay budget, packet error rate,
+     * The tuple is formed by: resource type, priority, packet delay budget, packet error rate,
      *  default maximum data burst, default averaging window (0 when does not apply)
      */
-    typedef std::
-        unordered_map<Qci, std::tuple<bool, uint8_t, uint16_t, double, uint32_t, uint32_t>, QciHash>
-            BearerRequirementsMap;
+    typedef std::unordered_map<Qci,
+                               std::tuple<uint8_t, uint8_t, uint16_t, double, uint32_t, uint32_t>,
+                               QciHash>
+        BearerRequirementsMap;
 
     /**
-     * \brief Is the selected QCI GBR?
+     * \brief Get the resource type (NON-GBR, GBR, DC-GBR) of the selected QCI
      * \param map Map between QCI and requirements
      * \param qci QCI to look for
-     * \return GBR flag for the selected CQI
+     * \return the resource type (NON-GBR, GBR, DC-GBR) of the selected QCI
      */
-    static uint32_t IsGbr(const BearerRequirementsMap& map, Qci qci)
+    static uint8_t GetResourceType(const BearerRequirementsMap& map, Qci qci)
     {
         return std::get<0>(map.at(qci));
     }
@@ -334,7 +349,7 @@
      *
      * To be revisited when GCC 4.9 will not be supported anymore.
      */
-    static BearerRequirementsMap* GetRequirementsRel11();
+    static const BearerRequirementsMap& GetRequirementsRel11();
 
     /**
      * \brief Retrieve requirements for Rel. 15
@@ -347,16 +362,22 @@
      *
      * To be revisited when GCC 4.9 will not be supported anymore.
      */
-    static BearerRequirementsMap* GetRequirementsRel15();
+    static const BearerRequirementsMap& GetRequirementsRel15();
+
+    /**
+     * \brief Retrieve requirements for Rel. 18
+     * \return the BearerRequirementsMap for Release 18
+     */
+    static const BearerRequirementsMap& GetRequirementsRel18();
 
     /**
      * \brief Requirements pointer per bearer
      *
      * It will point to a static map.
      */
-    BearerRequirementsMap* m_requirements;
+    BearerRequirementsMap m_requirements;
 
-    uint8_t m_release{30}; //!< Release (10 or 15)
+    uint8_t m_release{30}; //!< Release (10 or 15 or 18)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.cc ns-3.39/src/lte/model/fdbet-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/fdbet-ff-mac-scheduler.cc	2023-07-05 14:23:48.009428000 -0700
@@ -18,11 +18,13 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "fdbet-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
-#include <ns3/fdbet-ff-mac-scheduler.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
@@ -140,7 +142,7 @@
 
 void
 FdBetFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -153,7 +155,7 @@
 
 void
 FdBetFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -199,7 +201,7 @@
 
 void
 FdBetFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -228,7 +230,7 @@
 
 void
 FdBetFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -255,7 +257,7 @@
 
 void
 FdBetFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -295,7 +297,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -320,7 +322,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -328,7 +330,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -390,14 +392,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -405,7 +401,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -476,7 +472,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -508,7 +504,7 @@
     // RACH Allocation
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -550,7 +546,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -615,12 +611,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -711,7 +707,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -739,7 +735,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -810,7 +806,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -971,7 +967,7 @@
         do
         {
             NS_LOG_INFO(this << " ALLOCATION for RBG " << i << " of " << rbgNum);
-            if (rbgMap.at(i) == false)
+            if (!rbgMap.at(i))
             {
                 // allocate one RBG to current UE
                 std::map<uint16_t, std::vector<uint16_t>>::iterator itMap;
@@ -1130,7 +1126,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1142,7 +1138,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1172,7 +1168,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1233,7 +1229,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1242,7 +1238,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1336,7 +1332,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1365,7 +1361,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1405,7 +1401,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1541,7 +1537,7 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
@@ -1656,7 +1652,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1736,21 +1732,21 @@
 
 void
 FdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 FdBetFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 FdBetFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1793,7 +1789,7 @@
 
 void
 FdBetFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.h ns-3.39/src/lte/model/fdbet-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/fdbet-ff-mac-scheduler.h	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/fdbet-ff-mac-scheduler.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,37 +21,21 @@
 #ifndef FDBET_FF_MAC_SCHEDULER_H
 #define FDBET_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-#define NO_SINR -5000
-
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /// fdbetsFlowPerf_t structure
 struct fdbetsFlowPerf_t
 {
@@ -124,36 +108,31 @@
      * CSched cell config request function
      * \param params the CSched cell config request parameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * Csched UE config request function
      * \param params the CSched UE config request parameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * Csched LC config request function
      * \param params the CSched LC config request parameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * CSched LC release request function
      * \param params the CSched LC release request parameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * CSched UE release request function
      * \param params the CSChed UE release request parameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -164,78 +143,71 @@
      * Sched DL RLC buffer request function
      * \param params the Sched DL RLC buffer request parameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * Sched DL paging buffer request function
      * \param params the Sched DL paging buffer request parameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * Sched DL MAC buffer request function
      * \param params the Sched DL MAC buffer request parameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * Sched DL trigger request function
      *
-     * \param params struct FfMacSchedSapProvider::SchedDlTriggerReqParameters&
+     * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters&
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * Sched DL RACH info request function
      * \param params the Sched DL RACH info request parameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * Sched DL CGI info request function
      * \param params the Sched DL CGI info request parameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * Sched UL trigger request function
      * \param params the Sched UL trigger request parameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * Sched UL noise interference request function
      * \param params the Sched UL noise interference request parameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * Sched UL SR info request function
      * \param params the Schedul UL SR info request parameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * Sched UL MAC control info request function
      * \param params the Sched UL MAC control info request parameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * Sched UL CGI info request function
      * \param params the Sched UL CGI info request parameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * Get RBG size function
@@ -403,9 +375,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI Buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< rach list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< rach allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< rach list
+    std::vector<uint16_t> m_rachAllocationMap; ///< rach allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.cc ns-3.39/src/lte/model/fdmt-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/fdmt-ff-mac-scheduler.cc	2023-07-05 14:23:48.009428000 -0700
@@ -18,11 +18,13 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "fdmt-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
-#include <ns3/fdmt-ff-mac-scheduler.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
@@ -139,7 +141,7 @@
 
 void
 FdMtFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -152,7 +154,7 @@
 
 void
 FdMtFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -198,7 +200,7 @@
 
 void
 FdMtFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -217,7 +219,7 @@
 
 void
 FdMtFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -244,7 +246,7 @@
 
 void
 FdMtFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -284,7 +286,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -309,7 +311,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -317,7 +319,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -379,14 +381,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -394,7 +390,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -465,7 +461,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -497,7 +493,7 @@
     // RACH Allocation
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -539,7 +535,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -604,12 +600,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -700,7 +696,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -728,7 +724,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -799,7 +795,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -887,7 +883,7 @@
     for (int i = 0; i < rbgNum; i++)
     {
         NS_LOG_INFO(this << " ALLOCATION for RBG " << i << " of " << rbgNum);
-        if (rbgMap.at(i) == false)
+        if (!rbgMap.at(i))
         {
             std::set<uint16_t>::iterator it;
             std::set<uint16_t>::iterator itMax = m_flowStatsDl.end();
@@ -1117,7 +1113,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1129,7 +1125,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1159,7 +1155,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1193,7 +1189,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1202,7 +1198,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1296,7 +1292,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1325,7 +1321,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1365,7 +1361,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1500,7 +1496,7 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
@@ -1615,7 +1611,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1670,21 +1666,21 @@
 
 void
 FdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 FdMtFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 FdMtFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1727,7 +1723,7 @@
 
 void
 FdMtFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.h ns-3.39/src/lte/model/fdmt-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/fdmt-ff-mac-scheduler.h	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/fdmt-ff-mac-scheduler.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,43 +21,22 @@
 #ifndef FDMT_FF_MAC_SCHEDULER_H
 #define FDMT_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-/**
- * value for SINR outside the range defined by FF-API, used to indicate that there
- * is no CQI for this element
- */
-#define NO_SINR -5000
-
-/// number of HARQ processes
-#define HARQ_PROC_NUM 8
-/// HARQ DL timeout
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t; ///< vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t>
-    DlHarqRlcPduListBuffer_t; ///< vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /**
  * \ingroup ff-api
  * \brief Implements the SCHED SAP and CSCHED SAP for a Frequency Domain Maximize Throughput
@@ -121,36 +100,31 @@
      * Csched cell config request function
      * \param params the CSched cell config request parameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * CSched UE config request function
      * \param params the CSChed UE config request parameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * CSched LC config request function
      * \param params the CSChed LC config request parameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * CSched LC release request function
      * \param params the CSched LC release request parameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * CSched UE release request function
      * \param params th CSched UE release request parameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -161,78 +135,71 @@
      * Sched DL RLC buffer request function
      * \param params the Sched DL RLC buffer request parameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * Sched DL paging buffer request function
-     * \param params the Sched DL paging buffer request paarameters
+     * \param params the Sched DL paging buffer request parameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * Sched DL MAC buffer request function
      * \param params the Sched DL MAC buffer request parameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * Sched DL trigger request function
      *
-     * \param params struct FfMacSchedSapProvider::SchedDlTriggerReqParameters&
+     * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters&
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * Sched DL RACH info request function
      * \param params the Sched DL RACH info request parameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * Sched DL CQI info request function
      * \param params the Sched DL CQI info request parameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * Sched UL trigger request function
      * \param params the Sched UL trigger request parameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * Sched UL noise interference request function
      * \param params the Sched UL noise interference request parameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * Sched UL SR info request function
      * \param params the Sched UL SR info request parameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * Sched UL MAC control info request function
      * \param params the Sched UL MAC control info request parameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * Sched UL CQI info request function
      * \param params the Sched UL CQI info request parameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * Get RBG size function
@@ -397,9 +364,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.cc ns-3.39/src/lte/model/fdtbfq-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/fdtbfq-ff-mac-scheduler.cc	2023-07-05 14:23:48.009428000 -0700
@@ -18,12 +18,14 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "fdtbfq-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
-#include <ns3/fdtbfq-ff-mac-scheduler.h>
 #include <ns3/integer.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
@@ -165,7 +167,7 @@
 
 void
 FdTbfqFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -178,7 +180,7 @@
 
 void
 FdTbfqFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -224,7 +226,7 @@
 
 void
 FdTbfqFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -282,7 +284,7 @@
 
 void
 FdTbfqFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -309,7 +311,7 @@
 
 void
 FdTbfqFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -349,7 +351,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -374,7 +376,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -382,7 +384,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -444,14 +446,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -459,7 +455,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -530,7 +526,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -553,7 +549,7 @@
     rbgMap = m_ffrSapProvider->GetAvailableDlRbg();
     for (std::vector<bool>::iterator it = rbgMap.begin(); it != rbgMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbgAllocatedNum++;
         }
@@ -581,7 +577,7 @@
 
     for (std::vector<bool>::iterator it = ulRbMap.begin(); it != ulRbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             if (tmpMinBandwidth > maxContinuousUlBandwidth)
             {
@@ -610,7 +606,7 @@
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
     rbStart = ffrRbStartOffset;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -652,7 +648,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -717,12 +713,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -813,7 +809,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -841,7 +837,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -912,7 +908,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -1092,7 +1088,7 @@
             double metric =
                 (((double)(*it).second.counter) / ((double)(*it).second.tokenGenerationRate));
 
-            if (firstRnti == true)
+            if (firstRnti)
             {
                 metricMax = metric;
                 itMax = it;
@@ -1198,12 +1194,12 @@
                     continue;
                 }
 
-                if (rbgMap.at(k) == true) // this RBG is allocated in RACH procedure
+                if (rbgMap.at(k)) // this RBG is allocated in RACH procedure
                 {
                     continue;
                 }
 
-                if ((m_ffrSapProvider->IsDlRbgAvailableForUe(k, (*itMax).first)) == false)
+                if (!m_ffrSapProvider->IsDlRbgAvailableForUe(k, (*itMax).first))
                 {
                     continue;
                 }
@@ -1516,7 +1512,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1528,7 +1524,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1558,7 +1554,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1592,7 +1588,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1601,7 +1597,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     m_ffrSapProvider->ReportDlCqiInfo(params);
@@ -1696,7 +1692,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1722,7 +1718,7 @@
 
     for (std::vector<bool>::iterator it = rbMap.begin(); it != rbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbAllocatedNum++;
         }
@@ -1741,7 +1737,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1781,7 +1777,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1922,12 +1918,12 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
                 }
-                if ((m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first)) == false)
+                if (!m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first))
                 {
                     free = false;
                     break;
@@ -2044,7 +2040,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -2099,21 +2095,21 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 FdTbfqFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 FdTbfqFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -2156,7 +2152,7 @@
 
 void
 FdTbfqFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.h ns-3.39/src/lte/model/fdtbfq-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/fdtbfq-ff-mac-scheduler.h	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/fdtbfq-ff-mac-scheduler.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,42 +21,21 @@
 #ifndef FDTBFQ_FF_MAC_SCHEDULER_H
 #define FDTBFQ_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <vector>
 
-/**
- * value for SINR outside the range defined by FF-API, used to indicate that there
- * is no CQI for this element
- */
-#define NO_SINR -5000
-
-/// HARQ number of processes
-#define HARQ_PROC_NUM 8
-/// HARQ DL timeout
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t; ///< vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t>
-    DlHarqRlcPduListBuffer_t; ///< vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /**
  *  Flow information
  */
@@ -162,36 +141,31 @@
      * Csched cell config request function
      * \param params the CSched cell config request parameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * CSched UE config request function
      * \param params CSChed UE config request parameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * CSched LC config request function
      * \param params the CSched LC config request parameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * CSched LC release request function
-     * \param params the CSChed LC releease request parameters
+     * \param params the CSChed LC release request parameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * CSched UE release request function
      * \param params the CSched UE release request parameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -202,78 +176,71 @@
      * Sched DL RLC buffer request function
      * \param params the Sched DL RLC buffer request parameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * Sched DL paging buffer request function
      * \param params the Sched DL paging buffer request parameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * Sched DL MAC  buffer request function
      * \param params Sched DL MAC buffer request parameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * Sched DL trigger request function
      *
-     * \param params struct FfMacSchedSapProvider::SchedDlTriggerReqParameters&
+     * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters&
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * Sched DL RACH info request function
      * \param params the Sched DL RACH info request parameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * Sched DL CQI info request function
      * \param params the Sched DL CQI info request parameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * Sched UL trigger request function
      * \param params the Sched UL trigger request parameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * Sched UL noise interference request function
      * \param params the Sched UL noise interference request parameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * Sched UL SR info request function
      * \param params the Sched UL SR info request parameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * Sched UL MAC control info request function
      * \param params the Sched UL MAC control info request parameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * Sched UL CQI info request function
      * \param params the Sched UL CQI info request parameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * Get RBG size function
@@ -449,9 +416,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/ff-mac-common.cc ns-3.39/src/lte/model/ff-mac-common.cc
--- ns-3.38/src/lte/model/ff-mac-common.cc	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/ff-mac-common.cc	2023-07-05 14:23:48.009428000 -0700
@@ -17,7 +17,7 @@
  * Author: Marco Miozzo <mmiozzo@cttc.es>
  */
 
-#include <ns3/ff-mac-common.h>
+#include "ff-mac-common.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/ff-mac-common.h ns-3.39/src/lte/model/ff-mac-common.h
--- ns-3.38/src/lte/model/ff-mac-common.h	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/ff-mac-common.h	2023-07-05 14:23:48.009428000 -0700
@@ -215,6 +215,7 @@
     {
         QBT_NON_GBR,
         QBT_GBR,
+        QBT_DGBR,
         NotValid_QosBearerType
     } m_qosBearerType{NotValid_QosBearerType}; ///< the QOS bearer type
 
@@ -262,14 +263,14 @@
 };
 
 /**
- * \brief See section 4.3.8 builDataListElement
+ * \brief See section 4.3.8 buildDataListElement
  * \struct BuildDataListElement_s
  */
 struct BuildDataListElement_s
 {
     uint16_t m_rnti{UINT16_MAX};                                       ///< RNTI
     struct DlDciListElement_s m_dci;                                   ///< DCI
-    std::vector<enum CeBitmap_e> m_ceBitmap;                           ///< CE bitmap
+    std::vector<CeBitmap_e> m_ceBitmap;                                ///< CE bitmap
     std::vector<std::vector<struct RlcPduListElement_s>> m_rlcPduList; ///< RLC PDU list
 };
 
@@ -474,7 +475,8 @@
         NACK,
         DTX
     };
-    std::vector<enum HarqStatus_e> m_harqStatus; ///< HARQ status
+
+    std::vector<HarqStatus_e> m_harqStatus; ///< HARQ status
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/ff-mac-csched-sap.h ns-3.39/src/lte/model/ff-mac-csched-sap.h
--- ns-3.38/src/lte/model/ff-mac-csched-sap.h	2023-03-16 20:54:23.768869000 -0700
+++ ns-3.39/src/lte/model/ff-mac-csched-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -77,17 +77,17 @@
             PHICH_R_TWO
         } m_phichResource; ///< PHICH resource
 
-        enum NormalExtended_e m_phichDuration; ///< PHICH duration
+        NormalExtended_e m_phichDuration; ///< PHICH duration
 
         uint8_t m_initialNrOfPdcchOfdmSymbols; ///< initial number of PDCCH OFDM symbols
 
-        struct SiConfiguration_s m_siConfiguration; ///< SI configuration
+        SiConfiguration_s m_siConfiguration; ///< SI configuration
 
         uint16_t m_ulBandwidth; ///< UL bandwidth
         uint16_t m_dlBandwidth; ///< DL bandwidth
 
-        enum NormalExtended_e m_ulCyclicPrefixLength; ///< UL cyclic prefix length
-        enum NormalExtended_e m_dlCyclicPrefixLength; ///< DL cyclic prefix length
+        NormalExtended_e m_ulCyclicPrefixLength; ///< UL cyclic prefix length
+        NormalExtended_e m_dlCyclicPrefixLength; ///< DL cyclic prefix length
 
         uint8_t m_antennaPortsCount; ///< antenna port count
 
@@ -125,8 +125,7 @@
             MOD_64QAM
         } m_enable64Qam; ///< enable64Qam
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -136,11 +135,11 @@
     /// CschedUeConfigReqParameters structure
     struct CschedUeConfigReqParameters
     {
-        uint16_t m_rnti;                ///< RNTI
-        bool m_reconfigureFlag;         ///< reconfigure flag
-        bool m_drxConfigPresent;        ///< drx config present
-        struct DrxConfig_s m_drxConfig; ///< drx config
-        uint16_t m_timeAlignmentTimer;  ///< time alignment timer
+        uint16_t m_rnti;               ///< RNTI
+        bool m_reconfigureFlag;        ///< reconfigure flag
+        bool m_drxConfigPresent;       ///< drx config present
+        DrxConfig_s m_drxConfig;       ///< drx config
+        uint16_t m_timeAlignmentTimer; ///< time alignment timer
 
         /// MeasGapConfigPattern_e enumeration
         enum MeasGapConfigPattern_e
@@ -150,17 +149,17 @@
             OFF
         } m_measGapConfigPattern; ///< measGapConfigPattern
 
-        uint8_t m_measGapConfigSubframeOffset;    ///< measure gap config subframe offset
-        bool m_spsConfigPresent;                  ///< SPS config present
-        struct SpsConfig_s m_spsConfig;           ///< SPS config
-        bool m_srConfigPresent;                   ///< SR config present
-        struct SrConfig_s m_srConfig;             ///< SR config
-        bool m_cqiConfigPresent;                  ///< CQI config present
-        struct CqiConfig_s m_cqiConfig;           ///< CQI config
-        uint8_t m_transmissionMode;               ///< transmission mode
-        uint64_t m_ueAggregatedMaximumBitrateUl;  ///< UE aggregate maximum bit rate UL
-        uint64_t m_ueAggregatedMaximumBitrateDl;  ///< UE aggregate maximum bit rate DL
-        struct UeCapabilities_s m_ueCapabilities; ///< UE capabilities
+        uint8_t m_measGapConfigSubframeOffset;   ///< measure gap config subframe offset
+        bool m_spsConfigPresent;                 ///< SPS config present
+        SpsConfig_s m_spsConfig;                 ///< SPS config
+        bool m_srConfigPresent;                  ///< SR config present
+        SrConfig_s m_srConfig;                   ///< SR config
+        bool m_cqiConfigPresent;                 ///< CQI config present
+        CqiConfig_s m_cqiConfig;                 ///< CQI config
+        uint8_t m_transmissionMode;              ///< transmission mode
+        uint64_t m_ueAggregatedMaximumBitrateUl; ///< UE aggregate maximum bit rate UL
+        uint64_t m_ueAggregatedMaximumBitrateDl; ///< UE aggregate maximum bit rate DL
+        UeCapabilities_s m_ueCapabilities;       ///< UE capabilities
 
         /// OpenClosedLoop_e
         enum OpenClosedLoop_e
@@ -198,8 +197,7 @@
 
         uint8_t m_ackNackRepetitionFactor; ///< ackNackRepetitionFactor
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendorSpecificList
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendorSpecificList
     };
 
     /**
@@ -211,11 +209,10 @@
         uint16_t m_rnti;        ///< RNTI
         bool m_reconfigureFlag; ///< reconfigure flag
 
-        std::vector<struct LogicalChannelConfigListElement_s>
+        std::vector<LogicalChannelConfigListElement_s>
             m_logicalChannelConfigList; ///< logicalChannelConfigList
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendorSpecificList
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendorSpecificList
     };
 
     /**
@@ -228,8 +225,7 @@
 
         std::vector<uint8_t> m_logicalChannelIdentity; ///< logical channel identity
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendorSpecificList
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendorSpecificList
     };
 
     /**
@@ -240,8 +236,7 @@
     {
         uint16_t m_rnti; ///< RNTI
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendorSpecificList
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendorSpecificList
     };
 
     //
@@ -254,35 +249,35 @@
      *
      * \param params CschedCellConfigReqParameters
      */
-    virtual void CschedCellConfigReq(const struct CschedCellConfigReqParameters& params) = 0;
+    virtual void CschedCellConfigReq(const CschedCellConfigReqParameters& params) = 0;
 
     /**
      * \brief CSCHED_UE_CONFIG_REQ
      *
      * \param params CschedUeConfigReqParameters
      */
-    virtual void CschedUeConfigReq(const struct CschedUeConfigReqParameters& params) = 0;
+    virtual void CschedUeConfigReq(const CschedUeConfigReqParameters& params) = 0;
 
     /**
      * \brief CSCHED_LC_CONFIG_REQ
      *
      * \param params CschedLcConfigReqParameters
      */
-    virtual void CschedLcConfigReq(const struct CschedLcConfigReqParameters& params) = 0;
+    virtual void CschedLcConfigReq(const CschedLcConfigReqParameters& params) = 0;
 
     /**
      * \brief CSCHED_LC_RELEASE_REQ
      *
      * \param params CschedLcReleaseReqParameters
      */
-    virtual void CschedLcReleaseReq(const struct CschedLcReleaseReqParameters& params) = 0;
+    virtual void CschedLcReleaseReq(const CschedLcReleaseReqParameters& params) = 0;
 
     /**
      * \brief CSCHED_UE_RELEASE_REQ
      *
      * \param params CschedUeReleaseReqParameters
      */
-    virtual void CschedUeReleaseReq(const struct CschedUeReleaseReqParameters& params) = 0;
+    virtual void CschedUeReleaseReq(const CschedUeReleaseReqParameters& params) = 0;
 
   private:
 };
@@ -305,10 +300,9 @@
      */
     struct CschedCellConfigCnfParameters
     {
-        enum Result_e m_result; ///< result
+        Result_e m_result; ///< result
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendorSpecificList
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendorSpecificList
     };
 
     /**
@@ -317,11 +311,10 @@
      */
     struct CschedUeConfigCnfParameters
     {
-        uint16_t m_rnti;        ///< RNTI
-        enum Result_e m_result; ///< result
+        uint16_t m_rnti;   ///< RNTI
+        Result_e m_result; ///< result
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendorSpecificList
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendorSpecificList
     };
 
     /**
@@ -330,13 +323,12 @@
      */
     struct CschedLcConfigCnfParameters
     {
-        uint16_t m_rnti;        ///< RNTI
-        enum Result_e m_result; ///< result
+        uint16_t m_rnti;   ///< RNTI
+        Result_e m_result; ///< result
 
         std::vector<uint8_t> m_logicalChannelIdentity; ///< logical channel identity
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -345,13 +337,12 @@
      */
     struct CschedLcReleaseCnfParameters
     {
-        uint16_t m_rnti;        ///< RNTI
-        enum Result_e m_result; ///< result
+        uint16_t m_rnti;   ///< RNTI
+        Result_e m_result; ///< result
 
         std::vector<uint8_t> m_logicalChannelIdentity; ///< logical channel identity
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -360,11 +351,10 @@
      */
     struct CschedUeReleaseCnfParameters
     {
-        uint16_t m_rnti;        ///< RNTI
-        enum Result_e m_result; ///< result
+        uint16_t m_rnti;   ///< RNTI
+        Result_e m_result; ///< result
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -373,17 +363,16 @@
      */
     struct CschedUeConfigUpdateIndParameters
     {
-        uint16_t m_rnti;                ///< RNTI
-        uint8_t m_transmissionMode;     ///< transmission mode
-        bool m_spsConfigPresent;        ///< SPS config present
-        struct SpsConfig_s m_spsConfig; ///< SPS config
-        bool m_srConfigPresent;         ///< SR config present
-        struct SrConfig_s m_srConfig;   ///< SR config
-        bool m_cqiConfigPresent;        ///< CQI config present
-        struct CqiConfig_s m_cqiConfig; ///< CQI config
+        uint16_t m_rnti;            ///< RNTI
+        uint8_t m_transmissionMode; ///< transmission mode
+        bool m_spsConfigPresent;    ///< SPS config present
+        SpsConfig_s m_spsConfig;    ///< SPS config
+        bool m_srConfigPresent;     ///< SR config present
+        SrConfig_s m_srConfig;      ///< SR config
+        bool m_cqiConfigPresent;    ///< CQI config present
+        CqiConfig_s m_cqiConfig;    ///< CQI config
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -395,8 +384,7 @@
         uint8_t m_prbUtilizationDl; ///< DL utilization
         uint8_t m_prbUtilizationUl; ///< UL utilization
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     //
@@ -409,51 +397,49 @@
      *
      * \param params CschedCellConfigCnfParameters
      */
-    virtual void CschedCellConfigCnf(const struct CschedCellConfigCnfParameters& params) = 0;
+    virtual void CschedCellConfigCnf(const CschedCellConfigCnfParameters& params) = 0;
 
     /**
      * \brief CSCHED_UE_CONFIG_CNF
      *
      * \param params CschedUeConfigCnfParameters
      */
-    virtual void CschedUeConfigCnf(const struct CschedUeConfigCnfParameters& params) = 0;
+    virtual void CschedUeConfigCnf(const CschedUeConfigCnfParameters& params) = 0;
 
     /**
      * \brief CSCHED_LC_CONFIG_CNF
      *
      * \param params CschedLcConfigCnfParameters
      */
-    virtual void CschedLcConfigCnf(const struct CschedLcConfigCnfParameters& params) = 0;
+    virtual void CschedLcConfigCnf(const CschedLcConfigCnfParameters& params) = 0;
 
     /**
      * \brief CSCHED_LC_RELEASE_CNF
      *
      * \param params CschedLcReleaseCnfParameters
      */
-    virtual void CschedLcReleaseCnf(const struct CschedLcReleaseCnfParameters& params) = 0;
+    virtual void CschedLcReleaseCnf(const CschedLcReleaseCnfParameters& params) = 0;
 
     /**
      * \brief CSCHED_UE_RELEASE_CNF
      *
      * \param params CschedUeReleaseCnfParameters
      */
-    virtual void CschedUeReleaseCnf(const struct CschedUeReleaseCnfParameters& params) = 0;
+    virtual void CschedUeReleaseCnf(const CschedUeReleaseCnfParameters& params) = 0;
 
     /**
      * \brief CSCHED_UE_UPDATE_IND
      *
      * \param params CschedUeConfigUpdateIndParameters
      */
-    virtual void CschedUeConfigUpdateInd(
-        const struct CschedUeConfigUpdateIndParameters& params) = 0;
+    virtual void CschedUeConfigUpdateInd(const CschedUeConfigUpdateIndParameters& params) = 0;
 
     /**
      * \brief CSCHED_UE_CONFIG_IND
      *
      * \param params CschedCellConfigUpdateIndParameters
      */
-    virtual void CschedCellConfigUpdateInd(
-        const struct CschedCellConfigUpdateIndParameters& params) = 0;
+    virtual void CschedCellConfigUpdateInd(const CschedCellConfigUpdateIndParameters& params) = 0;
 
   private:
 };
@@ -474,11 +460,11 @@
     MemberCschedSapProvider() = delete;
 
     // inherited from FfMacCschedSapProvider
-    void CschedCellConfigReq(const struct CschedCellConfigReqParameters& params) override;
-    void CschedUeConfigReq(const struct CschedUeConfigReqParameters& params) override;
-    void CschedLcConfigReq(const struct CschedLcConfigReqParameters& params) override;
-    void CschedLcReleaseReq(const struct CschedLcReleaseReqParameters& params) override;
-    void CschedUeReleaseReq(const struct CschedUeReleaseReqParameters& params) override;
+    void CschedCellConfigReq(const CschedCellConfigReqParameters& params) override;
+    void CschedUeConfigReq(const CschedUeConfigReqParameters& params) override;
+    void CschedLcConfigReq(const CschedLcConfigReqParameters& params) override;
+    void CschedLcReleaseReq(const CschedLcReleaseReqParameters& params) override;
+    void CschedUeReleaseReq(const CschedUeReleaseReqParameters& params) override;
 
   private:
     C* m_scheduler; ///< scheduler class
@@ -492,35 +478,35 @@
 
 template <class C>
 void
-MemberCschedSapProvider<C>::CschedCellConfigReq(const struct CschedCellConfigReqParameters& params)
+MemberCschedSapProvider<C>::CschedCellConfigReq(const CschedCellConfigReqParameters& params)
 {
     m_scheduler->DoCschedCellConfigReq(params);
 }
 
 template <class C>
 void
-MemberCschedSapProvider<C>::CschedUeConfigReq(const struct CschedUeConfigReqParameters& params)
+MemberCschedSapProvider<C>::CschedUeConfigReq(const CschedUeConfigReqParameters& params)
 {
     m_scheduler->DoCschedUeConfigReq(params);
 }
 
 template <class C>
 void
-MemberCschedSapProvider<C>::CschedLcConfigReq(const struct CschedLcConfigReqParameters& params)
+MemberCschedSapProvider<C>::CschedLcConfigReq(const CschedLcConfigReqParameters& params)
 {
     m_scheduler->DoCschedLcConfigReq(params);
 }
 
 template <class C>
 void
-MemberCschedSapProvider<C>::CschedLcReleaseReq(const struct CschedLcReleaseReqParameters& params)
+MemberCschedSapProvider<C>::CschedLcReleaseReq(const CschedLcReleaseReqParameters& params)
 {
     m_scheduler->DoCschedLcReleaseReq(params);
 }
 
 template <class C>
 void
-MemberCschedSapProvider<C>::CschedUeReleaseReq(const struct CschedUeReleaseReqParameters& params)
+MemberCschedSapProvider<C>::CschedUeReleaseReq(const CschedUeReleaseReqParameters& params)
 {
     m_scheduler->DoCschedUeReleaseReq(params);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/ff-mac-sched-sap.h ns-3.39/src/lte/model/ff-mac-sched-sap.h
--- ns-3.38/src/lte/model/ff-mac-sched-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/ff-mac-sched-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -65,8 +65,7 @@
         uint16_t m_rlcRetransmissionHolDelay;    ///< RLC retransmission HOL delay
         uint16_t m_rlcStatusPduSize;             ///< RLC status PDU size
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -75,11 +74,10 @@
      */
     struct SchedDlPagingBufferReqParameters
     {
-        uint16_t m_rnti;                                              ///< RNTI
-        std::vector<struct PagingInfoListElement_s> m_pagingInfoList; ///< paging info list
+        uint16_t m_rnti;                                       ///< RNTI
+        std::vector<PagingInfoListElement_s> m_pagingInfoList; ///< paging info list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -88,11 +86,10 @@
      */
     struct SchedDlMacBufferReqParameters
     {
-        uint16_t m_rnti;            ///< RNTI
-        enum CeBitmap_e m_ceBitmap; ///< CE bitmap
+        uint16_t m_rnti;       ///< RNTI
+        CeBitmap_e m_ceBitmap; ///< CE bitmap
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -101,11 +98,10 @@
      */
     struct SchedDlTriggerReqParameters
     {
-        uint16_t m_sfnSf;                                     ///< sfn SF
-        std::vector<struct DlInfoListElement_s> m_dlInfoList; ///< DL info list
+        uint16_t m_sfnSf;                              ///< sfn SF
+        std::vector<DlInfoListElement_s> m_dlInfoList; ///< DL info list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -114,11 +110,10 @@
      */
     struct SchedDlRachInfoReqParameters
     {
-        uint16_t m_sfnSf;                                 ///< sfn SF
-        std::vector<struct RachListElement_s> m_rachList; ///< RACH list
+        uint16_t m_sfnSf;                          ///< sfn SF
+        std::vector<RachListElement_s> m_rachList; ///< RACH list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -127,11 +122,10 @@
      */
     struct SchedDlCqiInfoReqParameters
     {
-        uint16_t m_sfnSf;                               ///< sfn SF
-        std::vector<struct CqiListElement_s> m_cqiList; ///< CQI list
+        uint16_t m_sfnSf;                        ///< sfn SF
+        std::vector<CqiListElement_s> m_cqiList; ///< CQI list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -140,11 +134,10 @@
      */
     struct SchedUlTriggerReqParameters
     {
-        uint16_t m_sfnSf;                                     ///< sfn SF
-        std::vector<struct UlInfoListElement_s> m_ulInfoList; ///< UL info list
+        uint16_t m_sfnSf;                              ///< sfn SF
+        std::vector<UlInfoListElement_s> m_ulInfoList; ///< UL info list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -157,8 +150,7 @@
         uint16_t m_rip;   ///< RIP
         uint16_t m_tnp;   ///< TNP
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -167,11 +159,10 @@
      */
     struct SchedUlSrInfoReqParameters
     {
-        uint16_t m_sfnSf;                             ///< sfn SF
-        std::vector<struct SrListElement_s> m_srList; ///< SR list
+        uint16_t m_sfnSf;                      ///< sfn SF
+        std::vector<SrListElement_s> m_srList; ///< SR list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -180,11 +171,10 @@
      */
     struct SchedUlMacCtrlInfoReqParameters
     {
-        uint16_t m_sfnSf;                                   ///< sfn SF
-        std::vector<struct MacCeListElement_s> m_macCeList; ///< MAC CE list
+        uint16_t m_sfnSf;                            ///< sfn SF
+        std::vector<MacCeListElement_s> m_macCeList; ///< MAC CE list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -193,11 +183,10 @@
      */
     struct SchedUlCqiInfoReqParameters
     {
-        uint16_t m_sfnSf;       ///< sfn SF
-        struct UlCqi_s m_ulCqi; ///< UL CQI
+        uint16_t m_sfnSf; ///< sfn SF
+        UlCqi_s m_ulCqi;  ///< UL CQI
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     //
@@ -210,49 +199,49 @@
      *
      * \param params SchedDlRlcBufferReqParameters
      */
-    virtual void SchedDlRlcBufferReq(const struct SchedDlRlcBufferReqParameters& params) = 0;
+    virtual void SchedDlRlcBufferReq(const SchedDlRlcBufferReqParameters& params) = 0;
 
     /**
      * \brief SCHED_DL_PAGING_BUFFER_REQ
      *
      * \param params SchedDlPagingBufferReqParameters
      */
-    virtual void SchedDlPagingBufferReq(const struct SchedDlPagingBufferReqParameters& params) = 0;
+    virtual void SchedDlPagingBufferReq(const SchedDlPagingBufferReqParameters& params) = 0;
 
     /**
      * \brief SCHED_DL_MAC_BUFFER_REQ
      *
      * \param params SchedDlMacBufferReqParameters
      */
-    virtual void SchedDlMacBufferReq(const struct SchedDlMacBufferReqParameters& params) = 0;
+    virtual void SchedDlMacBufferReq(const SchedDlMacBufferReqParameters& params) = 0;
 
     /**
      * \brief SCHED_DL_TRIGGER_REQ
      *
      * \param params SchedDlTriggerReqParameters
      */
-    virtual void SchedDlTriggerReq(const struct SchedDlTriggerReqParameters& params) = 0;
+    virtual void SchedDlTriggerReq(const SchedDlTriggerReqParameters& params) = 0;
 
     /**
      * \brief SCHED_DL_RACH_INFO_REQ
      *
      * \param params SchedDlRachInfoReqParameters
      */
-    virtual void SchedDlRachInfoReq(const struct SchedDlRachInfoReqParameters& params) = 0;
+    virtual void SchedDlRachInfoReq(const SchedDlRachInfoReqParameters& params) = 0;
 
     /**
      * \brief SCHED_DL_CQI_INFO_REQ
      *
      * \param params SchedDlCqiInfoReqParameters
      */
-    virtual void SchedDlCqiInfoReq(const struct SchedDlCqiInfoReqParameters& params) = 0;
+    virtual void SchedDlCqiInfoReq(const SchedDlCqiInfoReqParameters& params) = 0;
 
     /**
      * \brief SCHED_UL_TRIGGER_REQ
      *
      * \param params SchedUlTriggerReqParameters
      */
-    virtual void SchedUlTriggerReq(const struct SchedUlTriggerReqParameters& params) = 0;
+    virtual void SchedUlTriggerReq(const SchedUlTriggerReqParameters& params) = 0;
 
     /**
      * \brief SCHED_UL_NOISE_INTERFERENCE_REQ
@@ -260,28 +249,28 @@
      * \param params SchedUlNoiseInterferenceReqParameters
      */
     virtual void SchedUlNoiseInterferenceReq(
-        const struct SchedUlNoiseInterferenceReqParameters& params) = 0;
+        const SchedUlNoiseInterferenceReqParameters& params) = 0;
 
     /**
      * \brief SCHED_UL_SR_INFO_REQ
      *
      * \param params SchedUlSrInfoReqParameters
      */
-    virtual void SchedUlSrInfoReq(const struct SchedUlSrInfoReqParameters& params) = 0;
+    virtual void SchedUlSrInfoReq(const SchedUlSrInfoReqParameters& params) = 0;
 
     /**
      * \brief SCHED_UL_MAC_CTRL_INFO_REQ
      *
      * \param params SchedUlMacCtrlInfoReqParameters
      */
-    virtual void SchedUlMacCtrlInfoReq(const struct SchedUlMacCtrlInfoReqParameters& params) = 0;
+    virtual void SchedUlMacCtrlInfoReq(const SchedUlMacCtrlInfoReqParameters& params) = 0;
 
     /**
      * \brief SCHED_UL_CQI_INFO_REQ
      *
      * \param params SchedUlCqiInfoReqParameters
      */
-    virtual void SchedUlCqiInfoReq(const struct SchedUlCqiInfoReqParameters& params) = 0;
+    virtual void SchedUlCqiInfoReq(const SchedUlCqiInfoReqParameters& params) = 0;
 
   private:
 };
@@ -302,15 +291,13 @@
      */
     struct SchedDlConfigIndParameters
     {
-        std::vector<struct BuildDataListElement_s> m_buildDataList; ///< build data list
-        std::vector<struct BuildRarListElement_s> m_buildRarList;   ///< build rar list
-        std::vector<struct BuildBroadcastListElement_s>
-            m_buildBroadcastList; ///< build broadcast list
+        std::vector<BuildDataListElement_s> m_buildDataList;           ///< build data list
+        std::vector<BuildRarListElement_s> m_buildRarList;             ///< build rar list
+        std::vector<BuildBroadcastListElement_s> m_buildBroadcastList; ///< build broadcast list
 
         uint8_t m_nrOfPdcchOfdmSymbols; ///< number of PDCCH OFDM symbols
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     /**
@@ -319,11 +306,10 @@
      */
     struct SchedUlConfigIndParameters
     {
-        std::vector<struct UlDciListElement_s> m_dciList;   ///< DCI list
-        std::vector<struct PhichListElement_s> m_phichList; ///< PHICH list
+        std::vector<UlDciListElement_s> m_dciList;   ///< DCI list
+        std::vector<PhichListElement_s> m_phichList; ///< PHICH list
 
-        std::vector<struct VendorSpecificListElement_s>
-            m_vendorSpecificList; ///< vendor specific list
+        std::vector<VendorSpecificListElement_s> m_vendorSpecificList; ///< vendor specific list
     };
 
     //
@@ -336,14 +322,14 @@
      *
      * \param params SchedDlConfigIndParameters
      */
-    virtual void SchedDlConfigInd(const struct SchedDlConfigIndParameters& params) = 0;
+    virtual void SchedDlConfigInd(const SchedDlConfigIndParameters& params) = 0;
 
     /**
      * \brief SCHED_UL_CONFIG_IND
      *
      * \param params SchedUlConfigIndParameters
      */
-    virtual void SchedUlConfigInd(const struct SchedUlConfigIndParameters& params) = 0;
+    virtual void SchedUlConfigInd(const SchedUlConfigIndParameters& params) = 0;
 
   private:
 };
@@ -364,18 +350,17 @@
     MemberSchedSapProvider() = delete;
 
     // inherited from FfMacSchedSapProvider
-    void SchedDlRlcBufferReq(const struct SchedDlRlcBufferReqParameters& params) override;
-    void SchedDlPagingBufferReq(const struct SchedDlPagingBufferReqParameters& params) override;
-    void SchedDlMacBufferReq(const struct SchedDlMacBufferReqParameters& params) override;
-    void SchedDlTriggerReq(const struct SchedDlTriggerReqParameters& params) override;
-    void SchedDlRachInfoReq(const struct SchedDlRachInfoReqParameters& params) override;
-    void SchedDlCqiInfoReq(const struct SchedDlCqiInfoReqParameters& params) override;
-    void SchedUlTriggerReq(const struct SchedUlTriggerReqParameters& params) override;
-    void SchedUlNoiseInterferenceReq(
-        const struct SchedUlNoiseInterferenceReqParameters& params) override;
-    void SchedUlSrInfoReq(const struct SchedUlSrInfoReqParameters& params) override;
-    void SchedUlMacCtrlInfoReq(const struct SchedUlMacCtrlInfoReqParameters& params) override;
-    void SchedUlCqiInfoReq(const struct SchedUlCqiInfoReqParameters& params) override;
+    void SchedDlRlcBufferReq(const SchedDlRlcBufferReqParameters& params) override;
+    void SchedDlPagingBufferReq(const SchedDlPagingBufferReqParameters& params) override;
+    void SchedDlMacBufferReq(const SchedDlMacBufferReqParameters& params) override;
+    void SchedDlTriggerReq(const SchedDlTriggerReqParameters& params) override;
+    void SchedDlRachInfoReq(const SchedDlRachInfoReqParameters& params) override;
+    void SchedDlCqiInfoReq(const SchedDlCqiInfoReqParameters& params) override;
+    void SchedUlTriggerReq(const SchedUlTriggerReqParameters& params) override;
+    void SchedUlNoiseInterferenceReq(const SchedUlNoiseInterferenceReqParameters& params) override;
+    void SchedUlSrInfoReq(const SchedUlSrInfoReqParameters& params) override;
+    void SchedUlMacCtrlInfoReq(const SchedUlMacCtrlInfoReqParameters& params) override;
+    void SchedUlCqiInfoReq(const SchedUlCqiInfoReqParameters& params) override;
 
   private:
     C* m_scheduler; ///< the scheduler class
@@ -389,50 +374,49 @@
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedDlRlcBufferReq(const struct SchedDlRlcBufferReqParameters& params)
+MemberSchedSapProvider<C>::SchedDlRlcBufferReq(const SchedDlRlcBufferReqParameters& params)
 {
     m_scheduler->DoSchedDlRlcBufferReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedDlPagingBufferReq(
-    const struct SchedDlPagingBufferReqParameters& params)
+MemberSchedSapProvider<C>::SchedDlPagingBufferReq(const SchedDlPagingBufferReqParameters& params)
 {
     m_scheduler->DoSchedDlPagingBufferReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedDlMacBufferReq(const struct SchedDlMacBufferReqParameters& params)
+MemberSchedSapProvider<C>::SchedDlMacBufferReq(const SchedDlMacBufferReqParameters& params)
 {
     m_scheduler->DoSchedDlMacBufferReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedDlTriggerReq(const struct SchedDlTriggerReqParameters& params)
+MemberSchedSapProvider<C>::SchedDlTriggerReq(const SchedDlTriggerReqParameters& params)
 {
     m_scheduler->DoSchedDlTriggerReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedDlRachInfoReq(const struct SchedDlRachInfoReqParameters& params)
+MemberSchedSapProvider<C>::SchedDlRachInfoReq(const SchedDlRachInfoReqParameters& params)
 {
     m_scheduler->DoSchedDlRachInfoReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedDlCqiInfoReq(const struct SchedDlCqiInfoReqParameters& params)
+MemberSchedSapProvider<C>::SchedDlCqiInfoReq(const SchedDlCqiInfoReqParameters& params)
 {
     m_scheduler->DoSchedDlCqiInfoReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedUlTriggerReq(const struct SchedUlTriggerReqParameters& params)
+MemberSchedSapProvider<C>::SchedUlTriggerReq(const SchedUlTriggerReqParameters& params)
 {
     m_scheduler->DoSchedUlTriggerReq(params);
 }
@@ -440,29 +424,28 @@
 template <class C>
 void
 MemberSchedSapProvider<C>::SchedUlNoiseInterferenceReq(
-    const struct SchedUlNoiseInterferenceReqParameters& params)
+    const SchedUlNoiseInterferenceReqParameters& params)
 {
     m_scheduler->DoSchedUlNoiseInterferenceReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedUlSrInfoReq(const struct SchedUlSrInfoReqParameters& params)
+MemberSchedSapProvider<C>::SchedUlSrInfoReq(const SchedUlSrInfoReqParameters& params)
 {
     m_scheduler->DoSchedUlSrInfoReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedUlMacCtrlInfoReq(
-    const struct SchedUlMacCtrlInfoReqParameters& params)
+MemberSchedSapProvider<C>::SchedUlMacCtrlInfoReq(const SchedUlMacCtrlInfoReqParameters& params)
 {
     m_scheduler->DoSchedUlMacCtrlInfoReq(params);
 }
 
 template <class C>
 void
-MemberSchedSapProvider<C>::SchedUlCqiInfoReq(const struct SchedUlCqiInfoReqParameters& params)
+MemberSchedSapProvider<C>::SchedUlCqiInfoReq(const SchedUlCqiInfoReqParameters& params)
 {
     m_scheduler->DoSchedUlCqiInfoReq(params);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/ff-mac-scheduler.h ns-3.39/src/lte/model/ff-mac-scheduler.h
--- ns-3.38/src/lte/model/ff-mac-scheduler.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/ff-mac-scheduler.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,6 +21,8 @@
 #ifndef FF_MAC_SCHEDULER_H
 #define FF_MAC_SCHEDULER_H
 
+#include "ff-mac-common.h"
+
 #include <ns3/object.h>
 
 namespace ns3
@@ -38,6 +40,37 @@
  * \defgroup ff-api FF MAC Schedulers
  */
 
+/// DL HARQ process status vector
+using DlHarqProcessesStatus_t = std::vector<uint8_t>;
+
+/// DL HARQ process timer vector
+using DlHarqProcessesTimer_t = std::vector<uint8_t>;
+
+/// DL HARQ process DCI buffer vector
+using DlHarqProcessesDciBuffer_t = std::vector<DlDciListElement_s>;
+
+/// Vector of the LCs and layers per UE
+using RlcPduList_t = std::vector<std::vector<RlcPduListElement_s>>;
+
+/// Vector of the 8 HARQ processes per UE
+using DlHarqRlcPduListBuffer_t = std::vector<RlcPduList_t>;
+
+/// UL HARQ process DCI buffer vector
+using UlHarqProcessesDciBuffer_t = std::vector<UlDciListElement_s>;
+
+/// UL HARQ process status vector
+using UlHarqProcessesStatus_t = std::vector<uint8_t>;
+
+/// Value for SINR outside the range defined by FF-API, used to indicate that there is
+/// no CQI for this element
+constexpr double NO_SINR = -5000;
+
+/// Number of HARQ processes
+constexpr uint32_t HARQ_PROC_NUM = 8;
+
+/// HARQ DL timeout
+constexpr uint32_t HARQ_DL_TIMEOUT = 11;
+
 /**
  * \ingroup ff-api
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-amc.cc ns-3.39/src/lte/model/lte-amc.cc
--- ns-3.38/src/lte/model/lte-amc.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-amc.cc	2023-07-05 14:23:48.009428000 -0700
@@ -19,12 +19,14 @@
  *                  Nicola Baldo   <nbaldo@cttc.es>
  */
 
+#include "lte-amc.h"
+
+#include "lte-mi-error-model.h"
+
 #include "ns3/enum.h"
 #include <ns3/assert.h>
 #include <ns3/double.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-mi-error-model.h>
 #include <ns3/math.h>
 #include <ns3/spectrum-value.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-anr.cc ns-3.39/src/lte/model/lte-anr.cc
--- ns-3.38/src/lte/model/lte-anr.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-anr.cc	2023-07-05 14:23:48.009428000 -0700
@@ -174,7 +174,7 @@
                     // Update neighbour relation entry
                     NS_LOG_LOGIC(this << " updating NRT of cell " << m_servingCellId
                                       << " with entry of cell " << it->physCellId);
-                    if (itNrt->second.noX2 == false)
+                    if (!itNrt->second.noX2)
                     {
                         NS_LOG_LOGIC(this << " enabling handover"
                                           << " from cell " << m_servingCellId << " to cell "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-anr.h ns-3.39/src/lte/model/lte-anr.h
--- ns-3.38/src/lte/model/lte-anr.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-anr.h	2023-07-05 14:23:48.009428000 -0700
@@ -27,8 +27,9 @@
 #ifndef LTE_ANR_H
 #define LTE_ANR_H
 
-#include <ns3/lte-anr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-anr-sap.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/object.h>
 
 #include <map>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-anr-sap.h ns-3.39/src/lte/model/lte-anr-sap.h
--- ns-3.38/src/lte/model/lte-anr-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-anr-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,7 +21,7 @@
 #ifndef LTE_ANR_SAP_H
 #define LTE_ANR_SAP_H
 
-#include <ns3/lte-rrc-sap.h>
+#include "lte-rrc-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-asn1-header.cc ns-3.39/src/lte/model/lte-asn1-header.cc
--- ns-3.38/src/lte/model/lte-asn1-header.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-asn1-header.cc	2023-07-05 14:23:48.009428000 -0700
@@ -17,7 +17,7 @@
  * Author: Lluis Parcerisa <lparcerisa@cttc.cat>
  */
 
-#include "ns3/lte-asn1-header.h"
+#include "lte-asn1-header.h"
 
 #include "ns3/log.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-as-sap.cc ns-3.39/src/lte/model/lte-as-sap.cc
--- ns-3.38/src/lte/model/lte-as-sap.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-as-sap.cc	2023-07-05 14:23:48.009428000 -0700
@@ -17,7 +17,7 @@
  * Author: Nicola Baldo <nbaldo@cttc.es>
  */
 
-#include "ns3/lte-as-sap.h"
+#include "lte-as-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ccm-mac-sap.h ns-3.39/src/lte/model/lte-ccm-mac-sap.h
--- ns-3.38/src/lte/model/lte-ccm-mac-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ccm-mac-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,11 +21,8 @@
 #ifndef LTE_CCM_MAC_SAP_H
 #define LTE_CCM_MAC_SAP_H
 
-#include <ns3/eps-bearer.h>
-#include <ns3/ff-mac-common.h>
-#include <ns3/lte-enb-cmac-sap.h>
-#include <ns3/lte-mac-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "ff-mac-common.h"
+#include "lte-mac-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ccm-rrc-sap.h ns-3.39/src/lte/model/lte-ccm-rrc-sap.h
--- ns-3.38/src/lte/model/lte-ccm-rrc-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ccm-rrc-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_CCM_RRC_SAP_H
 #define LTE_CCM_RRC_SAP_H
 
-#include <ns3/eps-bearer.h>
-#include <ns3/lte-enb-cmac-sap.h>
-#include <ns3/lte-mac-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "eps-bearer.h"
+#include "lte-enb-cmac-sap.h"
+#include "lte-mac-sap.h"
+#include "lte-rrc-sap.h"
 
 #include <map>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-common.h ns-3.39/src/lte/model/lte-common.h
--- ns-3.38/src/lte/model/lte-common.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-common.h	2023-07-05 14:23:48.009428000 -0700
@@ -32,6 +32,12 @@
 namespace ns3
 {
 
+/// Minimum number of carrier components allowed by 3GPP up to R13
+constexpr uint32_t MIN_NO_CC = 1;
+
+/// Maximum number of carrier components allowed by 3GPP up to R13
+constexpr uint32_t MAX_NO_CC = 5;
+
 /// LteFlowId structure
 struct LteFlowId_t
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-control-messages.cc ns-3.39/src/lte/model/lte-control-messages.cc
--- ns-3.38/src/lte/model/lte-control-messages.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-control-messages.cc	2023-07-05 14:23:48.009428000 -0700
@@ -20,9 +20,6 @@
 
 #include "lte-control-messages.h"
 
-#include "lte-net-device.h"
-#include "lte-ue-net-device.h"
-
 #include "ns3/address-utils.h"
 #include "ns3/log.h"
 #include "ns3/uinteger.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-control-messages.h ns-3.39/src/lte/model/lte-control-messages.h
--- ns-3.38/src/lte/model/lte-control-messages.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-control-messages.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,8 +21,9 @@
 #ifndef LTE_CONTROL_MESSAGES_H
 #define LTE_CONTROL_MESSAGES_H
 
-#include <ns3/ff-mac-common.h>
-#include <ns3/lte-rrc-sap.h>
+#include "ff-mac-common.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/ptr.h>
 #include <ns3/simple-ref-count.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-cmac-sap.h ns-3.39/src/lte/model/lte-enb-cmac-sap.h
--- ns-3.38/src/lte/model/lte-enb-cmac-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-cmac-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,9 +21,6 @@
 #ifndef LTE_ENB_CMAC_SAP_H
 #define LTE_ENB_CMAC_SAP_H
 
-#include <ns3/eps-bearer.h>
-#include <ns3/ff-mac-common.h>
-#include <ns3/lte-common.h>
 #include <ns3/packet.h>
 
 namespace ns3
@@ -70,15 +67,16 @@
      */
     struct LcInfo
     {
-        uint16_t rnti;   /**< C-RNTI identifying the UE */
-        uint8_t lcId;    /**< logical channel identifier */
-        uint8_t lcGroup; /**< logical channel group */
-        uint8_t qci;     /**< QoS Class Identifier */
-        bool isGbr;      /**< true if the bearer is GBR, false if the bearer is NON-GBR */
-        uint64_t mbrUl;  /**< maximum bitrate in uplink */
-        uint64_t mbrDl;  /**< maximum bitrate in downlink */
-        uint64_t gbrUl;  /**< guaranteed bitrate in uplink */
-        uint64_t gbrDl;  /**< guaranteed bitrate in downlink */
+        uint16_t rnti;        /**< C-RNTI identifying the UE */
+        uint8_t lcId;         /**< logical channel identifier */
+        uint8_t lcGroup;      /**< logical channel group */
+        uint8_t qci;          /**< QoS Class Identifier */
+        uint8_t resourceType; /**< 0 if the bearer is NON-GBR, 1 if the bearer
+                                   is GBR, 2 if the bearer in DC-GBR */
+        uint64_t mbrUl;       /**< maximum bitrate in uplink */
+        uint64_t mbrDl;       /**< maximum bitrate in downlink */
+        uint64_t gbrUl;       /**< guaranteed bitrate in uplink */
+        uint64_t gbrDl;       /**< guaranteed bitrate in downlink */
     };
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-component-carrier-manager.cc ns-3.39/src/lte/model/lte-enb-component-carrier-manager.cc
--- ns-3.38/src/lte/model/lte-enb-component-carrier-manager.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-component-carrier-manager.cc	2023-07-05 14:23:48.009428000 -0700
@@ -20,6 +20,8 @@
 
 #include "lte-enb-component-carrier-manager.h"
 
+#include "lte-common.h"
+
 #include <ns3/log.h>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-component-carrier-manager.h ns-3.39/src/lte/model/lte-enb-component-carrier-manager.h
--- ns-3.38/src/lte/model/lte-enb-component-carrier-manager.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-component-carrier-manager.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,12 +21,13 @@
 #ifndef LTE_ENB_COMPONENT_CARRIER_MANAGER_H
 #define LTE_ENB_COMPONENT_CARRIER_MANAGER_H
 
-#include <ns3/lte-ccm-mac-sap.h>
-#include <ns3/lte-ccm-rrc-sap.h>
-#include <ns3/lte-enb-cmac-sap.h>
-#include <ns3/lte-enb-rrc.h>
-#include <ns3/lte-mac-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ccm-mac-sap.h"
+#include "lte-ccm-rrc-sap.h"
+#include "lte-enb-cmac-sap.h"
+#include "lte-enb-rrc.h"
+#include "lte-mac-sap.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/object.h>
 
 #include <map>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-cphy-sap.cc ns-3.39/src/lte/model/lte-enb-cphy-sap.cc
--- ns-3.38/src/lte/model/lte-enb-cphy-sap.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-cphy-sap.cc	2023-07-05 14:23:48.009428000 -0700
@@ -17,7 +17,7 @@
  * Author: Nicola Baldo <nbaldo@cttc.es>
  */
 
-#include "ns3/lte-enb-cphy-sap.h"
+#include "lte-enb-cphy-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-cphy-sap.h ns-3.39/src/lte/model/lte-enb-cphy-sap.h
--- ns-3.38/src/lte/model/lte-enb-cphy-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-cphy-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,7 +21,8 @@
 #ifndef LTE_ENB_CPHY_SAP_H
 #define LTE_ENB_CPHY_SAP_H
 
-#include <ns3/lte-rrc-sap.h>
+#include "lte-rrc-sap.h"
+
 #include <ns3/ptr.h>
 
 #include <stdint.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-mac.cc ns-3.39/src/lte/model/lte-enb-mac.cc
--- ns-3.38/src/lte/model/lte-enb-mac.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-mac.cc	2023-07-05 14:23:48.009428000 -0700
@@ -21,18 +21,15 @@
  *          Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
  */
 
-#include "lte-amc.h"
+#include "lte-enb-mac.h"
+
+#include "lte-common.h"
 #include "lte-control-messages.h"
-#include "lte-enb-net-device.h"
-#include "lte-ue-net-device.h"
+#include "lte-enb-cmac-sap.h"
+#include "lte-mac-sap.h"
+#include "lte-radio-bearer-tag.h"
 
-#include "ns3/lte-enb-cmac-sap.h"
-#include "ns3/lte-mac-sap.h"
 #include <ns3/log.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-enb-mac.h>
-#include <ns3/lte-radio-bearer-tag.h>
-#include <ns3/lte-ue-phy.h>
 #include <ns3/packet.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
@@ -144,8 +141,8 @@
      */
     EnbMacMemberFfMacSchedSapUser(LteEnbMac* mac);
 
-    void SchedDlConfigInd(const struct SchedDlConfigIndParameters& params) override;
-    void SchedUlConfigInd(const struct SchedUlConfigIndParameters& params) override;
+    void SchedDlConfigInd(const SchedDlConfigIndParameters& params) override;
+    void SchedUlConfigInd(const SchedUlConfigIndParameters& params) override;
 
   private:
     LteEnbMac* m_mac; ///< the MAC
@@ -157,13 +154,13 @@
 }
 
 void
-EnbMacMemberFfMacSchedSapUser::SchedDlConfigInd(const struct SchedDlConfigIndParameters& params)
+EnbMacMemberFfMacSchedSapUser::SchedDlConfigInd(const SchedDlConfigIndParameters& params)
 {
     m_mac->DoSchedDlConfigInd(params);
 }
 
 void
-EnbMacMemberFfMacSchedSapUser::SchedUlConfigInd(const struct SchedUlConfigIndParameters& params)
+EnbMacMemberFfMacSchedSapUser::SchedUlConfigInd(const SchedUlConfigIndParameters& params)
 {
     m_mac->DoSchedUlConfigInd(params);
 }
@@ -179,14 +176,13 @@
      */
     EnbMacMemberFfMacCschedSapUser(LteEnbMac* mac);
 
-    void CschedCellConfigCnf(const struct CschedCellConfigCnfParameters& params) override;
-    void CschedUeConfigCnf(const struct CschedUeConfigCnfParameters& params) override;
-    void CschedLcConfigCnf(const struct CschedLcConfigCnfParameters& params) override;
-    void CschedLcReleaseCnf(const struct CschedLcReleaseCnfParameters& params) override;
-    void CschedUeReleaseCnf(const struct CschedUeReleaseCnfParameters& params) override;
-    void CschedUeConfigUpdateInd(const struct CschedUeConfigUpdateIndParameters& params) override;
-    void CschedCellConfigUpdateInd(
-        const struct CschedCellConfigUpdateIndParameters& params) override;
+    void CschedCellConfigCnf(const CschedCellConfigCnfParameters& params) override;
+    void CschedUeConfigCnf(const CschedUeConfigCnfParameters& params) override;
+    void CschedLcConfigCnf(const CschedLcConfigCnfParameters& params) override;
+    void CschedLcReleaseCnf(const CschedLcReleaseCnfParameters& params) override;
+    void CschedUeReleaseCnf(const CschedUeReleaseCnfParameters& params) override;
+    void CschedUeConfigUpdateInd(const CschedUeConfigUpdateIndParameters& params) override;
+    void CschedCellConfigUpdateInd(const CschedCellConfigUpdateIndParameters& params) override;
 
   private:
     LteEnbMac* m_mac; ///< the MAC
@@ -198,48 +194,45 @@
 }
 
 void
-EnbMacMemberFfMacCschedSapUser::CschedCellConfigCnf(
-    const struct CschedCellConfigCnfParameters& params)
+EnbMacMemberFfMacCschedSapUser::CschedCellConfigCnf(const CschedCellConfigCnfParameters& params)
 {
     m_mac->DoCschedCellConfigCnf(params);
 }
 
 void
-EnbMacMemberFfMacCschedSapUser::CschedUeConfigCnf(const struct CschedUeConfigCnfParameters& params)
+EnbMacMemberFfMacCschedSapUser::CschedUeConfigCnf(const CschedUeConfigCnfParameters& params)
 {
     m_mac->DoCschedUeConfigCnf(params);
 }
 
 void
-EnbMacMemberFfMacCschedSapUser::CschedLcConfigCnf(const struct CschedLcConfigCnfParameters& params)
+EnbMacMemberFfMacCschedSapUser::CschedLcConfigCnf(const CschedLcConfigCnfParameters& params)
 {
     m_mac->DoCschedLcConfigCnf(params);
 }
 
 void
-EnbMacMemberFfMacCschedSapUser::CschedLcReleaseCnf(
-    const struct CschedLcReleaseCnfParameters& params)
+EnbMacMemberFfMacCschedSapUser::CschedLcReleaseCnf(const CschedLcReleaseCnfParameters& params)
 {
     m_mac->DoCschedLcReleaseCnf(params);
 }
 
 void
-EnbMacMemberFfMacCschedSapUser::CschedUeReleaseCnf(
-    const struct CschedUeReleaseCnfParameters& params)
+EnbMacMemberFfMacCschedSapUser::CschedUeReleaseCnf(const CschedUeReleaseCnfParameters& params)
 {
     m_mac->DoCschedUeReleaseCnf(params);
 }
 
 void
 EnbMacMemberFfMacCschedSapUser::CschedUeConfigUpdateInd(
-    const struct CschedUeConfigUpdateIndParameters& params)
+    const CschedUeConfigUpdateIndParameters& params)
 {
     m_mac->DoCschedUeConfigUpdateInd(params);
 }
 
 void
 EnbMacMemberFfMacCschedSapUser::CschedCellConfigUpdateInd(
-    const struct CschedCellConfigUpdateIndParameters& params)
+    const CschedCellConfigUpdateIndParameters& params)
 {
     m_mac->DoCschedCellConfigUpdateInd(params);
 }
@@ -261,8 +254,8 @@
     void ReceiveLteControlMessage(Ptr<LteControlMessage> msg) override;
     void ReceiveRachPreamble(uint32_t prachId) override;
     void UlCqiReport(FfMacSchedSapProvider::SchedUlCqiInfoReqParameters ulcqi) override;
-    void UlInfoListElementHarqFeeback(UlInfoListElement_s params) override;
-    void DlInfoListElementHarqFeeback(DlInfoListElement_s params) override;
+    void UlInfoListElementHarqFeedback(UlInfoListElement_s params) override;
+    void DlInfoListElementHarqFeedback(DlInfoListElement_s params) override;
 
   private:
     LteEnbMac* m_mac; ///< the MAC
@@ -304,15 +297,15 @@
 }
 
 void
-EnbMacMemberLteEnbPhySapUser::UlInfoListElementHarqFeeback(UlInfoListElement_s params)
+EnbMacMemberLteEnbPhySapUser::UlInfoListElementHarqFeedback(UlInfoListElement_s params)
 {
-    m_mac->DoUlInfoListElementHarqFeeback(params);
+    m_mac->DoUlInfoListElementHarqFeedback(params);
 }
 
 void
-EnbMacMemberLteEnbPhySapUser::DlInfoListElementHarqFeeback(DlInfoListElement_s params)
+EnbMacMemberLteEnbPhySapUser::DlInfoListElementHarqFeedback(DlInfoListElement_s params)
 {
-    m_mac->DoDlInfoListElementHarqFeeback(params);
+    m_mac->DoDlInfoListElementHarqFeedback(params);
 }
 
 // //////////////////////////////////////
@@ -533,6 +526,14 @@
                 else
                 {
                     rnti = m_cmacSapUser->AllocateTemporaryCellRnti();
+
+                    if (rnti == 0)
+                    {
+                        // If rnti = 0, UE context was not created (not enough SRS)
+                        // Therefore don't send RAR for this preamble
+                        NS_LOG_INFO("UE context not created, no RAR to send");
+                        continue;
+                    }
                     NS_LOG_INFO("preambleId " << (uint32_t)it->first << ": allocated T-C-RNTI "
                                               << (uint32_t)rnti << ", sending RAR");
                 }
@@ -563,7 +564,7 @@
     FfMacSchedSapProvider::SchedDlTriggerReqParameters dlparams;
     dlparams.m_sfnSf = ((0x3FF & dlSchedFrameNo) << 4) | (0xF & dlSchedSubframeNo);
 
-    // Forward DL HARQ feebacks collected during last TTI
+    // Forward DL HARQ Feedbacks collected during last TTI
     if (!m_dlInfoListReceived.empty())
     {
         dlparams.m_dlInfoList = m_dlInfoListReceived;
@@ -617,7 +618,7 @@
     FfMacSchedSapProvider::SchedUlTriggerReqParameters ulparams;
     ulparams.m_sfnSf = ((0x3FF & ulSchedFrameNo) << 4) | (0xF & ulSchedSubframeNo);
 
-    // Forward DL HARQ feebacks collected during last TTI
+    // Forward DL HARQ Feedbacks collected during last TTI
     if (!m_ulInfoListReceived.empty())
     {
         ulparams.m_ulInfoList = m_ulInfoListReceived;
@@ -646,7 +647,7 @@
     {
         Ptr<DlHarqFeedbackLteControlMessage> dlharq =
             DynamicCast<DlHarqFeedbackLteControlMessage>(msg);
-        DoDlInfoListElementHarqFeeback(dlharq->GetDlHarqFeedback());
+        DoDlInfoListElementHarqFeedback(dlharq->GetDlHarqFeedback());
     }
     else
     {
@@ -893,21 +894,22 @@
     // 4.3.4 logicalChannelConfigListElement
     if (lcinfo.lcId != 0)
     {
-        struct FfMacCschedSapProvider::CschedLcConfigReqParameters params;
+        FfMacCschedSapProvider::CschedLcConfigReqParameters params;
         params.m_rnti = lcinfo.rnti;
         params.m_reconfigureFlag = false;
 
-        struct LogicalChannelConfigListElement_s lccle;
+        LogicalChannelConfigListElement_s lccle;
         lccle.m_logicalChannelIdentity = lcinfo.lcId;
         lccle.m_logicalChannelGroup = lcinfo.lcGroup;
         lccle.m_direction = LogicalChannelConfigListElement_s::DIR_BOTH;
-        lccle.m_qosBearerType = lcinfo.isGbr ? LogicalChannelConfigListElement_s::QBT_GBR
-                                             : LogicalChannelConfigListElement_s::QBT_NON_GBR;
         lccle.m_qci = lcinfo.qci;
         lccle.m_eRabMaximulBitrateUl = lcinfo.mbrUl;
         lccle.m_eRabMaximulBitrateDl = lcinfo.mbrDl;
         lccle.m_eRabGuaranteedBitrateUl = lcinfo.gbrUl;
         lccle.m_eRabGuaranteedBitrateDl = lcinfo.gbrDl;
+        lccle.m_qosBearerType =
+            static_cast<LogicalChannelConfigListElement_s::QosBearerType_e>(lcinfo.resourceType);
+
         params.m_logicalChannelConfigList.push_back(lccle);
 
         m_cschedSapProvider->CschedLcConfigReq(params);
@@ -930,7 +932,7 @@
         m_rlcAttached.find(rnti);
     rntiIt->second.erase(lcid);
 
-    struct FfMacCschedSapProvider::CschedLcReleaseReqParameters params;
+    FfMacCschedSapProvider::CschedLcReleaseReqParameters params;
     params.m_rnti = rnti;
     params.m_logicalChannelIdentity.push_back(lcid);
     m_cschedSapProvider->CschedLcReleaseReq(params);
@@ -952,7 +954,7 @@
 LteEnbCmacSapProvider::RachConfig
 LteEnbMac::DoGetRachConfig() const
 {
-    struct LteEnbCmacSapProvider::RachConfig rc;
+    LteEnbCmacSapProvider::RachConfig rc;
     rc.numberOfRaPreambles = m_numberOfRaPreambles;
     rc.preambleTransMax = m_preambleTransMax;
     rc.raResponseWindowSize = m_raResponseWindowSize;
@@ -1296,14 +1298,14 @@
 }
 
 void
-LteEnbMac::DoUlInfoListElementHarqFeeback(UlInfoListElement_s params)
+LteEnbMac::DoUlInfoListElementHarqFeedback(UlInfoListElement_s params)
 {
     NS_LOG_FUNCTION(this);
     m_ulInfoListReceived.push_back(params);
 }
 
 void
-LteEnbMac::DoDlInfoListElementHarqFeeback(DlInfoListElement_s params)
+LteEnbMac::DoDlInfoListElementHarqFeedback(DlInfoListElement_s params)
 {
     NS_LOG_FUNCTION(this);
     // Update HARQ buffer
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-mac.h ns-3.39/src/lte/model/lte-enb-mac.h
--- ns-3.38/src/lte/model/lte-enb-mac.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-mac.h	2023-07-05 14:23:48.009428000 -0700
@@ -24,17 +24,19 @@
 #ifndef LTE_ENB_MAC_H
 #define LTE_ENB_MAC_H
 
-#include "ns3/trace-source-accessor.h"
-#include "ns3/traced-value.h"
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/lte-ccm-mac-sap.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-enb-cmac-sap.h>
-#include <ns3/lte-enb-phy-sap.h>
-#include <ns3/lte-mac-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "lte-ccm-mac-sap.h"
+#include "lte-common.h"
+#include "lte-enb-cmac-sap.h"
+#include "lte-enb-phy-sap.h"
+#include "lte-mac-sap.h"
+
+#include <ns3/nstime.h>
 #include <ns3/packet-burst.h>
 #include <ns3/packet.h>
+#include <ns3/trace-source-accessor.h>
+#include <ns3/traced-value.h>
 
 #include <map>
 #include <vector>
@@ -365,12 +367,12 @@
      * \brief UL Info List ELements HARQ Feedback function
      * \param params UlInfoListElement_s
      */
-    void DoUlInfoListElementHarqFeeback(UlInfoListElement_s params);
+    void DoUlInfoListElementHarqFeedback(UlInfoListElement_s params);
     /**
      * \brief DL Info List ELements HARQ Feedback function
      * \param params DlInfoListElement_s
      */
-    void DoDlInfoListElementHarqFeeback(DlInfoListElement_s params);
+    void DoDlInfoListElementHarqFeedback(DlInfoListElement_s params);
 
     /// RNTI, LC ID, SAP of the RLC instance
     std::map<uint16_t, std::map<uint8_t, LteMacSapUser*>> m_rlcAttached;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-net-device.cc ns-3.39/src/lte/model/lte-enb-net-device.cc
--- ns-3.38/src/lte/model/lte-enb-net-device.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-net-device.cc	2023-07-05 14:23:48.009428000 -0700
@@ -21,25 +21,25 @@
  * 2015 - Carrier Aggregation
  */
 
+#include "lte-enb-net-device.h"
+
+#include "component-carrier-enb.h"
+#include "lte-anr.h"
+#include "lte-enb-component-carrier-manager.h"
+#include "lte-enb-mac.h"
+#include "lte-enb-phy.h"
+#include "lte-enb-rrc.h"
+#include "lte-ffr-algorithm.h"
+#include "lte-handover-algorithm.h"
+#include "lte-net-device.h"
+
 #include <ns3/abort.h>
 #include <ns3/callback.h>
 #include <ns3/enum.h>
-#include <ns3/ff-mac-scheduler.h>
 #include <ns3/ipv4-l3-protocol.h>
 #include <ns3/ipv6-l3-protocol.h>
 #include <ns3/llc-snap-header.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-anr.h>
-#include <ns3/lte-enb-component-carrier-manager.h>
-#include <ns3/lte-enb-mac.h>
-#include <ns3/lte-enb-net-device.h>
-#include <ns3/lte-enb-phy.h>
-#include <ns3/lte-enb-rrc.h>
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-handover-algorithm.h>
-#include <ns3/lte-net-device.h>
-#include <ns3/lte-ue-net-device.h>
 #include <ns3/node.h>
 #include <ns3/object-factory.h>
 #include <ns3/object-map.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-net-device.h ns-3.39/src/lte/model/lte-enb-net-device.h
--- ns-3.38/src/lte/model/lte-enb-net-device.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-net-device.h	2023-07-05 14:23:48.009428000 -0700
@@ -23,10 +23,10 @@
 #ifndef LTE_ENB_NET_DEVICE_H
 #define LTE_ENB_NET_DEVICE_H
 
-#include "ns3/component-carrier-enb.h"
+#include "component-carrier.h"
+#include "lte-net-device.h"
+
 #include "ns3/event-id.h"
-#include "ns3/lte-net-device.h"
-#include "ns3/lte-phy.h"
 #include "ns3/mac48-address.h"
 #include "ns3/nstime.h"
 #include "ns3/traced-callback.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-phy.cc ns-3.39/src/lte/model/lte-enb-phy.cc
--- ns-3.38/src/lte/model/lte-enb-phy.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-phy.cc	2023-07-05 14:23:48.009428000 -0700
@@ -20,27 +20,22 @@
 
 #include "lte-enb-phy.h"
 
+#include "lte-common.h"
 #include "lte-control-messages.h"
-#include "lte-enb-mac.h"
-#include "lte-enb-net-device.h"
 #include "lte-net-device.h"
 #include "lte-spectrum-value-helper.h"
-#include "lte-ue-phy.h"
-#include "lte-ue-rrc.h"
+#include "lte-vendor-specific-parameters.h"
 
 #include <ns3/attribute-accessor-helper.h>
 #include <ns3/double.h>
 #include <ns3/log.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/object-factory.h>
 #include <ns3/simulator.h>
 
 #include <cfloat>
 #include <cmath>
 
-// WILD HACK for the inizialization of direct eNB-UE ctrl messaging
-#include <ns3/lte-ue-net-device.h>
+// WILD HACK for the initialization of direct eNB-UE ctrl messaging
 #include <ns3/node-list.h>
 #include <ns3/node.h>
 #include <ns3/pointer.h>
@@ -736,7 +731,7 @@
                      it != rarMsg->RarListEnd();
                      ++it)
                 {
-                    if (it->rarPayload.m_grant.m_ulDelay == true)
+                    if (it->rarPayload.m_grant.m_ulDelay)
                     {
                         NS_FATAL_ERROR(" RAR delay is not yet implemented");
                     }
@@ -1161,7 +1156,7 @@
 {
     NS_LOG_FUNCTION(this);
     // forward to scheduler
-    m_enbPhySapUser->UlInfoListElementHarqFeeback(mes);
+    m_enbPhySapUser->UlInfoListElementHarqFeedback(mes);
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-phy.h ns-3.39/src/lte/model/lte-enb-phy.h
--- ns-3.38/src/lte/model/lte-enb-phy.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-phy.h	2023-07-05 14:23:48.009428000 -0700
@@ -21,11 +21,11 @@
 #ifndef ENB_LTE_PHY_H
 #define ENB_LTE_PHY_H
 
-#include <ns3/lte-control-messages.h>
-#include <ns3/lte-enb-cphy-sap.h>
-#include <ns3/lte-enb-phy-sap.h>
-#include <ns3/lte-harq-phy.h>
-#include <ns3/lte-phy.h>
+#include "lte-control-messages.h"
+#include "lte-enb-cphy-sap.h"
+#include "lte-enb-phy-sap.h"
+#include "lte-harq-phy.h"
+#include "lte-phy.h"
 
 #include <map>
 #include <set>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-phy-sap.h ns-3.39/src/lte/model/lte-enb-phy-sap.h
--- ns-3.38/src/lte/model/lte-enb-phy-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-phy-sap.h	2023-07-05 14:23:48.009428000 -0700
@@ -20,8 +20,9 @@
 #ifndef LTE_ENB_PHY_SAP_H
 #define LTE_ENB_PHY_SAP_H
 
-#include <ns3/ff-mac-common.h>
-#include <ns3/ff-mac-sched-sap.h>
+#include "ff-mac-common.h"
+#include "ff-mac-sched-sap.h"
+
 #include <ns3/packet.h>
 
 namespace ns3
@@ -109,14 +110,14 @@
      *
      * \param params
      */
-    virtual void UlInfoListElementHarqFeeback(UlInfoListElement_s params) = 0;
+    virtual void UlInfoListElementHarqFeedback(UlInfoListElement_s params) = 0;
 
     /**
      * Notify the HARQ on the DL transmission status
      *
      * \param params
      */
-    virtual void DlInfoListElementHarqFeeback(DlInfoListElement_s params) = 0;
+    virtual void DlInfoListElementHarqFeedback(DlInfoListElement_s params) = 0;
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-rrc.cc ns-3.39/src/lte/model/lte-enb-rrc.cc
--- ns-3.38/src/lte/model/lte-enb-rrc.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-rrc.cc	2023-07-05 14:23:48.009428000 -0700
@@ -25,16 +25,18 @@
 
 #include "lte-enb-rrc.h"
 
+#include "component-carrier-enb.h"
+#include "eps-bearer-tag.h"
+#include "lte-pdcp.h"
+#include "lte-radio-bearer-info.h"
+#include "lte-rlc-am.h"
+#include "lte-rlc-tm.h"
+#include "lte-rlc-um.h"
+#include "lte-rlc.h"
+
 #include <ns3/abort.h>
-#include <ns3/eps-bearer-tag.h>
 #include <ns3/fatal-error.h>
 #include <ns3/log.h>
-#include <ns3/lte-pdcp.h>
-#include <ns3/lte-radio-bearer-info.h>
-#include <ns3/lte-rlc-am.h>
-#include <ns3/lte-rlc-tm.h>
-#include <ns3/lte-rlc-um.h>
-#include <ns3/lte-rlc.h>
 #include <ns3/object-factory.h>
 #include <ns3/object-map.h>
 #include <ns3/packet.h>
@@ -204,7 +206,7 @@
         // Iinterface Specification v1.11, 4.3.4 logicalChannelConfigListElement
         lcinfo.lcGroup = 0;
         lcinfo.qci = 0;
-        lcinfo.isGbr = false;
+        lcinfo.resourceType = 0;
         lcinfo.mbrUl = 0;
         lcinfo.mbrDl = 0;
         lcinfo.gbrUl = 0;
@@ -249,7 +251,7 @@
         lcinfo.lcGroup = 0; // all SRBs always mapped to LCG 0
         lcinfo.qci =
             EpsBearer::GBR_CONV_VOICE; // not sure why the FF API requires a CQI even for SRBs...
-        lcinfo.isGbr = true;
+        lcinfo.resourceType = 1;       // GBR resource type
         lcinfo.mbrUl = 1e6;
         lcinfo.mbrDl = 1e6;
         lcinfo.gbrUl = 1e4;
@@ -436,6 +438,7 @@
     Ptr<LteRlc> rlc = rlcObjectFactory.Create()->GetObject<LteRlc>();
     rlc->SetLteMacSapProvider(m_rrc->m_macSapProvider);
     rlc->SetRnti(m_rnti);
+    rlc->SetPacketDelayBudgetMs(bearer.GetPacketDelayBudgetMs());
 
     drbInfo->m_rlc = rlc;
 
@@ -468,7 +471,7 @@
     // lcinfo.lcId = lcid;
     // lcinfo.lcGroup = m_rrc->GetLogicalChannelGroup (bearer);
     // lcinfo.qci = bearer.qci;
-    // lcinfo.isGbr = bearer.IsGbr ();
+    // lcinfo.resourceType = bearer.GetResourceType();
     // lcinfo.mbrUl = bearer.gbrQosInfo.mbrUl;
     // lcinfo.mbrDl = bearer.gbrQosInfo.mbrDl;
     // lcinfo.gbrUl = bearer.gbrQosInfo.gbrUl;
@@ -503,7 +506,7 @@
     drbInfo->m_logicalChannelIdentity = lcid;
     drbInfo->m_logicalChannelConfig.priority = m_rrc->GetLogicalChannelPriority(bearer);
     drbInfo->m_logicalChannelConfig.logicalChannelGroup = m_rrc->GetLogicalChannelGroup(bearer);
-    if (bearer.IsGbr())
+    if (bearer.GetResourceType() > 0) // 1, 2 for GBR and DC-GBR
     {
         drbInfo->m_logicalChannelConfig.prioritizedBitRateKbps = bearer.gbrQosInfo.gbrUl;
     }
@@ -679,7 +682,7 @@
             uint16_t rnti = m_rrc->AddUe(UeManager::HANDOVER_JOINING, componentCarrierId);
             LteEnbCmacSapProvider::AllocateNcRaPreambleReturnValue anrcrv =
                 m_rrc->m_cmacSapProvider.at(componentCarrierId)->AllocateNcRaPreamble(rnti);
-            if (anrcrv.valid == false)
+            if (!anrcrv.valid)
             {
                 NS_LOG_INFO(this << " failed to allocate a preamble for non-contention based RA => "
                                     "cannot perform HO");
@@ -958,7 +961,6 @@
     case CONNECTION_SETUP:
         NS_LOG_WARN("not connected, discarding packet");
         return;
-        break;
 
     case CONNECTED_NORMALLY:
     case CONNECTION_RECONFIGURATION:
@@ -1145,7 +1147,7 @@
     case INITIAL_RANDOM_ACCESS: {
         m_connectionRequestTimeout.Cancel();
 
-        if (m_rrc->m_admitRrcConnectionRequest == true)
+        if (m_rrc->m_admitRrcConnectionRequest)
         {
             m_imsi = msg.ueIdentity;
 
@@ -1195,7 +1197,7 @@
     {
     case CONNECTION_SETUP:
         m_connectionSetupTimeout.Cancel();
-        if (m_caSupportConfigured == false && m_rrc->m_numberOfComponentCarriers > 1)
+        if (!m_caSupportConfigured && m_rrc->m_numberOfComponentCarriers > 1)
         {
             m_pendingRrcConnectionReconfiguration = true; // Force Reconfiguration
             m_pendingStartDataRadioBearers = true;
@@ -1610,7 +1612,7 @@
     msg.haveMobilityControlInfo = false;
     msg.haveMeasConfig = true;
     msg.measConfig = m_rrc->m_ueMeasConfig;
-    if (m_caSupportConfigured == false && m_rrc->m_numberOfComponentCarriers > 1)
+    if (!m_caSupportConfigured && m_rrc->m_numberOfComponentCarriers > 1)
     {
         m_caSupportConfigured = true;
         NS_LOG_FUNCTION(this << "CA not configured. Configure now!");
@@ -1733,11 +1735,11 @@
         break;
 
     case CONNECTED_NORMALLY: {
-        if (m_pendingRrcConnectionReconfiguration == true)
+        if (m_pendingRrcConnectionReconfiguration)
         {
             ScheduleRrcConnectionReconfiguration();
         }
-        if (m_pendingStartDataRadioBearers == true && m_caSupportConfigured == true)
+        if (m_pendingStartDataRadioBearers && m_caSupportConfigured)
         {
             StartDataRadioBearers();
         }
@@ -2867,7 +2869,7 @@
     NS_LOG_LOGIC("mmeUeS1apId = " << req.mmeUeS1apId);
 
     // if no SRS index is available, then do not accept the handover
-    if (m_admitHandoverRequest == false || IsMaxSrsReached())
+    if (!m_admitHandoverRequest || IsMaxSrsReached())
     {
         NS_LOG_INFO("rejecting handover request from cellId " << req.sourceCellId);
         EpcX2Sap::HandoverPreparationFailureParams res;
@@ -2887,7 +2889,7 @@
     ueManager->SetImsi(req.mmeUeS1apId);
     LteEnbCmacSapProvider::AllocateNcRaPreambleReturnValue anrcrv =
         m_cmacSapProvider.at(componentCarrierId)->AllocateNcRaPreamble(rnti);
-    if (anrcrv.valid == false)
+    if (!anrcrv.valid)
     {
         NS_LOG_INFO(
             this
@@ -3121,6 +3123,7 @@
     // if no SRS index is available, then do not create a new UE context.
     if (IsMaxSrsReached())
     {
+        NS_LOG_WARN("Not enough SRS configuration indices, UE context not created");
         return 0; // return 0 since new RNTI was not assigned for the received preamble
     }
     return AddUe(UeManager::INITIAL_RANDOM_ACCESS, componentCarrierId);
@@ -3310,15 +3313,12 @@
     {
     case RLC_SM_ALWAYS:
         return LteRlcSm::GetTypeId();
-        break;
 
     case RLC_UM_ALWAYS:
         return LteRlcUm::GetTypeId();
-        break;
 
     case RLC_AM_ALWAYS:
         return LteRlcAm::GetTypeId();
-        break;
 
     case PER_BASED:
         if (bearer.GetPacketErrorLossRate() > 1.0e-5)
@@ -3329,11 +3329,9 @@
         {
             return LteRlcAm::GetTypeId();
         }
-        break;
 
     default:
         return LteRlcSm::GetTypeId();
-        break;
     }
 }
 
@@ -3487,20 +3485,13 @@
     NS_ASSERT(m_srsCurrentPeriodicityId < SRS_ENTRIES);
     NS_LOG_DEBUG(this << " SRS p " << g_srsPeriodicity[m_srsCurrentPeriodicityId] << " set "
                       << m_ueSrsConfigurationIndexSet.size());
-    if (m_ueSrsConfigurationIndexSet.size() >= g_srsPeriodicity[m_srsCurrentPeriodicityId])
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return m_ueSrsConfigurationIndexSet.size() >= g_srsPeriodicity[m_srsCurrentPeriodicityId];
 }
 
 uint8_t
 LteEnbRrc::GetLogicalChannelGroup(EpsBearer bearer)
 {
-    if (bearer.IsGbr())
+    if (bearer.GetResourceType() > 0) // 1, 2 for GBR and DC-GBR
     {
         return 1;
     }
@@ -3561,10 +3552,8 @@
     case UeManager::CONNECTED_NORMALLY:
     case UeManager::CONNECTION_RECONFIGURATION:
         return true;
-        break;
     default:
         return false;
-        break;
     }
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-enb-rrc.h ns-3.39/src/lte/model/lte-enb-rrc.h
--- ns-3.38/src/lte/model/lte-enb-rrc.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-enb-rrc.h	2023-07-05 14:23:48.009428000 -0700
@@ -27,22 +27,20 @@
 #ifndef LTE_ENB_RRC_H
 #define LTE_ENB_RRC_H
 
-#include <ns3/component-carrier-enb.h>
-#include <ns3/epc-enb-s1-sap.h>
-#include <ns3/epc-x2-sap.h>
+#include "component-carrier.h"
+#include "epc-enb-s1-sap.h"
+#include "epc-x2-sap.h"
+#include "lte-anr-sap.h"
+#include "lte-ccm-rrc-sap.h"
+#include "lte-enb-cmac-sap.h"
+#include "lte-enb-cphy-sap.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-handover-management-sap.h"
+#include "lte-mac-sap.h"
+#include "lte-pdcp-sap.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/event-id.h>
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/lte-anr-sap.h>
-#include <ns3/lte-ccm-rrc-sap.h>
-#include <ns3/lte-enb-cmac-sap.h>
-#include <ns3/lte-enb-cphy-sap.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-handover-management-sap.h>
-#include <ns3/lte-mac-sap.h>
-#include <ns3/lte-pdcp-sap.h>
-#include <ns3/lte-rlc.h>
-#include <ns3/lte-rrc-sap.h>
 #include <ns3/nstime.h>
 #include <ns3/object.h>
 #include <ns3/traced-callback.h>
@@ -51,9 +49,6 @@
 #include <set>
 #include <vector>
 
-#define MIN_NO_CC 1
-#define MAX_NO_CC 5 // this is the maximum number of carrier components allowed by 3GPP up to R13
-
 namespace ns3
 {
 
@@ -1637,7 +1632,7 @@
      * The `EpsBearerToRlcMapping` attribute. Specify which type of RLC will be
      * used for each type of EPS bearer.
      */
-    enum LteEpsBearerToRlcMapping_t m_epsBearerToRlcMapping;
+    LteEpsBearerToRlcMapping_t m_epsBearerToRlcMapping;
     /**
      * The `SystemInformationPeriodicity` attribute. The interval for sending
      * system information.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-algorithm.h ns-3.39/src/lte/model/lte-ffr-algorithm.h
--- ns-3.38/src/lte/model/lte-ffr-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,9 +21,10 @@
 #ifndef LTE_FFR_ALGORITHM_H
 #define LTE_FFR_ALGORITHM_H
 
-#include <ns3/epc-x2-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "epc-x2-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/object.h>
 
 #include <map>
@@ -176,7 +177,7 @@
      *
      */
     virtual void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) = 0;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) = 0;
 
     /**
      * \brief DoReportUlCqiInfo
@@ -184,7 +185,7 @@
      *
      */
     virtual void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) = 0;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) = 0;
 
     /**
      * \brief DoReportUlCqiInfo
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.cc ns-3.39/src/lte/model/lte-ffr-distributed-algorithm.cc
--- ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-distributed-algorithm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -306,7 +306,7 @@
 
 void
 LteFfrDistributedAlgorithm::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -314,7 +314,7 @@
 
 void
 LteFfrDistributedAlgorithm::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -465,7 +465,7 @@
                         found = true;
                     }
                 }
-                if (found == false)
+                if (!found)
                 {
                     m_neighborCell.push_back(it->physCellId);
                 }
@@ -587,7 +587,10 @@
 
             for (uint16_t i = 0; i < rbgNum; i++)
             {
-                metricA[i] += cellIt->second * rntpIt->second[i];
+                if (rntpIt->second[i])
+                {
+                    metricA[i] += cellIt->second;
+                }
             }
         }
 
@@ -706,21 +709,19 @@
     }
 
     NS_ASSERT(it1 != m_ueMeasures.end());
-    Ptr<UeMeasure> cellMeasures;
     std::map<uint16_t, Ptr<UeMeasure>>::iterator it2;
     it2 = it1->second.find(cellId);
 
     if (it2 != it1->second.end())
     {
-        cellMeasures = it2->second;
-        cellMeasures->m_cellId = cellId;
-        cellMeasures->m_rsrp = rsrp;
-        cellMeasures->m_rsrq = rsrq;
+        it2->second->m_cellId = cellId;
+        it2->second->m_rsrp = rsrp;
+        it2->second->m_rsrq = rsrq;
     }
     else
     {
         // insert a new cell entry
-        cellMeasures = Create<UeMeasure>();
+        Ptr<UeMeasure> cellMeasures = Create<UeMeasure>();
         cellMeasures->m_cellId = cellId;
         cellMeasures->m_rsrp = rsrp;
         cellMeasures->m_rsrq = rsrq;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.h ns-3.39/src/lte/model/lte-ffr-distributed-algorithm.h
--- ns-3.38/src/lte/model/lte-ffr-distributed-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-distributed-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_FFR_DISTRIBUTED_ALGORITHM_H
 #define LTE_FFR_DISTRIBUTED_ALGORITHM_H
 
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-ffr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ffr-algorithm.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-ffr-sap.h"
+#include "lte-rrc-sap.h"
 
 namespace ns3
 {
@@ -69,9 +69,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-enhanced-algorithm.cc ns-3.39/src/lte/model/lte-ffr-enhanced-algorithm.cc
--- ns-3.38/src/lte/model/lte-ffr-enhanced-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-enhanced-algorithm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -20,10 +20,10 @@
 
 #include "lte-ffr-enhanced-algorithm.h"
 
+#include "ff-mac-common.h"
+#include "lte-common.h"
+
 #include "ns3/boolean.h"
-#include "ns3/ff-mac-common.h"
-#include "ns3/lte-common.h"
-#include "ns3/lte-vendor-specific-parameters.h"
 #include <ns3/double.h>
 #include <ns3/log.h>
 
@@ -486,7 +486,7 @@
         for (uint32_t i = 0; i < rbgMap.size(); i++)
         {
             NS_LOG_INFO("\t rbgId: " << i << " available " << (int)rbgAvailableMap.at(i));
-            if (rbgAvailableMap.at(i) == true)
+            if (rbgAvailableMap.at(i))
             {
                 rbgMap.at(i) = false;
             }
@@ -546,7 +546,7 @@
         {
             NS_LOG_INFO("RNTI: " << rnti << "  rbgId: " << rbgId
                                  << "  available: " << it->second.at(rbgId));
-            if (it->second.at(rbgId) == true)
+            if (it->second.at(rbgId))
             {
                 return true;
             }
@@ -582,7 +582,7 @@
         for (uint32_t i = 0; i < rbgMap.size(); i++)
         {
             NS_LOG_INFO("\t rbgId: " << i << " available " << (int)rbAvailableMap.at(i));
-            if (rbAvailableMap.at(i) == true)
+            if (rbAvailableMap.at(i))
             {
                 rbgMap.at(i) = false;
             }
@@ -646,7 +646,7 @@
         {
             NS_LOG_INFO("RNTI: " << rnti << "  rbgId: " << rbgId
                                  << "  available: " << it->second.at(rbgId));
-            if (it->second.at(rbgId) == true)
+            if (it->second.at(rbgId))
             {
                 return true;
             }
@@ -659,7 +659,7 @@
 
 void
 LteFfrEnhancedAlgorithm::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -732,7 +732,7 @@
                 isSecondarySegmentRbg = m_dlSecondarySegmentRbgMap[i];
             }
 
-            rbgAvailable = (isSecondarySegmentRbg == true) ? rbgAvailable : false;
+            rbgAvailable = (isSecondarySegmentRbg ? rbgAvailable : false);
 
             rbgAvailableMap.push_back(rbgAvailable);
         }
@@ -766,7 +766,7 @@
 
 void
 LteFfrEnhancedAlgorithm::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     if (params.m_ulCqi.m_type == UlCqi_s::SRS)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-enhanced-algorithm.h ns-3.39/src/lte/model/lte-ffr-enhanced-algorithm.h
--- ns-3.38/src/lte/model/lte-ffr-enhanced-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-enhanced-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_FFR_ENHANCED_ALGORITHM_H
 #define LTE_FFR_ENHANCED_ALGORITHM_H
 
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-ffr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ffr-algorithm.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-ffr-sap.h"
+#include "lte-rrc-sap.h"
 
 #include <map>
 
@@ -78,9 +78,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-rrc-sap.h ns-3.39/src/lte/model/lte-ffr-rrc-sap.h
--- ns-3.38/src/lte/model/lte-ffr-rrc-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-rrc-sap.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,8 +21,8 @@
 #ifndef LTE_FFR_RRC_SAP_H
 #define LTE_FFR_RRC_SAP_H
 
-#include <ns3/epc-x2-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "epc-x2-sap.h"
+#include "lte-rrc-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-sap.h ns-3.39/src/lte/model/lte-ffr-sap.h
--- ns-3.38/src/lte/model/lte-ffr-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-sap.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,8 +21,7 @@
 #ifndef LTE_FFR_SAP_H
 #define LTE_FFR_SAP_H
 
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "ff-mac-sched-sap.h"
 
 #include <map>
 
@@ -101,14 +100,14 @@
      * \param params the struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters
      */
     virtual void ReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) = 0;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) = 0;
 
     /**
      * \brief ReportUlCqiInfo
      * \param params the struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
      */
     virtual void ReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) = 0;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) = 0;
 
     /**
      * \brief ReportUlCqiInfo
@@ -168,10 +167,8 @@
     bool IsDlRbgAvailableForUe(int i, uint16_t rnti) override;
     std::vector<bool> GetAvailableUlRbg() override;
     bool IsUlRbgAvailableForUe(int i, uint16_t rnti) override;
-    void ReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
-    void ReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+    void ReportDlCqiInfo(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+    void ReportUlCqiInfo(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void ReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t GetTpc(uint16_t rnti) override;
     uint16_t GetMinContinuousUlBandwidth() override;
@@ -218,7 +215,7 @@
 template <class C>
 void
 MemberLteFfrSapProvider<C>::ReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     m_owner->DoReportDlCqiInfo(params);
 }
@@ -226,7 +223,7 @@
 template <class C>
 void
 MemberLteFfrSapProvider<C>::ReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     m_owner->DoReportUlCqiInfo(params);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-soft-algorithm.cc ns-3.39/src/lte/model/lte-ffr-soft-algorithm.cc
--- ns-3.38/src/lte/model/lte-ffr-soft-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-soft-algorithm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -521,7 +521,7 @@
 
 void
 LteFfrSoftAlgorithm::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -529,7 +529,7 @@
 
 void
 LteFfrSoftAlgorithm::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -601,7 +601,7 @@
 
     for (std::size_t i = 0; i < m_ulCenterRbgMap.size(); i++)
     {
-        if (m_ulCenterRbgMap[i] == true)
+        if (m_ulCenterRbgMap[i])
         {
             centerSubBandwidth++;
         }
@@ -609,7 +609,7 @@
 
     for (std::size_t i = 0; i < m_ulMediumRbgMap.size(); i++)
     {
-        if (m_ulMediumRbgMap[i] == true)
+        if (m_ulMediumRbgMap[i])
         {
             mediumSubBandwidth++;
         }
@@ -617,7 +617,7 @@
 
     for (std::size_t i = 0; i < m_ulEdgeRbgMap.size(); i++)
     {
-        if (m_ulEdgeRbgMap[i] == true)
+        if (m_ulEdgeRbgMap[i])
         {
             edgeSubBandwidth++;
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ffr-soft-algorithm.h ns-3.39/src/lte/model/lte-ffr-soft-algorithm.h
--- ns-3.38/src/lte/model/lte-ffr-soft-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-ffr-soft-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_FFR_SOFT_ALGORITHM_H
 #define LTE_FFR_SOFT_ALGORITHM_H
 
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-ffr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ffr-algorithm.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-ffr-sap.h"
+#include "lte-rrc-sap.h"
 
 #include <map>
 
@@ -75,9 +75,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-hard-algorithm.cc ns-3.39/src/lte/model/lte-fr-hard-algorithm.cc
--- ns-3.38/src/lte/model/lte-fr-hard-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-hard-algorithm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -335,7 +335,7 @@
 
 void
 LteFrHardAlgorithm::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -343,7 +343,7 @@
 
 void
 LteFrHardAlgorithm::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-hard-algorithm.h ns-3.39/src/lte/model/lte-fr-hard-algorithm.h
--- ns-3.38/src/lte/model/lte-fr-hard-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-hard-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_FR_HARD_ALGORITHM_H
 #define LTE_FR_HARD_ALGORITHM_H
 
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-ffr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ffr-algorithm.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-ffr-sap.h"
+#include "lte-rrc-sap.h"
 
 namespace ns3
 {
@@ -73,9 +73,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-no-op-algorithm.cc ns-3.39/src/lte/model/lte-fr-no-op-algorithm.cc
--- ns-3.38/src/lte/model/lte-fr-no-op-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-no-op-algorithm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -137,7 +137,7 @@
 
 void
 LteFrNoOpAlgorithm::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -145,7 +145,7 @@
 
 void
 LteFrNoOpAlgorithm::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-no-op-algorithm.h ns-3.39/src/lte/model/lte-fr-no-op-algorithm.h
--- ns-3.38/src/lte/model/lte-fr-no-op-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-no-op-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_FR_NO_OP_ALGORITHM_H
 #define LTE_FR_NO_OP_ALGORITHM_H
 
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-ffr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ffr-algorithm.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-ffr-sap.h"
+#include "lte-rrc-sap.h"
 
 namespace ns3
 {
@@ -79,9 +79,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-soft-algorithm.cc ns-3.39/src/lte/model/lte-fr-soft-algorithm.cc
--- ns-3.38/src/lte/model/lte-fr-soft-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-soft-algorithm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -428,7 +428,7 @@
 
 void
 LteFrSoftAlgorithm::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -436,7 +436,7 @@
 
 void
 LteFrSoftAlgorithm::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-soft-algorithm.h ns-3.39/src/lte/model/lte-fr-soft-algorithm.h
--- ns-3.38/src/lte/model/lte-fr-soft-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-soft-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_FR_SOFT_ALGORITHM_H
 #define LTE_FR_SOFT_ALGORITHM_H
 
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-ffr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ffr-algorithm.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-ffr-sap.h"
+#include "lte-rrc-sap.h"
 
 #include <map>
 
@@ -75,9 +75,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-strict-algorithm.cc ns-3.39/src/lte/model/lte-fr-strict-algorithm.cc
--- ns-3.38/src/lte/model/lte-fr-strict-algorithm.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-strict-algorithm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -456,7 +456,7 @@
 
 void
 LteFrStrictAlgorithm::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
@@ -464,7 +464,7 @@
 
 void
 LteFrStrictAlgorithm::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_LOG_WARN("Method should not be called, because it is empty");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-fr-strict-algorithm.h ns-3.39/src/lte/model/lte-fr-strict-algorithm.h
--- ns-3.38/src/lte/model/lte-fr-strict-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-fr-strict-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_FR_STRICT_ALGORITHM_H
 #define LTE_FR_STRICT_ALGORITHM_H
 
-#include <ns3/lte-ffr-algorithm.h>
-#include <ns3/lte-ffr-rrc-sap.h>
-#include <ns3/lte-ffr-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ffr-algorithm.h"
+#include "lte-ffr-rrc-sap.h"
+#include "lte-ffr-sap.h"
+#include "lte-rrc-sap.h"
 
 #include <map>
 
@@ -75,9 +75,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
@@ -141,7 +141,7 @@
     };
 
     std::map<uint16_t, uint8_t> m_ues; ///< UEs
-    std::vector<uint16_t> m_egdeUes;   ///< Edge UEs
+    std::vector<uint16_t> m_edgeUes;   ///< Edge UEs
 
     uint8_t m_edgeSubBandThreshold; ///< Edge subband threshold
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-handover-algorithm.h ns-3.39/src/lte/model/lte-handover-algorithm.h
--- ns-3.38/src/lte/model/lte-handover-algorithm.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-handover-algorithm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,7 +21,8 @@
 #ifndef LTE_HANDOVER_ALGORITHM_H
 #define LTE_HANDOVER_ALGORITHM_H
 
-#include <ns3/lte-rrc-sap.h>
+#include "lte-rrc-sap.h"
+
 #include <ns3/object.h>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-handover-management-sap.h ns-3.39/src/lte/model/lte-handover-management-sap.h
--- ns-3.38/src/lte/model/lte-handover-management-sap.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-handover-management-sap.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,7 +21,7 @@
 #ifndef LTE_HANDOVER_MANAGEMENT_SAP_H
 #define LTE_HANDOVER_MANAGEMENT_SAP_H
 
-#include <ns3/lte-rrc-sap.h>
+#include "lte-rrc-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-harq-phy.cc ns-3.39/src/lte/model/lte-harq-phy.cc
--- ns-3.38/src/lte/model/lte-harq-phy.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-harq-phy.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,9 +17,10 @@
  * Author: Marco Miozzo  <marco.miozzo@cttc.es>
  */
 
+#include "lte-harq-phy.h"
+
 #include <ns3/assert.h>
 #include <ns3/log.h>
-#include <ns3/lte-harq-phy.h>
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-interference.cc ns-3.39/src/lte/model/lte-interference.cc
--- ns-3.38/src/lte/model/lte-interference.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-interference.cc	2023-07-05 14:23:48.013428000 -0700
@@ -66,7 +66,7 @@
 LteInterference::StartRx(Ptr<const SpectrumValue> rxPsd)
 {
     NS_LOG_FUNCTION(this << *rxPsd);
-    if (m_receiving == false)
+    if (!m_receiving)
     {
         NS_LOG_LOGIC("first signal");
         m_rxSignal = rxPsd->Copy();
@@ -109,7 +109,7 @@
 LteInterference::EndRx()
 {
     NS_LOG_FUNCTION(this);
-    if (m_receiving != true)
+    if (!m_receiving)
     {
         NS_LOG_INFO("EndRx was already evaluated or RX was aborted");
     }
@@ -235,7 +235,7 @@
     // reset m_allSignals (will reset if already set previously)
     // this is needed since this method can potentially change the SpectrumModel
     m_allSignals = Create<SpectrumValue>(noisePsd->GetSpectrumModel());
-    if (m_receiving == true)
+    if (m_receiving)
     {
         // abort rx
         m_receiving = false;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-mac-sap.cc ns-3.39/src/lte/model/lte-mac-sap.cc
--- ns-3.38/src/lte/model/lte-mac-sap.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-mac-sap.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,7 +17,7 @@
  * Author: Nicola Baldo  <nbaldo@cttc.es>
  */
 
-#include "ns3/lte-mac-sap.h"
+#include "lte-mac-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-mi-error-model.cc ns-3.39/src/lte/model/lte-mi-error-model.cc
--- ns-3.38/src/lte/model/lte-mi-error-model.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-mi-error-model.cc	2023-07-05 14:23:48.013428000 -0700
@@ -28,15 +28,15 @@
  *      Marco Miozzo <marco.miozzo@cttc.es>
  */
 
-#include "stdlib.h"
+#include "lte-mi-error-model.h"
 
 #include <ns3/log.h>
-#include <ns3/lte-mi-error-model.h>
 #include <ns3/pointer.h>
 
 #include <cmath>
 #include <list>
 #include <stdint.h>
+#include <stdlib.h>
 #include <vector>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-mi-error-model.h ns-3.39/src/lte/model/lte-mi-error-model.h
--- ns-3.38/src/lte/model/lte-mi-error-model.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-mi-error-model.h	2023-07-05 14:23:48.013428000 -0700
@@ -31,7 +31,8 @@
 #ifndef LTE_MI_ERROR_MODEL_H
 #define LTE_MI_ERROR_MODEL_H
 
-#include <ns3/lte-harq-phy.h>
+#include "lte-harq-phy.h"
+
 #include <ns3/ptr.h>
 #include <ns3/spectrum-value.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-net-device.cc ns-3.39/src/lte/model/lte-net-device.cc
--- ns-3.38/src/lte/model/lte-net-device.cc	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-net-device.cc	2023-07-05 14:23:48.013428000 -0700
@@ -20,9 +20,8 @@
 
 #include "lte-net-device.h"
 
-#include "lte-amc.h"
-
 #include "ns3/callback.h"
+#include "ns3/channel.h"
 #include "ns3/enum.h"
 #include "ns3/ipv4-header.h"
 #include "ns3/ipv6-header.h"
@@ -37,7 +36,6 @@
 #include <ns3/ipv4-l3-protocol.h>
 #include <ns3/ipv6-l3-protocol.h>
 #include <ns3/log.h>
-#include <ns3/lte-radio-bearer-tag.h>
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-net-device.h ns-3.39/src/lte/model/lte-net-device.h
--- ns-3.38/src/lte/model/lte-net-device.h	2023-03-16 20:54:23.772869000 -0700
+++ ns-3.39/src/lte/model/lte-net-device.h	2023-07-05 14:23:48.013428000 -0700
@@ -22,8 +22,6 @@
 #define LTE_NET_DEVICE_H
 
 #include <ns3/event-id.h>
-#include <ns3/lte-control-messages.h>
-#include <ns3/lte-phy.h>
 #include <ns3/mac64-address.h>
 #include <ns3/net-device.h>
 #include <ns3/nstime.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-pdcp.cc ns-3.39/src/lte/model/lte-pdcp.cc
--- ns-3.38/src/lte/model/lte-pdcp.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-pdcp.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,12 +17,13 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-pdcp.h"
+#include "lte-pdcp.h"
+
+#include "lte-pdcp-header.h"
+#include "lte-pdcp-sap.h"
+#include "lte-pdcp-tag.h"
 
 #include "ns3/log.h"
-#include "ns3/lte-pdcp-header.h"
-#include "ns3/lte-pdcp-sap.h"
-#include "ns3/lte-pdcp-tag.h"
 #include "ns3/simulator.h"
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-pdcp.h ns-3.39/src/lte/model/lte-pdcp.h
--- ns-3.38/src/lte/model/lte-pdcp.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-pdcp.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,8 +20,9 @@
 #ifndef LTE_PDCP_H
 #define LTE_PDCP_H
 
-#include "ns3/lte-pdcp-sap.h"
-#include "ns3/lte-rlc-sap.h"
+#include "lte-pdcp-sap.h"
+#include "lte-rlc-sap.h"
+
 #include "ns3/object.h"
 #include "ns3/trace-source-accessor.h"
 #include "ns3/traced-value.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-pdcp-header.cc ns-3.39/src/lte/model/lte-pdcp-header.cc
--- ns-3.38/src/lte/model/lte-pdcp-header.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-pdcp-header.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-pdcp-header.h"
+#include "lte-pdcp-header.h"
 
 #include "ns3/log.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-pdcp-sap.cc ns-3.39/src/lte/model/lte-pdcp-sap.cc
--- ns-3.38/src/lte/model/lte-pdcp-sap.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-pdcp-sap.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-pdcp-sap.h"
+#include "lte-pdcp-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-phy.cc ns-3.39/src/lte/model/lte-phy.cc
--- ns-3.38/src/lte/model/lte-phy.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-phy.cc	2023-07-05 14:23:48.013428000 -0700
@@ -20,6 +20,7 @@
 
 #include "lte-phy.h"
 
+#include "lte-control-messages.h"
 #include "lte-net-device.h"
 
 #include "ns3/spectrum-error-model.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-phy.h ns-3.39/src/lte/model/lte-phy.h
--- ns-3.38/src/lte/model/lte-phy.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-phy.h	2023-07-05 14:23:48.013428000 -0700
@@ -22,8 +22,9 @@
 #ifndef LTE_PHY_H
 #define LTE_PHY_H
 
+#include "lte-spectrum-phy.h"
+
 #include <ns3/generic-phy.h>
-#include <ns3/lte-spectrum-phy.h>
 #include <ns3/mobility-model.h>
 #include <ns3/nstime.h>
 #include <ns3/packet.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-radio-bearer-info.cc ns-3.39/src/lte/model/lte-radio-bearer-info.cc
--- ns-3.38/src/lte/model/lte-radio-bearer-info.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-radio-bearer-info.cc	2023-07-05 14:23:48.013428000 -0700
@@ -21,7 +21,6 @@
 
 #include "lte-pdcp.h"
 #include "lte-rlc.h"
-#include "lte-ue-rrc.h"
 
 #include <ns3/log.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-radio-bearer-info.h ns-3.39/src/lte/model/lte-radio-bearer-info.h
--- ns-3.38/src/lte/model/lte-radio-bearer-info.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-radio-bearer-info.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,9 +20,10 @@
 #ifndef LTE_RADIO_BEARER_INFO_H
 #define LTE_RADIO_BEARER_INFO_H
 
-#include <ns3/eps-bearer.h>
+#include "eps-bearer.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/ipv4-address.h>
-#include <ns3/lte-rrc-sap.h>
 #include <ns3/object.h>
 #include <ns3/pointer.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-am.cc ns-3.39/src/lte/model/lte-rlc-am.cc
--- ns-3.38/src/lte/model/lte-rlc-am.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-am.cc	2023-07-05 14:23:48.013428000 -0700
@@ -18,12 +18,13 @@
  *         Nicola Baldo <nbaldo@cttc.es>
  */
 
-#include "ns3/lte-rlc-am.h"
+#include "lte-rlc-am.h"
+
+#include "lte-rlc-am-header.h"
+#include "lte-rlc-sdu-status-tag.h"
+#include "lte-rlc-tag.h"
 
 #include "ns3/log.h"
-#include "ns3/lte-rlc-am-header.h"
-#include "ns3/lte-rlc-sdu-status-tag.h"
-#include "ns3/lte-rlc-tag.h"
 #include "ns3/simulator.h"
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-am.h ns-3.39/src/lte/model/lte-rlc-am.h
--- ns-3.38/src/lte/model/lte-rlc-am.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-am.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,9 +20,10 @@
 #ifndef LTE_RLC_AM_H
 #define LTE_RLC_AM_H
 
+#include "lte-rlc-sequence-number.h"
+#include "lte-rlc.h"
+
 #include <ns3/event-id.h>
-#include <ns3/lte-rlc-sequence-number.h>
-#include <ns3/lte-rlc.h>
 
 #include <map>
 #include <vector>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-am-header.cc ns-3.39/src/lte/model/lte-rlc-am-header.cc
--- ns-3.38/src/lte/model/lte-rlc-am-header.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-am-header.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-rlc-am-header.h"
+#include "lte-rlc-am-header.h"
 
 #include "ns3/log.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-am-header.h ns-3.39/src/lte/model/lte-rlc-am-header.h
--- ns-3.38/src/lte/model/lte-rlc-am-header.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-am-header.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,8 +20,9 @@
 #ifndef LTE_RLC_AM_HEADER_H
 #define LTE_RLC_AM_HEADER_H
 
+#include "lte-rlc-sequence-number.h"
+
 #include "ns3/header.h"
-#include "ns3/lte-rlc-sequence-number.h"
 
 #include <list>
 
@@ -73,11 +74,8 @@
         DATA_PDU = 1
     };
 
-    /// ControlPduType_t enumeration
-    enum ControPduType_t
-    {
-        STATUS_PDU = 000,
-    };
+    /// Control PDU type status
+    static constexpr uint8_t STATUS_PDU{0};
 
     //
     // DATA PDU
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc.cc ns-3.39/src/lte/model/lte-rlc.cc
--- ns-3.38/src/lte/model/lte-rlc.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,16 +17,16 @@
  * Author: Nicola Baldo <nbaldo@cttc.es>
  */
 
-#include "ns3/lte-rlc.h"
+#include "lte-rlc.h"
 
-#include "ns3/log.h"
-#include "ns3/lte-rlc-tag.h"
-#include "ns3/simulator.h"
+#include "lte-rlc-sap.h"
+#include "lte-rlc-tag.h"
 // #include "lte-mac-sap.h"
-#include "ns3/lte-rlc-sap.h"
-
 // #include "ff-mac-sched-sap.h"
 
+#include "ns3/log.h"
+#include "ns3/simulator.h"
+
 namespace ns3
 {
 
@@ -145,6 +145,13 @@
 }
 
 void
+LteRlc::SetPacketDelayBudgetMs(uint16_t packetDelayBudget)
+{
+    NS_LOG_FUNCTION(this << +packetDelayBudget);
+    m_packetDelayBudgetMs = packetDelayBudget;
+}
+
+void
 LteRlc::SetLteRlcSapUser(LteRlcSapUser* s)
 {
     NS_LOG_FUNCTION(this << s);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc.h ns-3.39/src/lte/model/lte-rlc.h
--- ns-3.38/src/lte/model/lte-rlc.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,8 +20,9 @@
 #ifndef LTE_RLC_H
 #define LTE_RLC_H
 
-#include "ns3/lte-mac-sap.h"
-#include "ns3/lte-rlc-sap.h"
+#include "lte-mac-sap.h"
+#include "lte-rlc-sap.h"
+
 #include "ns3/nstime.h"
 #include "ns3/object.h"
 #include "ns3/trace-source-accessor.h"
@@ -76,6 +77,11 @@
     void SetLcId(uint8_t lcId);
 
     /**
+     * \param packetDelayBudget
+     */
+    void SetPacketDelayBudgetMs(uint16_t packetDelayBudget);
+
+    /**
      *
      *
      * \param s the RLC SAP user to be used by this LTE_RLC
@@ -165,6 +171,8 @@
 
     uint16_t m_rnti; ///< RNTI
     uint8_t m_lcid;  ///< LCID
+    uint16_t m_packetDelayBudgetMs{
+        UINT16_MAX}; //!< the packet delay budget in ms of the corresponding logical channel
 
     /**
      * Used to inform of a PDU delivery to the MAC SAP provider
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-header.cc ns-3.39/src/lte/model/lte-rlc-header.cc
--- ns-3.38/src/lte/model/lte-rlc-header.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-header.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-rlc-header.h"
+#include "lte-rlc-header.h"
 
 #include "ns3/log.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-header.h ns-3.39/src/lte/model/lte-rlc-header.h
--- ns-3.38/src/lte/model/lte-rlc-header.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-header.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,8 +20,9 @@
 #ifndef LTE_RLC_HEADER_H
 #define LTE_RLC_HEADER_H
 
+#include "lte-rlc-sequence-number.h"
+
 #include "ns3/header.h"
-#include "ns3/lte-rlc-sequence-number.h"
 
 #include <list>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-sap.cc ns-3.39/src/lte/model/lte-rlc-sap.cc
--- ns-3.38/src/lte/model/lte-rlc-sap.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-sap.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-rlc-sap.h"
+#include "lte-rlc-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-sdu-status-tag.cc ns-3.39/src/lte/model/lte-rlc-sdu-status-tag.cc
--- ns-3.38/src/lte/model/lte-rlc-sdu-status-tag.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-sdu-status-tag.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,7 +17,7 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-rlc-sdu-status-tag.h"
+#include "lte-rlc-sdu-status-tag.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-sequence-number.h ns-3.39/src/lte/model/lte-rlc-sequence-number.h
--- ns-3.38/src/lte/model/lte-rlc-sequence-number.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-sequence-number.h	2023-07-05 14:23:48.013428000 -0700
@@ -26,8 +26,6 @@
 #include <limits>
 #include <stdint.h>
 
-// #include "ns3/lte-rlc.h"
-
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-tm.cc ns-3.39/src/lte/model/lte-rlc-tm.cc
--- ns-3.38/src/lte/model/lte-rlc-tm.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-tm.cc	2023-07-05 14:23:48.013428000 -0700
@@ -18,7 +18,7 @@
  *         Nicola Baldo <nbaldo@cttc.es>
  */
 
-#include "ns3/lte-rlc-tm.h"
+#include "lte-rlc-tm.h"
 
 #include "ns3/log.h"
 #include "ns3/simulator.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-tm.h ns-3.39/src/lte/model/lte-rlc-tm.h
--- ns-3.38/src/lte/model/lte-rlc-tm.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-tm.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,7 +21,8 @@
 #ifndef LTE_RLC_TM_H
 #define LTE_RLC_TM_H
 
-#include "ns3/lte-rlc.h"
+#include "lte-rlc.h"
+
 #include <ns3/event-id.h>
 
 #include <map>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-um.cc ns-3.39/src/lte/model/lte-rlc-um.cc
--- ns-3.38/src/lte/model/lte-rlc-um.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-um.cc	2023-07-05 14:23:48.013428000 -0700
@@ -17,12 +17,13 @@
  * Author: Manuel Requena <manuel.requena@cttc.es>
  */
 
-#include "ns3/lte-rlc-um.h"
+#include "lte-rlc-um.h"
+
+#include "lte-rlc-header.h"
+#include "lte-rlc-sdu-status-tag.h"
+#include "lte-rlc-tag.h"
 
 #include "ns3/log.h"
-#include "ns3/lte-rlc-header.h"
-#include "ns3/lte-rlc-sdu-status-tag.h"
-#include "ns3/lte-rlc-tag.h"
 #include "ns3/simulator.h"
 
 namespace ns3
@@ -68,7 +69,21 @@
                           "Value of the t-Reordering timer (See section 7.3 of 3GPP TS 36.322)",
                           TimeValue(MilliSeconds(100)),
                           MakeTimeAccessor(&LteRlcUm::m_reorderingTimerValue),
-                          MakeTimeChecker());
+                          MakeTimeChecker())
+            .AddAttribute(
+                "EnablePdcpDiscarding",
+                "Whether to use the PDCP discarding, i.e., perform discarding at the moment "
+                "of passing the PDCP SDU to RLC)",
+                BooleanValue(true),
+                MakeBooleanAccessor(&LteRlcUm::m_enablePdcpDiscarding),
+                MakeBooleanChecker())
+            .AddAttribute("DiscardTimerMs",
+                          "Discard timer in milliseconds to be used to discard packets. "
+                          "If set to 0 then packet delay budget will be used as the discard "
+                          "timer value, otherwise it will be used this value.",
+                          UintegerValue(0),
+                          MakeUintegerAccessor(&LteRlcUm::m_discardTimerMs),
+                          MakeUintegerChecker<uint32_t>());
     return tid;
 }
 
@@ -93,6 +108,29 @@
 
     if (m_txBufferSize + p->GetSize() <= m_maxTxBufferSize)
     {
+        if (m_enablePdcpDiscarding)
+        {
+            // discart the packet
+            uint32_t headOfLineDelayInMs = 0;
+            uint32_t discardTimerMs =
+                (m_discardTimerMs > 0) ? m_discardTimerMs : m_packetDelayBudgetMs;
+
+            if (!m_txBuffer.empty())
+            {
+                headOfLineDelayInMs =
+                    (Simulator::Now() - m_txBuffer.begin()->m_waitingSince).GetMilliSeconds();
+            }
+            NS_LOG_DEBUG("head of line delay in MS:" << headOfLineDelayInMs);
+            if (headOfLineDelayInMs > discardTimerMs)
+            {
+                NS_LOG_DEBUG("Tx HOL is higher than this packet can allow. RLC SDU discarded");
+                NS_LOG_DEBUG("headOfLineDelayInMs    = " << headOfLineDelayInMs);
+                NS_LOG_DEBUG("m_packetDelayBudgetMs    = " << m_packetDelayBudgetMs);
+                NS_LOG_DEBUG("packet size     = " << p->GetSize());
+                m_txDropTrace(p);
+            }
+        }
+
         /** Store PDCP PDU */
         LteRlcSduStatusTag tag;
         tag.SetStatus(LteRlcSduStatusTag::FULL_SDU);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rlc-um.h ns-3.39/src/lte/model/lte-rlc-um.h
--- ns-3.38/src/lte/model/lte-rlc-um.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rlc-um.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,8 +20,9 @@
 #ifndef LTE_RLC_UM_H
 #define LTE_RLC_UM_H
 
-#include "ns3/lte-rlc-sequence-number.h"
-#include "ns3/lte-rlc.h"
+#include "lte-rlc-sequence-number.h"
+#include "lte-rlc.h"
+
 #include <ns3/event-id.h>
 
 #include <map>
@@ -143,9 +144,12 @@
     /**
      * Timers. See section 7.3 in TS 36.322
      */
-    Time m_reorderingTimerValue; ///< reordering timer value
-    EventId m_reorderingTimer;   ///< reordering timer
-    EventId m_rbsTimer;          ///< RBS timer
+    Time m_reorderingTimerValue;        ///< reordering timer value
+    EventId m_reorderingTimer;          ///< reordering timer
+    EventId m_rbsTimer;                 ///< RBS timer
+    bool m_enablePdcpDiscarding{false}; //!< whether to use the PDCP discarding (perform discarding
+                                        //!< at the moment of passing the PDCP SDU to RLC)
+    uint32_t m_discardTimerMs{0};       //!< the discard timer value in milliseconds
 
     /**
      * Reassembling state
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rrc-header.cc ns-3.39/src/lte/model/lte-rrc-header.cc
--- ns-3.38/src/lte/model/lte-rrc-header.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rrc-header.cc	2023-07-05 14:23:48.013428000 -0700
@@ -20,7 +20,7 @@
  *          Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
  */
 
-#include "ns3/lte-rrc-header.h"
+#include "lte-rrc-header.h"
 
 #include "ns3/log.h"
 
@@ -1934,7 +1934,7 @@
         SerializeInteger(rrccsc.ulConfiguration.ulFreqInfo.ulCarrierFreq, 0, MAX_EARFCN);
         SerializeInteger(rrccsc.ulConfiguration.ulFreqInfo.ulBandwidth, 6, 100);
 
-        // Serialize UlPowerControllCommonSCell
+        // Serialize UlPowerControlCommonSCell
         std::bitset<2> UlPowerControlCommonSCell_r10;
         UlPowerControlCommonSCell_r10.set(1, 0); // p0-NominalPUSCH-r10 Not Implemented
         UlPowerControlCommonSCell_r10.set(0, 1); // alpha
@@ -5384,7 +5384,7 @@
         // Serialize t304
         SerializeEnum(8, 0);
 
-        // Serialize newUE-Identitiy
+        // Serialize newUE-Identity
         SerializeBitstring(std::bitset<16>(m_mobilityControlInfo.newUeIdentity));
 
         // Serialize radioResourceConfigCommon
@@ -6258,7 +6258,7 @@
 
     SerializeUlCcchMessage(0);
 
-    // Serialize RrcConnectionReestablishmentReques sequence:
+    // Serialize RrcConnectionReestablishmentRequest sequence:
     // no default or optional fields. Extension marker not present.
     SerializeSequence(std::bitset<0>(), false);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rrc-header.h ns-3.39/src/lte/model/lte-rrc-header.h
--- ns-3.38/src/lte/model/lte-rrc-header.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rrc-header.h	2023-07-05 14:23:48.013428000 -0700
@@ -23,9 +23,10 @@
 #ifndef RRC_HEADER_H
 #define RRC_HEADER_H
 
+#include "lte-asn1-header.h"
+#include "lte-rrc-sap.h"
+
 #include "ns3/header.h"
-#include "ns3/lte-asn1-header.h"
-#include "ns3/lte-rrc-sap.h"
 
 #include <bitset>
 #include <string>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rrc-protocol-ideal.h ns-3.39/src/lte/model/lte-rrc-protocol-ideal.h
--- ns-3.38/src/lte/model/lte-rrc-protocol-ideal.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rrc-protocol-ideal.h	2023-07-05 14:23:48.013428000 -0700
@@ -20,7 +20,8 @@
 #ifndef LTE_RRC_PROTOCOL_IDEAL_H
 #define LTE_RRC_PROTOCOL_IDEAL_H
 
-#include <ns3/lte-rrc-sap.h>
+#include "lte-rrc-sap.h"
+
 #include <ns3/object.h>
 #include <ns3/ptr.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rrc-protocol-real.cc ns-3.39/src/lte/model/lte-rrc-protocol-real.cc
--- ns-3.38/src/lte/model/lte-rrc-protocol-real.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rrc-protocol-real.cc	2023-07-05 14:23:48.013428000 -0700
@@ -22,6 +22,7 @@
 
 #include "lte-enb-net-device.h"
 #include "lte-enb-rrc.h"
+#include "lte-rrc-header.h"
 #include "lte-ue-net-device.h"
 #include "lte-ue-rrc.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-rrc-protocol-real.h ns-3.39/src/lte/model/lte-rrc-protocol-real.h
--- ns-3.38/src/lte/model/lte-rrc-protocol-real.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-rrc-protocol-real.h	2023-07-05 14:23:48.013428000 -0700
@@ -21,10 +21,10 @@
 #ifndef LTE_RRC_PROTOCOL_REAL_H
 #define LTE_RRC_PROTOCOL_REAL_H
 
-#include <ns3/lte-pdcp-sap.h>
-#include <ns3/lte-rlc-sap.h>
-#include <ns3/lte-rrc-header.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-pdcp-sap.h"
+#include "lte-rlc-sap.h"
+#include "lte-rrc-sap.h"
+
 #include <ns3/object.h>
 #include <ns3/ptr.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-spectrum-phy.cc ns-3.39/src/lte/model/lte-spectrum-phy.cc
--- ns-3.38/src/lte/model/lte-spectrum-phy.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-spectrum-phy.cc	2023-07-05 14:23:48.013428000 -0700
@@ -22,8 +22,8 @@
 #include "lte-spectrum-phy.h"
 
 #include "lte-chunk-processor.h"
-#include "lte-net-device.h"
-#include "lte-phy-tag.h"
+#include "lte-control-messages.h"
+#include "lte-mi-error-model.h"
 #include "lte-radio-bearer-tag.h"
 #include "lte-spectrum-signal-parameters.h"
 
@@ -32,8 +32,6 @@
 #include <ns3/config.h>
 #include <ns3/double.h>
 #include <ns3/log.h>
-#include <ns3/lte-mi-error-model.h>
-#include <ns3/lte-radio-bearer-tag.h>
 #include <ns3/object-factory.h>
 #include <ns3/simulator.h>
 #include <ns3/trace-source-accessor.h>
@@ -458,12 +456,10 @@
         m_endTxEvent = Simulator::Schedule(duration, &LteSpectrumPhy::EndTxData, this);
     }
         return false;
-        break;
 
     default:
         NS_FATAL_ERROR("unknown state");
         return true;
-        break;
     }
 }
 
@@ -516,12 +512,10 @@
         m_endTxEvent = Simulator::Schedule(DL_CTRL_DURATION, &LteSpectrumPhy::EndTxDlCtrl, this);
     }
         return false;
-        break;
 
     default:
         NS_FATAL_ERROR("unknown state");
         return true;
-        break;
     }
 }
 
@@ -572,12 +566,10 @@
         m_endTxEvent = Simulator::Schedule(UL_SRS_DURATION, &LteSpectrumPhy::EndTxUlSrs, this);
     }
         return false;
-        break;
 
     default:
         NS_FATAL_ERROR("unknown state");
         return true;
-        break;
     }
 }
 
@@ -764,7 +756,7 @@
 
         // common code for the two states
         // check presence of PSS for UE measuerements
-        if (lteDlCtrlRxParams->pss == true)
+        if (lteDlCtrlRxParams->pss)
         {
             if (!m_ltePhyRxPssCallback.IsNull())
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-spectrum-phy.h ns-3.39/src/lte/model/lte-spectrum-phy.h
--- ns-3.38/src/lte/model/lte-spectrum-phy.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-spectrum-phy.h	2023-07-05 14:23:48.013428000 -0700
@@ -22,19 +22,20 @@
 #ifndef LTE_SPECTRUM_PHY_H
 #define LTE_SPECTRUM_PHY_H
 
-#include "ns3/random-variable-stream.h"
+#include "ff-mac-common.h"
+#include "lte-common.h"
+#include "lte-harq-phy.h"
+#include "lte-interference.h"
+
 #include <ns3/data-rate.h>
 #include <ns3/event-id.h>
-#include <ns3/ff-mac-common.h>
 #include <ns3/generic-phy.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-harq-phy.h>
-#include <ns3/lte-interference.h>
 #include <ns3/mobility-model.h>
 #include <ns3/net-device.h>
 #include <ns3/nstime.h>
 #include <ns3/packet-burst.h>
 #include <ns3/packet.h>
+#include <ns3/random-variable-stream.h>
 #include <ns3/spectrum-channel.h>
 #include <ns3/spectrum-interference.h>
 #include <ns3/spectrum-phy.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-spectrum-signal-parameters.cc ns-3.39/src/lte/model/lte-spectrum-signal-parameters.cc
--- ns-3.38/src/lte/model/lte-spectrum-signal-parameters.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-spectrum-signal-parameters.cc	2023-07-05 14:23:48.013428000 -0700
@@ -18,9 +18,11 @@
  * Modified by Marco Miozzo <mmiozzo@cttc.es> (add data and ctrl diversity)
  */
 
+#include "lte-spectrum-signal-parameters.h"
+
+#include "lte-control-messages.h"
+
 #include <ns3/log.h>
-#include <ns3/lte-control-messages.h>
-#include <ns3/lte-spectrum-signal-parameters.h>
 #include <ns3/packet-burst.h>
 #include <ns3/ptr.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-spectrum-signal-parameters.h ns-3.39/src/lte/model/lte-spectrum-signal-parameters.h
--- ns-3.38/src/lte/model/lte-spectrum-signal-parameters.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-spectrum-signal-parameters.h	2023-07-05 14:23:48.013428000 -0700
@@ -23,6 +23,8 @@
 
 #include <ns3/spectrum-signal-parameters.h>
 
+#include <list>
+
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-ccm-rrc-sap.h ns-3.39/src/lte/model/lte-ue-ccm-rrc-sap.h
--- ns-3.38/src/lte/model/lte-ue-ccm-rrc-sap.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-ccm-rrc-sap.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,10 +21,8 @@
 #ifndef LTE_UE_CCM_RRC_SAP_H
 #define LTE_UE_CCM_RRC_SAP_H
 
-#include <ns3/eps-bearer.h>
-#include <ns3/lte-mac-sap.h>
-#include <ns3/lte-rrc-sap.h>
-#include <ns3/lte-ue-cmac-sap.h>
+#include "lte-mac-sap.h"
+#include "lte-ue-cmac-sap.h"
 
 #include <map>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-cmac-sap.h ns-3.39/src/lte/model/lte-ue-cmac-sap.h
--- ns-3.38/src/lte/model/lte-ue-cmac-sap.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-cmac-sap.h	2023-07-05 14:23:48.017428200 -0700
@@ -20,9 +20,6 @@
 #ifndef LTE_UE_CMAC_SAP_H
 #define LTE_UE_CMAC_SAP_H
 
-#include <ns3/eps-bearer.h>
-#include <ns3/ff-mac-common.h>
-#include <ns3/lte-common.h>
 #include <ns3/packet.h>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-component-carrier-manager.cc ns-3.39/src/lte/model/lte-ue-component-carrier-manager.cc
--- ns-3.38/src/lte/model/lte-ue-component-carrier-manager.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-component-carrier-manager.cc	2023-07-05 14:23:48.017428200 -0700
@@ -20,6 +20,8 @@
 
 #include "lte-ue-component-carrier-manager.h"
 
+#include "lte-common.h"
+
 #include <ns3/log.h>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-component-carrier-manager.h ns-3.39/src/lte/model/lte-ue-component-carrier-manager.h
--- ns-3.38/src/lte/model/lte-ue-component-carrier-manager.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-component-carrier-manager.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,17 +21,14 @@
 #ifndef LTE_UE_COMPONENT_CARRIER_MANAGER_H
 #define LTE_UE_COMPONENT_CARRIER_MANAGER_H
 
-#include <ns3/lte-mac-sap.h>
-#include <ns3/lte-rrc-sap.h>
-#include <ns3/lte-ue-ccm-rrc-sap.h>
+#include "lte-mac-sap.h"
+#include "lte-ue-ccm-rrc-sap.h"
+
 #include <ns3/object.h>
 
 #include <map>
 #include <vector>
 
-#define MIN_NO_CC 1
-#define MAX_NO_CC 5 // this is the maximum number of carrier components allowed by 3GPP up to R13
-
 namespace ns3
 {
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-cphy-sap.cc ns-3.39/src/lte/model/lte-ue-cphy-sap.cc
--- ns-3.38/src/lte/model/lte-ue-cphy-sap.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-cphy-sap.cc	2023-07-05 14:23:48.017428200 -0700
@@ -17,7 +17,7 @@
  * Author: Nicola Baldo <nbaldo@cttc.es>
  */
 
-#include "ns3/lte-ue-cphy-sap.h"
+#include "lte-ue-cphy-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-cphy-sap.h ns-3.39/src/lte/model/lte-ue-cphy-sap.h
--- ns-3.38/src/lte/model/lte-ue-cphy-sap.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-cphy-sap.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,7 +21,8 @@
 #ifndef LTE_UE_CPHY_SAP_H
 #define LTE_UE_CPHY_SAP_H
 
-#include <ns3/lte-rrc-sap.h>
+#include "lte-rrc-sap.h"
+
 #include <ns3/ptr.h>
 
 #include <stdint.h>
@@ -194,7 +195,7 @@
      * started by checking if the radio frames are in-sync for N311
      * consecutive times.
      */
-    virtual void StartInSnycDetection() = 0;
+    virtual void StartInSyncDetection() = 0;
 
     /**
      * \brief A method call by UE RRC to communicate the IMSI to the UE PHY
@@ -231,8 +232,8 @@
     /// UeMeasurementsParameters structure
     struct UeMeasurementsParameters
     {
-        std::vector<struct UeMeasurementsElement> m_ueMeasurementsList; ///< UE measurement list
-        uint8_t m_componentCarrierId;                                   ///< component carrier ID
+        std::vector<UeMeasurementsElement> m_ueMeasurementsList; ///< UE measurement list
+        uint8_t m_componentCarrierId;                            ///< component carrier ID
     };
 
     /**
@@ -327,7 +328,7 @@
     void SetRsrpFilterCoefficient(uint8_t rsrpFilterCoefficient) override;
     void ResetPhyAfterRlf() override;
     void ResetRlfParams() override;
-    void StartInSnycDetection() override;
+    void StartInSyncDetection() override;
     void SetImsi(uint64_t imsi) override;
 
   private:
@@ -454,9 +455,9 @@
 
 template <class C>
 void
-MemberLteUeCphySapProvider<C>::StartInSnycDetection()
+MemberLteUeCphySapProvider<C>::StartInSyncDetection()
 {
-    m_owner->DoStartInSnycDetection();
+    m_owner->DoStartInSyncDetection();
 }
 
 template <class C>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-mac.cc ns-3.39/src/lte/model/lte-ue-mac.cc
--- ns-3.38/src/lte/model/lte-ue-mac.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-mac.cc	2023-07-05 14:23:48.017428200 -0700
@@ -20,13 +20,12 @@
 
 #include "lte-ue-mac.h"
 
+#include "ff-mac-common.h"
+#include "lte-common.h"
+#include "lte-control-messages.h"
 #include "lte-radio-bearer-tag.h"
-#include "lte-ue-net-device.h"
 
-#include <ns3/ff-mac-common.h>
 #include <ns3/log.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-control-messages.h>
 #include <ns3/packet-burst.h>
 #include <ns3/packet.h>
 #include <ns3/pointer.h>
@@ -921,7 +920,7 @@
     m_frameNo = frameNo;
     m_subframeNo = subframeNo;
     RefreshHarqProcessesPacketBuffer();
-    if ((Simulator::Now() >= m_bsrLast + m_bsrPeriodicity) && (m_freshUlBsr == true))
+    if ((Simulator::Now() >= m_bsrLast + m_bsrPeriodicity) && m_freshUlBsr)
     {
         if (m_componentCarrierId == 0)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-mac.h ns-3.39/src/lte/model/lte-ue-mac.h
--- ns-3.38/src/lte/model/lte-ue-mac.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-mac.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,10 +21,12 @@
 #ifndef LTE_UE_MAC_ENTITY_H
 #define LTE_UE_MAC_ENTITY_H
 
+#include "ff-mac-common.h"
+#include "lte-mac-sap.h"
+#include "lte-ue-cmac-sap.h"
+#include "lte-ue-phy-sap.h"
+
 #include <ns3/event-id.h>
-#include <ns3/lte-mac-sap.h>
-#include <ns3/lte-ue-cmac-sap.h>
-#include <ns3/lte-ue-phy-sap.h>
 #include <ns3/nstime.h>
 #include <ns3/packet-burst.h>
 #include <ns3/packet.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-net-device.cc ns-3.39/src/lte/model/lte-ue-net-device.cc
--- ns-3.38/src/lte/model/lte-ue-net-device.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-net-device.cc	2023-07-05 14:23:48.017428200 -0700
@@ -24,10 +24,10 @@
 
 #include "lte-ue-net-device.h"
 
-#include "epc-tft.h"
 #include "epc-ue-nas.h"
-#include "lte-amc.h"
+#include "lte-enb-net-device.h"
 #include "lte-net-device.h"
+#include "lte-ue-component-carrier-manager.h"
 #include "lte-ue-mac.h"
 #include "lte-ue-phy.h"
 #include "lte-ue-rrc.h"
@@ -39,7 +39,6 @@
 #include "ns3/ipv6-header.h"
 #include "ns3/ipv6.h"
 #include "ns3/llc-snap-header.h"
-#include "ns3/lte-enb-net-device.h"
 #include "ns3/node.h"
 #include "ns3/packet-burst.h"
 #include "ns3/packet.h"
@@ -50,7 +49,6 @@
 #include <ns3/ipv4-l3-protocol.h>
 #include <ns3/ipv6-l3-protocol.h>
 #include <ns3/log.h>
-#include <ns3/lte-ue-component-carrier-manager.h>
 #include <ns3/object-factory.h>
 #include <ns3/object-map.h>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-net-device.h ns-3.39/src/lte/model/lte-ue-net-device.h
--- ns-3.38/src/lte/model/lte-ue-net-device.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-net-device.h	2023-07-05 14:23:48.017428200 -0700
@@ -24,11 +24,10 @@
 #ifndef LTE_UE_NET_DEVICE_H
 #define LTE_UE_NET_DEVICE_H
 
-#include "ns3/component-carrier-ue.h"
-#include "ns3/eps-bearer.h"
+#include "component-carrier-ue.h"
+#include "lte-net-device.h"
+
 #include "ns3/event-id.h"
-#include "ns3/lte-net-device.h"
-#include "ns3/lte-phy.h"
 #include "ns3/nstime.h"
 #include "ns3/traced-callback.h"
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-phy.cc ns-3.39/src/lte/model/lte-ue-phy.cc
--- ns-3.38/src/lte/model/lte-ue-phy.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-phy.cc	2023-07-05 14:23:48.017428200 -0700
@@ -26,19 +26,15 @@
 
 #include "ff-mac-common.h"
 #include "lte-amc.h"
-#include "lte-chunk-processor.h"
-#include "lte-enb-net-device.h"
-#include "lte-enb-phy.h"
+#include "lte-common.h"
 #include "lte-net-device.h"
 #include "lte-spectrum-value-helper.h"
-#include "lte-ue-mac.h"
 #include "lte-ue-net-device.h"
+#include "lte-ue-power-control.h"
 
 #include <ns3/boolean.h>
 #include <ns3/double.h>
 #include <ns3/log.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ue-power-control.h>
 #include <ns3/node.h>
 #include <ns3/object-factory.h>
 #include <ns3/pointer.h>
@@ -1585,7 +1581,7 @@
 }
 
 void
-LteUePhy::DoStartInSnycDetection()
+LteUePhy::DoStartInSyncDetection()
 {
     NS_LOG_FUNCTION(this);
     // indicates that the downlink radio link quality has to be monitored for in-sync indications
@@ -1617,7 +1613,7 @@
     m_numOfSubframes++;
     NS_LOG_LOGIC("No of Subframes: " << m_numOfSubframes
                                      << " UE synchronized: " << m_downlinkInSync);
-    // check for out_of_snyc indications first when UE is both DL and UL synchronized
+    // check for out_of_sync indications first when UE is both DL and UL synchronized
     // m_downlinkInSync=true indicates that the evaluation is for out-of-sync indications
     if (m_downlinkInSync && m_numOfSubframes == 10)
     {
@@ -1657,7 +1653,7 @@
         m_ueCphySapUser->NotifyOutOfSync();
         m_numOfFrames = 0;
     }
-    // check for in_snyc indications when T310 timer is started
+    // check for in_sync indications when T310 timer is started
     // m_downlinkInSync=false indicates that the evaluation is for in-sync indications
     if (!m_downlinkInSync && m_numOfSubframes == 10)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-phy.h ns-3.39/src/lte/model/lte-ue-phy.h
--- ns-3.38/src/lte/model/lte-ue-phy.h	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-phy.h	2023-07-05 14:23:48.017428200 -0700
@@ -24,13 +24,14 @@
 #ifndef LTE_UE_PHY_H
 #define LTE_UE_PHY_H
 
-#include <ns3/ff-mac-common.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-control-messages.h>
-#include <ns3/lte-phy.h>
-#include <ns3/lte-ue-cphy-sap.h>
-#include <ns3/lte-ue-phy-sap.h>
-#include <ns3/lte-ue-power-control.h>
+#include "ff-mac-common.h"
+#include "lte-amc.h"
+#include "lte-control-messages.h"
+#include "lte-phy.h"
+#include "lte-ue-cphy-sap.h"
+#include "lte-ue-phy-sap.h"
+#include "lte-ue-power-control.h"
+
 #include <ns3/ptr.h>
 
 #include <set>
@@ -580,7 +581,7 @@
      * consecutive times.
      *
      */
-    void DoStartInSnycDetection();
+    void DoStartInSyncDetection();
 
     /**
      * \brief Radio link failure detection function
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-rrc.cc ns-3.39/src/lte/model/lte-ue-rrc.cc
--- ns-3.38/src/lte/model/lte-ue-rrc.cc	2023-03-16 20:54:23.776869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-rrc.cc	2023-07-05 14:23:48.017428200 -0700
@@ -25,14 +25,16 @@
 
 #include "lte-ue-rrc.h"
 
+#include "lte-common.h"
+#include "lte-pdcp.h"
+#include "lte-radio-bearer-info.h"
+#include "lte-rlc-am.h"
+#include "lte-rlc-tm.h"
+#include "lte-rlc-um.h"
+#include "lte-rlc.h"
+
 #include <ns3/fatal-error.h>
 #include <ns3/log.h>
-#include <ns3/lte-pdcp.h>
-#include <ns3/lte-radio-bearer-info.h>
-#include <ns3/lte-rlc-am.h>
-#include <ns3/lte-rlc-tm.h>
-#include <ns3/lte-rlc-um.h>
-#include <ns3/lte-rlc.h>
 #include <ns3/object-factory.h>
 #include <ns3/object-map.h>
 #include <ns3/simulator.h>
@@ -1307,7 +1309,7 @@
         uint32_t cellCsgId = m_lastSib1.cellAccessRelatedInfo.csgIdentity;
         bool cellCsgIndication = m_lastSib1.cellAccessRelatedInfo.csgIndication;
 
-        isSuitableCell = (cellCsgIndication == false) || (cellCsgId == m_csgWhiteList);
+        isSuitableCell = (!cellCsgIndication || cellCsgId == m_csgWhiteList);
 
         NS_LOG_LOGIC(this << " csg(ue/cell/indication)=" << m_csgWhiteList << "/" << cellCsgId
                           << "/" << cellCsgIndication);
@@ -1415,7 +1417,7 @@
 LteUeRrc::ApplyRadioResourceConfigDedicated(LteRrcSap::RadioResourceConfigDedicated rrcd)
 {
     NS_LOG_FUNCTION(this);
-    const struct LteRrcSap::PhysicalConfigDedicated& pcd = rrcd.physicalConfigDedicated;
+    const LteRrcSap::PhysicalConfigDedicated& pcd = rrcd.physicalConfigDedicated;
 
     if (pcd.haveAntennaInfoDedicated)
     {
@@ -1473,7 +1475,7 @@
             m_srb1->m_logicalChannelConfig.logicalChannelGroup =
                 stamIt->logicalChannelConfig.logicalChannelGroup;
 
-            struct LteUeCmacSapProvider::LogicalChannelConfig lcConfig;
+            LteUeCmacSapProvider::LogicalChannelConfig lcConfig;
             lcConfig.priority = stamIt->logicalChannelConfig.priority;
             lcConfig.prioritizedBitRateKbps = stamIt->logicalChannelConfig.prioritizedBitRateKbps;
             lcConfig.bucketSizeDurationMs = stamIt->logicalChannelConfig.bucketSizeDurationMs;
@@ -1567,7 +1569,7 @@
 
             m_drbCreatedTrace(m_imsi, m_cellId, m_rnti, dtamIt->drbIdentity);
 
-            struct LteUeCmacSapProvider::LogicalChannelConfig lcConfig;
+            LteUeCmacSapProvider::LogicalChannelConfig lcConfig;
             lcConfig.priority = dtamIt->logicalChannelConfig.priority;
             lcConfig.prioritizedBitRateKbps = dtamIt->logicalChannelConfig.prioritizedBitRateKbps;
             lcConfig.bucketSizeDurationMs = dtamIt->logicalChannelConfig.bucketSizeDurationMs;
@@ -3374,7 +3376,7 @@
         {
             NS_LOG_INFO("t310 started");
         }
-        m_cphySapProvider.at(0)->StartInSnycDetection();
+        m_cphySapProvider.at(0)->StartInSyncDetection();
         m_noOfSyncIndications = 0;
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-ue-rrc.h ns-3.39/src/lte/model/lte-ue-rrc.h
--- ns-3.38/src/lte/model/lte-ue-rrc.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/lte-ue-rrc.h	2023-07-05 14:23:48.017428200 -0700
@@ -26,13 +26,13 @@
 #ifndef LTE_UE_RRC_H
 #define LTE_UE_RRC_H
 
-#include "ns3/component-carrier-ue.h"
-#include <ns3/lte-as-sap.h>
-#include <ns3/lte-pdcp-sap.h>
-#include <ns3/lte-rrc-sap.h>
-#include <ns3/lte-ue-ccm-rrc-sap.h>
-#include <ns3/lte-ue-cmac-sap.h>
-#include <ns3/lte-ue-cphy-sap.h>
+#include "lte-as-sap.h"
+#include "lte-pdcp-sap.h"
+#include "lte-rrc-sap.h"
+#include "lte-ue-ccm-rrc-sap.h"
+#include "lte-ue-cmac-sap.h"
+#include "lte-ue-cphy-sap.h"
+
 #include <ns3/object.h>
 #include <ns3/packet.h>
 #include <ns3/traced-callback.h>
@@ -41,9 +41,6 @@
 #include <set>
 #include <vector>
 
-#define MIN_NO_CC 1
-#define MAX_NO_CC 5 // this is the maximum number of carrier components allowed by 3GPP up to R13
-
 namespace ns3
 {
 
@@ -347,7 +344,7 @@
     typedef void (*ImsiCidRntiTracedCallback)(uint64_t imsi, uint16_t cellId, uint16_t rnti);
 
     /**
-     * TracedCallback signature for MIBRecieved, Sib1Received and
+     * TracedCallback signature for MIBReceived, Sib1Received and
      * HandoverStart events.
      *
      * \param [in] imsi
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-vendor-specific-parameters.cc ns-3.39/src/lte/model/lte-vendor-specific-parameters.cc
--- ns-3.38/src/lte/model/lte-vendor-specific-parameters.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/lte-vendor-specific-parameters.cc	2023-07-05 14:23:48.017428200 -0700
@@ -17,8 +17,9 @@
  * Author: Marco Miozzo  <marco.miozzo@cttc.es>
  */
 
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/log.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/lte-vendor-specific-parameters.h ns-3.39/src/lte/model/lte-vendor-specific-parameters.h
--- ns-3.38/src/lte/model/lte-vendor-specific-parameters.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/lte-vendor-specific-parameters.h	2023-07-05 14:23:48.017428200 -0700
@@ -20,7 +20,7 @@
 #ifndef LTE_VENDOR_SPECIFIC_PARAMETERS
 #define LTE_VENDOR_SPECIFIC_PARAMETERS
 
-#include <ns3/ff-mac-common.h>
+#include "ff-mac-common.h"
 
 #define SRS_CQI_RNTI_VSP 1
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/no-op-component-carrier-manager.cc ns-3.39/src/lte/model/no-op-component-carrier-manager.cc
--- ns-3.38/src/lte/model/no-op-component-carrier-manager.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/no-op-component-carrier-manager.cc	2023-07-05 14:23:48.017428200 -0700
@@ -22,6 +22,8 @@
 
 #include "no-op-component-carrier-manager.h"
 
+#include "lte-common.h"
+
 #include <ns3/log.h>
 #include <ns3/random-variable-stream.h>
 
@@ -205,7 +207,7 @@
         lci.qci = bearer.qci;
         if (ncc == 0)
         {
-            lci.isGbr = bearer.IsGbr();
+            lci.resourceType = bearer.GetResourceType();
             lci.mbrUl = bearer.gbrQosInfo.mbrUl;
             lci.mbrDl = bearer.gbrQosInfo.mbrDl;
             lci.gbrUl = bearer.gbrQosInfo.gbrUl;
@@ -213,7 +215,7 @@
         }
         else
         {
-            lci.isGbr = 0;
+            lci.resourceType = 0;
             lci.mbrUl = 0;
             lci.mbrDl = 0;
             lci.gbrUl = 0;
@@ -234,7 +236,7 @@
         lcinfo.lcId = lcid;
         lcinfo.lcGroup = lcGroup;
         lcinfo.qci = bearer.qci;
-        lcinfo.isGbr = bearer.IsGbr();
+        lcinfo.resourceType = bearer.GetResourceType();
         lcinfo.mbrUl = bearer.gbrQosInfo.mbrUl;
         lcinfo.mbrDl = bearer.gbrQosInfo.mbrDl;
         lcinfo.gbrUl = bearer.gbrQosInfo.gbrUl;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/no-op-component-carrier-manager.h ns-3.39/src/lte/model/no-op-component-carrier-manager.h
--- ns-3.38/src/lte/model/no-op-component-carrier-manager.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/no-op-component-carrier-manager.h	2023-07-05 14:23:48.017428200 -0700
@@ -22,9 +22,9 @@
 #ifndef NO_OP_COMPONENT_CARRIER_MANAGER_H
 #define NO_OP_COMPONENT_CARRIER_MANAGER_H
 
-#include <ns3/lte-ccm-rrc-sap.h>
-#include <ns3/lte-enb-component-carrier-manager.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-ccm-rrc-sap.h"
+#include "lte-enb-component-carrier-manager.h"
+#include "lte-rrc-sap.h"
 
 #include <map>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/no-op-handover-algorithm.h ns-3.39/src/lte/model/no-op-handover-algorithm.h
--- ns-3.38/src/lte/model/no-op-handover-algorithm.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/no-op-handover-algorithm.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,9 +21,9 @@
 #ifndef NO_OP_HANDOVER_ALGORITHM_H
 #define NO_OP_HANDOVER_ALGORITHM_H
 
-#include <ns3/lte-handover-algorithm.h>
-#include <ns3/lte-handover-management-sap.h>
-#include <ns3/lte-rrc-sap.h>
+#include "lte-handover-algorithm.h"
+#include "lte-handover-management-sap.h"
+#include "lte-rrc-sap.h"
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/pf-ff-mac-scheduler.cc ns-3.39/src/lte/model/pf-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/pf-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/pf-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -17,12 +17,14 @@
  * Author: Marco Miozzo <marco.miozzo@cttc.es>
  */
 
+#include "pf-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
-#include <ns3/pf-ff-mac-scheduler.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
 
@@ -142,7 +144,7 @@
 
 void
 PfFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -155,7 +157,7 @@
 
 void
 PfFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -201,7 +203,7 @@
 
 void
 PfFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -230,7 +232,7 @@
 
 void
 PfFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -257,7 +259,7 @@
 
 void
 PfFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -297,7 +299,7 @@
 
 void
 PfFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -322,7 +324,7 @@
 
 void
 PfFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -330,7 +332,7 @@
 
 void
 PfFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -392,14 +394,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -407,7 +403,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -478,7 +474,7 @@
 
 void
 PfFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -501,7 +497,7 @@
     rbgMap = m_ffrSapProvider->GetAvailableDlRbg();
     for (std::vector<bool>::iterator it = rbgMap.begin(); it != rbgMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbgAllocatedNum++;
         }
@@ -529,7 +525,7 @@
 
     for (std::vector<bool>::iterator it = ulRbMap.begin(); it != ulRbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             if (tmpMinBandwidth > maxContinuousUlBandwidth)
             {
@@ -558,7 +554,7 @@
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
     rbStart = ffrRbStartOffset;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -600,7 +596,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -665,12 +661,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -761,7 +757,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -789,7 +785,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -860,7 +856,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -948,20 +944,20 @@
     for (int i = 0; i < rbgNum; i++)
     {
         NS_LOG_INFO(this << " ALLOCATION for RBG " << i << " of " << rbgNum);
-        if (rbgMap.at(i) == false)
+        if (!rbgMap.at(i))
         {
             std::map<uint16_t, pfsFlowPerf_t>::iterator it;
             std::map<uint16_t, pfsFlowPerf_t>::iterator itMax = m_flowStatsDl.end();
             double rcqiMax = 0.0;
             for (it = m_flowStatsDl.begin(); it != m_flowStatsDl.end(); it++)
             {
-                if ((m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*it).first)) == false)
+                if (!m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*it).first))
                 {
                     continue;
                 }
 
                 std::set<uint16_t>::iterator itRnti = rntiAllocated.find((*it).first);
-                if ((itRnti != rntiAllocated.end()) || (!HarqProcessAvailability((*it).first)))
+                if (itRnti != rntiAllocated.end() || !HarqProcessAvailability((*it).first))
                 {
                     // UE already allocated for HARQ or without HARQ process available -> drop it
                     if (itRnti != rntiAllocated.end())
@@ -1194,7 +1190,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1206,7 +1202,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1236,7 +1232,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1297,7 +1293,7 @@
 
 void
 PfFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1306,7 +1302,7 @@
 
 void
 PfFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     m_ffrSapProvider->ReportDlCqiInfo(params);
@@ -1401,7 +1397,7 @@
 
 void
 PfFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1426,7 +1422,7 @@
 
     for (std::vector<bool>::iterator it = rbMap.begin(); it != rbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbAllocatedNum++;
         }
@@ -1445,7 +1441,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
 
@@ -1486,7 +1482,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1627,12 +1623,12 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
                 }
-                if ((m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first)) == false)
+                if (!m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first))
                 {
                     free = false;
                     break;
@@ -1749,7 +1745,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1829,21 +1825,21 @@
 
 void
 PfFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 PfFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 PfFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1886,7 +1882,7 @@
 
 void
 PfFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     m_ffrSapProvider->ReportUlCqiInfo(params);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/pf-ff-mac-scheduler.h ns-3.39/src/lte/model/pf-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/pf-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/pf-ff-mac-scheduler.h	2023-07-05 14:23:48.017428200 -0700
@@ -20,37 +20,21 @@
 #ifndef PF_FF_MAC_SCHEDULER_H
 #define PF_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-#define NO_SINR -5000
-
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /// pfsFlowPerf_t structure
 struct pfsFlowPerf_t
 {
@@ -124,40 +108,35 @@
      *
      * \param params FfMacCschedSapProvider::CschedCellConfigReqParameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * \brief CSched UE config request
      *
      * \param params FfMacCschedSapProvider::CschedUeConfigReqParameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * \brief CSched LC config request
      *
      * \param params FfMacCschedSapProvider::CschedLcConfigReqParameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * \brief CSched LC release request
      *
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * \brief CSched UE release request
      *
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -169,8 +148,7 @@
      *
      * \param params FfMacSchedSapProvider::SchedDlRlcBufferReqParameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * \brief Sched DL paging buffer request
@@ -178,47 +156,42 @@
      * \param params FfMacSchedSapProvider::SchedDlPagingBufferReqParameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * \brief Sched DL MAC buffer request
      *
      * \param params FfMacSchedSapProvider::SchedDlMacBufferReqParameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * \brief Sched DL trigger request
      *
      * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * \brief Sched DL RACH info request
      *
      * \param params FfMacSchedSapProvider::SchedDlRachInfoReqParameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * \brief Sched DL CQI info request
      *
      * \param params FfMacSchedSapProvider::SchedDlCqiInfoReqParameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * \brief Sched UL trigger request
      *
      * \param params FfMacSchedSapProvider::SchedUlTriggerReqParameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * \brief Sched UL noise interference request
@@ -226,14 +199,14 @@
      * \param params FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * \brief Sched UL SR info request
      *
      * \param params FfMacSchedSapProvider::SchedUlSrInfoReqParameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * \brief Sched UL MAC control info request
@@ -241,15 +214,14 @@
      * \param params FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * \brief Sched UL CQI info request
      *
      * \param params FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * \brief Get RBG size
@@ -422,9 +394,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/pss-ff-mac-scheduler.cc ns-3.39/src/lte/model/pss-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/pss-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/pss-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -18,13 +18,15 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "pss-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
-#include <ns3/pss-ff-mac-scheduler.h>
 #include <ns3/simulator.h>
 #include <ns3/string.h>
 
@@ -155,7 +157,7 @@
 
 void
 PssFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -168,7 +170,7 @@
 
 void
 PssFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -214,7 +216,7 @@
 
 void
 PssFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -262,7 +264,7 @@
 
 void
 PssFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -289,7 +291,7 @@
 
 void
 PssFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -329,7 +331,7 @@
 
 void
 PssFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -354,7 +356,7 @@
 
 void
 PssFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -362,7 +364,7 @@
 
 void
 PssFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -424,14 +426,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -439,7 +435,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -510,7 +506,7 @@
 
 void
 PssFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -533,7 +529,7 @@
     rbgMap = m_ffrSapProvider->GetAvailableDlRbg();
     for (std::vector<bool>::iterator it = rbgMap.begin(); it != rbgMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbgAllocatedNum++;
         }
@@ -561,7 +557,7 @@
 
     for (std::vector<bool>::iterator it = ulRbMap.begin(); it != ulRbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             if (tmpMinBandwidth > maxContinuousUlBandwidth)
             {
@@ -590,7 +586,7 @@
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
     rbStart = ffrRbStartOffset;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -632,7 +628,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -697,12 +693,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -793,7 +789,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -821,7 +817,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -892,7 +888,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -1115,38 +1111,23 @@
                     nMux = (int)((ueSet1.size() + ueSet2.size()) / 2);
                 }
             }
-            for (it = m_flowStatsDl.begin(); it != m_flowStatsDl.end(); it--)
-            {
-                std::vector<std::pair<double, uint16_t>>::iterator itSet;
-                for (itSet = ueSet1.begin(); itSet != ueSet1.end() && nMux != 0; itSet++)
-                {
-                    std::map<uint16_t, pssFlowPerf_t>::iterator itUe;
-                    itUe = m_flowStatsDl.find((*itSet).second);
-                    tdUeSet.insert(
-                        std::pair<uint16_t, pssFlowPerf_t>((*itUe).first, (*itUe).second));
-                    nMux--;
-                }
-
-                if (nMux == 0)
-                {
-                    break;
-                }
 
-                for (itSet = ueSet2.begin(); itSet != ueSet2.end() && nMux != 0; itSet++)
-                {
-                    std::map<uint16_t, pssFlowPerf_t>::iterator itUe;
-                    itUe = m_flowStatsDl.find((*itSet).second);
-                    tdUeSet.insert(
-                        std::pair<uint16_t, pssFlowPerf_t>((*itUe).first, (*itUe).second));
-                    nMux--;
-                }
-
-                if (nMux == 0)
-                {
-                    break;
-                }
+            std::vector<std::pair<double, uint16_t>>::iterator itSet;
+            for (itSet = ueSet1.begin(); itSet != ueSet1.end() && nMux != 0; itSet++)
+            {
+                std::map<uint16_t, pssFlowPerf_t>::iterator itUe;
+                itUe = m_flowStatsDl.find((*itSet).second);
+                tdUeSet.insert(std::pair<uint16_t, pssFlowPerf_t>((*itUe).first, (*itUe).second));
+                nMux--;
+            }
 
-            } // end of m_flowStatsDl
+            for (itSet = ueSet2.begin(); itSet != ueSet2.end() && nMux != 0; itSet++)
+            {
+                std::map<uint16_t, pssFlowPerf_t>::iterator itUe;
+                itUe = m_flowStatsDl.find((*itSet).second);
+                tdUeSet.insert(std::pair<uint16_t, pssFlowPerf_t>((*itUe).first, (*itUe).second));
+                nMux--;
+            }
 
             if (m_fdSchedulerType == "CoItA")
             {
@@ -1212,7 +1193,7 @@
 
                 for (int i = 0; i < rbgNum; i++)
                 {
-                    if (rbgMap.at(i) == true)
+                    if (rbgMap.at(i))
                     {
                         continue;
                     }
@@ -1221,7 +1202,7 @@
                     double metricMax = 0.0;
                     for (it = tdUeSet.begin(); it != tdUeSet.end(); it++)
                     {
-                        if ((m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*it).first)) == false)
+                        if (!m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*it).first))
                         {
                             continue;
                         }
@@ -1322,7 +1303,7 @@
                 // FD scheduler: Proportional Fair scheduled (PFsch)
                 for (int i = 0; i < rbgNum; i++)
                 {
-                    if (rbgMap.at(i) == true)
+                    if (rbgMap.at(i))
                     {
                         continue;
                     }
@@ -1331,7 +1312,7 @@
                     double metricMax = 0.0;
                     for (it = tdUeSet.begin(); it != tdUeSet.end(); it++)
                     {
-                        if ((m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*it).first)) == false)
+                        if (!m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*it).first))
                         {
                             continue;
                         }
@@ -1549,7 +1530,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1561,7 +1542,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1591,7 +1572,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1659,7 +1640,7 @@
 
 void
 PssFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1668,7 +1649,7 @@
 
 void
 PssFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     m_ffrSapProvider->ReportDlCqiInfo(params);
@@ -1763,7 +1744,7 @@
 
 void
 PssFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1789,7 +1770,7 @@
 
     for (std::vector<bool>::iterator it = rbMap.begin(); it != rbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbAllocatedNum++;
         }
@@ -1808,7 +1789,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1848,7 +1829,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1989,12 +1970,12 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
                 }
-                if ((m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first)) == false)
+                if (!m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first))
                 {
                     free = false;
                     break;
@@ -2111,7 +2092,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -2166,21 +2147,21 @@
 
 void
 PssFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 PssFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 PssFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -2223,7 +2204,7 @@
 
 void
 PssFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/pss-ff-mac-scheduler.h ns-3.39/src/lte/model/pss-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/pss-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/pss-ff-mac-scheduler.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,37 +21,21 @@
 #ifndef PSS_FF_MAC_SCHEDULER_H
 #define PSS_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-#define NO_SINR -5000
-
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /**
  *  Flow information
  */
@@ -129,40 +113,35 @@
      *
      * \param params FfMacCschedSapProvider::CschedCellConfigReqParameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * \brief CSched UE config request function
      *
      * \param params FfMacCschedSapProvider::CschedUeConfigReqParameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * \brief CSched LC config request function
      *
      * \param params FfMacCschedSapProvider::CschedLcConfigReqParameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * \brief CSched LC release request function
      *
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * \brief CSched UE release request function
      *
      * \param params FfMacCschedSapProvider::CschedUeReleaseReqParameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -174,8 +153,7 @@
      *
      * \param params FfMacSchedSapProvider::SchedDlRlcBufferReqParameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * \brief Sched DL paging buffer request function
@@ -183,47 +161,42 @@
      * \param params FfMacSchedSapProvider::SchedDlPagingBufferReqParameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * \brief Sched DL MAC buffer request function
      *
      * \param params FfMacSchedSapProvider::SchedDlMacBufferReqParameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * \brief Sched DL trigger request function
      *
      * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * \brief Sched DL RACH info request function
      *
      * \param params FfMacSchedSapProvider::SchedDlRachInfoReqParameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * \brief Sched DL CQI info request function
      *
      * \param params FfMacSchedSapProvider::SchedDlCqiInfoReqParameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * \brief Sched UL trigger request function
      *
      * \param params FfMacSchedSapProvider::SchedUlTriggerReqParameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * \brief Sched UL noise interference request function
@@ -231,14 +204,14 @@
      * \param params FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * \brief Sched UL SR info request function
      *
      * \param params FfMacSchedSapProvider::SchedUlSrInfoReqParameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * \brief Sched UL MAC control info request function
@@ -246,15 +219,14 @@
      * \param params FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * \brief Sched UL CQI info request function
      *
      * \param params FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * \brief Get RBG size function
@@ -431,9 +403,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/rem-spectrum-phy.cc ns-3.39/src/lte/model/rem-spectrum-phy.cc
--- ns-3.38/src/lte/model/rem-spectrum-phy.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/rem-spectrum-phy.cc	2023-07-05 14:23:48.017428200 -0700
@@ -21,10 +21,11 @@
 
 #include "rem-spectrum-phy.h"
 
+#include "lte-spectrum-signal-parameters.h"
+
 #include <ns3/antenna-model.h>
 #include <ns3/double.h>
 #include <ns3/log.h>
-#include <ns3/lte-spectrum-signal-parameters.h>
 #include <ns3/object-factory.h>
 #include <ns3/simulator.h>
 #include <ns3/trace-source-accessor.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/rr-ff-mac-scheduler.cc ns-3.39/src/lte/model/rr-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/rr-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/rr-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -17,14 +17,16 @@
  * Author: Marco Miozzo <marco.miozzo@cttc.es>
  */
 
+#include "rr-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
-#include <ns3/rr-ff-mac-scheduler.h>
 #include <ns3/simulator.h>
 
 #include <cfloat>
@@ -141,7 +143,7 @@
 
 void
 RrFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -154,7 +156,7 @@
 
 void
 RrFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -200,7 +202,7 @@
 
 void
 RrFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Not used at this stage (LCs updated by DoSchedDlRlcBufferReq)
@@ -208,7 +210,7 @@
 
 void
 RrFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -232,7 +234,7 @@
 
 void
 RrFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Release RNTI " << params.m_rnti);
 
@@ -274,7 +276,7 @@
 
 void
 RrFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -303,7 +305,7 @@
                      << params.m_rlcRetransmissionQueueSize << " RLC stat size "
                      << params.m_rlcStatusPduSize);
     // initialize statistics of the flow in case of new flows
-    if (newLc == true)
+    if (newLc)
     {
         m_p10CqiRxed.insert(
             std::pair<uint16_t, uint8_t>(params.m_rnti, 1)); // only codeword 0 at this stage (SISO)
@@ -314,7 +316,7 @@
 
 void
 RrFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -322,7 +324,7 @@
 
 void
 RrFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -370,14 +372,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -385,7 +381,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -455,7 +451,7 @@
 
 void
 RrFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " DL Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -483,7 +479,7 @@
     // RACH Allocation
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -525,7 +521,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -590,12 +586,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -686,7 +682,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -714,7 +710,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -786,7 +782,7 @@
 
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -978,10 +974,10 @@
         if ((itLcRnti == lcActivesPerRnti.end()) || (itRnti != rntiAllocated.end()))
         {
             // skip this RNTI (no active queue or yet allocated for HARQ)
-            uint16_t rntiDiscared = (*it).m_rnti;
+            uint16_t rntiDiscarded = (*it).m_rnti;
             while (it != m_rlcBufferReq.end())
             {
-                if ((*it).m_rnti != rntiDiscared)
+                if ((*it).m_rnti != rntiDiscarded)
                 {
                     break;
                 }
@@ -1029,7 +1025,7 @@
             if (((*it).m_rlcTransmissionQueueSize > 0) ||
                 ((*it).m_rlcRetransmissionQueueSize > 0) || ((*it).m_rlcStatusPduSize > 0))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1043,7 +1039,7 @@
                                           rlcPduSize);
                     newRlcPduLe.push_back(newRlcEl);
 
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1076,7 +1072,7 @@
         NS_LOG_INFO("RBG:");
         while (i < rbgPerTb)
         {
-            if (rbgMap.at(rbgAllocated) == false)
+            if (!rbgMap.at(rbgAllocated))
             {
                 rbgMask = rbgMask + (0x1 << rbgAllocated);
                 NS_LOG_INFO("\t " << rbgAllocated);
@@ -1098,7 +1094,7 @@
         newDci.m_tpc = 1; // 1 is mapped to 0 in Accumulated Mode and to -1 in Absolute Mode
 
         newEl.m_dci = newDci;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1135,7 +1131,7 @@
 
 void
 RrFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1144,7 +1140,7 @@
 
 void
 RrFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1191,7 +1187,7 @@
 
 void
 RrFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1220,7 +1216,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1260,7 +1256,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1389,7 +1385,7 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
@@ -1497,7 +1493,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1552,21 +1548,21 @@
 
 void
 RrFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 RrFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 RrFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1610,7 +1606,7 @@
 
 void
 RrFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/rr-ff-mac-scheduler.h ns-3.39/src/lte/model/rr-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/rr-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/rr-ff-mac-scheduler.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,32 +21,18 @@
 #ifndef RR_FF_MAC_SCHEDULER_H
 #define RR_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-ffr-sap.h"
 
 #include <map>
 #include <vector>
 
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /**
  * \ingroup ff-api
  * \brief Implements the SCHED SAP and CSCHED SAP for a Round Robin scheduler
@@ -109,36 +95,31 @@
      * \brief CSched cell config request
      * \param params FfMacCschedSapProvider::CschedCellConfigReqParameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * \brief CSched UE config request
      * \param params FfMacCschedSapProvider::CschedUeConfigReqParameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * \brief CSched LC config request
      * \param params FfMacCschedSapProvider::CschedLcConfigReqParameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * \brief CSched LC release request
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * \brief CSched UE release request
      * \param params FfMacCschedSapProvider::CschedUeReleaseReqParameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -149,77 +130,70 @@
      * \brief Sched DL RLC buffer request
      * \param params FfMacSchedSapProvider::SchedDlRlcBufferReqParameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * \brief Sched DL paging buffer request
      * \param params FfMacSchedSapProvider::SchedDlPagingBufferReqParameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * \brief Sched DL MAC buffer request
      * \param params FfMacSchedSapProvider::SchedDlMacBufferReqParameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * \brief Sched DL trigger request
      * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * \brief Sched DL RACH info request
      * \param params FfMacSchedSapProvider::SchedDlRachInfoReqParameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * \brief Sched DL CQI info request
      * \param params FfMacSchedSapProvider::SchedDlCqiInfoReqParameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * \brief Sched UL trigger request
      * \param params FfMacSchedSapProvider::SchedUlTriggerReqParameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * \brief Sched UL noise interference request
      * \param params FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * \brief Sched UL SRS info request
      * \param params FfMacSchedSapProvider::SchedUlSrInfoReqParameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * \brief Sched UL MAC control info request
      * \param params FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * \brief Sched UL CQI info request
      * \param params FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * \brief Get RBG size function
@@ -361,9 +335,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/simple-ue-component-carrier-manager.cc ns-3.39/src/lte/model/simple-ue-component-carrier-manager.cc
--- ns-3.38/src/lte/model/simple-ue-component-carrier-manager.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/simple-ue-component-carrier-manager.cc	2023-07-05 14:23:48.017428200 -0700
@@ -21,7 +21,6 @@
 #include "simple-ue-component-carrier-manager.h"
 
 #include <ns3/log.h>
-#include <ns3/lte-ue-mac.h>
 
 namespace ns3
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/simple-ue-component-carrier-manager.h ns-3.39/src/lte/model/simple-ue-component-carrier-manager.h
--- ns-3.38/src/lte/model/simple-ue-component-carrier-manager.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/simple-ue-component-carrier-manager.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,9 +21,9 @@
 #ifndef SIMPLE_UE_COMPONENT_CARRIER_MANAGER_H
 #define SIMPLE_UE_COMPONENT_CARRIER_MANAGER_H
 
-#include <ns3/lte-rrc-sap.h>
-#include <ns3/lte-ue-ccm-rrc-sap.h>
-#include <ns3/lte-ue-component-carrier-manager.h>
+#include "lte-rrc-sap.h"
+#include "lte-ue-ccm-rrc-sap.h"
+#include "lte-ue-component-carrier-manager.h"
 
 #include <map>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.cc ns-3.39/src/lte/model/tdbet-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tdbet-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -18,14 +18,16 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "tdbet-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
-#include <ns3/tdbet-ff-mac-scheduler.h>
 
 #include <cfloat>
 #include <set>
@@ -140,7 +142,7 @@
 
 void
 TdBetFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -153,7 +155,7 @@
 
 void
 TdBetFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -199,7 +201,7 @@
 
 void
 TdBetFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -228,7 +230,7 @@
 
 void
 TdBetFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -255,7 +257,7 @@
 
 void
 TdBetFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -295,7 +297,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -320,7 +322,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -328,7 +330,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -390,14 +392,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -405,7 +401,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -476,7 +472,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -508,7 +504,7 @@
     // RACH Allocation
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -550,7 +546,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -615,12 +611,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -711,7 +707,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -739,7 +735,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -810,7 +806,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -1046,7 +1042,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1058,7 +1054,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1088,7 +1084,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1149,7 +1145,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1158,7 +1154,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1252,7 +1248,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1281,7 +1277,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1321,7 +1317,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1457,7 +1453,7 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
@@ -1572,7 +1568,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1652,21 +1648,21 @@
 
 void
 TdBetFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TdBetFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TdBetFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1709,7 +1705,7 @@
 
 void
 TdBetFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.h ns-3.39/src/lte/model/tdbet-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/tdbet-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tdbet-ff-mac-scheduler.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,37 +21,21 @@
 #ifndef TDBET_FF_MAC_SCHEDULER_H
 #define TDBET_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-#define NO_SINR -5000
-
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /// tdbetsFlowPerf_t structure
 struct tdbetsFlowPerf_t
 {
@@ -123,36 +107,31 @@
      * \brief CSched cell config request
      * \param params FfMacCschedSapProvider::CschedCellConfigReqParameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * \brief CSched UE config request
      * \param params FfMacCschedSapProvider::CschedUeConfigReqParameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * \brief CSched LC config request
      * \param params FfMacCschedSapProvider::CschedLcConfigReqParameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * \brief CSched LC release request
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * \brief CSched UE release request
      * \param params FfMacCschedSapProvider::CschedUeReleaseReqParameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -163,77 +142,70 @@
      * \brief Sched DL RLC buffer request
      * \param params FfMacSchedSapProvider::SchedDlRlcBufferReqParameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * \brief Sched DL paging buffer request
      * \param params FfMacSchedSapProvider::SchedDlPagingBufferReqParameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * \brief Sched DL MAC buffer request
      * \param params FfMacSchedSapProvider::SchedDlMacBufferReqParameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * \brief Sched DL trigger request
      * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * \brief Sched DL RACH info request
      * \param params FfMacSchedSapProvider::SchedDlRachInfoReqParameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * \brief Sched DL CQI info request
      * \param params FfMacSchedSapProvider::SchedDlCqiInfoReqParameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * \brief Sched UL trigger request
      * \param params FfMacSchedSapProvider::SchedUlTriggerReqParameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * \brief Sched UL noise interference request
      * \param params FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * \brief Sched UL SR info request
      * \param params FfMacSchedSapProvider::SchedUlSrInfoReqParameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * \brief Sched UL MAC control info request
      * \param params FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * \brief Sched UL CQI info request
      * \param params FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * \brief Get RBG size function
@@ -401,9 +373,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.cc ns-3.39/src/lte/model/tdmt-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tdmt-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -18,14 +18,16 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "tdmt-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
-#include <ns3/tdmt-ff-mac-scheduler.h>
 
 #include <cfloat>
 #include <set>
@@ -139,7 +141,7 @@
 
 void
 TdMtFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -152,7 +154,7 @@
 
 void
 TdMtFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -198,7 +200,7 @@
 
 void
 TdMtFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -217,7 +219,7 @@
 
 void
 TdMtFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -244,7 +246,7 @@
 
 void
 TdMtFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -284,7 +286,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -309,7 +311,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -317,7 +319,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -379,14 +381,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -394,7 +390,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -465,7 +461,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -497,7 +493,7 @@
     // RACH Allocation
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -539,7 +535,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -604,12 +600,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -700,7 +696,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -728,7 +724,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -799,7 +795,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -967,7 +963,7 @@
         {
             NS_LOG_INFO(this << " ALLOCATION for RBG " << i << " of " << rbgNum);
             NS_LOG_DEBUG(this << " ALLOCATION for RBG " << i << " of " << rbgNum);
-            if (rbgMap.at(i) == false)
+            if (!rbgMap.at(i))
             {
                 rbgMap.at(i) = true;
                 tempMap.push_back(i);
@@ -1046,7 +1042,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1058,7 +1054,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1088,7 +1084,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1122,7 +1118,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1131,7 +1127,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1225,7 +1221,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1254,7 +1250,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1294,7 +1290,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1429,7 +1425,7 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
@@ -1544,7 +1540,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1599,21 +1595,21 @@
 
 void
 TdMtFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TdMtFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TdMtFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1656,7 +1652,7 @@
 
 void
 TdMtFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.h ns-3.39/src/lte/model/tdmt-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/tdmt-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tdmt-ff-mac-scheduler.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,38 +21,22 @@
 #ifndef TDMT_FF_MAC_SCHEDULER_H
 #define TDMT_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-#define NO_SINR -5000
-
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /**
  * \ingroup ff-api
  * \brief Implements the SCHED SAP and CSCHED SAP for a Time Domain Maximize Throughput scheduler
@@ -115,36 +99,31 @@
      * \brief CSched cell config request
      * \param params FfMacCschedSapProvider::CschedCellConfigReqParameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * \brief CSched UE config request
      * \param params FfMacCschedSapProvider::CschedUeConfigReqParameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * \brief CSched LC config request
      * \param params FfMacCschedSapProvider::CschedLcConfigReqParameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * \brief CSched LC release request
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * \brief CSched UE release request
      * \param params FfMacCschedSapProvider::CschedUeReleaseReqParameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -155,77 +134,70 @@
      * \brief Sched DL RLC buffer request
      * \param params FfMacSchedSapProvider::SchedDlRlcBufferReqParameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * \brief Sched DL paging buffer request
      * \param params FfMacSchedSapProvider::SchedDlPagingBufferReqParameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * \brief Sched DL MAC buffer request
      * \param params FfMacSchedSapProvider::SchedDlMacBufferReqParameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * \brief Sched DL trigger request
      * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * \brief Sched DL RACH info request
      * \param params FfMacSchedSapProvider::SchedDlRachInfoReqParameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * \brief Sched DL CQI info request
      * \param params FfMacSchedSapProvider::SchedDlCqiInfoReqParameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * \brief Sched UL trigger request
      * \param params FfMacSchedSapProvider::SchedUlTriggerReqParameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * \brief Sched UL noise interference request
      * \param params FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * \brief Sched UL SR info request
      * \param params FfMacSchedSapProvider::SchedUlSrInfoReqParameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * \brief Sched UL MAC control info request
      * \param params FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * \brief Sched UL CQI info request
      * \param params FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * \brief Get RBG size function
@@ -391,9 +363,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.cc ns-3.39/src/lte/model/tdtbfq-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tdtbfq-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -18,15 +18,17 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "tdtbfq-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
 #include <ns3/integer.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
-#include <ns3/tdtbfq-ff-mac-scheduler.h>
 
 #include <cfloat>
 #include <set>
@@ -165,7 +167,7 @@
 
 void
 TdTbfqFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -178,7 +180,7 @@
 
 void
 TdTbfqFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -224,7 +226,7 @@
 
 void
 TdTbfqFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -280,7 +282,7 @@
 
 void
 TdTbfqFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -307,7 +309,7 @@
 
 void
 TdTbfqFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -347,7 +349,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -372,7 +374,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -380,7 +382,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -442,14 +444,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -457,7 +453,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -528,7 +524,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -551,7 +547,7 @@
     rbgMap = m_ffrSapProvider->GetAvailableDlRbg();
     for (std::vector<bool>::iterator it = rbgMap.begin(); it != rbgMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbgAllocatedNum++;
         }
@@ -579,7 +575,7 @@
 
     for (std::vector<bool>::iterator it = ulRbMap.begin(); it != ulRbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             if (tmpMinBandwidth > maxContinuousUlBandwidth)
             {
@@ -608,7 +604,7 @@
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
     rbStart = ffrRbStartOffset;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -650,7 +646,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -715,12 +711,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -811,7 +807,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -839,7 +835,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -910,7 +906,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -1080,7 +1076,7 @@
         double metric =
             (((double)(*it).second.counter) / ((double)(*it).second.tokenGenerationRate));
 
-        if (firstRnti == true)
+        if (firstRnti)
         {
             metricMax = metric;
             itMax = it;
@@ -1106,12 +1102,12 @@
         std::vector<uint16_t> tempMap;
         for (int i = 0; i < rbgNum; i++)
         {
-            if (rbgMap.at(i) == true)
+            if (rbgMap.at(i))
             { // this RBG is allocated in RACH procedure
                 continue;
             }
 
-            if ((m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*itMax).first)) == false)
+            if (!m_ffrSapProvider->IsDlRbgAvailableForUe(i, (*itMax).first))
             {
                 continue;
             }
@@ -1232,7 +1228,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1244,7 +1240,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1274,7 +1270,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1328,7 +1324,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1337,7 +1333,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     m_ffrSapProvider->ReportDlCqiInfo(params);
@@ -1432,7 +1428,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1458,7 +1454,7 @@
 
     for (std::vector<bool>::iterator it = rbMap.begin(); it != rbMap.end(); it++)
     {
-        if ((*it) == true)
+        if (*it)
         {
             rbAllocatedNum++;
         }
@@ -1477,7 +1473,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1517,7 +1513,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1658,12 +1654,12 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
                 }
-                if ((m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first)) == false)
+                if (!m_ffrSapProvider->IsUlRbgAvailableForUe(j, (*it).first))
                 {
                     free = false;
                     break;
@@ -1780,7 +1776,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1835,21 +1831,21 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TdTbfqFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TdTbfqFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1892,7 +1888,7 @@
 
 void
 TdTbfqFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.h ns-3.39/src/lte/model/tdtbfq-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/tdtbfq-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tdtbfq-ff-mac-scheduler.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,37 +21,21 @@
 #ifndef TDTBFQ_FF_MAC_SCHEDULER_H
 #define TDTBFQ_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-#define NO_SINR -5000
-
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /**
  *  Flow information
  */
@@ -132,36 +116,31 @@
      * \brief CSched cell config request
      * \param params FfMacCschedSapProvider::CschedCellConfigReqParameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * \brief CSched UE config request
      * \param params FfMacCschedSapProvider::CschedUeConfigReqParameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * \brief CSched LC config request
      * \param params FfMacCschedSapProvider::CschedLcConfigReqParameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * \brief CSched LC release request
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * \brief CSched UE release request
      * \param params FfMacCschedSapProvider::CschedUeReleaseReqParameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -172,77 +151,70 @@
      * \brief Sched DL RLC buffer request
      * \param params FfMacSchedSapProvider::SchedDlRlcBufferReqParameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * \brief Sched DL paging buffer request
      * \param params FfMacSchedSapProvider::SchedDlPagingBufferReqParameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * \brief Sched DL MAC buffer request
      * \param params FfMacSchedSapProvider::SchedDlMacBufferReqParameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * \brief Sched DL trigger request
      * \param params FfMacSchedSapProvider::SchedDlTriggerReqParameters
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * \brief Sched DL RACH info request
      * \param params FfMacSchedSapProvider::SchedDlRachInfoReqParameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * \brief Sched DL CQI info request
      * \param params FfMacSchedSapProvider::SchedDlCqiInfoReqParameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * \brief Sched UL trigger request
      * \param params FfMacSchedSapProvider::SchedUlTriggerReqParameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * \brief Sched UL noise interference request
      * \param params FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * \brief Sched UL SR info request
      * \param params FfMacSchedSapProvider::SchedUlSrInfoReqParameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * \brief Sched UL MAC control info request
      * \param params FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * \brief Sched UL CQI info request
      * \param params FfMacSchedSapProvider::SchedUlCqiInfoReqParameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * \brief Get RBG size
@@ -418,9 +390,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tta-ff-mac-scheduler.cc ns-3.39/src/lte/model/tta-ff-mac-scheduler.cc
--- ns-3.38/src/lte/model/tta-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tta-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -18,14 +18,16 @@
  * Modification: Dizhi Zhou <dizhi.zhou@gmail.com>    // modify codes related to downlink scheduler
  */
 
+#include "tta-ff-mac-scheduler.h"
+
+#include "lte-amc.h"
+#include "lte-vendor-specific-parameters.h"
+
 #include <ns3/boolean.h>
 #include <ns3/log.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-vendor-specific-parameters.h>
 #include <ns3/math.h>
 #include <ns3/pointer.h>
 #include <ns3/simulator.h>
-#include <ns3/tta-ff-mac-scheduler.h>
 
 #include <cfloat>
 #include <set>
@@ -139,7 +141,7 @@
 
 void
 TtaFfMacScheduler::DoCschedCellConfigReq(
-    const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedCellConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // Read the subset of parameters used
@@ -152,7 +154,7 @@
 
 void
 TtaFfMacScheduler::DoCschedUeConfigReq(
-    const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " RNTI " << params.m_rnti << " txMode "
                          << (uint16_t)params.m_transmissionMode);
@@ -198,7 +200,7 @@
 
 void
 TtaFfMacScheduler::DoCschedLcConfigReq(
-    const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcConfigReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " New LC, rnti: " << params.m_rnti);
 
@@ -217,7 +219,7 @@
 
 void
 TtaFfMacScheduler::DoCschedLcReleaseReq(
-    const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     for (std::size_t i = 0; i < params.m_logicalChannelIdentity.size(); i++)
@@ -244,7 +246,7 @@
 
 void
 TtaFfMacScheduler::DoCschedUeReleaseReq(
-    const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
+    const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -284,7 +286,7 @@
 
 void
 TtaFfMacScheduler::DoSchedDlRlcBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this << params.m_rnti << (uint32_t)params.m_logicalChannelIdentity);
     // API generated by RLC for updating RLC parameters on a LC (tx and retx queues)
@@ -309,7 +311,7 @@
 
 void
 TtaFfMacScheduler::DoSchedDlPagingBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -317,7 +319,7 @@
 
 void
 TtaFfMacScheduler::DoSchedDlMacBufferReq(
-    const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     NS_FATAL_ERROR("method not implemented");
@@ -379,14 +381,8 @@
     {
         i = (i + 1) % HARQ_PROC_NUM;
     } while (((*itStat).second.at(i) != 0) && (i != (*it).second));
-    if ((*itStat).second.at(i) == 0)
-    {
-        return (true);
-    }
-    else
-    {
-        return (false); // return a not valid harq proc id
-    }
+
+    return (*itStat).second.at(i) == 0;
 }
 
 uint8_t
@@ -394,7 +390,7 @@
 {
     NS_LOG_FUNCTION(this << rnti);
 
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         return (0);
     }
@@ -465,7 +461,7 @@
 
 void
 TtaFfMacScheduler::DoSchedDlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf));
@@ -497,7 +493,7 @@
     // RACH Allocation
     m_rachAllocationMap.resize(m_cschedCellConfig.m_ulBandwidth, 0);
     uint16_t rbStart = 0;
-    std::vector<struct RachListElement_s>::iterator itRach;
+    std::vector<RachListElement_s>::iterator itRach;
     for (itRach = m_rachList.begin(); itRach != m_rachList.end(); itRach++)
     {
         NS_ASSERT_MSG(m_amc->GetUlTbSizeFromMcs(m_ulGrantMcs, m_cschedCellConfig.m_ulBandwidth) >
@@ -539,7 +535,7 @@
             m_rachAllocationMap.at(i) = (*itRach).m_rnti;
         }
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // generate UL-DCI for HARQ retransmissions
             UlDciListElement_s uldci;
@@ -604,12 +600,12 @@
             m_dlInfoListBuffered = params.m_dlInfoList;
         }
     }
-    if (m_harqOn == false)
+    if (!m_harqOn)
     {
         // Ignore HARQ feedback
         m_dlInfoListBuffered.clear();
     }
-    std::vector<struct DlInfoListElement_s> dlInfoListUntxed;
+    std::vector<DlInfoListElement_s> dlInfoListUntxed;
     for (std::size_t i = 0; i < m_dlInfoListBuffered.size(); i++)
     {
         std::set<uint16_t>::iterator itRnti = rntiAllocated.find(m_dlInfoListBuffered.at(i).m_rnti);
@@ -700,7 +696,7 @@
             bool free = true;
             for (std::size_t j = 0; j < dciRbg.size(); j++)
             {
-                if (rbgMap.at(dciRbg.at(j)) == true)
+                if (rbgMap.at(dciRbg.at(j)))
                 {
                     free = false;
                     break;
@@ -728,7 +724,7 @@
                 std::vector<bool> rbgMapCopy = rbgMap;
                 while ((j < dciRbg.size()) && (startRbg != rbgId))
                 {
-                    if (rbgMapCopy.at(rbgId) == false)
+                    if (!rbgMapCopy.at(rbgId))
                     {
                         rbgMapCopy.at(rbgId) = true;
                         dciRbg.at(j) = rbgId;
@@ -799,7 +795,7 @@
             }
             for (std::size_t k = 0; k < (*itRlcPdu).second.at(0).at(dci.m_harqProcess).size(); k++)
             {
-                std::vector<struct RlcPduListElement_s> rlcPduListPerLc;
+                std::vector<RlcPduListElement_s> rlcPduListPerLc;
                 for (std::size_t j = 0; j < nLayers; j++)
                 {
                     if (retx.at(j))
@@ -887,7 +883,7 @@
     for (int i = 0; i < rbgNum; i++)
     {
         NS_LOG_INFO(this << " ALLOCATION for RBG " << i << " of " << rbgNum);
-        if (rbgMap.at(i) == false)
+        if (!rbgMap.at(i))
         {
             std::set<uint16_t>::iterator it;
             std::set<uint16_t>::iterator itMax = m_flowStatsDl.end();
@@ -1134,7 +1130,7 @@
                  ((*itBufReq).second.m_rlcRetransmissionQueueSize > 0) ||
                  ((*itBufReq).second.m_rlcStatusPduSize > 0)))
             {
-                std::vector<struct RlcPduListElement_s> newRlcPduLe;
+                std::vector<RlcPduListElement_s> newRlcPduLe;
                 for (uint8_t j = 0; j < nLayer; j++)
                 {
                     RlcPduListElement_s newRlcEl;
@@ -1146,7 +1142,7 @@
                     UpdateDlRlcBufferInfo(newDci.m_rnti,
                                           newRlcEl.m_logicalChannelIdentity,
                                           newRlcEl.m_size);
-                    if (m_harqOn == true)
+                    if (m_harqOn)
                     {
                         // store RLC PDU list for HARQ
                         std::map<uint16_t, DlHarqRlcPduListBuffer_t>::iterator itRlcPdu =
@@ -1176,7 +1172,7 @@
 
         newEl.m_dci = newDci;
 
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             // store DCI for HARQ
             std::map<uint16_t, DlHarqProcessesDciBuffer_t>::iterator itDci =
@@ -1210,7 +1206,7 @@
 
 void
 TtaFfMacScheduler::DoSchedDlRachInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1219,7 +1215,7 @@
 
 void
 TtaFfMacScheduler::DoSchedDlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1313,7 +1309,7 @@
 
 void
 TtaFfMacScheduler::DoSchedUlTriggerReq(
-    const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params)
 {
     NS_LOG_FUNCTION(this << " UL - Frame no. " << (params.m_sfnSf >> 4) << " subframe no. "
                          << (0xF & params.m_sfnSf) << " size " << params.m_ulInfoList.size());
@@ -1342,7 +1338,7 @@
         }
     }
 
-    if (m_harqOn == true)
+    if (m_harqOn)
     {
         //   Process UL HARQ feedback
         for (std::size_t i = 0; i < params.m_ulInfoList.size(); i++)
@@ -1382,7 +1378,7 @@
                 bool free = true;
                 for (int j = dci.m_rbStart; j < dci.m_rbStart + dci.m_rbLen; j++)
                 {
-                    if (rbMap.at(j) == true)
+                    if (rbMap.at(j))
                     {
                         free = false;
                         NS_LOG_INFO(this << " BUSY " << j);
@@ -1517,7 +1513,7 @@
             bool free = true;
             for (int j = rbAllocated; j < rbAllocated + rbPerFlow; j++)
             {
-                if (rbMap.at(j) == true)
+                if (rbMap.at(j))
                 {
                     free = false;
                     break;
@@ -1632,7 +1628,7 @@
         ret.m_dciList.push_back(uldci);
         // store DCI for HARQ_PERIOD
         uint8_t harqId = 0;
-        if (m_harqOn == true)
+        if (m_harqOn)
         {
             std::map<uint16_t, uint8_t>::iterator itProcId;
             itProcId = m_ulHarqCurrentProcessId.find(uldci.m_rnti);
@@ -1687,21 +1683,21 @@
 
 void
 TtaFfMacScheduler::DoSchedUlNoiseInterferenceReq(
-    const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TtaFfMacScheduler::DoSchedUlSrInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
 TtaFfMacScheduler::DoSchedUlMacCtrlInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 
@@ -1744,7 +1740,7 @@
 
 void
 TtaFfMacScheduler::DoSchedUlCqiInfoReq(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+    const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
     // retrieve the allocation for this subframe
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/model/tta-ff-mac-scheduler.h ns-3.39/src/lte/model/tta-ff-mac-scheduler.h
--- ns-3.38/src/lte/model/tta-ff-mac-scheduler.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/model/tta-ff-mac-scheduler.h	2023-07-05 14:23:48.017428200 -0700
@@ -21,38 +21,22 @@
 #ifndef TTA_FF_MAC_SCHEDULER_H
 #define TTA_FF_MAC_SCHEDULER_H
 
-#include <ns3/ff-mac-csched-sap.h>
-#include <ns3/ff-mac-sched-sap.h>
-#include <ns3/ff-mac-scheduler.h>
-#include <ns3/lte-amc.h>
-#include <ns3/lte-common.h>
-#include <ns3/lte-ffr-sap.h>
+#include "ff-mac-csched-sap.h"
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-scheduler.h"
+#include "lte-amc.h"
+#include "lte-common.h"
+#include "lte-ffr-sap.h"
+
 #include <ns3/nstime.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-// value for SINR outside the range defined by FF-API, used to indicate that there
-// is no CQI for this element
-#define NO_SINR -5000
-
-#define HARQ_PROC_NUM 8
-#define HARQ_DL_TIMEOUT 11
-
 namespace ns3
 {
 
-typedef std::vector<uint8_t> DlHarqProcessesStatus_t;
-typedef std::vector<uint8_t> DlHarqProcessesTimer_t;
-typedef std::vector<DlDciListElement_s> DlHarqProcessesDciBuffer_t;
-typedef std::vector<std::vector<struct RlcPduListElement_s>>
-    RlcPduList_t;                                           // vector of the LCs and layers per UE
-typedef std::vector<RlcPduList_t> DlHarqRlcPduListBuffer_t; // vector of the 8 HARQ processes per UE
-
-typedef std::vector<UlDciListElement_s> UlHarqProcessesDciBuffer_t;
-typedef std::vector<uint8_t> UlHarqProcessesStatus_t;
-
 /**
  * \ingroup ff-api
  * \brief Implements the SCHED SAP and CSCHED SAP for a Throughput to Average scheduler
@@ -115,36 +99,31 @@
      * \brief CSched cell config request function
      * \param params FfMacCschedSapProvider::CschedCellConfigReqParameters
      */
-    void DoCschedCellConfigReq(
-        const struct FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
+    void DoCschedCellConfigReq(const FfMacCschedSapProvider::CschedCellConfigReqParameters& params);
 
     /**
      * \brief CSched UE config request function
      * \param params FfMacCschedSapProvider::CschedUeConfigReqParameters
      */
-    void DoCschedUeConfigReq(
-        const struct FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
+    void DoCschedUeConfigReq(const FfMacCschedSapProvider::CschedUeConfigReqParameters& params);
 
     /**
      * \brief CSched LC config request function
      * \param params FfMacCschedSapProvider::CschedLcConfigReqParameters
      */
-    void DoCschedLcConfigReq(
-        const struct FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
+    void DoCschedLcConfigReq(const FfMacCschedSapProvider::CschedLcConfigReqParameters& params);
 
     /**
      * \brief CSched LC release request function
      * \param params FfMacCschedSapProvider::CschedLcReleaseReqParameters
      */
-    void DoCschedLcReleaseReq(
-        const struct FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
+    void DoCschedLcReleaseReq(const FfMacCschedSapProvider::CschedLcReleaseReqParameters& params);
 
     /**
      * \brief CSched UE release request function
      * \param params FfMacCschedSapProvider::CschedUeReleaseReqParameters
      */
-    void DoCschedUeReleaseReq(
-        const struct FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
+    void DoCschedUeReleaseReq(const FfMacCschedSapProvider::CschedUeReleaseReqParameters& params);
 
     //
     // Implementation of the SCHED API primitives
@@ -155,77 +134,70 @@
      * \brief Sched DL RLC buffer request function
      * \param params FfMacSchedSapProvider::SchedDlRlcBufferReqParameters
      */
-    void DoSchedDlRlcBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
+    void DoSchedDlRlcBufferReq(const FfMacSchedSapProvider::SchedDlRlcBufferReqParameters& params);
 
     /**
      * \brief Sched DL paging buffer request function
      * \param params FfMacCschedSapProvider::SchedDlPagingBufferReqParameters
      */
     void DoSchedDlPagingBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
+        const FfMacSchedSapProvider::SchedDlPagingBufferReqParameters& params);
 
     /**
      * \brief Sched DL MAC buffer request function
      * \param params FfMacCschedSapProvider::SchedDlMacBufferReqParameters
      */
-    void DoSchedDlMacBufferReq(
-        const struct FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
+    void DoSchedDlMacBufferReq(const FfMacSchedSapProvider::SchedDlMacBufferReqParameters& params);
 
     /**
      * \brief Sched DL trigger request function
      * \param params FfMacCschedSapProvider::SchedDlTriggerReqParameters
      */
-    void DoSchedDlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
+    void DoSchedDlTriggerReq(const FfMacSchedSapProvider::SchedDlTriggerReqParameters& params);
 
     /**
      * \brief Sched DL RACH info request function
      * \param params FfMacCschedSapProvider::SchedDlRachInfoReqParameters
      */
-    void DoSchedDlRachInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
+    void DoSchedDlRachInfoReq(const FfMacSchedSapProvider::SchedDlRachInfoReqParameters& params);
 
     /**
      * \brief Sched DL CQI info request function
      * \param params FfMacCschedSapProvider::SchedDlCqiInfoReqParameters
      */
-    void DoSchedDlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
+    void DoSchedDlCqiInfoReq(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params);
 
     /**
      * \brief Sched UL trigger request function
      * \param params FfMacCschedSapProvider::SchedUlTriggerReqParameters
      */
-    void DoSchedUlTriggerReq(
-        const struct FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
+    void DoSchedUlTriggerReq(const FfMacSchedSapProvider::SchedUlTriggerReqParameters& params);
 
     /**
      * \brief Sched UL noise interference request function
      * \param params FfMacCschedSapProvider::SchedUlNoiseInterferenceReqParameters
      */
     void DoSchedUlNoiseInterferenceReq(
-        const struct FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlNoiseInterferenceReqParameters& params);
 
     /**
      * \brief Sched UL SR info request function
      * \param params FfMacCschedSapProvider::SchedUlSrInfoReqParameters
      */
-    void DoSchedUlSrInfoReq(const struct FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
+    void DoSchedUlSrInfoReq(const FfMacSchedSapProvider::SchedUlSrInfoReqParameters& params);
 
     /**
      * \brief Sched UL MAC control info request function
      * \param params FfMacCschedSapProvider::SchedUlMacCtrlInfoReqParameters
      */
     void DoSchedUlMacCtrlInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
+        const FfMacSchedSapProvider::SchedUlMacCtrlInfoReqParameters& params);
 
     /**
      * \brief Sched UL CQI info request function
      * \param params FfMacCschedSapProvider::SchedUlCqiInfoReqParameters
      */
-    void DoSchedUlCqiInfoReq(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
+    void DoSchedUlCqiInfoReq(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params);
 
     /**
      * \brief Get RBG size function
@@ -391,9 +363,9 @@
         m_ulHarqProcessesDciBuffer; ///< UL HARQ process DCI buffer
 
     // RACH attributes
-    std::vector<struct RachListElement_s> m_rachList; ///< RACH list
-    std::vector<uint16_t> m_rachAllocationMap;        ///< RACH allocation map
-    uint8_t m_ulGrantMcs;                             ///< MCS for UL grant (default 0)
+    std::vector<RachListElement_s> m_rachList; ///< RACH list
+    std::vector<uint16_t> m_rachAllocationMap; ///< RACH allocation map
+    uint8_t m_ulGrantMcs;                      ///< MCS for UL grant (default 0)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/examples-to-run.py ns-3.39/src/lte/test/examples-to-run.py
--- ns-3.38/src/lte/test/examples-to-run.py	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/examples-to-run.py	2023-07-05 14:23:48.017428200 -0700
@@ -1,6 +1,4 @@
 #! /usr/bin/env python3
-# -*- coding: utf-8 -*-
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
@@ -32,16 +30,16 @@
     ("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", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdTbfqFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdMtFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::TdBetFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::RrFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::PssFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::PfFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdTbfqFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdMtFfMacScheduler", "options.valgrind", "True"),
-    ("lena-simple-epc --simTime=1.1 --ns3::LteHelper::Scheduler=ns3::FdBetFfMacScheduler", "options.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"),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-ffr-simple.cc ns-3.39/src/lte/test/lte-ffr-simple.cc
--- ns-3.38/src/lte/test/lte-ffr-simple.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-ffr-simple.cc	2023-07-05 14:23:48.017428200 -0700
@@ -224,15 +224,13 @@
 }
 
 void
-LteFfrSimple::DoReportDlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
+LteFfrSimple::DoReportDlCqiInfo(const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
 
 void
-LteFfrSimple::DoReportUlCqiInfo(
-    const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
+LteFfrSimple::DoReportUlCqiInfo(const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params)
 {
     NS_LOG_FUNCTION(this);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-ffr-simple.h ns-3.39/src/lte/test/lte-ffr-simple.h
--- ns-3.38/src/lte/test/lte-ffr-simple.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-ffr-simple.h	2023-07-05 14:23:48.017428200 -0700
@@ -108,9 +108,9 @@
     std::vector<bool> DoGetAvailableUlRbg() override;
     bool DoIsUlRbgAvailableForUe(int i, uint16_t rnti) override;
     void DoReportDlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedDlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(
-        const struct FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
+        const FfMacSchedSapProvider::SchedUlCqiInfoReqParameters& params) override;
     void DoReportUlCqiInfo(std::map<uint16_t, std::vector<double>> ulCqiMap) override;
     uint8_t DoGetTpc(uint16_t rnti) override;
     uint16_t DoGetMinContinuousUlBandwidth() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-carrier-aggregation.cc ns-3.39/src/lte/test/lte-test-carrier-aggregation.cc
--- ns-3.38/src/lte/test/lte-test-carrier-aggregation.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-carrier-aggregation.cc	2023-07-05 14:23:48.017428200 -0700
@@ -49,6 +49,7 @@
 
 #include <errno.h>
 #include <iostream>
+#include <map>
 
 using namespace ns3;
 
@@ -376,7 +377,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-carrier-aggregation-configuration.cc ns-3.39/src/lte/test/lte-test-carrier-aggregation-configuration.cc
--- ns-3.38/src/lte/test/lte-test-carrier-aggregation-configuration.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-carrier-aggregation-configuration.cc	2023-07-05 14:23:48.017428200 -0700
@@ -370,7 +370,7 @@
     }
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-carrier-aggregation.h ns-3.39/src/lte/test/lte-test-carrier-aggregation.h
--- ns-3.38/src/lte/test/lte-test-carrier-aggregation.h	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-carrier-aggregation.h	2023-07-05 14:23:48.017428200 -0700
@@ -27,6 +27,8 @@
 #include "ns3/simulator.h"
 #include "ns3/test.h"
 
+#include <map>
+
 using namespace ns3;
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-cqa-ff-mac-scheduler.cc	2023-07-05 14:23:48.017428200 -0700
@@ -431,7 +431,7 @@
         qos.mbrDl = 0;
         qos.mbrUl = 0;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
@@ -690,7 +690,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = qos.gbrUl;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-cqi-generation.cc ns-3.39/src/lte/test/lte-test-cqi-generation.cc
--- ns-3.38/src/lte/test/lte-test-cqi-generation.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-cqi-generation.cc	2023-07-05 14:23:48.017428200 -0700
@@ -267,7 +267,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
@@ -417,7 +417,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-deactivate-bearer.cc ns-3.39/src/lte/test/lte-test-deactivate-bearer.cc
--- ns-3.38/src/lte/test/lte-test-deactivate-bearer.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-deactivate-bearer.cc	2023-07-05 14:23:48.017428200 -0700
@@ -271,7 +271,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = qos.gbrUl;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         bearer.arp.priorityLevel = 15 - (u + 1);
         bearer.arp.preemptionCapability = true;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-downlink-power-control.cc ns-3.39/src/lte/test/lte-test-downlink-power-control.cc
--- ns-3.38/src/lte/test/lte-test-downlink-power-control.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-downlink-power-control.cc	2023-07-05 14:23:48.021428000 -0700
@@ -489,7 +489,7 @@
     simpleFfrAlgorithm->SetPdschConfigDedicated(m_pdschConfigDedicated);
 
     // Activate the default EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+    EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -659,7 +659,7 @@
     simpleFfrAlgorithm->SetPdschConfigDedicated(pdschConfigDedicated);
 
     // Activate the default EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+    EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-fdbet-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -280,7 +280,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -462,7 +462,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-fdmt-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -257,7 +257,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-fdtbfq-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -432,7 +432,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = 0;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
@@ -704,7 +704,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = 0;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-frequency-reuse.cc ns-3.39/src/lte/test/lte-test-frequency-reuse.cc
--- ns-3.38/src/lte/test/lte-test-frequency-reuse.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-frequency-reuse.cc	2023-07-05 14:23:48.021428000 -0700
@@ -487,7 +487,7 @@
         NS_LOG_DEBUG("RB " << i << " POWER: "
                            << " " << power << " isAvailable: " << m_availableDlRb[i]);
 
-        if (m_availableDlRb[i] == false && power > 0)
+        if (!m_availableDlRb[i] && power > 0)
         {
             m_usedMutedDlRbg = true;
         }
@@ -507,7 +507,7 @@
         NS_LOG_DEBUG("RB " << i << " POWER: "
                            << " " << power << " isAvailable: " << m_availableUlRb[i]);
 
-        if (m_availableUlRb[i] == false && power > 0)
+        if (!m_availableUlRb[i] && power > 0)
         {
             m_usedMutedUlRbg = true;
         }
@@ -598,7 +598,7 @@
     qos.gbrUl = 1e4;
     qos.gbrDl = 1e4;
 
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q, qos);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -736,7 +736,7 @@
     qos.gbrUl = 1e4;
     qos.gbrDl = 1e4;
 
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q, qos);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -832,11 +832,11 @@
                            << " " << power << " Available: " << m_expectedDlRb[i]
                            << " Expected Power: " << m_expectedDlPower);
 
-        if (m_expectedDlRb[i] == false && power > 0)
+        if (!m_expectedDlRb[i] && power > 0)
         {
             m_usedWrongDlRbg = true;
         }
-        else if (m_expectedDlRb[i] == true && power > 0)
+        else if (m_expectedDlRb[i] && power > 0)
         {
             NS_TEST_ASSERT_MSG_EQ_TOL(power,
                                       m_expectedDlPower,
@@ -869,7 +869,7 @@
     {
         // Count the RB as active if it is part of
         // the expected UL RBs and has Power Spectral Density (PSD) > 0
-        if (m_expectedUlRb[numActiveRbs] == true && (*it) > 0)
+        if (m_expectedUlRb[numActiveRbs] && (*it) > 0)
         {
             numActiveRbs++;
         }
@@ -883,11 +883,11 @@
         double power = (*it) * (numActiveRbs * 180000);
         NS_LOG_DEBUG("RB " << i << " POWER: " << power
                            << " expectedUlPower: " << m_expectedUlPower);
-        if (m_expectedUlRb[i] == false && power > 0)
+        if (!m_expectedUlRb[i] && power > 0)
         {
             m_usedWrongUlRbg = true;
         }
-        else if (m_expectedUlRb[i] == true && power > 0)
+        else if (m_expectedUlRb[i] && power > 0)
         {
             NS_TEST_ASSERT_MSG_EQ_TOL(power,
                                       m_expectedUlPower,
@@ -1066,7 +1066,7 @@
     qos.gbrUl = 1e4;
     qos.gbrDl = 1e4;
 
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q, qos);
 
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
@@ -1283,7 +1283,7 @@
     qos.gbrUl = 1e4;
     qos.gbrDl = 1e4;
 
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q, qos);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
@@ -1512,7 +1512,7 @@
     qos.gbrUl = 1e4;
     qos.gbrDl = 1e4;
 
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q, qos);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
@@ -1783,7 +1783,7 @@
     qos.gbrUl = 1e4;
     qos.gbrDl = 1e4;
 
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q, qos);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
@@ -2159,7 +2159,7 @@
             qos.gbrUl = 1e4;
             qos.gbrDl = 1e4;
 
-            enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+            EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
             EpsBearer bearer(q, qos);
             lteHelper->ActivateDedicatedEpsBearer(ueLteDevs.Get(u), bearer, tft);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-harq.cc ns-3.39/src/lte/test/lte-test-harq.cc
--- ns-3.38/src/lte/test/lte-test-harq.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-harq.cc	2023-07-05 14:23:48.021428000 -0700
@@ -209,7 +209,7 @@
     lena->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lena->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-interference.cc ns-3.39/src/lte/test/lte-test-interference.cc
--- ns-3.38/src/lte/test/lte-test-interference.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-interference.cc	2023-07-05 14:23:48.021428000 -0700
@@ -278,7 +278,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-interference-fr.cc ns-3.39/src/lte/test/lte-test-interference-fr.cc
--- ns-3.38/src/lte/test/lte-test-interference-fr.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-interference-fr.cc	2023-07-05 14:23:48.021428000 -0700
@@ -217,7 +217,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
@@ -404,7 +404,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-ipv6-routing.cc ns-3.39/src/lte/test/lte-test-ipv6-routing.cc
--- ns-3.38/src/lte/test/lte-test-ipv6-routing.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-ipv6-routing.cc	2023-07-05 14:23:48.021428000 -0700
@@ -181,7 +181,7 @@
             Ptr<Packet> p2 = (*it2)->Copy();
             Ipv6Header ipv6header2;
             p2->RemoveHeader(ipv6header2);
-            Ipv6Address sorceAddress = ipv6header2.GetSource();
+            Ipv6Address sourceAddress = ipv6header2.GetSource();
             Ipv6Address destinationAddress = ipv6header2.GetDestination();
             UdpHeader udpHeader2;
             p2->RemoveHeader(udpHeader2);
@@ -190,7 +190,7 @@
             sourcePort = udpHeader2.GetSourcePort();
             destinationPort = udpHeader2.GetDestinationPort();
             // Check whether the uids, addresses and ports match
-            if ((p2->GetUid() == p1->GetUid()) && sorceAddress == ipv6header1.GetDestination() &&
+            if ((p2->GetUid() == p1->GetUid()) && sourceAddress == ipv6header1.GetDestination() &&
                 destinationAddress == ipv6header1.GetSource() &&
                 sourcePort == udpHeader1.GetDestinationPort() &&
                 destinationPort == udpHeader1.GetSourcePort())
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-link-adaptation.cc ns-3.39/src/lte/test/lte-test-link-adaptation.cc
--- ns-3.38/src/lte/test/lte-test-link-adaptation.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-link-adaptation.cc	2023-07-05 14:23:48.021428000 -0700
@@ -189,7 +189,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate the default EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+    EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-mimo.cc ns-3.39/src/lte/test/lte-test-mimo.cc
--- ns-3.38/src/lte/test/lte-test-mimo.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-mimo.cc	2023-07-05 14:23:48.021428000 -0700
@@ -183,7 +183,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-pathloss-model.cc ns-3.39/src/lte/test/lte-test-pathloss-model.cc
--- ns-3.38/src/lte/test/lte-test-pathloss-model.cc	2023-03-16 20:54:23.780869000 -0700
+++ ns-3.39/src/lte/test/lte-test-pathloss-model.cc	2023-07-05 14:23:48.021428000 -0700
@@ -238,7 +238,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-pf-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-pf-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-pf-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-pf-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -297,7 +297,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -476,7 +476,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-phy-error-model.cc ns-3.39/src/lte/test/lte-test-phy-error-model.cc
--- ns-3.38/src/lte/test/lte-test-phy-error-model.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-phy-error-model.cc	2023-07-05 14:23:48.021428000 -0700
@@ -205,7 +205,7 @@
     lena->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lena->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -366,7 +366,7 @@
     lena->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lena->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-pss-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-pss-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-pss-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-pss-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -429,7 +429,7 @@
         qos.mbrDl = 0;
         qos.mbrUl = 0;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
@@ -692,7 +692,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = qos.gbrUl;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-radio-link-failure.h ns-3.39/src/lte/test/lte-test-radio-link-failure.h
--- ns-3.38/src/lte/test/lte-test-radio-link-failure.h	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-radio-link-failure.h	2023-07-05 14:23:48.021428000 -0700
@@ -21,6 +21,7 @@
 #define LTE_TEST_RADIO_LINK_FAILURE_H
 
 #include <ns3/lte-ue-rrc.h>
+#include <ns3/mobility-model.h>
 #include <ns3/net-device-container.h>
 #include <ns3/node-container.h>
 #include <ns3/nstime.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-rr-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-rr-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-rr-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-rr-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -266,7 +266,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-tdbet-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -283,7 +283,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -467,7 +467,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-tdmt-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -259,7 +259,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-tdtbfq-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -432,7 +432,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = 0;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
@@ -702,7 +702,7 @@
         qos.mbrDl = qos.gbrDl;
         qos.mbrUl = 0;
 
-        enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+        EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
         EpsBearer bearer(q, qos);
         lteHelper->ActivateDedicatedEpsBearer(ueDevice, bearer, EpcTft::Default());
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-tta-ff-mac-scheduler.cc ns-3.39/src/lte/test/lte-test-tta-ff-mac-scheduler.cc
--- ns-3.38/src/lte/test/lte-test-tta-ff-mac-scheduler.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-tta-ff-mac-scheduler.cc	2023-07-05 14:23:48.021428000 -0700
@@ -259,7 +259,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-ue-measurements.cc ns-3.39/src/lte/test/lte-test-ue-measurements.cc
--- ns-3.38/src/lte/test/lte-test-ue-measurements.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-ue-measurements.cc	2023-07-05 14:23:48.021428000 -0700
@@ -324,7 +324,7 @@
     lteHelper->Attach(ueDevs2, enbDevs.Get(1));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs1, bearer);
     lteHelper->ActivateDataRadioBearer(ueDevs2, bearer);
@@ -794,7 +794,7 @@
     lteHelper->Attach(ueDevs.Get(0), enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -1502,7 +1502,7 @@
     lteHelper->Attach(ueDevs.Get(0), enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -1826,7 +1826,7 @@
     lteHelper->Attach(ueDevs.Get(0), enbDevs.Get(0));
 
     // Activate an EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/lte-test-uplink-power-control.cc ns-3.39/src/lte/test/lte-test-uplink-power-control.cc
--- ns-3.38/src/lte/test/lte-test-uplink-power-control.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/lte-test-uplink-power-control.cc	2023-07-05 14:23:48.021428000 -0700
@@ -279,7 +279,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -462,7 +462,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
@@ -626,7 +626,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate a data radio bearer
-    enum EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
+    EpsBearer::Qci q = EpsBearer::GBR_CONV_VOICE;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/test-lte-antenna.cc ns-3.39/src/lte/test/test-lte-antenna.cc
--- ns-3.38/src/lte/test/test-lte-antenna.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/test-lte-antenna.cc	2023-07-05 14:23:48.025428000 -0700
@@ -174,7 +174,7 @@
     lteHelper->Attach(ueDevs, enbDevs.Get(0));
 
     // Activate the default EPS bearer
-    enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+    EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
     EpsBearer bearer(q);
     lteHelper->ActivateDataRadioBearer(ueDevs, bearer);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/test-lte-handover-delay.cc ns-3.39/src/lte/test/test-lte-handover-delay.cc
--- ns-3.38/src/lte/test/test-lte-handover-delay.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/test-lte-handover-delay.cc	2023-07-05 14:23:48.029428000 -0700
@@ -20,6 +20,7 @@
 
 #include <ns3/boolean.h>
 #include <ns3/callback.h>
+#include <ns3/cc-helper.h>
 #include <ns3/config.h>
 #include <ns3/data-rate.h>
 #include <ns3/internet-stack-helper.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/test-lte-handover-failure.cc ns-3.39/src/lte/test/test-lte-handover-failure.cc
--- ns-3.38/src/lte/test/test-lte-handover-failure.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/test-lte-handover-failure.cc	2023-07-05 14:23:48.029428000 -0700
@@ -110,7 +110,7 @@
           m_handoverJoiningTimeout(handoverJoiningTimeout),
           m_handoverLeavingTimeout(handoverLeavingTimeout),
           m_targeteNodeBPosition(targeteNodeBPosition),
-          m_hasHandoverFailureOccured(false)
+          m_hasHandoverFailureOccurred(false)
     {
     }
 
@@ -198,8 +198,8 @@
     uint8_t m_raResponseWindowSize; ///< window length for reception of RAR
     Time m_handoverJoiningTimeout;  ///< handover joining timeout duration at target eNodeB
     Time m_handoverLeavingTimeout;  ///< handover leaving timeout duration at source eNodeB
-    uint16_t m_targeteNodeBPosition;  ///< position of the target eNodeB
-    bool m_hasHandoverFailureOccured; ///< has handover failure occurred in simulation
+    uint16_t m_targeteNodeBPosition;   ///< position of the target eNodeB
+    bool m_hasHandoverFailureOccurred; ///< has handover failure occurred in simulation
 
 }; // end of class LteHandoverFailureTestCase
 
@@ -321,7 +321,7 @@
                                                    uint16_t targetCellId)
 {
     NS_LOG_FUNCTION(this << context << imsi << rnti << targetCellId);
-    m_hasHandoverFailureOccured = true;
+    m_hasHandoverFailureOccurred = true;
 }
 
 void
@@ -331,7 +331,7 @@
                                                       uint16_t targetCellId)
 {
     NS_LOG_FUNCTION(this << context << imsi << rnti << targetCellId);
-    m_hasHandoverFailureOccured = true;
+    m_hasHandoverFailureOccurred = true;
 }
 
 void
@@ -341,7 +341,7 @@
                                                    uint16_t targetCellId)
 {
     NS_LOG_FUNCTION(this << context << imsi << rnti << targetCellId);
-    m_hasHandoverFailureOccured = true;
+    m_hasHandoverFailureOccurred = true;
 }
 
 void
@@ -351,14 +351,14 @@
                                                    uint16_t targetCellId)
 {
     NS_LOG_FUNCTION(this << context << imsi << rnti << targetCellId);
-    m_hasHandoverFailureOccured = true;
+    m_hasHandoverFailureOccurred = true;
 }
 
 void
 LteHandoverFailureTestCase::DoTeardown()
 {
     NS_LOG_FUNCTION(this);
-    NS_TEST_ASSERT_MSG_EQ(m_hasHandoverFailureOccured, true, "Handover failure did not occur");
+    NS_TEST_ASSERT_MSG_EQ(m_hasHandoverFailureOccurred, true, "Handover failure did not occur");
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/test-lte-rrc.cc ns-3.39/src/lte/test/test-lte-rrc.cc
--- ns-3.38/src/lte/test/test-lte-rrc.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/test-lte-rrc.cc	2023-07-05 14:23:48.029428000 -0700
@@ -389,7 +389,7 @@
 
     for (uint32_t b = 0; b < m_nBearers; ++b)
     {
-        enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+        EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
         EpsBearer bearer(q);
         m_lteHelper->ActivateDataRadioBearer(ueDevice, bearer);
     }
@@ -784,13 +784,10 @@
 
     for (uint32_t useIdealRrc = 0; useIdealRrc <= 1; ++useIdealRrc)
     {
-        //         <----- all times in ms ----------------->
+        // <----- all times in ms ----------------->
 
-        //                                                     nUes      tConnBase delayDiscStart
-        //                                                     useIdealRrc
-        //                                                        nBearers       tConnIncrPerUe
-        //                                                        errorExpected
-        //                                                        admitRrcConnectionRequest
+        // nUes tConnBase delayDiscStart useIdealRrc nBearers tConnIncrPerUe errorExpected
+        // admitRrcConnectionRequest
         AddTestCase(
             new LteRrcConnectionEstablishmentTestCase(1, 0, 0, 0, 1, false, useIdealRrc, true),
             TestCase::EXTENSIVE);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/test-lte-x2-handover.cc ns-3.39/src/lte/test/test-lte-x2-handover.cc
--- ns-3.38/src/lte/test/test-lte-x2-handover.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/test-lte-x2-handover.cc	2023-07-05 14:23:48.029428000 -0700
@@ -427,7 +427,7 @@
             Ptr<NetDevice> ueDev = ueDevices.Get(u);
             for (uint32_t b = 0; b < m_nDedicatedBearers; ++b)
             {
-                enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+                EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
                 EpsBearer bearer(q);
                 m_lteHelper->ActivateDataRadioBearer(ueDev, bearer);
             }
@@ -708,48 +708,48 @@
     ue2bwd.sourceEnbDeviceIndex = 1;
     ue2bwd.targetEnbDeviceIndex = 0;
 
-    std::string hel0name("none");
-    std::list<HandoverEvent> hel0;
+    std::string handoverEventList0name("none");
+    std::list<HandoverEvent> handoverEventList0;
 
-    std::string hel1name("1 fwd");
-    const std::list<HandoverEvent> hel1{
+    std::string handoverEventList1name("1 fwd");
+    const std::list<HandoverEvent> handoverEventList1{
         ue1fwd,
     };
 
-    std::string hel2name("1 fwd & bwd");
-    const std::list<HandoverEvent> hel2{
+    std::string handoverEventList2name("1 fwd & bwd");
+    const std::list<HandoverEvent> handoverEventList2{
         ue1fwd,
         ue1bwd,
     };
 
-    std::string hel3name("1 fwd & bwd & fwd");
-    const std::list<HandoverEvent> hel3{
+    std::string handoverEventList3name("1 fwd & bwd & fwd");
+    const std::list<HandoverEvent> handoverEventList3{
         ue1fwd,
         ue1bwd,
         ue1fwdagain,
     };
 
-    std::string hel4name("1+2 fwd");
-    const std::list<HandoverEvent> hel4{
+    std::string handoverEventList4name("1+2 fwd");
+    const std::list<HandoverEvent> handoverEventList4{
         ue1fwd,
         ue2fwd,
     };
 
-    std::string hel5name("1+2 fwd & bwd");
-    const std::list<HandoverEvent> hel5{
+    std::string handoverEventList5name("1+2 fwd & bwd");
+    const std::list<HandoverEvent> handoverEventList5{
         ue1fwd,
         ue1bwd,
         ue2fwd,
         ue2bwd,
     };
 
-    // std::string hel6name("2 fwd");
-    // const std::list<HandoverEvent> hel6{
+    // std::string handoverEventList6name("2 fwd");
+    // const std::list<HandoverEvent> handoverEventList6{
     //     ue2fwd,
     // };
 
-    // std::string hel7name("2 fwd & bwd");
-    // const std::list<HandoverEvent> hel7{
+    // std::string handoverEventList7name("2 fwd & bwd");
+    // const std::list<HandoverEvent> handoverEventList7{
     //     ue2fwd,
     //     ue2bwd,
     // };
@@ -766,126 +766,326 @@
         for (int32_t useIdealRrc = 1; useIdealRrc >= 0; --useIdealRrc)
         {
             // nUes, nDBearers, helist, name, sched, admitHo, idealRrc
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 0, hel0, hel0name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 0, hel0, hel0name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 5, hel0, hel0name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 5, hel0, hel0name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 0, hel1, hel1name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 1, hel1, hel1name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 2, hel1, hel1name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 0, hel1, hel1name, *schedIt, false, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 1, hel1, hel1name, *schedIt, false, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 2, hel1, hel1name, *schedIt, false, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 0, hel1, hel1name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 1, hel1, hel1name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 2, hel1, hel1name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 0, hel1, hel1name, *schedIt, false, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 1, hel1, hel1name, *schedIt, false, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 2, hel1, hel1name, *schedIt, false, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 0, hel2, hel2name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 1, hel2, hel2name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 2, hel2, hel2name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 0, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 1, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(1, 2, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 0, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 1, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 2, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::QUICK);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 0, hel4, hel4name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 1, hel4, hel4name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 2, hel4, hel4name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 0, hel5, hel5name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 1, hel5, hel5name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(2, 2, hel5, hel5name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 0, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 1, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 2, hel3, hel3name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 0, hel4, hel4name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 1, hel4, hel4name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 2, hel4, hel4name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 0, hel5, hel5name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 1, hel5, hel5name, *schedIt, true, useIdealRrc),
-                TestCase::EXTENSIVE);
-            AddTestCase(
-                new LteX2HandoverTestCase(3, 2, hel5, hel5name, *schedIt, true, useIdealRrc),
-                TestCase::QUICK);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  0,
+                                                  handoverEventList0,
+                                                  handoverEventList0name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  0,
+                                                  handoverEventList0,
+                                                  handoverEventList0name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  5,
+                                                  handoverEventList0,
+                                                  handoverEventList0name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  5,
+                                                  handoverEventList0,
+                                                  handoverEventList0name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  0,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  1,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  2,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  0,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  false,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  1,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  false,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  2,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  false,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  0,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  1,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  2,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  0,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  false,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  1,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  false,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  2,
+                                                  handoverEventList1,
+                                                  handoverEventList1name,
+                                                  *schedIt,
+                                                  false,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  0,
+                                                  handoverEventList2,
+                                                  handoverEventList2name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  1,
+                                                  handoverEventList2,
+                                                  handoverEventList2name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  2,
+                                                  handoverEventList2,
+                                                  handoverEventList2name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  0,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  1,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(1,
+                                                  2,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  0,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  1,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  2,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::QUICK);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  0,
+                                                  handoverEventList4,
+                                                  handoverEventList4name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  1,
+                                                  handoverEventList4,
+                                                  handoverEventList4name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  2,
+                                                  handoverEventList4,
+                                                  handoverEventList4name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  0,
+                                                  handoverEventList5,
+                                                  handoverEventList5name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  1,
+                                                  handoverEventList5,
+                                                  handoverEventList5name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(2,
+                                                  2,
+                                                  handoverEventList5,
+                                                  handoverEventList5name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  0,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  1,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  2,
+                                                  handoverEventList3,
+                                                  handoverEventList3name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  0,
+                                                  handoverEventList4,
+                                                  handoverEventList4name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  1,
+                                                  handoverEventList4,
+                                                  handoverEventList4name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  2,
+                                                  handoverEventList4,
+                                                  handoverEventList4name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  0,
+                                                  handoverEventList5,
+                                                  handoverEventList5name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  1,
+                                                  handoverEventList5,
+                                                  handoverEventList5name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::EXTENSIVE);
+            AddTestCase(new LteX2HandoverTestCase(3,
+                                                  2,
+                                                  handoverEventList5,
+                                                  handoverEventList5name,
+                                                  *schedIt,
+                                                  true,
+                                                  useIdealRrc),
+                        TestCase::QUICK);
         }
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/lte/test/test-lte-x2-handover-measures.cc ns-3.39/src/lte/test/test-lte-x2-handover-measures.cc
--- ns-3.38/src/lte/test/test-lte-x2-handover-measures.cc	2023-03-16 20:54:23.784869000 -0700
+++ ns-3.39/src/lte/test/test-lte-x2-handover-measures.cc	2023-07-05 14:23:48.029428000 -0700
@@ -527,7 +527,7 @@
             Ptr<NetDevice> ueDev = ueDevices.Get(u);
             for (uint32_t b = 0; b < m_nDedicatedBearers; ++b)
             {
-                enum EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
+                EpsBearer::Qci q = EpsBearer::NGBR_VIDEO_TCP_DEFAULT;
                 EpsBearer bearer(q);
                 m_lteHelper->ActivateDataRadioBearer(ueDev, bearer);
             }
@@ -565,13 +565,12 @@
                                 ueDevices.Get(checkPointEventIt->ueDeviceIndex),
                                 enbDevices.Get(checkPointEventIt->enbDeviceIndex));
 
-            Time saveStatsTime = checkPointTime;
-            Simulator::Schedule(saveStatsTime,
+            Simulator::Schedule(checkPointTime,
                                 &LteX2HandoverMeasuresTestCase::SaveStats,
                                 this,
                                 checkPointEventIt->ueDeviceIndex);
 
-            Time checkStats = saveStatsTime + m_statsDuration;
+            Time checkStats = checkPointTime + m_statsDuration;
             Simulator::Schedule(checkStats,
                                 &LteX2HandoverMeasuresTestCase::CheckStats,
                                 this,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/dot11s/hwmp-protocol.h ns-3.39/src/mesh/model/dot11s/hwmp-protocol.h
--- ns-3.38/src/mesh/model/dot11s/hwmp-protocol.h	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/dot11s/hwmp-protocol.h	2023-07-05 14:23:48.033428200 -0700
@@ -136,12 +136,12 @@
     bool Install(Ptr<MeshPointDevice> mp);
     /**
      * Peer link status function
-     * \param meshPontAddress The MAC address of the mesh point
+     * \param meshPointAddress The MAC address of the mesh point
      * \param peerAddress The MAC address of the peer
      * \param interface The interface number
      * \param status The status of the peer link
      */
-    void PeerLinkStatus(Mac48Address meshPontAddress,
+    void PeerLinkStatus(Mac48Address meshPointAddress,
                         Mac48Address peerAddress,
                         uint32_t interface,
                         bool status);
@@ -355,9 +355,9 @@
     /// Route discovery time:
     TracedCallback<Time> m_routeDiscoveryTimeCallback;
     /// RouteChangeTracedCallback typedef
-    typedef TracedCallback<struct RouteChange> RouteChangeTracedCallback;
+    typedef TracedCallback<RouteChange> RouteChangeTracedCallback;
     /// Route change trace source
-    TracedCallback<struct RouteChange> m_routeChangeTraceSource;
+    TracedCallback<RouteChange> m_routeChangeTraceSource;
 
     // /\name Methods related to Queue/Dequeue procedures
     ///@{
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/dot11s/ie-dot11s-id.cc ns-3.39/src/mesh/model/dot11s/ie-dot11s-id.cc
--- ns-3.38/src/mesh/model/dot11s/ie-dot11s-id.cc	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/dot11s/ie-dot11s-id.cc	2023-07-05 14:23:48.033428200 -0700
@@ -67,21 +67,13 @@
     {
         i++;
     }
-    if (m_meshId[i] != o.m_meshId[i])
-    {
-        return false;
-    }
-    return true;
+    return m_meshId[i] == o.m_meshId[i];
 }
 
 bool
 IeMeshId::IsBroadcast() const
 {
-    if (m_meshId[0] == 0)
-    {
-        return true;
-    }
-    return false;
+    return m_meshId[0] == 0;
 }
 
 char*
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/dot11s/peer-link-frame.cc ns-3.39/src/mesh/model/dot11s/peer-link-frame.cc
--- ns-3.38/src/mesh/model/dot11s/peer-link-frame.cc	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/dot11s/peer-link-frame.cc	2023-07-05 14:23:48.033428200 -0700
@@ -34,6 +34,7 @@
 PeerLinkOpenStart::PeerLinkOpenStart()
     : m_capability(0),
       m_rates(SupportedRates()),
+      m_extendedRates(ExtendedSupportedRatesIE()),
       m_meshId(),
       m_config(IeConfiguration())
 {
@@ -44,6 +45,7 @@
 {
     m_capability = fields.capability;
     m_rates = fields.rates;
+    m_extendedRates = fields.extendedRates;
     m_meshId = fields.meshId;
     m_config = fields.config;
 }
@@ -55,6 +57,7 @@
     /// \todo protocol version:
     retval.capability = m_capability;
     retval.rates = m_rates;
+    retval.extendedRates = m_extendedRates;
     retval.meshId = m_meshId;
     retval.config = m_config;
 
@@ -93,9 +96,9 @@
     uint32_t size = 0; // Peering protocol
     size += 2;         // capability
     size += m_rates.GetSerializedSize();
-    if (m_rates.GetNRates() > 8)
+    if (m_extendedRates)
     {
-        size += m_rates.extended->GetSerializedSize();
+        size += m_extendedRates->GetSerializedSize();
     }
     size += m_meshId.GetInformationFieldSize() + 2;
     size += m_config.GetInformationFieldSize() + 2;
@@ -109,9 +112,9 @@
 
     i.WriteHtolsbU16(m_capability);
     i = m_rates.Serialize(i);
-    if (m_rates.GetNRates() > 8)
+    if (m_extendedRates)
     {
-        i = m_rates.extended->Serialize(i);
+        i = m_extendedRates->Serialize(i);
     }
     i = m_meshId.Serialize(i);
     i = m_config.Serialize(i);
@@ -124,7 +127,13 @@
 
     m_capability = i.ReadLsbtohU16();
     i = m_rates.Deserialize(i);
-    i = WifiInformationElement::DeserializeIfPresent(m_rates.extended, i);
+    m_extendedRates.emplace();
+    auto tmp = m_extendedRates->DeserializeIfPresent(i);
+    if (tmp.GetDistanceFrom(i) == 0)
+    {
+        m_extendedRates.reset();
+    }
+    i = tmp;
     uint8_t id = i.ReadU8();
     uint8_t length = i.ReadU8();
     m_meshId.DeserializeInformationField(i, length);
@@ -300,9 +309,9 @@
     size += 2;         // capability
     size += 2;         // AID of remote peer
     size += m_rates.GetSerializedSize();
-    if (m_rates.GetNRates() > 8)
+    if (m_extendedRates)
     {
-        size += m_rates.extended->GetSerializedSize();
+        size += m_extendedRates->GetSerializedSize();
     }
     size += m_config.GetInformationFieldSize() + 2;
     return size;
@@ -316,9 +325,9 @@
     i.WriteHtolsbU16(m_capability);
     i.WriteHtolsbU16(m_aid);
     i = m_rates.Serialize(i);
-    if (m_rates.GetNRates() > 8)
+    if (m_extendedRates)
     {
-        i = m_rates.extended->Serialize(i);
+        i = m_extendedRates->Serialize(i);
     }
     i = m_config.Serialize(i);
 }
@@ -331,7 +340,13 @@
     m_capability = i.ReadLsbtohU16();
     m_aid = i.ReadLsbtohU16();
     i = m_rates.Deserialize(i);
-    i = WifiInformationElement::DeserializeIfPresent(m_rates.extended, i);
+    m_extendedRates.emplace();
+    auto tmp = m_extendedRates->DeserializeIfPresent(i);
+    if (tmp.GetDistanceFrom(i) == 0)
+    {
+        m_extendedRates.reset();
+    }
+    i = tmp;
     uint8_t id = i.ReadU8();
     uint8_t length = i.ReadU8();
     m_config.DeserializeInformationField(i, length);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/dot11s/peer-link-frame.h ns-3.39/src/mesh/model/dot11s/peer-link-frame.h
--- ns-3.38/src/mesh/model/dot11s/peer-link-frame.h	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/dot11s/peer-link-frame.h	2023-07-05 14:23:48.033428200 -0700
@@ -27,6 +27,8 @@
 #include "ns3/header.h"
 #include "ns3/supported-rates.h"
 
+#include <optional>
+
 namespace ns3
 {
 namespace dot11s
@@ -57,8 +59,9 @@
         IePeeringProtocol protocol; ///< Peering protocol version - 3 octets
         uint16_t capability;        ///< open and confirm
         SupportedRates rates;       ///< open and confirm
-        IeMeshId meshId;            ///< open and close
-        IeConfiguration config;     ///< open and confirm
+        std::optional<ExtendedSupportedRatesIE> extendedRates; ///< open and confirm
+        IeMeshId meshId;                                       ///< open and close
+        IeConfiguration config;                                ///< open and confirm
     };
 
     /**
@@ -85,10 +88,11 @@
     uint32_t Deserialize(Buffer::Iterator start) override;
 
   private:
-    uint16_t m_capability;    ///< capability
-    SupportedRates m_rates;   ///< rates
-    IeMeshId m_meshId;        ///< mesh ID
-    IeConfiguration m_config; ///< config
+    uint16_t m_capability;                                   ///< capability
+    SupportedRates m_rates;                                  ///< rates
+    std::optional<ExtendedSupportedRatesIE> m_extendedRates; ///< extended rates
+    IeMeshId m_meshId;                                       ///< mesh ID
+    IeConfiguration m_config;                                ///< config
 
     /**
      * equality operator
@@ -190,7 +194,8 @@
         uint16_t capability;        ///< open and confirm
         uint16_t aid;               ///< confirm only
         SupportedRates rates;       ///< open and confirm
-        IeConfiguration config;     ///< open and confirm
+        std::optional<ExtendedSupportedRatesIE> extendedRates; ///< open and confirm
+        IeConfiguration config;                                ///< open and confirm
     };
 
     /**
@@ -217,10 +222,11 @@
     uint32_t Deserialize(Buffer::Iterator start) override;
 
   private:
-    uint16_t m_capability;    ///< capability
-    uint16_t m_aid;           ///< aid
-    SupportedRates m_rates;   ///< rates
-    IeConfiguration m_config; ///< config
+    uint16_t m_capability;                                   ///< capability
+    uint16_t m_aid;                                          ///< aid
+    SupportedRates m_rates;                                  ///< rates
+    std::optional<ExtendedSupportedRatesIE> m_extendedRates; ///< extended rates
+    IeConfiguration m_config;                                ///< config
 
     /**
      * equality operator
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/dot11s/peer-management-protocol.h ns-3.39/src/mesh/model/dot11s/peer-management-protocol.h
--- ns-3.38/src/mesh/model/dot11s/peer-management-protocol.h	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/dot11s/peer-management-protocol.h	2023-07-05 14:23:48.037428100 -0700
@@ -300,13 +300,13 @@
      * \brief Indicates changes in peer links
      * \param interface the interface
      * \param peerAddress the peer address
-     * \param peerMeshPointAddres the peer mesh point address
+     * \param peerMeshPointAddress the peer mesh point address
      * \param ostate old state
      * \param nstate new state
      */
     void PeerLinkStatus(uint32_t interface,
                         Mac48Address peerAddress,
-                        Mac48Address peerMeshPointAddres,
+                        Mac48Address peerMeshPointAddress,
                         PeerLink::PeerState ostate,
                         PeerLink::PeerState nstate);
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/dot11s/peer-management-protocol-mac.cc ns-3.39/src/mesh/model/dot11s/peer-management-protocol-mac.cc
--- ns-3.38/src/mesh/model/dot11s/peer-management-protocol-mac.cc	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/dot11s/peer-management-protocol-mac.cc	2023-07-05 14:23:48.037428100 -0700
@@ -139,7 +139,8 @@
                 m_stats.brokenMgt++;
                 return false;
             }
-            if (!(m_parent->CheckSupportedRates(fields.rates)))
+            if (!(m_parent->CheckSupportedRates(
+                    AllSupportedRates{fields.rates, fields.extendedRates})))
             {
                 NS_LOG_DEBUG("PEER_LINK_OPEN:  configuration mismatch");
                 m_protocol->ConfigurationMismatch(m_ifIndex, peerAddress);
@@ -156,7 +157,8 @@
             PeerLinkConfirmStart peerFrame;
             packet->RemoveHeader(peerFrame);
             fields = peerFrame.GetFields();
-            if (!(m_parent->CheckSupportedRates(fields.rates)))
+            if (!(m_parent->CheckSupportedRates(
+                    AllSupportedRates{fields.rates, fields.extendedRates})))
             {
                 NS_LOG_DEBUG("PEER_LINK_CONFIRM:  configuration mismatch");
                 m_protocol->ConfigurationMismatch(m_ifIndex, peerAddress);
@@ -290,7 +292,9 @@
     if (peerElement.SubtypeIsOpen())
     {
         PeerLinkOpenStart::PlinkOpenStartFields fields;
-        fields.rates = m_parent->GetSupportedRates();
+        auto allSupportedRates = m_parent->GetSupportedRates();
+        fields.rates = allSupportedRates.rates;
+        fields.extendedRates = allSupportedRates.extendedRates;
         fields.capability = 0;
         fields.meshId = *(m_protocol->GetMeshId());
         fields.config = meshConfig;
@@ -307,7 +311,9 @@
     if (peerElement.SubtypeIsConfirm())
     {
         PeerLinkConfirmStart::PlinkConfirmStartFields fields;
-        fields.rates = m_parent->GetSupportedRates();
+        auto allSupportedRates = m_parent->GetSupportedRates();
+        fields.rates = allSupportedRates.rates;
+        fields.extendedRates = allSupportedRates.extendedRates;
         fields.capability = 0;
         fields.config = meshConfig;
         PeerLinkConfirmStart plinkConfirm;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/dot11s/peer-management-protocol-mac.h ns-3.39/src/mesh/model/dot11s/peer-management-protocol-mac.h
--- ns-3.38/src/mesh/model/dot11s/peer-management-protocol-mac.h	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/dot11s/peer-management-protocol-mac.h	2023-07-05 14:23:48.037428100 -0700
@@ -186,7 +186,7 @@
      * Set peer manager protocol function
      * \param protocol the peer manager protocol
      */
-    void SetPeerManagerProtcol(Ptr<PeerManagementProtocol> protocol);
+    void SetPeerManagerProtocol(Ptr<PeerManagementProtocol> protocol);
     /**
      * Send peer link management frame function
      * \param peerAddress the peer MAC address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/mesh-information-element-vector.cc ns-3.39/src/mesh/model/mesh-information-element-vector.cc
--- ns-3.38/src/mesh/model/mesh-information-element-vector.cc	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/mesh-information-element-vector.cc	2023-07-05 14:23:48.037428100 -0700
@@ -41,18 +41,24 @@
 NS_OBJECT_ENSURE_REGISTERED(MeshInformationElementVector);
 
 MeshInformationElementVector::MeshInformationElementVector()
+    : m_maxSize(1500)
 {
 }
 
 MeshInformationElementVector::~MeshInformationElementVector()
 {
+    for (IE_VECTOR::iterator i = m_elements.begin(); i != m_elements.end(); i++)
+    {
+        *i = nullptr;
+    }
+    m_elements.clear();
 }
 
 TypeId
 MeshInformationElementVector::GetTypeId()
 {
     static TypeId tid = TypeId("ns3::MeshInformationElementVector")
-                            .SetParent<WifiInformationElementVector>()
+                            .SetParent<Header>()
                             .SetGroupName("Mesh")
                             .AddConstructor<MeshInformationElementVector>();
     return tid;
@@ -65,6 +71,44 @@
 }
 
 uint32_t
+MeshInformationElementVector::GetSerializedSize() const
+{
+    return GetSize();
+}
+
+void
+MeshInformationElementVector::Serialize(Buffer::Iterator start) const
+{
+    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
+    {
+        start = (*i)->Serialize(start);
+    }
+}
+
+uint32_t
+MeshInformationElementVector::Deserialize(Buffer::Iterator start)
+{
+    NS_FATAL_ERROR("This variant should not be called on a variable-sized header");
+    return 0;
+}
+
+uint32_t
+MeshInformationElementVector::Deserialize(Buffer::Iterator start, Buffer::Iterator end)
+{
+    uint32_t size = start.GetDistanceFrom(end);
+    uint32_t remaining = size;
+    while (remaining > 0)
+    {
+        uint32_t deserialized = DeserializeSingleIe(start);
+        start.Next(deserialized);
+        NS_ASSERT(deserialized <= remaining);
+        remaining -= deserialized;
+    }
+    NS_ASSERT_MSG(remaining == 0, "Error in deserialization");
+    return size;
+}
+
+uint32_t
 MeshInformationElementVector::DeserializeSingleIe(Buffer::Iterator start)
 {
     Buffer::Iterator i = start;
@@ -105,7 +149,8 @@
         newElement = Create<dot11s::IePeeringProtocol>();
         break;
     default:
-        return WifiInformationElementVector::DeserializeSingleIe(i);
+        NS_FATAL_ERROR("Information element " << +id << " is not implemented");
+        return 0;
     }
     if (GetSize() + length > m_maxSize)
     {
@@ -116,4 +161,91 @@
     return i.GetDistanceFrom(start);
 }
 
+void
+MeshInformationElementVector::Print(std::ostream& os) const
+{
+    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
+    {
+        os << "(";
+        (*i)->Print(os);
+        os << ")";
+    }
+}
+
+MeshInformationElementVector::Iterator
+MeshInformationElementVector::Begin()
+{
+    return m_elements.begin();
+}
+
+MeshInformationElementVector::Iterator
+MeshInformationElementVector::End()
+{
+    return m_elements.end();
+}
+
+bool
+MeshInformationElementVector::AddInformationElement(Ptr<WifiInformationElement> element)
+{
+    if (element->GetSerializedSize() + GetSize() > m_maxSize)
+    {
+        return false;
+    }
+    m_elements.push_back(element);
+    return true;
+}
+
+Ptr<WifiInformationElement>
+MeshInformationElementVector::FindFirst(WifiInformationElementId id) const
+{
+    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
+    {
+        if ((*i)->ElementId() == id)
+        {
+            return (*i);
+        }
+    }
+    return nullptr;
+}
+
+uint32_t
+MeshInformationElementVector::GetSize() const
+{
+    uint32_t size = 0;
+    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
+    {
+        size += (*i)->GetSerializedSize();
+    }
+    return size;
+}
+
+bool
+MeshInformationElementVector::operator==(const MeshInformationElementVector& a) const
+{
+    if (m_elements.size() != a.m_elements.size())
+    {
+        NS_ASSERT(false);
+        return false;
+    }
+    // In principle we could bypass some of the faffing about (and speed
+    // the comparison) by simply serialising each IE vector into a
+    // buffer and memcmp'ing the two.
+    //
+    // I'm leaving it like this, however, so that there is the option of
+    // having individual Information Elements implement slightly more
+    // flexible equality operators.
+    MeshInformationElementVector::IE_VECTOR::const_iterator j = a.m_elements.begin();
+    for (MeshInformationElementVector::IE_VECTOR::const_iterator i = m_elements.begin();
+         i != m_elements.end();
+         i++, j++)
+    {
+        if (!(*(*i) == *(*j)))
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/mesh-information-element-vector.h ns-3.39/src/mesh/model/mesh-information-element-vector.h
--- ns-3.38/src/mesh/model/mesh-information-element-vector.h	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/mesh-information-element-vector.h	2023-07-05 14:23:48.037428100 -0700
@@ -21,7 +21,7 @@
 #ifndef MESH_INFORMATION_ELEMENT_VECTOR_H
 #define MESH_INFORMATION_ELEMENT_VECTOR_H
 
-#include "ns3/wifi-information-element-vector.h"
+#include "ns3/wifi-information-element.h"
 
 namespace ns3
 {
@@ -30,7 +30,17 @@
     ((WifiInformationElementId)74) // to be removed (Protocol ID should be part of the Mesh Peering
                                    // Management IE)
 
-class MeshInformationElementVector : public WifiInformationElementVector
+/**
+ * \brief Information element vector
+ * \ingroup wifi
+ *
+ * Implements a vector of WifiInformationElements.
+ * Information elements typically come in groups, and the
+ * WifiInformationElementVector class provides a representation of a
+ * series of IEs, and the facility for serialisation to and
+ * deserialisation from the over-the-air format.
+ */
+class MeshInformationElementVector : public Header
 {
   public:
     MeshInformationElementVector();
@@ -41,8 +51,88 @@
      * \return the object TypeId
      */
     static TypeId GetTypeId();
+
     TypeId GetInstanceTypeId() const override;
-    uint32_t DeserializeSingleIe(Buffer::Iterator start) override;
+    uint32_t GetSerializedSize() const override;
+    void Serialize(Buffer::Iterator start) const override;
+    /**
+     * \attention This variant should not be used but is implemented due to
+     * backward compatibility reasons
+     *
+     * \param start buffer location to start deserializing from
+     * \return number of bytes deserialized
+     */
+    uint32_t Deserialize(Buffer::Iterator start) override;
+    /**
+     * Deserialize a number of WifiInformationElements
+     *
+     * The size of this Header should equal start.GetDistanceFrom (end).
+     *
+     * \param start starting buffer location
+     * \param end ending buffer location
+     * \return number of bytes deserialized
+     */
+    uint32_t Deserialize(Buffer::Iterator start, Buffer::Iterator end) override;
+    void Print(std::ostream& os) const override;
+
+    /**
+     * \brief Needed when you try to deserialize a lonely IE inside other header
+     *
+     * \param start is the start of the buffer
+     *
+     * \return deserialized bytes
+     */
+    uint32_t DeserializeSingleIe(Buffer::Iterator start);
+
+    /// As soon as this is a vector, we define an Iterator
+    typedef std::vector<Ptr<WifiInformationElement>>::iterator Iterator;
+    /**
+     * Returns Begin of the vector
+     * \returns the begin of the vector
+     */
+    Iterator Begin();
+    /**
+     * Returns End of the vector
+     * \returns the end of the vector
+     */
+    Iterator End();
+    /**
+     * add an IE, if maxSize has exceeded, returns false
+     *
+     * \param element wifi information element to add
+     * \returns true is added
+     */
+    bool AddInformationElement(Ptr<WifiInformationElement> element);
+    /**
+     * vector of pointers to information elements is the body of IeVector
+     *
+     * \param id the element id to find
+     * \returns the information element
+     */
+    Ptr<WifiInformationElement> FindFirst(WifiInformationElementId id) const;
+
+    /**
+     * Check if the given WifiInformationElementVectors are equivalent.
+     *
+     * \param a another WifiInformationElementVector
+     *
+     * \return true if the given WifiInformationElementVectors are equivalent,
+     *         false otherwise
+     */
+    virtual bool operator==(const MeshInformationElementVector& a) const;
+
+  protected:
+    /**
+     * typedef for a vector of WifiInformationElements.
+     */
+    typedef std::vector<Ptr<WifiInformationElement>> IE_VECTOR;
+    /**
+     * Current number of bytes
+     * \returns the number of bytes
+     */
+    uint32_t GetSize() const;
+    IE_VECTOR m_elements; //!< Information element vector
+    uint16_t m_maxSize;   //!< Size in bytes (actually, max packet length)
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/mesh-point-device.cc ns-3.39/src/mesh/model/mesh-point-device.cc
--- ns-3.38/src/mesh/model/mesh-point-device.cc	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/mesh-point-device.cc	2023-07-05 14:23:48.037428100 -0700
@@ -189,7 +189,7 @@
                                                   packet,
                                                   protocol,
                                                   MakeCallback(&MeshPointDevice::DoSend, this));
-    if (result == false)
+    if (!result)
     {
         NS_LOG_DEBUG("Request to forward packet " << packet << " to destination " << dst
                                                   << " failed; dropping packet");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/mesh-wifi-beacon.cc ns-3.39/src/mesh/model/mesh-wifi-beacon.cc
--- ns-3.38/src/mesh/model/mesh-wifi-beacon.cc	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/mesh-wifi-beacon.cc	2023-07-05 14:23:48.037428100 -0700
@@ -25,10 +25,11 @@
 namespace ns3
 {
 
-MeshWifiBeacon::MeshWifiBeacon(Ssid ssid, SupportedRates rates, uint64_t us)
+MeshWifiBeacon::MeshWifiBeacon(Ssid ssid, AllSupportedRates rates, uint64_t us)
 {
-    m_header.SetSsid(ssid);
-    m_header.SetSupportedRates(rates);
+    m_header.Get<Ssid>() = ssid;
+    m_header.Get<SupportedRates>() = rates.rates;
+    m_header.Get<ExtendedSupportedRatesIE>() = rates.extendedRates;
     m_header.SetBeaconIntervalUs(us);
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/mesh-wifi-beacon.h ns-3.39/src/mesh/model/mesh-wifi-beacon.h
--- ns-3.38/src/mesh/model/mesh-wifi-beacon.h	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/mesh-wifi-beacon.h	2023-07-05 14:23:48.037428100 -0700
@@ -47,7 +47,7 @@
      * \param rates is a set of supported rates
      * \param us beacon interval in microseconds
      */
-    MeshWifiBeacon(Ssid ssid, SupportedRates rates, uint64_t us);
+    MeshWifiBeacon(Ssid ssid, AllSupportedRates rates, uint64_t us);
 
     /**
      * Read standard Wifi beacon header
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/mesh-wifi-interface-mac.cc ns-3.39/src/mesh/model/mesh-wifi-interface-mac.cc
--- ns-3.38/src/mesh/model/mesh-wifi-interface-mac.cc	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/mesh-wifi-interface-mac.cc	2023-07-05 14:23:48.037428100 -0700
@@ -317,12 +317,12 @@
     }
 }
 
-SupportedRates
+AllSupportedRates
 MeshWifiInterfaceMac::GetSupportedRates() const
 {
     // set the set of supported rates and make sure that we indicate
     // the Basic Rate set in this set of supported rates.
-    SupportedRates rates;
+    AllSupportedRates rates;
     for (const auto& mode : GetWifiPhy()->GetModeList())
     {
         uint16_t gi = ConvertGuardIntervalToNanoSeconds(mode, GetWifiPhy()->GetDevice());
@@ -339,7 +339,7 @@
 }
 
 bool
-MeshWifiInterfaceMac::CheckSupportedRates(SupportedRates rates) const
+MeshWifiInterfaceMac::CheckSupportedRates(AllSupportedRates rates) const
 {
     for (uint32_t i = 0; i < GetWifiRemoteStationManager()->GetNBasicModes(); i++)
     {
@@ -459,9 +459,11 @@
                                              << " microseconds");
 
         // update supported rates
-        if (beacon_hdr.GetSsid().IsEqual(GetSsid()))
+        if (beacon_hdr.Get<Ssid>()->IsEqual(GetSsid()))
         {
-            SupportedRates rates = beacon_hdr.GetSupportedRates();
+            NS_ASSERT(beacon_hdr.Get<SupportedRates>());
+            auto rates = AllSupportedRates{*beacon_hdr.Get<SupportedRates>(),
+                                           beacon_hdr.Get<ExtendedSupportedRatesIE>()};
 
             for (const auto& mode : GetWifiPhy()->GetModeList())
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/model/mesh-wifi-interface-mac.h ns-3.39/src/mesh/model/mesh-wifi-interface-mac.h
--- ns-3.38/src/mesh/model/mesh-wifi-interface-mac.h	2023-03-16 20:54:23.788869100 -0700
+++ ns-3.39/src/mesh/model/mesh-wifi-interface-mac.h	2023-07-05 14:23:48.037428100 -0700
@@ -163,13 +163,13 @@
      * \param rates Rates.
      * \return true if rates are supported
      */
-    bool CheckSupportedRates(SupportedRates rates) const;
+    bool CheckSupportedRates(AllSupportedRates rates) const;
 
     /**
      * Get supported rates.
      * \return list of supported bitrates
      */
-    SupportedRates GetSupportedRates() const;
+    AllSupportedRates GetSupportedRates() const;
 
     /// \name Metric Calculation routines:
     ///@{
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mesh/test/examples-to-run.py ns-3.39/src/mesh/test/examples-to-run.py
--- ns-3.38/src/mesh/test/examples-to-run.py	2023-03-16 20:54:23.792869000 -0700
+++ ns-3.39/src/mesh/test/examples-to-run.py	2023-07-05 14:23:48.041428000 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mobility/examples/reference-point-group-mobility-example.cc ns-3.39/src/mobility/examples/reference-point-group-mobility-example.cc
--- ns-3.38/src/mobility/examples/reference-point-group-mobility-example.cc	2023-03-16 20:54:23.792869000 -0700
+++ ns-3.39/src/mobility/examples/reference-point-group-mobility-example.cc	2023-07-05 14:23:48.045428000 -0700
@@ -16,7 +16,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * Author: Michele Polese <michele.polese@gmail.com>
- * Heavily edited by Tom Henderson (to reuse HierachicalMobilityModel)
+ * Heavily edited by Tom Henderson (to reuse HierarchicalMobilityModel)
  */
 
 /**
@@ -140,7 +140,7 @@
     waypointMm->AddWaypoint(Waypoint(Seconds(700), Vector(90, 10, 0)));
     waypointMm->AddWaypoint(Waypoint(Seconds(800), Vector(10, 10, 0)));
 
-    // Each HierachicalMobilityModel contains the above model as the Parent,
+    // Each HierarchicalMobilityModel contains the above model as the Parent,
     // and a user defined model as the Child.  Two MobilityModel objects are
     // instantiated per node (one hierarchical, and one child model), and
     // a single parent model is reused across all nodes.
@@ -150,7 +150,7 @@
     // configuration.
 
     int64_t streamIndex = 1;
-    if (useHelper == false)
+    if (!useHelper)
     {
         // Assign random variable stream numbers on the parent and each child
         streamIndex += waypointMm->AssignStreams(streamIndex);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mobility/helper/ns2-mobility-helper.cc ns-3.39/src/mobility/helper/ns2-mobility-helper.cc
--- ns-3.38/src/mobility/helper/ns2-mobility-helper.cc	2023-03-16 20:54:23.792869000 -0700
+++ ns-3.39/src/mobility/helper/ns2-mobility-helper.cc	2023-07-05 14:23:48.045428000 -0700
@@ -622,8 +622,7 @@
     }
     else if (IsNumber(str))
     {
-        std::string s2 = str;
-        std::istringstream s(s2);
+        std::istringstream s(str);
         s >> ret;
         return true;
     }
@@ -651,15 +650,8 @@
 
     nodeId = str.substr(startNodeId + 1, endNodeId - (startNodeId + 1)); // set node id
 
-    //   is number              is integer                                       is not negative
-    if (IsNumber(nodeId) && (nodeId.find_first_of('.') == std::string::npos) && (nodeId[0] != '-'))
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    //     is number           is integer                                        is not negative
+    return IsNumber(nodeId) && nodeId.find_first_of('.') == std::string::npos && nodeId[0] != '-';
 }
 
 std::string
@@ -708,13 +700,10 @@
     {
     case 4: // line like $node_(0) set X_ 11
         return pr.svals[0];
-        break;
     case 7: // line like $ns_ at 4 "$node_(0) set X_ 28"
         return pr.svals[3];
-        break;
     case 8: // line like $ns_ at 1 "$node_(0) setdest 2 3 4"
         return pr.svals[3];
-        break;
     default:
         return "";
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mobility/model/position-allocator.cc ns-3.39/src/mobility/model/position-allocator.cc
--- ns-3.38/src/mobility/model/position-allocator.cc	2023-03-16 20:54:23.792869000 -0700
+++ ns-3.39/src/mobility/model/position-allocator.cc	2023-07-05 14:23:48.045428000 -0700
@@ -267,7 +267,7 @@
     return m_n;
 }
 
-enum GridPositionAllocator::LayoutType
+GridPositionAllocator::LayoutType
 GridPositionAllocator::GetLayoutType() const
 {
     return m_layoutType;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mobility/test/examples-to-run.py ns-3.39/src/mobility/test/examples-to-run.py
--- ns-3.38/src/mobility/test/examples-to-run.py	2023-03-16 20:54:23.792869000 -0700
+++ ns-3.39/src/mobility/test/examples-to-run.py	2023-07-05 14:23:48.045428000 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/examples/mpi-test-fixtures.h ns-3.39/src/mpi/examples/mpi-test-fixtures.h
--- ns-3.38/src/mpi/examples/mpi-test-fixtures.h	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/examples/mpi-test-fixtures.h	2023-07-05 14:23:48.049428200 -0700
@@ -17,6 +17,9 @@
  * Author: Steven Smith <smith84@llnl.gov>
  */
 
+#ifndef MPI_TEST_FIXTURES_H
+#define MPI_TEST_FIXTURES_H
+
 #include <iomanip>
 #include <ios>
 #include <sstream>
@@ -156,3 +159,5 @@
 };
 
 } // namespace ns3
+
+#endif // MPI_TEST_FIXTURES_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/examples/simple-distributed.cc ns-3.39/src/mpi/examples/simple-distributed.cc
--- ns-3.38/src/mpi/examples/simple-distributed.cc	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/examples/simple-distributed.cc	2023-07-05 14:23:48.049428200 -0700
@@ -226,7 +226,7 @@
         Ipv4GlobalRoutingHelper::PopulateRoutingTables();
     }
 
-    if (tracing == true)
+    if (tracing)
     {
         if (systemId == 0)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/examples/simple-distributed-empty-node.cc ns-3.39/src/mpi/examples/simple-distributed-empty-node.cc
--- ns-3.38/src/mpi/examples/simple-distributed-empty-node.cc	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/examples/simple-distributed-empty-node.cc	2023-07-05 14:23:48.049428200 -0700
@@ -253,7 +253,7 @@
         Ipv4GlobalRoutingHelper::PopulateRoutingTables();
     }
 
-    if (tracing == true)
+    if (tracing)
     {
         if (systemId == 0)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/examples/simple-distributed-mpi-comm.cc ns-3.39/src/mpi/examples/simple-distributed-mpi-comm.cc
--- ns-3.38/src/mpi/examples/simple-distributed-mpi-comm.cc	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/examples/simple-distributed-mpi-comm.cc	2023-07-05 14:23:48.049428200 -0700
@@ -436,7 +436,7 @@
         Ipv4GlobalRoutingHelper::PopulateRoutingTables();
     }
 
-    if (tracing == true)
+    if (tracing)
     {
         if (systemId == 0)
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/examples/third-distributed.cc ns-3.39/src/mpi/examples/third-distributed.cc
--- ns-3.38/src/mpi/examples/third-distributed.cc	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/examples/third-distributed.cc	2023-07-05 14:23:48.049428200 -0700
@@ -264,7 +264,7 @@
 
     Simulator::Stop(Seconds(10.0));
 
-    if (tracing == true)
+    if (tracing)
     {
         // Depending on the system Id (rank), the pcap information
         // traced will be different.  For example, the ethernet pcap
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/model/distributed-simulator-impl.cc ns-3.39/src/mpi/model/distributed-simulator-impl.cc
--- ns-3.38/src/mpi/model/distributed-simulator-impl.cc	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/model/distributed-simulator-impl.cc	2023-07-05 14:23:48.049428200 -0700
@@ -609,16 +609,9 @@
         }
         return true;
     }
-    if (id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
-        (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
-        id.PeekEventImpl()->IsCancelled())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
+           (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
+           id.PeekEventImpl()->IsCancelled();
 }
 
 Time
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/model/mpi-receiver.h ns-3.39/src/mpi/model/mpi-receiver.h
--- ns-3.38/src/mpi/model/mpi-receiver.h	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/model/mpi-receiver.h	2023-07-05 14:23:48.049428200 -0700
@@ -18,7 +18,7 @@
 /**
  * \file
  * \ingroup mpi
- * ns3::MpiReciver declaration,
+ * ns3::MpiReceiver declaration,
  * provides an interface to aggregate to MPI-compatible NetDevices.
  */
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/model/null-message-simulator-impl.cc ns-3.39/src/mpi/model/null-message-simulator-impl.cc
--- ns-3.38/src/mpi/model/null-message-simulator-impl.cc	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/model/null-message-simulator-impl.cc	2023-07-05 14:23:48.049428200 -0700
@@ -532,16 +532,9 @@
         }
         return true;
     }
-    if (id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
-        (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
-        id.PeekEventImpl()->IsCancelled())
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return id.PeekEventImpl() == nullptr || id.GetTs() < m_currentTs ||
+           (id.GetTs() == m_currentTs && id.GetUid() <= m_currentUid) ||
+           id.PeekEventImpl()->IsCancelled();
 }
 
 Time
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/mpi/model/null-message-simulator-impl.h ns-3.39/src/mpi/model/null-message-simulator-impl.h
--- ns-3.38/src/mpi/model/null-message-simulator-impl.h	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/mpi/model/null-message-simulator-impl.h	2023-07-05 14:23:48.049428200 -0700
@@ -148,14 +148,14 @@
      * \param bundle Bundle to reschedule Null Message event for
      *
      * Reschedule Null Message event for the specified
-     * RemoteChannelBundel.  Existing event will be canceled.
+     * RemoteChannelBundle.  Existing event will be canceled.
      */
     void RescheduleNullMessageEvent(Ptr<RemoteChannelBundle> bundle);
 
     /**
      * \param nodeSysId SystemID to reschedule null event for
      *
-     * Reschedule Null Message event for the RemoteChannelBundel to the
+     * Reschedule Null Message event for the RemoteChannelBundle to the
      * task nodeSysId.  Existing event will be canceled.
      */
     void RescheduleNullMessageEvent(uint32_t nodeSysId);
@@ -165,7 +165,7 @@
      *
      * \return Guarantee time
      *
-     * Calculate the guarantee time for incoming RemoteChannelBundel
+     * Calculate the guarantee time for incoming RemoteChannelBundle
      * from task nodeSysId.  No message should arrive from task
      * nodeSysId with a receive time less than the guarantee time.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/netanim/CMakeLists.txt ns-3.39/src/netanim/CMakeLists.txt
--- ns-3.38/src/netanim/CMakeLists.txt	2023-03-16 20:54:23.796869000 -0700
+++ ns-3.39/src/netanim/CMakeLists.txt	2023-07-05 14:23:48.049428200 -0700
@@ -12,7 +12,6 @@
     ${libuan}
     ${libenergy}
     ${liblr-wpan}
-    ${libwave}
     ${libpoint-to-point-layout}
   TEST_SOURCES test/netanim-test.cc
 )
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/netanim/model/animation-interface.cc ns-3.39/src/netanim/model/animation-interface.cc
--- ns-3.38/src/netanim/model/animation-interface.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/netanim/model/animation-interface.cc	2023-07-05 14:23:48.057428100 -0700
@@ -35,7 +35,6 @@
 #ifdef __WIN32__
 #include "ns3/bs-net-device.h"
 #include "ns3/csma-net-device.h"
-#include "ns3/wave-net-device.h"
 #endif
 #include "animation-interface.h"
 
@@ -108,7 +107,7 @@
      * The .dll.a/.lib however, only gets linked if we instantiate at
      * least one symbol exported by the .dll.
      *
-     * To ensure TypeIds from the Csma, Uan, Wave, Wifi and Wimax
+     * To ensure TypeIds from the Csma, Uan, Wifi and Wimax
      * modules are registered during runtime, we need to instantiate
      * at least one symbol exported by each of these module libraries.
      */
@@ -116,7 +115,6 @@
     static CsmaNetDevice c;
     static WifiNetDevice w;
     static UanNetDevice u;
-    static WaveNetDevice wv;
 #endif
 }
 
@@ -471,16 +469,8 @@
 AnimationInterface::NodeHasMoved(Ptr<Node> n, Vector newLocation)
 {
     Vector oldLocation = GetPosition(n);
-    bool moved = true;
-    if ((ceil(oldLocation.x) == ceil(newLocation.x)) &&
-        (ceil(oldLocation.y) == ceil(newLocation.y)))
-    {
-        moved = false;
-    }
-    else
-    {
-        moved = true;
-    }
+    bool moved = !((ceil(oldLocation.x) == ceil(newLocation.x)) &&
+                   (ceil(oldLocation.y) == ceil(newLocation.y)));
     return moved;
 }
 
@@ -503,7 +493,6 @@
         PurgePendingPackets(AnimationInterface::LTE);
         PurgePendingPackets(AnimationInterface::CSMA);
         PurgePendingPackets(AnimationInterface::LRWPAN);
-        PurgePendingPackets(AnimationInterface::WAVE);
         Simulator::Schedule(m_mobilityPollInterval, &AnimationInterface::MobilityAutoCheck, this);
     }
 }
@@ -1093,50 +1082,6 @@
 }
 
 void
-AnimationInterface::WavePhyTxBeginTrace(std::string context, Ptr<const Packet> p)
-{
-    NS_LOG_FUNCTION(this);
-    return GenericWirelessTxTrace(context, p, AnimationInterface::WAVE);
-}
-
-void
-AnimationInterface::WavePhyRxBeginTrace(std::string context, Ptr<const Packet> p)
-{
-    NS_LOG_FUNCTION(this);
-    CHECK_STARTED_INTIMEWINDOW_TRACKPACKETS;
-    Ptr<NetDevice> ndev = GetNetDeviceFromContext(context);
-    NS_ASSERT(ndev);
-    UpdatePosition(ndev);
-    uint64_t animUid = GetAnimUidFromPacket(p);
-    NS_LOG_INFO("Wave RxBeginTrace for packet:" << animUid);
-    if (!IsPacketPending(animUid, AnimationInterface::WAVE))
-    {
-        NS_ASSERT_MSG(false, "WavePhyRxBeginTrace: unknown Uid");
-        std::ostringstream oss;
-        WifiMacHeader hdr;
-        if (!p->PeekHeader(hdr))
-        {
-            NS_LOG_WARN("WaveMacHeader not present");
-            return;
-        }
-        oss << hdr.GetAddr2();
-        if (m_macToNodeIdMap.find(oss.str()) == m_macToNodeIdMap.end())
-        {
-            NS_LOG_WARN("Transmitter Mac address " << oss.str() << " never seen before. Skipping");
-            return;
-        }
-        Ptr<Node> txNode = NodeList::GetNode(m_macToNodeIdMap[oss.str()]);
-        UpdatePosition(txNode);
-        AnimPacketInfo pktInfo(nullptr, Simulator::Now(), m_macToNodeIdMap[oss.str()]);
-        AddPendingPacket(AnimationInterface::WAVE, animUid, pktInfo);
-        NS_LOG_WARN("WavePhyRxBegin: unknown Uid, but we are adding a wave packet");
-    }
-    /// \todo NS_ASSERT (WavePacketIsPending (animUid) == true);
-    m_pendingWavePackets[animUid].ProcessRxBegin(ndev, Simulator::Now().GetSeconds());
-    OutputWirelessPacketRxInfo(p, m_pendingWavePackets[animUid], animUid);
-}
-
-void
 AnimationInterface::WimaxTxTrace(std::string context, Ptr<const Packet> p, const Mac48Address& m)
 {
     NS_LOG_FUNCTION(this);
@@ -1427,10 +1372,6 @@
         pendingPackets = &m_pendingLrWpanPackets;
         break;
     }
-    case AnimationInterface::WAVE: {
-        pendingPackets = &m_pendingWavePackets;
-        break;
-    }
     }
     return pendingPackets;
 }
@@ -1465,10 +1406,6 @@
         result = "LRWPAN";
         break;
     }
-    case AnimationInterface::WAVE: {
-        result = "WAVE";
-        break;
-    }
     }
     return result;
 }
@@ -1783,14 +1720,6 @@
                             MakeCallback(&AnimationInterface::LrWpanMacRxTrace, this));
     Config::ConnectFailSafe("/NodeList/*/DeviceList/*/$ns3::LrWpanNetDevice/Mac/MacRxDrop",
                             MakeCallback(&AnimationInterface::LrWpanMacRxDropTrace, this));
-
-    // Wave
-    Config::ConnectFailSafe(
-        "/NodeList/*/DeviceList/*/$ns3::WaveNetDevice/PhyEntities/*/$ns3::WifiPhy/PhyTxBegin",
-        MakeCallback(&AnimationInterface::WavePhyTxBeginTrace, this));
-    Config::ConnectFailSafe(
-        "/NodeList/*/DeviceList/*/$ns3::WaveNetDevice/PhyEntities/*/$ns3::WifiPhy/PhyRxBegin",
-        MakeCallback(&AnimationInterface::WavePhyRxBeginTrace, this));
 }
 
 Vector
@@ -2015,7 +1944,7 @@
             }
             NS_LOG_DEBUG("Got ChannelType" << channelType);
 
-            if (!ch || (channelType != std::string("ns3::PointToPointChannel")))
+            if (!ch || (channelType != "ns3::PointToPointChannel"))
             {
                 NS_LOG_DEBUG("No channel can't be a p2p device");
                 /*
@@ -2050,7 +1979,7 @@
                 continue;
             }
 
-            else if (channelType == std::string("ns3::PointToPointChannel"))
+            else if (channelType == "ns3::PointToPointChannel")
             { // Since these are duplex links, we only need to dump
                 // if srcid < dstid
                 std::size_t nChDev = ch->GetNDevices();
@@ -2153,14 +2082,7 @@
 bool
 AnimationInterface::IsInTimeWindow()
 {
-    if ((Simulator::Now() >= m_startTime) && (Simulator::Now() <= m_stopTime))
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return Simulator::Now() >= m_startTime && Simulator::Now() <= m_stopTime;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/netanim/model/animation-interface.h ns-3.39/src/netanim/model/animation-interface.h
--- ns-3.38/src/netanim/model/animation-interface.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/netanim/model/animation-interface.h	2023-07-05 14:23:48.057428100 -0700
@@ -50,7 +50,7 @@
 
 #define MAX_PKTS_PER_TRACE_FILE 100000
 #define PURGE_INTERVAL 5
-#define NETANIM_VERSION "netanim-3.108"
+#define NETANIM_VERSION "netanim-3.109"
 #define CHECK_STARTED_INTIMEWINDOW                                                                 \
     {                                                                                              \
         if (!m_started || !IsInTimeWindow())                                                       \
@@ -544,8 +544,7 @@
         WIFI,
         WIMAX,
         CSMA,
-        LRWPAN,
-        WAVE
+        LRWPAN
     };
 
     /// NodeSize structure
@@ -669,7 +668,6 @@
     AnimUidPacketInfoMap m_pendingLtePackets;    ///< pending LTE packets
     AnimUidPacketInfoMap m_pendingCsmaPackets;   ///< pending CSMA packets
     AnimUidPacketInfoMap m_pendingUanPackets;    ///< pending UAN packets
-    AnimUidPacketInfoMap m_pendingWavePackets;   ///< pending WAVE packets
 
     std::map<uint32_t, Vector> m_nodeLocation;         ///< node location
     std::map<std::string, uint32_t> m_macToNodeIdMap;  ///< MAC to node ID map
@@ -1078,19 +1076,6 @@
                              Ptr<const Packet> p,
                              RxPowerWattPerChannelBand rxPowersW);
     /**
-     * WAVE Phy transmit begin trace function
-     * \param context the context
-     * \param p the packet
-     */
-    void WavePhyTxBeginTrace(std::string context, Ptr<const Packet> p);
-    /**
-     * WAVE Phy receive begin trace function
-     *
-     * \param context the context
-     * \param p the packet
-     */
-    void WavePhyRxBeginTrace(std::string context, Ptr<const Packet> p);
-    /**
      * LR-WPAN Phy receive begin trace function
      *
      * \param context the context
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/netanim/test/examples-to-run.py ns-3.39/src/netanim/test/examples-to-run.py
--- ns-3.38/src/netanim/test/examples-to-run.py	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/netanim/test/examples-to-run.py	2023-07-05 14:23:48.057428100 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/examples/CMakeLists.txt ns-3.39/src/network/examples/CMakeLists.txt
--- ns-3.38/src/network/examples/CMakeLists.txt	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/examples/CMakeLists.txt	2023-07-05 14:23:48.057428100 -0700
@@ -3,7 +3,7 @@
     main-packet-header
     main-packet-tag
     packet-socket-apps
-    lollipop-comparisions
+    lollipop-comparisons
 )
 
 foreach(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/examples/lollipop-comparisions.cc ns-3.39/src/network/examples/lollipop-comparisions.cc
--- ns-3.38/src/network/examples/lollipop-comparisions.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/examples/lollipop-comparisions.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2020 Universita' di Firenze, Italy
- *
- * 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: Tommaso Pecorella <tommaso.pecorella@unifi.it>
- */
-
-#include "ns3/lollipop-counter.h"
-
-using namespace ns3;
-
-int
-main(int argc, char* argv[])
-{
-    LollipopCounter8 counterA;
-    LollipopCounter8 counterB;
-
-    //  counterA.SetSequenceWindowSize (5);
-    //  counterB.SetSequenceWindowSize (5);
-
-    for (uint8_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++)
-    {
-        std::cout << +i << " -- ";
-        for (uint8_t j = 0; j < std::numeric_limits<uint8_t>::max(); j++)
-        {
-            counterA = i;
-            counterB = j;
-            if (counterA < counterB)
-            {
-                std::cout << "<";
-            }
-            else if (counterA == counterB)
-            {
-                std::cout << "=";
-            }
-            else if (counterA > counterB)
-            {
-                std::cout << ">";
-            }
-            else
-            {
-                std::cout << ".";
-            }
-        }
-        std::cout << std::endl;
-    }
-
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/examples/lollipop-comparisons.cc ns-3.39/src/network/examples/lollipop-comparisons.cc
--- ns-3.38/src/network/examples/lollipop-comparisons.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/network/examples/lollipop-comparisons.cc	2023-07-05 14:23:48.057428100 -0700
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2020 Universita' di Firenze, Italy
+ *
+ * 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: Tommaso Pecorella <tommaso.pecorella@unifi.it>
+ */
+
+#include "ns3/lollipop-counter.h"
+
+using namespace ns3;
+
+int
+main(int argc, char* argv[])
+{
+    LollipopCounter8 counterA;
+    LollipopCounter8 counterB;
+
+    //  counterA.SetSequenceWindowSize (5);
+    //  counterB.SetSequenceWindowSize (5);
+
+    for (uint8_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++)
+    {
+        std::cout << +i << " -- ";
+        for (uint8_t j = 0; j < std::numeric_limits<uint8_t>::max(); j++)
+        {
+            counterA = i;
+            counterB = j;
+            if (counterA < counterB)
+            {
+                std::cout << "<";
+            }
+            else if (counterA == counterB)
+            {
+                std::cout << "=";
+            }
+            else if (counterA > counterB)
+            {
+                std::cout << ">";
+            }
+            else
+            {
+                std::cout << ".";
+            }
+        }
+        std::cout << std::endl;
+    }
+
+    return 0;
+}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/address.h ns-3.39/src/network/model/address.h
--- ns-3.38/src/network/model/address.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/address.h	2023-07-05 14:23:48.057428100 -0700
@@ -103,10 +103,7 @@
      * The maximum size of a byte buffer which
      * can be stored in an Address instance.
      */
-    enum MaxSize_e
-    {
-        MAX_SIZE = 20
-    };
+    static constexpr uint32_t MAX_SIZE{20};
 
     /**
      * Create an invalid address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/buffer.cc ns-3.39/src/network/model/buffer.cc
--- ns-3.38/src/network/model/buffer.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/buffer.cc	2023-07-05 14:23:48.061428300 -0700
@@ -81,7 +81,7 @@
 #define UNINITIALIZED ((Buffer::FreeList*)0)
 uint32_t Buffer::g_maxSize = 0;
 Buffer::FreeList* Buffer::g_freeList = nullptr;
-struct Buffer::LocalStaticDestructor Buffer::g_localStaticDestructor;
+Buffer::LocalStaticDestructor Buffer::g_localStaticDestructor;
 
 Buffer::LocalStaticDestructor::~LocalStaticDestructor()
 {
@@ -98,7 +98,7 @@
 }
 
 void
-Buffer::Recycle(struct Buffer::Data* data)
+Buffer::Recycle(Buffer::Data* data)
 {
     NS_LOG_FUNCTION(data);
     NS_ASSERT(data->m_count == 0);
@@ -129,7 +129,7 @@
     {
         while (!g_freeList->empty())
         {
-            struct Buffer::Data* data = g_freeList->back();
+            Buffer::Data* data = g_freeList->back();
             g_freeList->pop_back();
             if (data->m_size >= dataSize)
             {
@@ -139,13 +139,13 @@
             Buffer::Deallocate(data);
         }
     }
-    struct Buffer::Data* data = Buffer::Allocate(dataSize);
+    Buffer::Data* data = Buffer::Allocate(dataSize);
     NS_ASSERT(data->m_count == 1);
     return data;
 }
 #else  /* BUFFER_FREE_LIST */
 void
-Buffer::Recycle(struct Buffer::Data* data)
+Buffer::Recycle(Buffer::Data* data)
 {
     NS_LOG_FUNCTION(data);
     NS_ASSERT(data->m_count == 0);
@@ -160,7 +160,9 @@
 }
 #endif /* BUFFER_FREE_LIST */
 
-struct Buffer::Data*
+constexpr uint32_t ALLOC_OVER_PROVISION = 100; //!< Additional bytes to over-provision.
+
+Buffer::Data*
 Buffer::Allocate(uint32_t reqSize)
 {
     NS_LOG_FUNCTION(reqSize);
@@ -169,16 +171,17 @@
         reqSize = 1;
     }
     NS_ASSERT(reqSize >= 1);
-    uint32_t size = reqSize - 1 + sizeof(struct Buffer::Data);
+    reqSize += ALLOC_OVER_PROVISION;
+    uint32_t size = reqSize - 1 + sizeof(Buffer::Data);
     uint8_t* b = new uint8_t[size];
-    struct Buffer::Data* data = reinterpret_cast<struct Buffer::Data*>(b);
+    Buffer::Data* data = reinterpret_cast<Buffer::Data*>(b);
     data->m_size = reqSize;
     data->m_count = 1;
     return data;
 }
 
 void
-Buffer::Deallocate(struct Buffer::Data* data)
+Buffer::Deallocate(Buffer::Data* data)
 {
     NS_LOG_FUNCTION(data);
     NS_ASSERT(data->m_count == 0);
@@ -201,7 +204,7 @@
 Buffer::Buffer(uint32_t dataSize, bool initialize)
 {
     NS_LOG_FUNCTION(this << dataSize << initialize);
-    if (initialize == true)
+    if (initialize)
     {
         Initialize(dataSize);
     }
@@ -328,7 +331,7 @@
     else
     {
         uint32_t newSize = GetInternalSize() + start;
-        struct Buffer::Data* newData = Buffer::Create(newSize);
+        Buffer::Data* newData = Buffer::Create(newSize);
         memcpy(newData->m_data + start, m_data->m_data + m_start, GetInternalSize());
         m_data->m_count--;
         if (m_data->m_count == 0)
@@ -374,7 +377,7 @@
     else
     {
         uint32_t newSize = GetInternalSize() + end;
-        struct Buffer::Data* newData = Buffer::Create(newSize);
+        Buffer::Data* newData = Buffer::Create(newSize);
         memcpy(newData->m_data, m_data->m_data + m_start, GetInternalSize());
         m_data->m_count--;
         if (m_data->m_count == 0)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/buffer.h ns-3.39/src/network/model/buffer.h
--- ns-3.38/src/network/model/buffer.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/buffer.h	2023-07-05 14:23:48.061428300 -0700
@@ -732,26 +732,26 @@
      * \brief Recycle the buffer memory
      * \param data the buffer data storage
      */
-    static void Recycle(struct Buffer::Data* data);
+    static void Recycle(Buffer::Data* data);
     /**
      * \brief Create a buffer data storage
      * \param size the storage size to create
      * \returns a pointer to the created buffer storage
      */
-    static struct Buffer::Data* Create(uint32_t size);
+    static Buffer::Data* Create(uint32_t size);
     /**
      * \brief Allocate a buffer data storage
      * \param reqSize the storage size to create
      * \returns a pointer to the allocated buffer storage
      */
-    static struct Buffer::Data* Allocate(uint32_t reqSize);
+    static Buffer::Data* Allocate(uint32_t reqSize);
     /**
      * \brief Deallocate the buffer memory
      * \param data the buffer data storage
      */
-    static void Deallocate(struct Buffer::Data* data);
+    static void Deallocate(Buffer::Data* data);
 
-    struct Data* m_data; //!< the buffer data storage
+    Data* m_data; //!< the buffer data storage
 
     /**
      * keep track of the maximum value of m_zeroAreaStart across
@@ -794,7 +794,7 @@
 
 #ifdef BUFFER_FREE_LIST
     /// Container for buffer data
-    typedef std::vector<struct Buffer::Data*> FreeList;
+    typedef std::vector<Buffer::Data*> FreeList;
 
     /// Local static destructor structure
     struct LocalStaticDestructor
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/byte-tag-list.cc ns-3.39/src/network/model/byte-tag-list.cc
--- ns-3.38/src/network/model/byte-tag-list.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/byte-tag-list.cc	2023-07-05 14:23:48.061428300 -0700
@@ -56,7 +56,7 @@
  *
  * Internal use only.
  */
-static class ByteTagListDataFreeList : public std::vector<struct ByteTagListData*>
+static class ByteTagListDataFreeList : public std::vector<ByteTagListData*>
 {
   public:
     ~ByteTagListDataFreeList();
@@ -88,7 +88,7 @@
     return m_current < m_end;
 }
 
-struct ByteTagList::Iterator::Item
+ByteTagList::Iterator::Item
 ByteTagList::Iterator::Next()
 {
     NS_ASSERT(HasNext());
@@ -213,7 +213,7 @@
     }
     else if (m_data->size < spaceNeeded || (m_data->count != 1 && m_data->dirty != m_used))
     {
-        struct ByteTagListData* newData = Allocate(spaceNeeded);
+        ByteTagListData* newData = Allocate(spaceNeeded);
         std::memcpy(&newData->data, &m_data->data, m_used);
         Deallocate(m_data);
         m_data = newData;
@@ -351,13 +351,13 @@
 
 #ifdef USE_FREE_LIST
 
-struct ByteTagListData*
+ByteTagListData*
 ByteTagList::Allocate(uint32_t size)
 {
     NS_LOG_FUNCTION(this << size);
     while (!g_freeList.empty())
     {
-        struct ByteTagListData* data = g_freeList.back();
+        ByteTagListData* data = g_freeList.back();
         g_freeList.pop_back();
         NS_ASSERT(data != nullptr);
         if (data->size >= size)
@@ -369,8 +369,8 @@
         uint8_t* buffer = (uint8_t*)data;
         delete[] buffer;
     }
-    uint8_t* buffer = new uint8_t[std::max(size, g_maxSize) + sizeof(struct ByteTagListData) - 4];
-    struct ByteTagListData* data = (struct ByteTagListData*)buffer;
+    uint8_t* buffer = new uint8_t[std::max(size, g_maxSize) + sizeof(ByteTagListData) - 4];
+    ByteTagListData* data = (ByteTagListData*)buffer;
     data->count = 1;
     data->size = size;
     data->dirty = 0;
@@ -378,7 +378,7 @@
 }
 
 void
-ByteTagList::Deallocate(struct ByteTagListData* data)
+ByteTagList::Deallocate(ByteTagListData* data)
 {
     NS_LOG_FUNCTION(this << data);
     if (data == nullptr)
@@ -403,12 +403,12 @@
 
 #else /* USE_FREE_LIST */
 
-struct ByteTagListData*
+ByteTagListData*
 ByteTagList::Allocate(uint32_t size)
 {
     NS_LOG_FUNCTION(this << size);
-    uint8_t* buffer = new uint8_t[size + sizeof(struct ByteTagListData) - 4];
-    struct ByteTagListData* data = (struct ByteTagListData*)buffer;
+    uint8_t* buffer = new uint8_t[size + sizeof(ByteTagListData) - 4];
+    ByteTagListData* data = (ByteTagListData*)buffer;
     data->count = 1;
     data->size = size;
     data->dirty = 0;
@@ -416,7 +416,7 @@
 }
 
 void
-ByteTagList::Deallocate(struct ByteTagListData* data)
+ByteTagList::Deallocate(ByteTagListData* data)
 {
     NS_LOG_FUNCTION(this << data);
     if (data == 0)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/byte-tag-list.h ns-3.39/src/network/model/byte-tag-list.h
--- ns-3.38/src/network/model/byte-tag-list.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/byte-tag-list.h	2023-07-05 14:23:48.061428300 -0700
@@ -111,7 +111,7 @@
          *
          * \returns the next Item in the ByteTagList
          */
-        struct ByteTagList::Iterator::Item Next();
+        ByteTagList::Iterator::Item Next();
 
         /**
          * \brief Returns the offset from the start of the virtual byte buffer to the ByteTagList
@@ -275,19 +275,19 @@
      * \param size the memory to allocate
      * \returns the ByteTagListData structure
      */
-    struct ByteTagListData* Allocate(uint32_t size);
+    ByteTagListData* Allocate(uint32_t size);
 
     /**
      * \brief Deallocates a ByteTagListData
      * \param data the ByteTagListData to deallocate
      */
-    void Deallocate(struct ByteTagListData* data);
+    void Deallocate(ByteTagListData* data);
 
-    int32_t m_minStart;             //!< minimal start offset
-    int32_t m_maxEnd;               //!< maximal end offset
-    int32_t m_adjustment;           //!< adjustment to byte tag offsets
-    uint32_t m_used;                //!< the number of used bytes in the buffer
-    struct ByteTagListData* m_data; //!< the ByteTagListData structure
+    int32_t m_minStart;      //!< minimal start offset
+    int32_t m_maxEnd;        //!< maximal end offset
+    int32_t m_adjustment;    //!< adjustment to byte tag offsets
+    uint32_t m_used;         //!< the number of used bytes in the buffer
+    ByteTagListData* m_data; //!< the ByteTagListData structure
 };
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/net-device.h ns-3.39/src/network/model/net-device.h
--- ns-3.38/src/network/model/net-device.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/net-device.h	2023-07-05 14:23:48.061428300 -0700
@@ -348,7 +348,7 @@
                      uint16_t,
                      const Address&,
                      const Address&,
-                     enum PacketType>
+                     PacketType>
         PromiscReceiveCallback;
 
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/nix-vector.cc ns-3.39/src/network/model/nix-vector.cc
--- ns-3.38/src/network/model/nix-vector.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/nix-vector.cc	2023-07-05 14:23:48.061428300 -0700
@@ -257,13 +257,13 @@
         m_used = *p++;
 
         // NixVector is packed in 32-bit unsigned ints.
-        uint32_t nixVectorLenth = m_totalBitSize / 32;
-        nixVectorLenth += (m_totalBitSize % 32) ? 1 : 0;
+        uint32_t nixVectorLength = m_totalBitSize / 32;
+        nixVectorLength += (m_totalBitSize % 32) ? 1 : 0;
 
-        NS_ASSERT_MSG(size >= 16 + nixVectorLenth,
-                      "NixVector serialized length should have been " << 16 + nixVectorLenth
+        NS_ASSERT_MSG(size >= 16 + nixVectorLength,
+                      "NixVector serialized length should have been " << 16 + nixVectorLength
                                                                       << " but buffer is shorter");
-        if (size < 16 + nixVectorLenth * 4)
+        if (size < 16 + nixVectorLength * 4)
         {
             // return zero if an entire nix-vector was
             // not deserialized
@@ -273,7 +273,7 @@
         // make sure the nix-vector
         // is empty
         m_nixVector.clear();
-        for (uint32_t j = 0; j < nixVectorLenth; j++)
+        for (uint32_t j = 0; j < nixVectorLength; j++)
         {
             uint32_t nix = *p++;
             m_nixVector.push_back(nix);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/node.cc ns-3.39/src/network/model/node.cc
--- ns-3.38/src/network/model/node.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/node.cc	2023-07-05 14:23:48.061428300 -0700
@@ -245,7 +245,7 @@
                               bool promiscuous)
 {
     NS_LOG_FUNCTION(this << &handler << protocolType << device << promiscuous);
-    struct Node::ProtocolHandlerEntry entry;
+    Node::ProtocolHandlerEntry entry;
     entry.handler = handler;
     entry.protocol = protocolType;
     entry.device = device;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/node.h ns-3.39/src/network/model/node.h
--- ns-3.38/src/network/model/node.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/node.h	2023-07-05 14:23:48.061428300 -0700
@@ -293,7 +293,7 @@
     };
 
     /// Typedef for protocol handlers container
-    typedef std::vector<struct Node::ProtocolHandlerEntry> ProtocolHandlerList;
+    typedef std::vector<Node::ProtocolHandlerEntry> ProtocolHandlerList;
     /// Typedef for NetDevice addition listeners container
     typedef std::vector<DeviceAdditionListener> DeviceAdditionListenerList;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/packet.cc ns-3.39/src/network/model/packet.cc
--- ns-3.38/src/network/model/packet.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/packet.cc	2023-07-05 14:23:48.061428300 -0700
@@ -89,7 +89,7 @@
 {
 }
 
-PacketTagIterator::PacketTagIterator(const struct PacketTagList::TagData* head)
+PacketTagIterator::PacketTagIterator(const PacketTagList::TagData* head)
     : m_current(head)
 {
 }
@@ -104,12 +104,12 @@
 PacketTagIterator::Next()
 {
     NS_ASSERT(HasNext());
-    const struct PacketTagList::TagData* prev = m_current;
+    const PacketTagList::TagData* prev = m_current;
     m_current = m_current->next;
     return PacketTagIterator::Item(prev);
 }
 
-PacketTagIterator::Item::Item(const struct PacketTagList::TagData* data)
+PacketTagIterator::Item::Item(const PacketTagList::TagData* data)
     : m_data(data)
 {
 }
@@ -471,8 +471,8 @@
                 os << item.tid.GetName();
                 break;
             }
-            os << " Fragment [" << item.currentTrimedFromStart << ":"
-               << (item.currentTrimedFromStart + item.currentSize) << "]";
+            os << " Fragment [" << item.currentTrimmedFromStart << ":"
+               << (item.currentTrimmedFromStart + item.currentSize) << "]";
         }
         else
         {
@@ -540,8 +540,8 @@
               os << item.tid.GetName ();
               break;
             }
-          os << " Fragment [" << item.currentTrimedFromStart<<":"
-             << (item.currentTrimedFromStart + item.currentSize) << "]";
+          os << " Fragment [" << item.currentTrimmedFromStart<<":"
+             << (item.currentTrimmedFromStart + item.currentSize) << "]";
         }
       else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/packet.h ns-3.39/src/network/model/packet.h
--- ns-3.38/src/network/model/packet.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/network/model/packet.h	2023-07-05 14:23:48.061428300 -0700
@@ -30,7 +30,6 @@
 
 #include "ns3/assert.h"
 #include "ns3/callback.h"
-#include "ns3/deprecated.h"
 #include "ns3/mac48-address.h"
 #include "ns3/ptr.h"
 
@@ -163,8 +162,8 @@
          * Constructor
          * \param data the data to copy.
          */
-        Item(const struct PacketTagList::TagData* data);
-        const struct PacketTagList::TagData* m_data; //!< the tag data
+        Item(const PacketTagList::TagData* data);
+        const PacketTagList::TagData* m_data; //!< the tag data
     };
 
     /**
@@ -183,9 +182,8 @@
      * Constructor
      * \param head head of the items
      */
-    PacketTagIterator(const struct PacketTagList::TagData* head);
-    const struct PacketTagList::TagData*
-        m_current; //!< actual position over the set of tags in a packet
+    PacketTagIterator(const PacketTagList::TagData* head);
+    const PacketTagList::TagData* m_current; //!< actual position over the set of tags in a packet
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/packet-metadata.cc ns-3.39/src/network/model/packet-metadata.cc
--- ns-3.38/src/network/model/packet-metadata.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/packet-metadata.cc	2023-07-05 14:23:48.061428300 -0700
@@ -77,7 +77,7 @@
 PacketMetadata::ReserveCopy(uint32_t size)
 {
     NS_LOG_FUNCTION(this << size);
-    struct PacketMetadata::Data* newData = PacketMetadata::Create(m_used + size);
+    PacketMetadata::Data* newData = PacketMetadata::Create(m_used + size);
     memcpy(newData->m_data, m_data->m_data, m_used);
     newData->m_dirtyEnd = m_used;
     m_data->m_count--;
@@ -142,7 +142,7 @@
     uint16_t current = m_head;
     while (ok && current != 0xffff)
     {
-        struct PacketMetadata::SmallItem item;
+        PacketMetadata::SmallItem item;
         PacketMetadata::ExtraItem extraItem;
         ReadItems(current, &item, &extraItem);
         ok &= IsSharedPointerOk(item.next);
@@ -375,7 +375,7 @@
 }
 
 uint16_t
-PacketMetadata::AddSmall(const struct PacketMetadata::SmallItem* item)
+PacketMetadata::AddSmall(const PacketMetadata::SmallItem* item)
 {
     NS_LOG_FUNCTION(this << item->next << item->prev << item->typeUid << item->size
                          << item->chunkUid);
@@ -507,7 +507,7 @@
     uint16_t current = m_head;
     while (current != 0xffff && current != m_tail)
     {
-        struct PacketMetadata::SmallItem tmpItem;
+        PacketMetadata::SmallItem tmpItem;
         PacketMetadata::ExtraItem tmpExtraItem;
         ReadItems(current, &tmpItem, &tmpExtraItem);
         uint16_t written = h.AddBig(0xffff, h.m_tail, &tmpItem, &tmpExtraItem);
@@ -523,8 +523,8 @@
 
 uint32_t
 PacketMetadata::ReadItems(uint16_t current,
-                          struct PacketMetadata::SmallItem* item,
-                          struct PacketMetadata::ExtraItem* extraItem) const
+                          PacketMetadata::SmallItem* item,
+                          PacketMetadata::ExtraItem* extraItem) const
 {
     NS_LOG_FUNCTION(this << current << item->chunkUid << item->prev << item->next << item->size
                          << item->typeUid << extraItem->fragmentEnd << extraItem->fragmentStart
@@ -563,7 +563,7 @@
     return buffer - &m_data->m_data[current];
 }
 
-struct PacketMetadata::Data*
+PacketMetadata::Data*
 PacketMetadata::Create(uint32_t size)
 {
     NS_LOG_FUNCTION(size);
@@ -574,7 +574,7 @@
     }
     while (!m_freeList.empty())
     {
-        struct PacketMetadata::Data* data = m_freeList.back();
+        PacketMetadata::Data* data = m_freeList.back();
         m_freeList.pop_back();
         if (data->m_size >= size)
         {
@@ -590,7 +590,7 @@
 }
 
 void
-PacketMetadata::Recycle(struct PacketMetadata::Data* data)
+PacketMetadata::Recycle(PacketMetadata::Data* data)
 {
     NS_LOG_FUNCTION(data);
     if (!m_enable)
@@ -610,18 +610,18 @@
     }
 }
 
-struct PacketMetadata::Data*
+PacketMetadata::Data*
 PacketMetadata::Allocate(uint32_t n)
 {
     NS_LOG_FUNCTION(n);
-    uint32_t size = sizeof(struct Data);
+    uint32_t size = sizeof(Data);
     if (n <= PACKET_METADATA_DATA_M_DATA_SIZE)
     {
         n = PACKET_METADATA_DATA_M_DATA_SIZE;
     }
     size += n - PACKET_METADATA_DATA_M_DATA_SIZE;
     uint8_t* buf = new uint8_t[size];
-    struct PacketMetadata::Data* data = (struct PacketMetadata::Data*)buf;
+    PacketMetadata::Data* data = (PacketMetadata::Data*)buf;
     data->m_size = n;
     data->m_count = 1;
     data->m_dirtyEnd = 0;
@@ -629,7 +629,7 @@
 }
 
 void
-PacketMetadata::Deallocate(struct PacketMetadata::Data* data)
+PacketMetadata::Deallocate(PacketMetadata::Data* data)
 {
     NS_LOG_FUNCTION(data);
     uint8_t* buf = (uint8_t*)data;
@@ -650,7 +650,6 @@
 PacketMetadata::AddHeader(const Header& header, uint32_t size)
 {
     NS_LOG_FUNCTION(this << &header << size);
-    NS_ASSERT(IsStateOk());
     uint32_t uid = header.GetInstanceTypeId().GetUid() << 1;
     DoAddHeader(uid, size);
     NS_ASSERT(IsStateOk());
@@ -666,7 +665,7 @@
         return;
     }
 
-    struct PacketMetadata::SmallItem item;
+    PacketMetadata::SmallItem item;
     item.next = m_head;
     item.prev = 0xffff;
     item.typeUid = uid;
@@ -682,14 +681,13 @@
 {
     uint32_t uid = header.GetInstanceTypeId().GetUid() << 1;
     NS_LOG_FUNCTION(this << &header << size);
-    NS_ASSERT(IsStateOk());
     if (!m_enable)
     {
         m_metadataSkipped = true;
         return;
     }
-    struct PacketMetadata::SmallItem item;
-    struct PacketMetadata::ExtraItem extraItem;
+    PacketMetadata::SmallItem item;
+    PacketMetadata::ExtraItem extraItem;
     uint32_t read = ReadItems(m_head, &item, &extraItem);
     if ((item.typeUid & 0xfffffffe) != uid || item.size != size)
     {
@@ -728,13 +726,12 @@
 {
     uint32_t uid = trailer.GetInstanceTypeId().GetUid() << 1;
     NS_LOG_FUNCTION(this << &trailer << size);
-    NS_ASSERT(IsStateOk());
     if (!m_enable)
     {
         m_metadataSkipped = true;
         return;
     }
-    struct PacketMetadata::SmallItem item;
+    PacketMetadata::SmallItem item;
     item.next = 0xffff;
     item.prev = m_tail;
     item.typeUid = uid;
@@ -751,14 +748,13 @@
 {
     uint32_t uid = trailer.GetInstanceTypeId().GetUid() << 1;
     NS_LOG_FUNCTION(this << &trailer << size);
-    NS_ASSERT(IsStateOk());
     if (!m_enable)
     {
         m_metadataSkipped = true;
         return;
     }
-    struct PacketMetadata::SmallItem item;
-    struct PacketMetadata::ExtraItem extraItem;
+    PacketMetadata::SmallItem item;
+    PacketMetadata::ExtraItem extraItem;
     uint32_t read = ReadItems(m_tail, &item, &extraItem);
     if ((item.typeUid & 0xfffffffe) != uid || item.size != size)
     {
@@ -796,7 +792,6 @@
 PacketMetadata::AddAtEnd(const PacketMetadata& o)
 {
     NS_LOG_FUNCTION(this << &o);
-    NS_ASSERT(IsStateOk());
     if (!m_enable)
     {
         m_metadataSkipped = true;
@@ -820,12 +815,12 @@
 
     // We read the current tail because we are going to append
     // after this item.
-    struct PacketMetadata::SmallItem tailItem;
+    PacketMetadata::SmallItem tailItem;
     PacketMetadata::ExtraItem tailExtraItem;
     uint32_t tailSize = ReadItems(m_tail, &tailItem, &tailExtraItem);
 
     uint16_t current;
-    struct PacketMetadata::SmallItem item;
+    PacketMetadata::SmallItem item;
     PacketMetadata::ExtraItem extraItem;
     o.ReadItems(o.m_head, &item, &extraItem);
     if (extraItem.packetUid == tailExtraItem.packetUid && item.typeUid == tailItem.typeUid &&
@@ -884,7 +879,6 @@
 PacketMetadata::RemoveAtStart(uint32_t start)
 {
     NS_LOG_FUNCTION(this << start);
-    NS_ASSERT(IsStateOk());
     if (!m_enable)
     {
         m_metadataSkipped = true;
@@ -895,7 +889,7 @@
     uint16_t current = m_head;
     while (current != 0xffff && leftToRemove > 0)
     {
-        struct PacketMetadata::SmallItem item;
+        PacketMetadata::SmallItem item;
         PacketMetadata::ExtraItem extraItem;
         ReadItems(current, &item, &extraItem);
         uint32_t itemRealSize = extraItem.fragmentEnd - extraItem.fragmentStart;
@@ -946,7 +940,6 @@
 PacketMetadata::RemoveAtEnd(uint32_t end)
 {
     NS_LOG_FUNCTION(this << end);
-    NS_ASSERT(IsStateOk());
     if (!m_enable)
     {
         m_metadataSkipped = true;
@@ -958,7 +951,7 @@
     uint16_t current = m_tail;
     while (current != 0xffff && leftToRemove > 0)
     {
-        struct PacketMetadata::SmallItem item;
+        PacketMetadata::SmallItem item;
         PacketMetadata::ExtraItem extraItem;
         ReadItems(current, &item, &extraItem);
         uint32_t itemRealSize = extraItem.fragmentEnd - extraItem.fragmentStart;
@@ -1015,7 +1008,7 @@
     uint16_t tail = m_tail;
     while (current != 0xffff)
     {
-        struct PacketMetadata::SmallItem item;
+        PacketMetadata::SmallItem item;
         PacketMetadata::ExtraItem extraItem;
         ReadItems(current, &item, &extraItem);
         totalSize += extraItem.fragmentEnd - extraItem.fragmentStart;
@@ -1072,9 +1065,9 @@
 PacketMetadata::ItemIterator::Next()
 {
     NS_LOG_FUNCTION(this);
-    struct PacketMetadata::Item item;
-    struct PacketMetadata::SmallItem smallItem;
-    struct PacketMetadata::ExtraItem extraItem;
+    PacketMetadata::Item item;
+    PacketMetadata::SmallItem smallItem;
+    PacketMetadata::ExtraItem extraItem;
     m_metadata->ReadItems(m_current, &smallItem, &extraItem);
     if (m_current == m_metadata->m_tail)
     {
@@ -1083,17 +1076,10 @@
     m_current = smallItem.next;
     uint32_t uid = (smallItem.typeUid & 0xfffffffe) >> 1;
     item.tid.SetUid(uid);
-    item.currentTrimedFromStart = extraItem.fragmentStart;
-    item.currentTrimedFromEnd = extraItem.fragmentEnd - smallItem.size;
+    item.currentTrimmedFromStart = extraItem.fragmentStart;
+    item.currentTrimmedFromEnd = extraItem.fragmentEnd - smallItem.size;
     item.currentSize = extraItem.fragmentEnd - extraItem.fragmentStart;
-    if (extraItem.fragmentStart != 0 || extraItem.fragmentEnd != smallItem.size)
-    {
-        item.isFragment = true;
-    }
-    else
-    {
-        item.isFragment = false;
-    }
+    item.isFragment = (extraItem.fragmentStart != 0 || extraItem.fragmentEnd != smallItem.size);
     TypeId tid;
     tid.SetUid(uid);
     if (uid == 0)
@@ -1143,8 +1129,8 @@
         return totalSize;
     }
 
-    struct PacketMetadata::SmallItem item;
-    struct PacketMetadata::ExtraItem extraItem;
+    PacketMetadata::SmallItem item;
+    PacketMetadata::ExtraItem extraItem;
     uint32_t current = m_head;
     while (current != 0xffff)
     {
@@ -1183,8 +1169,8 @@
         return 0;
     }
 
-    struct PacketMetadata::SmallItem item;
-    struct PacketMetadata::ExtraItem extraItem;
+    PacketMetadata::SmallItem item;
+    PacketMetadata::ExtraItem extraItem;
     uint32_t current = m_head;
     while (current != 0xffff)
     {
@@ -1287,8 +1273,8 @@
     buffer = ReadFromRawU64(m_packetUid, start, buffer, size);
     desSize -= 8;
 
-    struct PacketMetadata::SmallItem item = {0};
-    struct PacketMetadata::ExtraItem extraItem = {0};
+    PacketMetadata::SmallItem item = {0};
+    PacketMetadata::ExtraItem extraItem = {0};
     while (desSize > 0)
     {
         uint32_t uidStringSize = 0;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/packet-metadata.h ns-3.39/src/network/model/packet-metadata.h
--- ns-3.38/src/network/model/packet-metadata.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/packet-metadata.h	2023-07-05 14:23:48.061428300 -0700
@@ -117,12 +117,12 @@
          * how many bytes were trimmed from the start of a fragment.
          * if isFragment is true, this field is zero.
          */
-        uint32_t currentTrimedFromStart;
+        uint32_t currentTrimmedFromStart;
         /**
          * how many bytes were trimmed from the end of a fragment.
          * if isFragment is true, this field is zero.
          */
-        uint32_t currentTrimedFromEnd;
+        uint32_t currentTrimmedFromEnd;
         /**
          * an iterator which can be fed to Deserialize. Valid only
          * if isFragment and isPayload are false.
@@ -512,7 +512,7 @@
     /**
      * \brief Class to hold all the metadata
      */
-    class DataFreeList : public std::vector<struct Data*>
+    class DataFreeList : public std::vector<Data*>
     {
       public:
         ~DataFreeList();
@@ -626,8 +626,8 @@
      * \returns the number of bytes read.
      */
     uint32_t ReadItems(uint16_t current,
-                       struct PacketMetadata::SmallItem* item,
-                       struct PacketMetadata::ExtraItem* extraItem) const;
+                       PacketMetadata::SmallItem* item,
+                       PacketMetadata::ExtraItem* extraItem) const;
     /**
      * \brief Add an header
      * \param uid header's uid to add
@@ -656,24 +656,24 @@
      * \brief Recycle the buffer memory
      * \param data the buffer data storage
      */
-    static void Recycle(struct PacketMetadata::Data* data);
+    static void Recycle(PacketMetadata::Data* data);
     /**
      * \brief Create a buffer data storage
      * \param size the storage size to create
      * \returns a pointer to the created buffer storage
      */
-    static struct PacketMetadata::Data* Create(uint32_t size);
+    static PacketMetadata::Data* Create(uint32_t size);
     /**
      * \brief Allocate a buffer data storage
      * \param n the storage size to create
      * \returns a pointer to the allocated buffer storage
      */
-    static struct PacketMetadata::Data* Allocate(uint32_t n);
+    static PacketMetadata::Data* Allocate(uint32_t n);
     /**
      * \brief Deallocate the buffer memory
      * \param data the buffer data storage
      */
-    static void Deallocate(struct PacketMetadata::Data* data);
+    static void Deallocate(PacketMetadata::Data* data);
 
     static DataFreeList m_freeList; //!< the metadata data storage
     static bool m_enable;           //!< Enable the packet metadata
@@ -689,7 +689,7 @@
     static uint32_t m_maxSize;  //!< maximum metadata size
     static uint16_t m_chunkUid; //!< Chunk Uid
 
-    struct Data* m_data; //!< Metadata storage
+    Data* m_data; //!< Metadata storage
     /*
        head -(next)-> tail
          ^             |
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/packet-tag-list.cc ns-3.39/src/network/model/packet-tag-list.cc
--- ns-3.38/src/network/model/packet-tag-list.cc	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/packet-tag-list.cc	2023-07-05 14:23:48.061428300 -0700
@@ -67,9 +67,9 @@
 
     bool found = false;
 
-    struct TagData** prevNext = &m_next; // previous node's next pointer
-    struct TagData* cur = m_next;        // cursor to current node
-    struct TagData* it = nullptr;        // utility
+    TagData** prevNext = &m_next; // previous node's next pointer
+    TagData* cur = m_next;        // cursor to current node
+    TagData* it = nullptr;        // utility
 
     // Search from the head of the list until we find tid or a merge
     while (cur != nullptr)
@@ -151,7 +151,7 @@
         NS_ASSERT(cur != nullptr);
         NS_ASSERT(cur->count > 1);
         cur->count--; // unmerge cur
-        struct TagData* copy = CreateTagData(cur->size);
+        TagData* copy = CreateTagData(cur->size);
         copy->tid = cur->tid;
         copy->count = 1;
         copy->size = cur->size;
@@ -182,8 +182,8 @@
 bool
 PacketTagList::RemoveWriter(Tag& tag,
                             bool preMerge,
-                            struct PacketTagList::TagData* cur,
-                            struct PacketTagList::TagData** prevNext)
+                            PacketTagList::TagData* cur,
+                            PacketTagList::TagData** prevNext)
 {
     NS_LOG_FUNCTION_NOARGS();
 
@@ -227,8 +227,8 @@
 bool
 PacketTagList::ReplaceWriter(Tag& tag,
                              bool preMerge,
-                             struct PacketTagList::TagData* cur,
-                             struct PacketTagList::TagData** prevNext)
+                             PacketTagList::TagData* cur,
+                             PacketTagList::TagData** prevNext)
 {
     NS_LOG_FUNCTION_NOARGS();
 
@@ -244,7 +244,7 @@
         // cur is always a merge at this point
         // need to copy, replace, and link past cur
         cur->count--; // unmerge cur
-        struct TagData* copy = CreateTagData(tag.GetSerializedSize());
+        TagData* copy = CreateTagData(tag.GetSerializedSize());
         copy->tid = tag.GetInstanceTypeId();
         copy->count = 1;
         tag.Serialize(TagBuffer(copy->data, copy->data + copy->size));
@@ -263,12 +263,12 @@
 {
     NS_LOG_FUNCTION(this << tag.GetInstanceTypeId());
     // ensure this id was not yet added
-    for (struct TagData* cur = m_next; cur != nullptr; cur = cur->next)
+    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.");
     }
-    struct TagData* head = CreateTagData(tag.GetSerializedSize());
+    TagData* head = CreateTagData(tag.GetSerializedSize());
     head->count = 1;
     head->next = nullptr;
     head->tid = tag.GetInstanceTypeId();
@@ -283,7 +283,7 @@
 {
     NS_LOG_FUNCTION(this << tag.GetInstanceTypeId());
     TypeId tid = tag.GetInstanceTypeId();
-    for (struct TagData* cur = m_next; cur != nullptr; cur = cur->next)
+    for (TagData* cur = m_next; cur != nullptr; cur = cur->next)
     {
         if (cur->tid == tid)
         {
@@ -296,7 +296,7 @@
     return false;
 }
 
-const struct PacketTagList::TagData*
+const PacketTagList::TagData*
 PacketTagList::Head() const
 {
     return m_next;
@@ -311,7 +311,7 @@
 
     size = 4; // numberOfTags
 
-    for (struct TagData* cur = m_next; cur != nullptr; cur = cur->next)
+    for (TagData* cur = m_next; cur != nullptr; cur = cur->next)
     {
         size += 4; // TagData -> size
 
@@ -348,7 +348,7 @@
         return 0;
     }
 
-    for (struct TagData* cur = m_next; cur != nullptr; cur = cur->next)
+    for (TagData* cur = m_next; cur != nullptr; cur = cur->next)
     {
         if (size + 4 <= maxSize)
         {
@@ -409,7 +409,7 @@
 
     NS_LOG_INFO("Deserializing number of tags " << numberOfTags);
 
-    struct TagData* prevTag = nullptr;
+    TagData* prevTag = nullptr;
     for (uint32_t i = 0; i < numberOfTags; ++i)
     {
         NS_ASSERT(sizeCheck >= 4);
@@ -427,7 +427,7 @@
 
         NS_LOG_INFO("Deserializing tag of type " << tid);
 
-        struct TagData* newTag = CreateTagData(tagSize);
+        TagData* newTag = CreateTagData(tagSize);
         newTag->count = 1;
         newTag->next = nullptr;
         newTag->tid = tid;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/packet-tag-list.h ns-3.39/src/network/model/packet-tag-list.h
--- ns-3.38/src/network/model/packet-tag-list.h	2023-03-16 20:54:23.804869200 -0700
+++ ns-3.39/src/network/model/packet-tag-list.h	2023-07-05 14:23:48.061428300 -0700
@@ -141,11 +141,11 @@
      */
     struct TagData
     {
-        struct TagData* next; //!< Pointer to next in list
-        uint32_t count;       //!< Number of incoming links
-        TypeId tid;           //!< Type of the tag serialized into #data
-        uint32_t size;        //!< Size of the \c data buffer
-        uint8_t data[1];      //!< Serialization buffer
+        TagData* next;   //!< Pointer to next in list
+        uint32_t count;  //!< Number of incoming links
+        TypeId tid;      //!< Type of the tag serialized into #data
+        uint32_t size;   //!< Size of the \c data buffer
+        uint8_t data[1]; //!< Serialization buffer
     };
 
     /**
@@ -217,7 +217,7 @@
     /**
      * \returns pointer to head of tag list
      */
-    const struct PacketTagList::TagData* Head() const;
+    const PacketTagList::TagData* Head() const;
     /**
      * Returns number of bytes required for packet serialization.
      *
@@ -266,8 +266,8 @@
      */
     typedef bool (PacketTagList::*COWWriter)(Tag& tag,
                                              bool preMerge,
-                                             struct TagData* cur,
-                                             struct TagData** prevNext);
+                                             TagData* cur,
+                                             TagData** prevNext);
     /**
      * Traverse the list implementing copy-on-write, using \pname{Writer}.
      *
@@ -287,7 +287,7 @@
      *          pointing to \pname{cur}.
      * \returns True, since tag will definitely be removed.
      */
-    bool RemoveWriter(Tag& tag, bool preMerge, struct TagData* cur, struct TagData** prevNext);
+    bool RemoveWriter(Tag& tag, bool preMerge, TagData* cur, TagData** prevNext);
     /**
      * Copy-on-write implementing Replace
      *
@@ -299,12 +299,12 @@
      *          pointing to \pname{cur}.
      * \returns True, since tag value will definitely be replaced.
      */
-    bool ReplaceWriter(Tag& tag, bool preMerge, struct TagData* cur, struct TagData** prevNext);
+    bool ReplaceWriter(Tag& tag, bool preMerge, TagData* cur, TagData** prevNext);
 
     /**
      * Pointer to first \ref TagData on the list
      */
-    struct TagData* m_next;
+    TagData* m_next;
 };
 
 } // namespace ns3
@@ -355,8 +355,8 @@
 void
 PacketTagList::RemoveAll()
 {
-    struct TagData* prev = nullptr;
-    for (struct TagData* cur = m_next; cur != nullptr; cur = cur->next)
+    TagData* prev = nullptr;
+    for (TagData* cur = m_next; cur != nullptr; cur = cur->next)
     {
         cur->count--;
         if (cur->count > 0)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/model/socket.h ns-3.39/src/network/model/socket.h
--- ns-3.38/src/network/model/socket.h	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/model/socket.h	2023-07-05 14:23:48.061428300 -0700
@@ -162,11 +162,11 @@
      *         socket. Each socket's errno is initialized to zero
      *         when the socket is created.
      */
-    virtual enum Socket::SocketErrno GetErrno() const = 0;
+    virtual Socket::SocketErrno GetErrno() const = 0;
     /**
      * \return the socket type, analogous to getsockopt (SO_TYPE)
      */
-    virtual enum Socket::SocketType GetSocketType() const = 0;
+    virtual Socket::SocketType GetSocketType() const = 0;
     /**
      * \brief Return the node this socket is associated with.
      * \returns the node
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/test/examples-to-run.py ns-3.39/src/network/test/examples-to-run.py
--- ns-3.38/src/network/test/examples-to-run.py	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/test/examples-to-run.py	2023-07-05 14:23:48.061428300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/test/packet-metadata-test.cc ns-3.39/src/network/test/packet-metadata-test.cc
--- ns-3.38/src/network/test/packet-metadata-test.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/test/packet-metadata-test.cc	2023-07-05 14:23:48.061428300 -0700
@@ -372,7 +372,7 @@
     std::list<int> got;
     while (k.HasNext())
     {
-        struct PacketMetadata::Item item = k.Next();
+        PacketMetadata::Item item = k.Next();
         if (item.isFragment || item.type == PacketMetadata::Item::PAYLOAD)
         {
             got.push_back(item.currentSize);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/test/packet-test-suite.cc ns-3.39/src/network/test/packet-test-suite.cc
--- ns-3.38/src/network/test/packet-test-suite.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/test/packet-test-suite.cc	2023-07-05 14:23:48.061428300 -0700
@@ -548,7 +548,7 @@
 void
 PacketTest::DoCheck(Ptr<const Packet> p, uint32_t n, ...)
 {
-    std::vector<struct Expected> expected;
+    std::vector<Expected> expected;
     va_list ap;
     va_start(ap, n);
     for (uint32_t k = 0; k < n; ++k)
@@ -585,7 +585,7 @@
 void
 PacketTest::DoCheckData(Ptr<const Packet> p, uint32_t n, ...)
 {
-    std::vector<struct Expected> expected;
+    std::vector<Expected> expected;
     va_list ap;
     va_start(ap, n);
     for (uint32_t k = 0; k < n; ++k)
@@ -1119,11 +1119,15 @@
     { // Copy ctor, assignment
         std::cout << GetName() << "check copy and assignment" << std::endl;
         {
+            // Test copy constructor
+            // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
             PacketTagList ptl(ref);
             CheckRefList(ref, "copy ctor orig");
             CheckRefList(ptl, "copy ctor copy");
         }
         {
+            // Test copy constructor
+            // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
             PacketTagList ptl = ref;
             CheckRefList(ref, "assignment orig");
             CheckRefList(ptl, "assignment copy");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/bit-deserializer.cc ns-3.39/src/network/utils/bit-deserializer.cc
--- ns-3.38/src/network/utils/bit-deserializer.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/bit-deserializer.cc	2023-07-05 14:23:48.061428300 -0700
@@ -85,7 +85,7 @@
 BitDeserializer::PrepareDeserialization()
 {
     NS_LOG_FUNCTION(this);
-    if (m_deserializing == false)
+    if (!m_deserializing)
     {
         m_deserializing = true;
         for (auto index = m_bytesBlob.begin(); index != m_bytesBlob.end(); index++)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/data-rate.h ns-3.39/src/network/utils/data-rate.h
--- ns-3.38/src/network/utils/data-rate.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/network/utils/data-rate.h	2023-07-05 14:23:48.061428300 -0700
@@ -22,7 +22,6 @@
 
 #include "ns3/attribute-helper.h"
 #include "ns3/attribute.h"
-#include "ns3/deprecated.h"
 #include "ns3/nstime.h"
 
 #include <iostream>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/ipv4-address.cc ns-3.39/src/network/utils/ipv4-address.cc
--- ns-3.38/src/network/utils/ipv4-address.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/ipv4-address.cc	2023-07-05 14:23:48.065428300 -0700
@@ -77,14 +77,7 @@
 Ipv4Mask::IsMatch(Ipv4Address a, Ipv4Address b) const
 {
     NS_LOG_FUNCTION(this << a << b);
-    if ((a.Get() & m_mask) == (b.Get() & m_mask))
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return (a.Get() & m_mask) == (b.Get() & m_mask);
 }
 
 uint32_t
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/ipv4-address.h ns-3.39/src/network/utils/ipv4-address.h
--- ns-3.38/src/network/utils/ipv4-address.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/network/utils/ipv4-address.h	2023-07-05 14:23:48.065428300 -0700
@@ -22,7 +22,6 @@
 
 #include "ns3/address.h"
 #include "ns3/attribute-helper.h"
-#include "ns3/deprecated.h"
 
 #include <ostream>
 #include <stdint.h>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/ipv6-address.cc ns-3.39/src/network/utils/ipv6-address.cc
--- ns-3.38/src/network/utils/ipv6-address.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/ipv6-address.cc	2023-07-05 14:23:48.065428300 -0700
@@ -552,22 +552,14 @@
 Ipv6Address::IsMulticast() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_address[0] == 0xff)
-    {
-        return true;
-    }
-    return false;
+    return m_address[0] == 0xff;
 }
 
 bool
 Ipv6Address::IsLinkLocalMulticast() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_address[0] == 0xff && m_address[1] == 0x02)
-    {
-        return true;
-    }
-    return false;
+    return m_address[0] == 0xff && m_address[1] == 0x02;
 }
 
 bool
@@ -610,11 +602,7 @@
     NS_LOG_FUNCTION(this);
 
     static Ipv6Address documentation("ff02::1:ff00:0");
-    if (CombinePrefix(Ipv6Prefix(104)) == documentation)
-    {
-        return true;
-    }
-    return false;
+    return CombinePrefix(Ipv6Prefix(104)) == documentation;
 }
 
 bool
@@ -652,11 +640,7 @@
 {
     NS_LOG_FUNCTION(this);
     static Ipv6Address documentation("2001:db8::0");
-    if (CombinePrefix(Ipv6Prefix(32)) == documentation)
-    {
-        return true;
-    }
-    return false;
+    return CombinePrefix(Ipv6Prefix(32)) == documentation;
 }
 
 bool
@@ -777,11 +761,7 @@
 {
     NS_LOG_FUNCTION(this);
     static Ipv6Address linkLocal("fe80::0");
-    if (CombinePrefix(Ipv6Prefix(64)) == linkLocal)
-    {
-        return true;
-    }
-    return false;
+    return CombinePrefix(Ipv6Prefix(64)) == linkLocal;
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/ipv6-address.h ns-3.39/src/network/utils/ipv6-address.h
--- ns-3.38/src/network/utils/ipv6-address.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/network/utils/ipv6-address.h	2023-07-05 14:23:48.065428300 -0700
@@ -24,7 +24,6 @@
 
 #include "ns3/address.h"
 #include "ns3/attribute-helper.h"
-#include "ns3/deprecated.h"
 #include "ns3/ipv4-address.h"
 
 #include <cstring>
@@ -647,7 +646,7 @@
  * \brief Stream extraction operator.
  *
  * \param is the reference to the input stream
- * \param prefix the Ipv6Preofix
+ * \param prefix the Ipv6Prefix
  * \returns the reference to the input stream
  */
 std::istream& operator>>(std::istream& is, Ipv6Prefix& prefix);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/lollipop-counter.h ns-3.39/src/network/utils/lollipop-counter.h
--- ns-3.38/src/network/utils/lollipop-counter.h	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/lollipop-counter.h	2023-07-05 14:23:48.065428300 -0700
@@ -182,11 +182,7 @@
      */
     bool IsInit() const
     {
-        if (m_value > m_circularRegion)
-        {
-            return true;
-        }
-        return false;
+        return m_value > m_circularRegion;
     }
 
     /**
@@ -200,11 +196,7 @@
         NS_ABORT_MSG_IF(lhs.m_sequenceWindow != rhs.m_sequenceWindow,
                         "Can not compare two Lollipop Counters with different sequence windows");
 
-        if (lhs.m_value == rhs.m_value)
-        {
-            return true;
-        }
-        return false;
+        return lhs.m_value == rhs.m_value;
     }
 
     /**
@@ -263,25 +255,11 @@
                      1; // this is guaranteed to be positive and between [1...m_lollipopMaxValue].
         if (distance > lhs.m_sequenceWindow)
         {
-            if (lhsIsHigher)
-            {
-                return true;
-            }
-            else
-            {
-                return false;
-            }
+            return lhsIsHigher;
         }
         else
         {
-            if (lhsIsHigher)
-            {
-                return false;
-            }
-            else
-            {
-                return true;
-            }
+            return !lhsIsHigher;
         }
 
         // this should never be reached.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/mac16-address.cc ns-3.39/src/network/utils/mac16-address.cc
--- ns-3.38/src/network/utils/mac16-address.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/mac16-address.cc	2023-07-05 14:23:48.065428300 -0700
@@ -104,6 +104,13 @@
     NS_ASSERT(i == 2);
 }
 
+Mac16Address::Mac16Address(uint16_t addr)
+{
+    NS_LOG_FUNCTION(this);
+    m_address[1] = addr & 0xFF;
+    m_address[0] = (addr >> 8) & 0xFF;
+}
+
 void
 Mac16Address::CopyFrom(const uint8_t buffer[2])
 {
@@ -147,6 +154,15 @@
     return Address(GetType(), m_address, 2);
 }
 
+uint16_t
+Mac16Address::ConvertToInt() const
+{
+    uint16_t addr = m_address[1] & (0xFF);
+    addr |= (m_address[0] << 8) & (0xFF << 8);
+
+    return addr;
+}
+
 Mac16Address
 Mac16Address::Allocate()
 {
@@ -212,11 +228,7 @@
 Mac16Address::IsBroadcast() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_address[0] == 0xff && m_address[1] == 0xff)
-    {
-        return true;
-    }
-    return false;
+    return m_address[0] == 0xff && m_address[1] == 0xff;
 }
 
 bool
@@ -225,11 +237,7 @@
     NS_LOG_FUNCTION(this);
     uint8_t val = m_address[0];
     val >>= 5;
-    if (val == 0x4)
-    {
-        return true;
-    }
-    return false;
+    return val == 0x4;
 }
 
 std::ostream&
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/mac16-address.h ns-3.39/src/network/utils/mac16-address.h
--- ns-3.38/src/network/utils/mac16-address.h	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/mac16-address.h	2023-07-05 14:23:48.065428300 -0700
@@ -51,23 +51,33 @@
     Mac16Address(const char* str);
 
     /**
+     * \param addr The 16 bit unsigned integer used to create a Mac16Address object.
+     *
+     * Create a Mac16Address from an 16 bit unsigned integer.
+     */
+    Mac16Address(uint16_t addr);
+
+    /**
      * \param buffer address in network order
      *
      * Copy the input address to our internal buffer.
      */
     void CopyFrom(const uint8_t buffer[2]);
+
     /**
      * \param buffer address in network order
      *
      * Copy the internal address to the input buffer.
      */
     void CopyTo(uint8_t buffer[2]) const;
+
     /**
      * \returns a new Address instance
      *
      * Convert an instance of this class to a polymorphic Address instance.
      */
     operator Address() const;
+
     /**
      * \param address a polymorphic address
      * \returns a new Mac16Address from the polymorphic address
@@ -77,6 +87,7 @@
      * Mac16Address.
      */
     static Mac16Address ConvertFrom(const Address& address);
+
     /**
      * \returns a new Address instance
      *
@@ -85,10 +96,18 @@
     Address ConvertTo() const;
 
     /**
+     * \return the mac address in a 16 bit unsigned integer
+     *
+     * Convert an instance of this class to a 16 bit unsigned integer.
+     */
+    uint16_t ConvertToInt() const;
+
+    /**
      * \param address address to test
      * \returns true if the address matches, false otherwise.
      */
     static bool IsMatchingType(const Address& address);
+
     /**
      * Allocate a new Mac16Address.
      * \returns newly allocated mac16Address
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/mac64-address.cc ns-3.39/src/network/utils/mac64-address.cc
--- ns-3.38/src/network/utils/mac64-address.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/mac64-address.cc	2023-07-05 14:23:48.065428300 -0700
@@ -104,6 +104,19 @@
     NS_ASSERT(i == 8);
 }
 
+Mac64Address::Mac64Address(uint64_t addr)
+{
+    NS_LOG_FUNCTION(this);
+    m_address[7] = addr & 0xFF;
+    m_address[6] = (addr >> 8) & 0xFF;
+    m_address[5] = (addr >> 16) & 0xFF;
+    m_address[4] = (addr >> 24) & 0xFF;
+    m_address[3] = (addr >> 32) & 0xFF;
+    m_address[2] = (addr >> 40) & 0xFF;
+    m_address[1] = (addr >> 48) & 0xFF;
+    m_address[0] = (addr >> 56) & 0xFF;
+}
+
 void
 Mac64Address::CopyFrom(const uint8_t buffer[8])
 {
@@ -147,6 +160,23 @@
     return Address(GetType(), m_address, 8);
 }
 
+uint64_t
+Mac64Address::ConvertToInt() const
+{
+    uint64_t shift = 0xFF;
+    uint64_t addr = static_cast<uint64_t>(m_address[7]) & (shift);
+    addr |= (static_cast<uint64_t>(m_address[6]) << 8) & (shift << 8);
+    addr |= (static_cast<uint64_t>(m_address[5]) << 16) & (shift << 16);
+    addr |= (static_cast<uint64_t>(m_address[4]) << 24) & (shift << 24);
+
+    addr |= (static_cast<uint64_t>(m_address[3]) << 32) & (shift << 32);
+    addr |= (static_cast<uint64_t>(m_address[2]) << 40) & (shift << 40);
+    addr |= (static_cast<uint64_t>(m_address[1]) << 48) & (shift << 48);
+    addr |= (static_cast<uint64_t>(m_address[0]) << 56) & (shift << 56);
+
+    return addr;
+}
+
 Mac64Address
 Mac64Address::Allocate()
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/mac64-address.h ns-3.39/src/network/utils/mac64-address.h
--- ns-3.38/src/network/utils/mac64-address.h	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/mac64-address.h	2023-07-05 14:23:48.065428300 -0700
@@ -49,11 +49,18 @@
     /**
      * \param str a string representing the new Mac64Address
      *
-     * The format of the string is "xx:xx:xx:xx:xx:xx"
+     * The format of the string is "xx:xx:xx:xx:xx:xx:xx:xx"
      */
     Mac64Address(const char* str);
 
     /**
+     * \param addr The 64 bit unsigned integer used to create a Mac64Address object.
+     *
+     * Create a Mac64Address from an 64 bit unsigned integer.
+     */
+    Mac64Address(uint64_t addr);
+
+    /**
      * \param buffer address in network order
      *
      * Copy the input address to our internal buffer.
@@ -88,6 +95,13 @@
     Address ConvertTo() const;
 
     /**
+     * \return the mac address in a 64 bit unsigned integer.
+     *
+     * Convert an instance of this class to a 64 bit unsigned integer.
+     */
+    uint64_t ConvertToInt() const;
+
+    /**
      * \param address address to test
      * \returns true if the address matches, false otherwise.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/packetbb.cc ns-3.39/src/network/utils/packetbb.cc
--- ns-3.38/src/network/utils/packetbb.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/packetbb.cc	2023-07-05 14:23:48.065428300 -0700
@@ -1508,7 +1508,6 @@
         break;
     default:
         return nullptr;
-        break;
     }
     newmsg->Deserialize(start);
     return newmsg;
@@ -2417,12 +2416,7 @@
         }
     }
 
-    if (m_addressTlvList != other.m_addressTlvList)
-    {
-        return false;
-    }
-
-    return true;
+    return m_addressTlvList == other.m_addressTlvList;
 }
 
 bool
@@ -2439,13 +2433,10 @@
     {
     case 0:
         return 0;
-        break;
     case 1:
         return AHAS_SINGLE_PRE_LEN;
-        break;
     default:
         return AHAS_MULTI_PRE_LEN;
-        break;
     }
 
     /* Quiet compiler */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/packet-socket.cc ns-3.39/src/network/utils/packet-socket.cc
--- ns-3.38/src/network/utils/packet-socket.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/packet-socket.cc	2023-07-05 14:23:48.065428300 -0700
@@ -87,14 +87,14 @@
     m_device = 0;
 }
 
-enum Socket::SocketErrno
+Socket::SocketErrno
 PacketSocket::GetErrno() const
 {
     NS_LOG_FUNCTION(this);
     return m_errno;
 }
 
-enum Socket::SocketType
+Socket::SocketType
 PacketSocket::GetSocketType() const
 {
     NS_LOG_FUNCTION(this);
@@ -520,11 +520,7 @@
 PacketSocket::SetAllowBroadcast(bool allowBroadcast)
 {
     NS_LOG_FUNCTION(this << allowBroadcast);
-    if (allowBroadcast)
-    {
-        return false;
-    }
-    return true;
+    return !allowBroadcast;
 }
 
 bool
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/pcap-file.cc ns-3.39/src/network/utils/pcap-file.cc
--- ns-3.38/src/network/utils/pcap-file.cc	2023-03-16 20:54:23.808869100 -0700
+++ ns-3.39/src/network/utils/pcap-file.cc	2023-07-05 14:23:48.065428300 -0700
@@ -225,7 +225,7 @@
     //
     PcapFileHeader* headerOut = nullptr;
 
-    if (m_swapMode == false)
+    if (!m_swapMode)
     {
         headerOut = &m_fileHeader;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/network/utils/simple-net-device.cc ns-3.39/src/network/utils/simple-net-device.cc
--- ns-3.38/src/network/utils/simple-net-device.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/network/utils/simple-net-device.cc	2023-07-05 14:23:48.065428300 -0700
@@ -373,11 +373,7 @@
 SimpleNetDevice::IsBroadcast() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_pointToPointMode)
-    {
-        return false;
-    }
-    return true;
+    return !m_pointToPointMode;
 }
 
 Address
@@ -391,11 +387,7 @@
 SimpleNetDevice::IsMulticast() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_pointToPointMode)
-    {
-        return false;
-    }
-    return true;
+    return !m_pointToPointMode;
 }
 
 Address
@@ -416,11 +408,7 @@
 SimpleNetDevice::IsPointToPoint() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_pointToPointMode)
-    {
-        return true;
-    }
-    return false;
+    return m_pointToPointMode;
 }
 
 bool
@@ -538,11 +526,7 @@
 SimpleNetDevice::NeedsArp() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_pointToPointMode)
-    {
-        return false;
-    }
-    return true;
+    return !m_pointToPointMode;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/nix-vector-routing/model/nix-vector-routing.cc ns-3.39/src/nix-vector-routing/model/nix-vector-routing.cc
--- ns-3.38/src/nix-vector-routing/model/nix-vector-routing.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/nix-vector-routing/model/nix-vector-routing.cc	2023-07-05 14:23:48.065428300 -0700
@@ -870,10 +870,10 @@
 NixVectorRouting<T>::RouteInput(Ptr<const Packet> p,
                                 const IpHeader& header,
                                 Ptr<const NetDevice> idev,
-                                UnicastForwardCallback ucb,
-                                MulticastForwardCallback mcb,
-                                LocalDeliverCallback lcb,
-                                ErrorCallback ecb)
+                                const UnicastForwardCallback& ucb,
+                                const MulticastForwardCallback& mcb,
+                                const LocalDeliverCallback& lcb,
+                                const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << p << header << header.GetSource() << header.GetDestination() << idev);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/nix-vector-routing/model/nix-vector-routing.h ns-3.39/src/nix-vector-routing/model/nix-vector-routing.h
--- ns-3.38/src/nix-vector-routing/model/nix-vector-routing.h	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/nix-vector-routing/model/nix-vector-routing.h	2023-07-05 14:23:48.065428300 -0700
@@ -365,10 +365,10 @@
     virtual bool RouteInput(Ptr<const Packet> p,
                             const IpHeader& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb);
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb);
 
     /**
      * \param interface the index of the interface we are being notified about
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/nix-vector-routing/test/examples-to-run.py ns-3.39/src/nix-vector-routing/test/examples-to-run.py
--- ns-3.38/src/nix-vector-routing/test/examples-to-run.py	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/nix-vector-routing/test/examples-to-run.py	2023-07-05 14:23:48.065428300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/doc/olsr.rst ns-3.39/src/olsr/doc/olsr.rst
--- ns-3.38/src/olsr/doc/olsr.rst	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/doc/olsr.rst	2023-07-05 14:23:48.065428300 -0700
@@ -115,7 +115,7 @@
 * TcInterval (time, default 5s), TC messages emission interval.
 * MidInterval (time, default 5s), MID messages emission interval.
 * HnaInterval (time, default 5s), HNA messages emission interval.
-* Willingness (enum, default OLSR_WILL_DEFAULT), Willingness of a node to carry and forward traffic for other nodes.
+* Willingness (enum, default olsr::Willingness::DEFAULT), Willingness of a node to carry and forward traffic for other nodes.
 
 Tracing
 +++++++
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/model/olsr-header.cc ns-3.39/src/olsr/model/olsr-header.cc
--- ns-3.38/src/olsr/model/olsr-header.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/model/olsr-header.cc	2023-07-05 14:23:48.069428200 -0700
@@ -399,7 +399,7 @@
 MessageHeader::Hello::Print(std::ostream& os) const
 {
     os << " Interval: " << +hTime << " (" << EmfToSeconds(hTime) << "s)";
-    os << " Willingness: " << +willingness;
+    os << " Willingness: " << willingness;
 
     for (const auto& ilinkMessage : linkMessages)
     {
@@ -430,7 +430,7 @@
 
     i.WriteU16(0); // Reserved
     i.WriteU8(this->hTime);
-    i.WriteU8(this->willingness);
+    i.WriteU8(static_cast<uint8_t>(this->willingness));
 
     for (std::vector<LinkMessage>::const_iterator iter = this->linkMessages.begin();
          iter != this->linkMessages.end();
@@ -470,7 +470,7 @@
 
     i.ReadNtohU16(); // Reserved
     this->hTime = i.ReadU8();
-    this->willingness = i.ReadU8();
+    this->willingness = Willingness(i.ReadU8());
 
     helloSizeLeft -= 4;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/model/olsr-header.h ns-3.39/src/olsr/model/olsr-header.h
--- ns-3.38/src/olsr/model/olsr-header.h	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/model/olsr-header.h	2023-07-05 14:23:48.069428200 -0700
@@ -20,6 +20,8 @@
 #ifndef OLSR_HEADER_H
 #define OLSR_HEADER_H
 
+#include "olsr-repositories.h"
+
 #include "ns3/header.h"
 #include "ns3/ipv4-address.h"
 #include "ns3/nstime.h"
@@ -412,8 +414,8 @@
             return Seconds(EmfToSeconds(this->hTime));
         }
 
-        uint8_t willingness; //!< The willingness of a node to carry and forward traffic for other
-                             //!< nodes.
+        Willingness willingness; //!< The willingness of a node to carry and forward traffic for
+                                 //!< other nodes.
         std::vector<LinkMessage> linkMessages; //!< Link messages container.
 
         /**
@@ -684,14 +686,14 @@
     }
 };
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const PacketHeader& packet)
 {
     packet.Print(os);
     return os;
 }
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const MessageHeader& message)
 {
     message.Print(os);
@@ -700,7 +702,7 @@
 
 typedef std::vector<MessageHeader> MessageList;
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const MessageList& messages)
 {
     os << "[";
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/model/olsr-repositories.h ns-3.39/src/olsr/model/olsr-repositories.h
--- ns-3.38/src/olsr/model/olsr-repositories.h	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/model/olsr-repositories.h	2023-07-05 14:23:48.069428200 -0700
@@ -25,6 +25,7 @@
 #include "ns3/ipv4-address.h"
 #include "ns3/nstime.h"
 
+#include <iostream>
 #include <set>
 #include <vector>
 
@@ -33,6 +34,52 @@
 namespace olsr
 {
 
+/**
+ * \ingroup olsr
+ *
+ * Willingness for forwarding packets from other nodes.
+ * The standard defines the following set of values.
+ * Values 0 - 7 are allowed by the standard, but this is not enforced in the code.
+ *
+ * See \RFC{3626} section 18.8
+ */
+enum Willingness : uint8_t
+{
+    NEVER = 0,
+    LOW = 1,
+    DEFAULT = 3, // medium
+    HIGH = 6,
+    ALWAYS = 7,
+};
+
+/**
+ * Stream insertion operator for OLSR willingness.
+ *
+ * \param os Output stream.
+ * \param willingness Willingness.
+ * \return A reference to the output stream.
+ */
+inline std::ostream&
+operator<<(std::ostream& os, Willingness willingness)
+{
+    switch (willingness)
+    {
+    case Willingness::NEVER:
+        return (os << "NEVER");
+    case Willingness::LOW:
+        return (os << "LOW");
+    case Willingness::DEFAULT:
+        return (os << "DEFAULT");
+    case Willingness::HIGH:
+        return (os << "HIGH");
+    case Willingness::ALWAYS:
+        return (os << "ALWAYS");
+    default:
+        return (os << static_cast<uint32_t>(willingness)); // Cast to uint32_t to print correctly
+    }
+    return os;
+}
+
 /// \ingroup olsr
 /// An Interface Association Tuple.
 struct IfaceAssocTuple
@@ -45,13 +92,13 @@
     Time time;
 };
 
-static inline bool
+inline bool
 operator==(const IfaceAssocTuple& a, const IfaceAssocTuple& b)
 {
     return (a.ifaceAddr == b.ifaceAddr && a.mainAddr == b.mainAddr);
 }
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const IfaceAssocTuple& tuple)
 {
     os << "IfaceAssocTuple(ifaceAddr=" << tuple.ifaceAddr << ", mainAddr=" << tuple.mainAddr
@@ -75,13 +122,13 @@
     Time time;
 };
 
-static inline bool
+inline bool
 operator==(const LinkTuple& a, const LinkTuple& b)
 {
     return (a.localIfaceAddr == b.localIfaceAddr && a.neighborIfaceAddr == b.neighborIfaceAddr);
 }
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const LinkTuple& tuple)
 {
     os << "LinkTuple(localIfaceAddr=" << tuple.localIfaceAddr
@@ -109,22 +156,22 @@
 
     /// A value between 0 and 7 specifying the node's willingness to carry traffic on behalf of
     /// other nodes.
-    uint8_t willingness;
+    Willingness willingness;
 };
 
-static inline bool
+inline bool
 operator==(const NeighborTuple& a, const NeighborTuple& b)
 {
     return (a.neighborMainAddr == b.neighborMainAddr && a.status == b.status &&
             a.willingness == b.willingness);
 }
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const NeighborTuple& tuple)
 {
     os << "NeighborTuple(neighborMainAddr=" << tuple.neighborMainAddr
        << ", status=" << (tuple.status == NeighborTuple::STATUS_SYM ? "SYM" : "NOT_SYM")
-       << ", willingness=" << (int)tuple.willingness << ")";
+       << ", willingness=" << tuple.willingness << ")";
     return os;
 }
 
@@ -140,7 +187,7 @@
     Time expirationTime; // previously called 'time_'
 };
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const TwoHopNeighborTuple& tuple)
 {
     os << "TwoHopNeighborTuple(neighborMainAddr=" << tuple.neighborMainAddr
@@ -149,7 +196,7 @@
     return os;
 }
 
-static inline bool
+inline bool
 operator==(const TwoHopNeighborTuple& a, const TwoHopNeighborTuple& b)
 {
     return (a.neighborMainAddr == b.neighborMainAddr &&
@@ -166,7 +213,7 @@
     Time expirationTime; // previously called 'time_'
 };
 
-static inline bool
+inline bool
 operator==(const MprSelectorTuple& a, const MprSelectorTuple& b)
 {
     return (a.mainAddr == b.mainAddr);
@@ -191,7 +238,7 @@
     Time expirationTime;
 };
 
-static inline bool
+inline bool
 operator==(const DuplicateTuple& a, const DuplicateTuple& b)
 {
     return (a.address == b.address && a.sequenceNumber == b.sequenceNumber);
@@ -211,14 +258,14 @@
     Time expirationTime;
 };
 
-static inline bool
+inline bool
 operator==(const TopologyTuple& a, const TopologyTuple& b)
 {
     return (a.destAddr == b.destAddr && a.lastAddr == b.lastAddr &&
             a.sequenceNumber == b.sequenceNumber);
 }
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const TopologyTuple& tuple)
 {
     os << "TopologyTuple(destAddr=" << tuple.destAddr << ", lastAddr=" << tuple.lastAddr
@@ -235,13 +282,13 @@
     Ipv4Mask netmask;        //!< IPv4 Network mask.
 };
 
-static inline bool
+inline bool
 operator==(const Association& a, const Association& b)
 {
     return (a.networkAddr == b.networkAddr && a.netmask == b.netmask);
 }
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const Association& tuple)
 {
     os << "Association(networkAddr=" << tuple.networkAddr << ", netmask=" << tuple.netmask << ")";
@@ -262,14 +309,14 @@
     Time expirationTime;
 };
 
-static inline bool
+inline bool
 operator==(const AssociationTuple& a, const AssociationTuple& b)
 {
     return (a.gatewayAddr == b.gatewayAddr && a.networkAddr == b.networkAddr &&
             a.netmask == b.netmask);
 }
 
-static inline std::ostream&
+inline std::ostream&
 operator<<(std::ostream& os, const AssociationTuple& tuple)
 {
     os << "AssociationTuple(gatewayAddr=" << tuple.gatewayAddr
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/model/olsr-routing-protocol.cc ns-3.39/src/olsr/model/olsr-routing-protocol.cc
--- ns-3.38/src/olsr/model/olsr-routing-protocol.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/model/olsr-routing-protocol.cc	2023-07-05 14:23:48.069428200 -0700
@@ -22,7 +22,7 @@
 ///
 /// \brief Implementation of OLSR agent and related classes.
 ///
-/// This is the main file of this software because %OLSR's behaviour is
+/// This is the main file of this software because OLSR's behaviour is
 /// implemented here.
 ///
 
@@ -34,6 +34,8 @@
 
 #include "olsr-routing-protocol.h"
 
+#include "olsr-repositories.h"
+
 #include "ns3/boolean.h"
 #include "ns3/enum.h"
 #include "ns3/inet-socket-address.h"
@@ -51,6 +53,7 @@
 #include "ns3/uinteger.h"
 
 #include <iomanip>
+#include <iostream>
 
 /********** Useful macros **********/
 
@@ -84,39 +87,6 @@
 /// HNA holding time.
 #define OLSR_HNA_HOLD_TIME Time(3 * m_hnaInterval)
 
-/********** Link types **********/
-
-/// Unspecified link type.
-#define OLSR_UNSPEC_LINK 0
-/// Asymmetric link type.
-#define OLSR_ASYM_LINK 1
-/// Symmetric link type.
-#define OLSR_SYM_LINK 2
-/// Lost link type.
-#define OLSR_LOST_LINK 3
-
-/********** Neighbor types **********/
-
-/// Not neighbor type.
-#define OLSR_NOT_NEIGH 0
-/// Symmetric neighbor type.
-#define OLSR_SYM_NEIGH 1
-/// Asymmetric neighbor type.
-#define OLSR_MPR_NEIGH 2
-
-/********** Willingness **********/
-
-/// Willingness for forwarding packets from other nodes: never.
-#define OLSR_WILL_NEVER 0
-/// Willingness for forwarding packets from other nodes: low.
-#define OLSR_WILL_LOW 1
-/// Willingness for forwarding packets from other nodes: medium.
-#define OLSR_WILL_DEFAULT 3
-/// Willingness for forwarding packets from other nodes: high.
-#define OLSR_WILL_HIGH 6
-/// Willingness for forwarding packets from other nodes: always.
-#define OLSR_WILL_ALWAYS 7
-
 /********** Miscellaneous constants **********/
 
 /// Maximum allowed jitter.
@@ -143,6 +113,81 @@
 namespace olsr
 {
 
+/**
+ * \ingroup olsr
+ *
+ * OLSR link types.
+ * See \RFC{3626} section 18.5.
+ */
+enum class LinkType : uint8_t
+{
+    UNSPEC_LINK = 0, //!< Unspecified link type
+    ASYM_LINK = 1,   //!< Asymmetric link type
+    SYM_LINK = 2,    //!< Symmetric link type
+    LOST_LINK = 3,   //!< Lost link type
+};
+
+/**
+ * Stream insertion operator for OLSR link type.
+ *
+ * \param os Output stream.
+ * \param linkType OLSR link type.
+ * \return A reference to the output stream.
+ */
+inline std::ostream&
+operator<<(std::ostream& os, LinkType linkType)
+{
+    switch (linkType)
+    {
+    case LinkType::UNSPEC_LINK:
+        return (os << "UNSPEC_LINK");
+    case LinkType::ASYM_LINK:
+        return (os << "ASYM_LINK");
+    case LinkType::SYM_LINK:
+        return (os << "SYM_LINK");
+    case LinkType::LOST_LINK:
+        return (os << "LOST_LINK");
+    default:
+        return (os << "Unknown link type");
+    }
+}
+
+/**
+ * \ingroup olsr
+ *
+ * OLSR neighbor types.
+ * See \RFC{3626} section 18.6.
+ */
+enum class NeighborType : uint8_t
+{
+    NOT_NEIGH = 0, //!< Not neighbor type
+    SYM_NEIGH = 1, //!< Symmetric neighbor type
+    MPR_NEIGH = 2, //!< Asymmetric neighbor type
+};
+
+/**
+ * Stream insertion operator for OLSR link type.
+ *
+ * \param os Output stream.
+ * \param neighborType OLSR neighbor type.
+ * \return A reference to the output stream.
+ */
+inline std::ostream&
+operator<<(std::ostream& os, NeighborType neighborType)
+{
+    switch (neighborType)
+    {
+    case NeighborType::NOT_NEIGH:
+        return (os << "NOT_NEIGH");
+    case NeighborType::SYM_NEIGH:
+        return (os << "SYM_NEIGH");
+    case NeighborType::MPR_NEIGH:
+        return (os << "MPR_NEIGH");
+    default:
+        return (os << "Unknown neighbor type");
+    }
+}
+
 /********** OLSR class **********/
 
 NS_OBJECT_ENSURE_REGISTERED(RoutingProtocol);
@@ -180,17 +225,17 @@
                           MakeTimeChecker())
             .AddAttribute("Willingness",
                           "Willingness of a node to carry and forward traffic for other nodes.",
-                          EnumValue(OLSR_WILL_DEFAULT),
+                          EnumValue(Willingness::DEFAULT),
                           MakeEnumAccessor(&RoutingProtocol::m_willingness),
-                          MakeEnumChecker(OLSR_WILL_NEVER,
+                          MakeEnumChecker(Willingness::NEVER,
                                           "never",
-                                          OLSR_WILL_LOW,
+                                          Willingness::LOW,
                                           "low",
-                                          OLSR_WILL_DEFAULT,
+                                          Willingness::DEFAULT,
                                           "default",
-                                          OLSR_WILL_HIGH,
+                                          Willingness::HIGH,
                                           "high",
-                                          OLSR_WILL_ALWAYS,
+                                          Willingness::ALWAYS,
                                           "always"))
             .AddTraceSource("Rx",
                             "Receive OLSR packet.",
@@ -696,7 +741,7 @@
 
     // N2 is the set of 2-hop neighbors reachable from "the interface
     // I", excluding:
-    // (i)   the nodes only reachable by members of N with willingness WILL_NEVER
+    // (i)   the nodes only reachable by members of N with willingness Willingness::NEVER
     // (ii)  the node performing the computation
     // (iii) all the symmetric neighbors: the nodes for which there exists a symmetric
     //       link to this node on some interface.
@@ -713,22 +758,14 @@
         }
 
         //  excluding:
-        // (i)   the nodes only reachable by members of N with willingness WILL_NEVER
+        // (i)   the nodes only reachable by members of N with willingness Willingness::NEVER
         bool ok = false;
         for (NeighborSet::const_iterator neigh = N.begin(); neigh != N.end(); neigh++)
         {
             if (neigh->neighborMainAddr == twoHopNeigh->neighborMainAddr)
             {
-                if (neigh->willingness == OLSR_WILL_NEVER)
-                {
-                    ok = false;
-                    break;
-                }
-                else
-                {
-                    ok = true;
-                    break;
-                }
+                ok = (neigh->willingness != Willingness::NEVER);
+                break;
             }
         }
         if (!ok)
@@ -774,10 +811,10 @@
 #endif // NS3_LOG_ENABLE
 
     // 1. Start with an MPR set made of all members of N with
-    // N_willingness equal to WILL_ALWAYS
+    // N_willingness equal to Willingness::ALWAYS
     for (NeighborSet::const_iterator neighbor = N.begin(); neighbor != N.end(); neighbor++)
     {
-        if (neighbor->willingness == OLSR_WILL_ALWAYS)
+        if (neighbor->willingness == Willingness::ALWAYS)
         {
             mprSet.insert(neighbor->neighborMainAddr);
             // (not in RFC but I think is needed: remove the 2-hop
@@ -1058,7 +1095,7 @@
     //  neighbor node or the node itself, and such that there exist at
     //  least one entry in the 2-hop neighbor set where
     //  N_neighbor_main_addr correspond to a neighbor node with
-    //  willingness different of WILL_NEVER,
+    //  willingness different of Willingness::NEVER,
     const TwoHopNeighborSet& twoHopNeighbors = m_state.GetTwoHopNeighbors();
     for (TwoHopNeighborSet::const_iterator it = twoHopNeighbors.begin();
          it != twoHopNeighbors.end();
@@ -1083,14 +1120,14 @@
 
         // ...and such that there exist at least one entry in the 2-hop
         // neighbor set where N_neighbor_main_addr correspond to a
-        // neighbor node with willingness different of WILL_NEVER...
+        // neighbor node with willingness different of Willingness::NEVER...
         bool nb2hopOk = false;
         for (NeighborSet::const_iterator neighbor = neighborSet.begin();
              neighbor != neighborSet.end();
              neighbor++)
         {
             if (neighbor->neighborMainAddr == nb2hop_tuple.neighborMainAddr &&
-                neighbor->willingness != OLSR_WILL_NEVER)
+                neighbor->willingness != Willingness::NEVER)
             {
                 nb2hopOk = true;
                 break;
@@ -1746,26 +1783,26 @@
             continue;
         }
 
-        uint8_t link_type;
-        uint8_t nb_type = 0xff;
+        LinkType linkType;
+        NeighborType neighborType;
 
         // Establishes link type
         if (link_tuple->symTime >= now)
         {
-            link_type = OLSR_SYM_LINK;
+            linkType = LinkType::SYM_LINK;
         }
         else if (link_tuple->asymTime >= now)
         {
-            link_type = OLSR_ASYM_LINK;
+            linkType = LinkType::ASYM_LINK;
         }
         else
         {
-            link_type = OLSR_LOST_LINK;
+            linkType = LinkType::LOST_LINK;
         }
         // Establishes neighbor type.
         if (m_state.FindMprAddress(GetMainAddress(link_tuple->neighborIfaceAddr)))
         {
-            nb_type = OLSR_MPR_NEIGH;
+            neighborType = NeighborType::MPR_NEIGH;
             NS_LOG_DEBUG("I consider neighbor " << GetMainAddress(link_tuple->neighborIfaceAddr)
                                                 << " to be MPR_NEIGH.");
         }
@@ -1783,11 +1820,11 @@
                         NS_LOG_DEBUG("I consider neighbor "
                                      << GetMainAddress(link_tuple->neighborIfaceAddr)
                                      << " to be SYM_NEIGH.");
-                        nb_type = OLSR_SYM_NEIGH;
+                        neighborType = NeighborType::SYM_NEIGH;
                     }
                     else if (nb_tuple->status == NeighborTuple::STATUS_NOT_SYM)
                     {
-                        nb_type = OLSR_NOT_NEIGH;
+                        neighborType = NeighborType::NOT_NEIGH;
                         NS_LOG_DEBUG("I consider neighbor "
                                      << GetMainAddress(link_tuple->neighborIfaceAddr)
                                      << " to be NOT_NEIGH.");
@@ -1809,7 +1846,8 @@
         }
 
         olsr::MessageHeader::Hello::LinkMessage linkMessage;
-        linkMessage.linkCode = (link_type & 0x03) | ((nb_type << 2) & 0x0f);
+        linkMessage.linkCode = (static_cast<uint8_t>(linkType) & 0x03) |
+                               ((static_cast<uint8_t>(neighborType) << 2) & 0x0f);
         linkMessage.neighborInterfaceAddresses.push_back(link_tuple->neighborIfaceAddr);
 
         std::vector<Ipv4Address> interfaces =
@@ -2055,53 +2093,16 @@
          linkMessage != hello.linkMessages.end();
          linkMessage++)
     {
-        int lt = linkMessage->linkCode & 0x03;        // Link Type
-        int nt = (linkMessage->linkCode >> 2) & 0x03; // Neighbor Type
-
-#ifdef NS3_LOG_ENABLE
-        const char* linkTypeName;
-        switch (lt)
-        {
-        case OLSR_UNSPEC_LINK:
-            linkTypeName = "UNSPEC_LINK";
-            break;
-        case OLSR_ASYM_LINK:
-            linkTypeName = "ASYM_LINK";
-            break;
-        case OLSR_SYM_LINK:
-            linkTypeName = "SYM_LINK";
-            break;
-        case OLSR_LOST_LINK:
-            linkTypeName = "LOST_LINK";
-            break;
-        default:
-            linkTypeName = "(invalid value!)";
-        }
-
-        const char* neighborTypeName;
-        switch (nt)
-        {
-        case OLSR_NOT_NEIGH:
-            neighborTypeName = "NOT_NEIGH";
-            break;
-        case OLSR_SYM_NEIGH:
-            neighborTypeName = "SYM_NEIGH";
-            break;
-        case OLSR_MPR_NEIGH:
-            neighborTypeName = "MPR_NEIGH";
-            break;
-        default:
-            neighborTypeName = "(invalid value!)";
-        }
+        LinkType linkType = LinkType(linkMessage->linkCode & 0x03);
+        NeighborType neighborType = NeighborType((linkMessage->linkCode >> 2) & 0x03);
 
         NS_LOG_DEBUG("Looking at HELLO link messages with Link Type "
-                     << lt << " (" << linkTypeName << ") and Neighbor Type " << nt << " ("
-                     << neighborTypeName << ")");
-#endif // NS3_LOG_ENABLE
+                     << linkType << " and Neighbor Type " << neighborType);
 
         // We must not process invalid advertised links
-        if ((lt == OLSR_SYM_LINK && nt == OLSR_NOT_NEIGH) ||
-            (nt != OLSR_SYM_NEIGH && nt != OLSR_MPR_NEIGH && nt != OLSR_NOT_NEIGH))
+        if ((linkType == LinkType::SYM_LINK && neighborType == NeighborType::NOT_NEIGH) ||
+            (neighborType != NeighborType::SYM_NEIGH && neighborType != NeighborType::MPR_NEIGH &&
+             neighborType != NeighborType::NOT_NEIGH))
         {
             NS_LOG_LOGIC("HELLO link code is invalid => IGNORING");
             continue;
@@ -2115,13 +2116,13 @@
             NS_LOG_DEBUG("   -> Neighbor: " << *neighIfaceAddr);
             if (*neighIfaceAddr == receiverIface)
             {
-                if (lt == OLSR_LOST_LINK)
+                if (linkType == LinkType::LOST_LINK)
                 {
                     NS_LOG_LOGIC("link is LOST => expiring it");
                     link_tuple->symTime = now - Seconds(1);
                     updated = true;
                 }
-                else if (lt == OLSR_SYM_LINK || lt == OLSR_ASYM_LINK)
+                else if (linkType == LinkType::SYM_LINK || linkType == LinkType::ASYM_LINK)
                 {
                     NS_LOG_DEBUG(*link_tuple << ": link is SYM or ASYM => should become SYM now"
                                                 " (symTime being increased to "
@@ -2211,14 +2212,9 @@
              linkMessage != hello.linkMessages.end();
              linkMessage++)
         {
-            int neighborType = (linkMessage->linkCode >> 2) & 0x3;
-#ifdef NS3_LOG_ENABLE
-            const char* neighborTypeNames[3] = {"NOT_NEIGH", "SYM_NEIGH", "MPR_NEIGH"};
-            const char* neighborTypeName =
-                ((neighborType < 3) ? neighborTypeNames[neighborType] : "(invalid value)");
-            NS_LOG_DEBUG("Looking at Link Message from HELLO message: neighborType="
-                         << neighborType << " (" << neighborTypeName << ")");
-#endif // NS3_LOG_ENABLE
+            NeighborType neighborType = NeighborType((linkMessage->linkCode >> 2) & 0x3);
+            NS_LOG_DEBUG(
+                "Looking at Link Message from HELLO message: neighborType=" << neighborType);
 
             for (std::vector<Ipv4Address>::const_iterator nb2hop_addr_iter =
                      linkMessage->neighborInterfaceAddresses.begin();
@@ -2228,7 +2224,8 @@
                 Ipv4Address nb2hop_addr = GetMainAddress(*nb2hop_addr_iter);
                 NS_LOG_DEBUG("Looking at 2-hop neighbor address from HELLO message: "
                              << *nb2hop_addr_iter << " (main address is " << nb2hop_addr << ")");
-                if (neighborType == OLSR_SYM_NEIGH || neighborType == OLSR_MPR_NEIGH)
+                if (neighborType == NeighborType::SYM_NEIGH ||
+                    neighborType == NeighborType::MPR_NEIGH)
                 {
                     // If the main address of the 2-hop neighbor address == main address
                     // of the receiving node, silently discard the 2-hop
@@ -2263,7 +2260,7 @@
                         nb2hop_tuple->expirationTime = now + msg.GetVTime();
                     }
                 }
-                else if (neighborType == OLSR_NOT_NEIGH)
+                else if (neighborType == NeighborType::NOT_NEIGH)
                 {
                     // For each 2-hop node listed in the HELLO message
                     // with Neighbor Type equal to NOT_NEIGH all 2-hop
@@ -2300,8 +2297,8 @@
          linkMessage != hello.linkMessages.end();
          linkMessage++)
     {
-        int nt = linkMessage->linkCode >> 2;
-        if (nt == OLSR_MPR_NEIGH)
+        NeighborType neighborType = NeighborType(linkMessage->linkCode >> 2);
+        if (neighborType == NeighborType::MPR_NEIGH)
         {
             NS_LOG_DEBUG("Processing a link message with neighbor type MPR_NEIGH");
 
@@ -2386,7 +2383,7 @@
 {
     NS_LOG_DEBUG(Simulator::Now().As(Time::S) << ": OLSR Node " << m_mainAddress << " LinkTuple "
                                               << tuple.neighborIfaceAddr << " -> neighbor loss.");
-    LinkTupleUpdated(tuple, OLSR_WILL_DEFAULT);
+    LinkTupleUpdated(tuple, Willingness::DEFAULT);
     m_state.EraseTwoHopNeighborTuples(GetMainAddress(tuple.neighborIfaceAddr));
     m_state.EraseMprSelectorTuples(GetMainAddress(tuple.neighborIfaceAddr));
 
@@ -2417,7 +2414,7 @@
 }
 
 void
-RoutingProtocol::LinkTupleAdded(const LinkTuple& tuple, uint8_t willingness)
+RoutingProtocol::LinkTupleAdded(const LinkTuple& tuple, Willingness willingness)
 {
     // Creates associated neighbor tuple
     NeighborTuple nb_tuple;
@@ -2447,7 +2444,7 @@
 }
 
 void
-RoutingProtocol::LinkTupleUpdated(const LinkTuple& tuple, uint8_t willingness)
+RoutingProtocol::LinkTupleUpdated(const LinkTuple& tuple, Willingness willingness)
 {
     // Each time a link tuple changes, the associated neighbor tuple must be recomputed
 
@@ -3005,10 +3002,10 @@
 RoutingProtocol::RouteInput(Ptr<const Packet> p,
                             const Ipv4Header& header,
                             Ptr<const NetDevice> idev,
-                            UnicastForwardCallback ucb,
-                            MulticastForwardCallback mcb,
-                            LocalDeliverCallback lcb,
-                            ErrorCallback ecb)
+                            const UnicastForwardCallback& ucb,
+                            const MulticastForwardCallback& mcb,
+                            const LocalDeliverCallback& lcb,
+                            const ErrorCallback& ecb)
 {
     NS_LOG_FUNCTION(this << " " << m_ipv4->GetObject<Node>()->GetId() << " "
                          << header.GetDestination());
@@ -3017,7 +3014,7 @@
     Ipv4Address origin = header.GetSource();
 
     // Consume self-originated packets
-    if (IsMyOwnAddress(origin) == true)
+    if (IsMyOwnAddress(origin))
     {
         return true;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/model/olsr-routing-protocol.h ns-3.39/src/olsr/model/olsr-routing-protocol.h
--- ns-3.38/src/olsr/model/olsr-routing-protocol.h	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/model/olsr-routing-protocol.h	2023-07-05 14:23:48.069428200 -0700
@@ -56,7 +56,7 @@
 /// functional description, please refer to the ns-3 manual.
 
 /// \ingroup olsr
-/// An %OLSR's routing table entry.
+/// An OLSR's routing table entry.
 struct RoutingTableEntry
 {
     Ipv4Address destAddr; //!< Address of the destination node.
@@ -225,12 +225,12 @@
 
     /**
      * \brief Associates the specified Ipv4StaticRouting routing table
-     *         to the OLSR routing protocol. Entries from this associated
-     *         routing table that use non-olsr outgoing interfaces are added
-     *         to the list of local HNA associations so that they are included
-     *         in HNA messages sent by the node.
-     *         If this method is called more than once, entries from the old
-     *         association are deleted before entries from the new one are added.
+     *        to the OLSR routing protocol. Entries from this associated
+     *        routing table that use non-olsr outgoing interfaces are added
+     *        to the list of local HNA associations so that they are included
+     *        in HNA messages sent by the node.
+     *        If this method is called more than once, entries from the old
+     *        association are deleted before entries from the new one are added.
      * \param routingTable the Ipv4StaticRouting routing table to be associated.
      */
     void SetRoutingTableAssociation(Ptr<Ipv4StaticRouting> routingTable);
@@ -256,11 +256,11 @@
     uint16_t m_messageSequenceNumber; //!< Messages sequence number counter.
     uint16_t m_ansn;                  //!< Advertised Neighbor Set sequence number.
 
-    Time m_helloInterval;  //!< HELLO messages' emission interval.
-    Time m_tcInterval;     //!< TC messages' emission interval.
-    Time m_midInterval;    //!< MID messages' emission interval.
-    Time m_hnaInterval;    //!< HNA messages' emission interval.
-    uint8_t m_willingness; //!<  Willingness for forwarding packets on behalf of other nodes.
+    Time m_helloInterval;      //!< HELLO messages' emission interval.
+    Time m_tcInterval;         //!< TC messages' emission interval.
+    Time m_midInterval;        //!< MID messages' emission interval.
+    Time m_hnaInterval;        //!< HNA messages' emission interval.
+    Willingness m_willingness; //!< Willingness for forwarding packets on behalf of other nodes.
 
     OlsrState m_state; //!< Internal state with all needed data structs.
     Ptr<Ipv4> m_ipv4;  //!< IPv4 object the routing is linked to.
@@ -350,10 +350,10 @@
     bool RouteInput(Ptr<const Packet> p,
                     const Ipv4Header& header,
                     Ptr<const NetDevice> idev,
-                    UnicastForwardCallback ucb,
-                    MulticastForwardCallback mcb,
-                    LocalDeliverCallback lcb,
-                    ErrorCallback ecb) override;
+                    const UnicastForwardCallback& ucb,
+                    const MulticastForwardCallback& mcb,
+                    const LocalDeliverCallback& lcb,
+                    const ErrorCallback& ecb) override;
     void SetIpv4(Ptr<Ipv4> ipv4) override;
 
     void PrintRoutingTable(Ptr<OutputStreamWrapper> stream,
@@ -391,7 +391,7 @@
     void RecvOlsr(Ptr<Socket> socket);
 
     /**
-     * \brief Computates MPR set of a node following \RFC{3626} hints.
+     * \brief Computes MPR set of a node following \RFC{3626} hints.
      */
     void MprComputation();
 
@@ -547,7 +547,7 @@
                         const Ipv4Address& senderAddress);
 
     /**
-     * \brief Enques an %OLSR message which will be sent with a delay of (0, delay].
+     * \brief Enqueues an %OLSR message which will be sent with a delay of (0, delay].
      *
      * This buffering system is used in order to piggyback several %OLSR messages in
      * a same %OLSR packet.
@@ -611,10 +611,10 @@
 
     /**
      * Adds a link tuple.
-     * \param tuple Thetuple to be added.
+     * \param tuple The tuple to be added.
      * \param willingness The tuple willingness.
      */
-    void LinkTupleAdded(const LinkTuple& tuple, uint8_t willingness);
+    void LinkTupleAdded(const LinkTuple& tuple, Willingness willingness);
 
     /**
      * \brief Removes a link tuple from the Link Set.
@@ -630,7 +630,7 @@
      * \param tuple The link tuple which has been updated.
      * \param willingness The tuple willingness.
      */
-    void LinkTupleUpdated(const LinkTuple& tuple, uint8_t willingness);
+    void LinkTupleUpdated(const LinkTuple& tuple, Willingness willingness);
 
     /**
      * \brief Adds a neighbor tuple to the Neighbor Set.
@@ -832,7 +832,7 @@
     /// Tx packet trace.
     TracedCallback<const PacketHeader&, const MessageList&> m_txPacketTrace;
 
-    /// Routing table chanes challback
+    /// Routing table changes callback
     TracedCallback<uint32_t> m_routingTableChanged;
 
     /// Provides uniform random variables.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/model/olsr-state.cc ns-3.39/src/olsr/model/olsr-state.cc
--- ns-3.38/src/olsr/model/olsr-state.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/model/olsr-state.cc	2023-07-05 14:23:48.069428200 -0700
@@ -132,7 +132,7 @@
 }
 
 NeighborTuple*
-OlsrState::FindNeighborTuple(const Ipv4Address& mainAddr, uint8_t willingness)
+OlsrState::FindNeighborTuple(const Ipv4Address& mainAddr, Willingness willingness)
 {
     for (NeighborSet::iterator it = m_neighborSet.begin(); it != m_neighborSet.end(); it++)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/model/olsr-state.h ns-3.39/src/olsr/model/olsr-state.h
--- ns-3.38/src/olsr/model/olsr-state.h	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/model/olsr-state.h	2023-07-05 14:23:48.069428200 -0700
@@ -137,7 +137,7 @@
      * \param willingness The neighbor willingness.
      * \returns The neighbor tuple, if found. Else it returns a null pointer.
      */
-    NeighborTuple* FindNeighborTuple(const Ipv4Address& mainAddr, uint8_t willingness);
+    NeighborTuple* FindNeighborTuple(const Ipv4Address& mainAddr, Willingness willingness);
 
     /**
      * Erases a neighbor tuple.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/test/examples-to-run.py ns-3.39/src/olsr/test/examples-to-run.py
--- ns-3.38/src/olsr/test/examples-to-run.py	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/test/examples-to-run.py	2023-07-05 14:23:48.069428200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/test/olsr-header-test-suite.cc ns-3.39/src/olsr/test/olsr-header-test-suite.cc
--- ns-3.38/src/olsr/test/olsr-header-test-suite.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/test/olsr-header-test-suite.cc	2023-07-05 14:23:48.069428200 -0700
@@ -18,6 +18,7 @@
  */
 
 #include "ns3/olsr-header.h"
+#include "ns3/olsr-repositories.h"
 #include "ns3/packet.h"
 #include "ns3/test.h"
 
@@ -191,7 +192,7 @@
     olsr::MessageHeader::Hello& helloIn = msgIn.GetHello();
 
     helloIn.SetHTime(Seconds(7));
-    helloIn.willingness = 66;
+    helloIn.willingness = olsr::Willingness::HIGH;
 
     {
         olsr::MessageHeader::Hello::LinkMessage lm1;
@@ -214,7 +215,7 @@
     olsr::MessageHeader::Hello& helloOut = msgOut.GetHello();
 
     NS_TEST_ASSERT_MSG_EQ(helloOut.GetHTime(), Seconds(7), "300");
-    NS_TEST_ASSERT_MSG_EQ(helloOut.willingness, 66, "301");
+    NS_TEST_ASSERT_MSG_EQ(helloOut.willingness, olsr::Willingness::HIGH, "301");
     NS_TEST_ASSERT_MSG_EQ(helloOut.linkMessages.size(), 2, "302");
 
     NS_TEST_ASSERT_MSG_EQ(helloOut.linkMessages[0].linkCode, 2, "303");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/olsr/test/olsr-routing-protocol-test-suite.cc ns-3.39/src/olsr/test/olsr-routing-protocol-test-suite.cc
--- ns-3.38/src/olsr/test/olsr-routing-protocol-test-suite.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/olsr/test/olsr-routing-protocol-test-suite.cc	2023-07-05 14:23:48.069428200 -0700
@@ -20,6 +20,7 @@
  */
 
 #include "ns3/ipv4-header.h"
+#include "ns3/olsr-repositories.h"
 #include "ns3/olsr-routing-protocol.h"
 #include "ns3/test.h"
 
@@ -28,19 +29,6 @@
  * \defgroup olsr-test olsr module tests
  */
 
-/********** Willingness **********/
-
-/// Willingness for forwarding packets from other nodes: never.
-#define OLSR_WILL_NEVER 0
-/// Willingness for forwarding packets from other nodes: low.
-#define OLSR_WILL_LOW 1
-/// Willingness for forwarding packets from other nodes: medium.
-#define OLSR_WILL_DEFAULT 3
-/// Willingness for forwarding packets from other nodes: high.
-#define OLSR_WILL_HIGH 6
-/// Willingness for forwarding packets from other nodes: always.
-#define OLSR_WILL_ALWAYS 7
-
 using namespace ns3;
 using namespace olsr;
 
@@ -83,7 +71,7 @@
      */
     NeighborTuple neighbor;
     neighbor.status = NeighborTuple::STATUS_SYM;
-    neighbor.willingness = OLSR_WILL_DEFAULT;
+    neighbor.willingness = Willingness::DEFAULT;
     neighbor.neighborMainAddr = Ipv4Address("10.0.0.2");
     protocol->m_state.InsertNeighborTuple(neighbor);
     neighbor.neighborMainAddr = Ipv4Address("10.0.0.3");
@@ -139,7 +127,7 @@
                           true,
                           "Node 1 must select node 3 as MPR");
     /*
-     *  7 (OLSR_WILL_ALWAYS)
+     *  7 (Willingness::ALWAYS)
      *  |
      *  1 -- 2 -- 5
      *  |    |
@@ -147,9 +135,9 @@
      *  |
      *  6
      *
-     * Node 1 must select nodes 2, 3 and 7 (since it is WILL_ALWAYS) as MPRs.
+     * Node 1 must select nodes 2, 3 and 7 (since it is Willingness::ALWAYS) as MPRs.
      */
-    neighbor.willingness = OLSR_WILL_ALWAYS;
+    neighbor.willingness = olsr::Willingness::ALWAYS;
     neighbor.neighborMainAddr = Ipv4Address("10.0.0.7");
     protocol->m_state.InsertNeighborTuple(neighbor);
 
@@ -160,18 +148,18 @@
                           true,
                           "Node 1 must select node 7 as MPR");
     /*
-     *                7 <- WILL_ALWAYS
-     *                |
-     *      9 -- 8 -- 1 -- 2 -- 5
-     *                |    |
-     *           ^    3 -- 4
-     *           |    |
-     *   WILL_NEVER   6
+     *                        7 <- Willingness::ALWAYS
+     *                        |
+     *              9 -- 8 -- 1 -- 2 -- 5
+     *                        |    |
+     *                   ^    3 -- 4
+     *                   |    |
+     *   Willingness::NEVER   6
      *
-     * Node 1 must select nodes 2, 3 and 7 (since it is WILL_ALWAYS) as MPRs.
-     * Node 1 must NOT select node 8 as MPR since it is WILL_NEVER
+     * Node 1 must select nodes 2, 3 and 7 (since it is Willingness::ALWAYS) as MPRs.
+     * Node 1 must NOT select node 8 as MPR since it is Willingness::NEVER
      */
-    neighbor.willingness = OLSR_WILL_NEVER;
+    neighbor.willingness = Willingness::NEVER;
     neighbor.neighborMainAddr = Ipv4Address("10.0.0.8");
     protocol->m_state.InsertNeighborTuple(neighbor);
     tuple.neighborMainAddr = Ipv4Address("10.0.0.8");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/openflow/examples/openflow-switch.cc ns-3.39/src/openflow/examples/openflow-switch.cc
--- ns-3.38/src/openflow/examples/openflow-switch.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/openflow/examples/openflow-switch.cc	2023-07-05 14:23:48.069428200 -0700
@@ -1,4 +1,5 @@
 /*
+ *
  * 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;
@@ -11,6 +12,9 @@
  * 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: Blake Hurd <naimorai@gmail.com>
+ * Modified by: Josh Pelkey <joshpelkey@gmail.com>
  */
 
 // Network topology
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/openflow/examples/openflow-switch.py ns-3.39/src/openflow/examples/openflow-switch.py
--- ns-3.38/src/openflow/examples/openflow-switch.py	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/openflow/examples/openflow-switch.py	2023-07-05 14:23:48.069428200 -0700
@@ -0,0 +1,85 @@
+#
+#
+# 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: Blake Hurd <naimorai@gmail.com>
+# Modified by: Josh Pelkey <joshpelkey@gmail.com>
+#              Gabriel Ferreira <gabrielcarvfer@gmail.com>
+#
+
+from ns import ns
+
+ns.LogComponentEnable("OpenFlowInterface", ns.LOG_LEVEL_ALL)
+ns.LogComponentEnable("OpenFlowSwitchNetDevice", ns.LOG_LEVEL_ALL)
+
+terminals = ns.NodeContainer()
+terminals.Create(4)
+
+csmaSwitch = ns.NodeContainer()
+csmaSwitch.Create(1)
+
+csma = ns.CsmaHelper()
+csma.SetChannelAttribute("DataRate", ns.DataRateValue(5000000))
+csma.SetChannelAttribute("Delay", ns.TimeValue(ns.MilliSeconds(2)))
+
+terminalDevices = ns.NetDeviceContainer()
+switchDevices = ns.NetDeviceContainer()
+for i in range(4):
+    container = ns.NodeContainer()
+    container.Add(terminals.Get(i))
+    container.Add(csmaSwitch)
+    link = csma.Install(container)
+    terminalDevices.Add(link.Get(0))
+    switchDevices.Add(link.Get(1))
+
+switchNode = csmaSwitch.Get(0)
+swtch = ns.OpenFlowSwitchHelper()
+controller = ns.ofi.DropController()
+# controller = ns.CreateObject("ns3::ofi::LearningController")
+swtch.Install(switchNode, switchDevices, controller)
+# controller->SetAttribute("ExpirationTime", TimeValue(timeout))
+
+
+internet = ns.InternetStackHelper()
+internet.Install(terminals)
+
+ipv4 = ns.Ipv4AddressHelper()
+ipv4.SetBase("10.1.1.0", "255.255.255.0")
+ipv4.Assign(terminalDevices)
+
+port = 9
+
+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))
+
+app.Start(ns.Seconds(1.0))
+app.Stop(ns.Seconds(10.0))
+
+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()))
+app = onoff.Install(terminals.Get(3))
+app.Start(ns.Seconds(1.1))
+app.Stop(ns.Seconds(10.0))
+
+app = sink.Install(terminals.Get(0))
+app.Start(ns.Seconds(0.0))
+
+ns.Simulator.Run()
+ns.Simulator.Destroy()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/openflow/model/openflow-switch-net-device.cc ns-3.39/src/openflow/model/openflow-switch-net-device.cc
--- ns-3.38/src/openflow/model/openflow-switch-net-device.cc	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/openflow/model/openflow-switch-net-device.cc	2023-07-05 14:23:48.069428200 -0700
@@ -984,7 +984,7 @@
 }
 
 void
-OpenFlowSwitchNetDevice::SendFlowExpired(sw_flow* flow, enum ofp_flow_expired_reason reason)
+OpenFlowSwitchNetDevice::SendFlowExpired(sw_flow* flow, ofp_flow_expired_reason reason)
 {
     ofpbuf* buffer;
     ofp_flow_expired* ofe =
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/openflow/model/openflow-switch-net-device.h ns-3.39/src/openflow/model/openflow-switch-net-device.h
--- ns-3.38/src/openflow/model/openflow-switch-net-device.h	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/openflow/model/openflow-switch-net-device.h	2023-07-05 14:23:48.069428200 -0700
@@ -400,7 +400,7 @@
      * \param flow The flow that expired.
      * \param reason The reason for sending this expiration notification.
      */
-    void SendFlowExpired(sw_flow* flow, enum ofp_flow_expired_reason reason);
+    void SendFlowExpired(sw_flow* flow, ofp_flow_expired_reason reason);
 
     /**
      * Send a reply about a Port's status to the controller.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/openflow/test/examples-to-run.py ns-3.39/src/openflow/test/examples-to-run.py
--- ns-3.38/src/openflow/test/examples-to-run.py	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/openflow/test/examples-to-run.py	2023-07-05 14:23:48.069428200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
@@ -17,4 +16,6 @@
 #     (example_name, do_run).
 #
 # See test.py for more information.
-python_examples = []
+python_examples = [
+    ("openflow-switch.py", "ENABLE_OPENFLOW == True"),
+]
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/point-to-point/model/point-to-point-net-device.cc ns-3.39/src/point-to-point/model/point-to-point-net-device.cc
--- ns-3.38/src/point-to-point/model/point-to-point-net-device.cc	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/point-to-point/model/point-to-point-net-device.cc	2023-07-05 14:23:48.069428200 -0700
@@ -256,7 +256,7 @@
     Simulator::Schedule(txCompleteTime, &PointToPointNetDevice::TransmitComplete, this);
 
     bool result = m_channel->TransmitStart(p, this, txTime);
-    if (result == false)
+    if (!result)
     {
         m_phyTxDropTrace(p);
     }
@@ -520,7 +520,7 @@
     // If IsLinkUp() is false it means there is no channel to send any packet
     // over so we just hit the drop trace on the packet and return an error.
     //
-    if (IsLinkUp() == false)
+    if (!IsLinkUp())
     {
         m_macTxDropTrace(packet);
         return false;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/point-to-point/test/examples-to-run.py ns-3.39/src/point-to-point/test/examples-to-run.py
--- ns-3.38/src/point-to-point/test/examples-to-run.py	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/point-to-point/test/examples-to-run.py	2023-07-05 14:23:48.069428200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/point-to-point-layout/doc/point-to-point-dumbbell.rst ns-3.39/src/point-to-point-layout/doc/point-to-point-dumbbell.rst
--- ns-3.38/src/point-to-point-layout/doc/point-to-point-dumbbell.rst	2023-03-16 20:54:23.812869000 -0700
+++ ns-3.39/src/point-to-point-layout/doc/point-to-point-dumbbell.rst	2023-07-05 14:23:48.069428200 -0700
@@ -38,7 +38,7 @@
 objects for the left, right and router nodes for assigning IPv4 address to
 each node. It provides a function ``AssignIpv6Addresses`` which takes a
 ``Ipv6Address`` address base and a ``Ipv6Prefix`` prefix for assigning
-IPv6Adddress to every node.
+IPv6Address to every node.
 
 Node Positioning
 ================
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/propagation/model/jakes-process.cc ns-3.39/src/propagation/model/jakes-process.cc
--- ns-3.38/src/propagation/model/jakes-process.cc	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/propagation/model/jakes-process.cc	2023-07-05 14:23:48.073428200 -0700
@@ -143,12 +143,12 @@
 std::complex<double>
 JakesProcess::GetComplexGain() const
 {
-    std::complex<double> sumAplitude = std::complex<double>(0, 0);
+    std::complex<double> sumAmplitude = std::complex<double>(0, 0);
     for (unsigned int i = 0; i < m_oscillators.size(); i++)
     {
-        sumAplitude += m_oscillators[i].GetValueAt(Now());
+        sumAmplitude += m_oscillators[i].GetValueAt(Now());
     }
-    return sumAplitude;
+    return sumAmplitude;
 }
 
 double
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/propagation/model/three-gpp-propagation-loss-model.cc ns-3.39/src/propagation/model/three-gpp-propagation-loss-model.cc
--- ns-3.38/src/propagation/model/three-gpp-propagation-loss-model.cc	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/propagation/model/three-gpp-propagation-loss-model.cc	2023-07-05 14:23:48.073428200 -0700
@@ -144,6 +144,12 @@
     return m_frequency;
 }
 
+bool
+ThreeGppPropagationLossModel::IsO2iLowPenetrationLoss(Ptr<const ChannelCondition> cond) const
+{
+    return DoIsO2iLowPenetrationLoss(cond);
+}
+
 double
 ThreeGppPropagationLossModel::DoCalcRxPower(double txPowerDbm,
                                             Ptr<MobilityModel> a,
@@ -179,17 +185,26 @@
     if (cond->GetO2iCondition() == ChannelCondition::O2iConditionValue::O2I &&
         m_buildingPenLossesEnabled)
     {
-        if (cond->GetO2iLowHighCondition() == ChannelCondition::O2iLowHighConditionValue::LOW)
+        if (IsO2iLowPenetrationLoss(cond))
         {
             rxPow -= GetO2iLowPenetrationLoss(a, b, cond->GetLosCondition());
         }
-        else if (cond->GetO2iLowHighCondition() == ChannelCondition::O2iLowHighConditionValue::HIGH)
+        else
         {
             rxPow -= GetO2iHighPenetrationLoss(a, b, cond->GetLosCondition());
         }
+    }
+    else if (cond->GetO2iCondition() == ChannelCondition::O2iConditionValue::I2I &&
+             cond->GetLosCondition() == ChannelCondition::LosConditionValue::NLOS &&
+             m_buildingPenLossesEnabled)
+    {
+        if (IsO2iLowPenetrationLoss(cond))
+        {
+            rxPow -= GetO2iLowPenetrationLoss(a, b, cond->GetLosCondition());
+        }
         else
         {
-            NS_ABORT_MSG("If we have set the O2I condition, we shouldn't be here");
+            rxPow -= GetO2iHighPenetrationLoss(a, b, cond->GetLosCondition());
         }
     }
 
@@ -368,6 +383,23 @@
     return o2iLossValue;
 }
 
+bool
+ThreeGppPropagationLossModel::DoIsO2iLowPenetrationLoss(Ptr<const ChannelCondition> cond) const
+{
+    if (cond->GetO2iLowHighCondition() == ChannelCondition::O2iLowHighConditionValue::LOW)
+    {
+        return true;
+    }
+    else if (cond->GetO2iLowHighCondition() == ChannelCondition::O2iLowHighConditionValue::HIGH)
+    {
+        return false;
+    }
+    else
+    {
+        NS_ABORT_MSG("If we have set the O2I condition, we shouldn't be here");
+    }
+}
+
 double
 ThreeGppPropagationLossModel::GetLossNlosv(double distance2D,
                                            double distance3D,
@@ -547,6 +579,15 @@
     return std::min(m_randomO2iVar1->GetValue(0, 10), m_randomO2iVar2->GetValue(0, 10));
 }
 
+bool
+ThreeGppRmaPropagationLossModel::DoIsO2iLowPenetrationLoss(Ptr<const ChannelCondition> cond
+                                                           [[maybe_unused]]) const
+{
+    // Based on 3GPP 38.901 7.4.3.1 in RMa only low losses are applied.
+    // Therefore enforce low losses.
+    return true;
+}
+
 double
 ThreeGppRmaPropagationLossModel::GetLossLos(double distance2D,
                                             double distance3D,
@@ -1163,7 +1204,7 @@
         // We cannot know who is the BS and who is the UT, we assume that the
         // tallest node is the BS and the smallest is the UT
         hBs = std::max(za, zb);
-        hUt = std::min(za, za);
+        hUt = std::min(za, zb);
     }
 
     return std::pair<double, double>(hUt, hBs);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/propagation/model/three-gpp-propagation-loss-model.h ns-3.39/src/propagation/model/three-gpp-propagation-loss-model.h
--- ns-3.38/src/propagation/model/three-gpp-propagation-loss-model.h	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/propagation/model/three-gpp-propagation-loss-model.h	2023-07-05 14:23:48.073428200 -0700
@@ -78,6 +78,14 @@
      */
     double GetFrequency() const;
 
+    /**
+     * \brief Return true if the O2I Building Penetration loss
+     *        corresponds to a low loss condition.
+     * \param cond The ptr to the channel condition model
+     * \return True for low loss, false for high
+     */
+    bool IsO2iLowPenetrationLoss(Ptr<const ChannelCondition> cond) const;
+
   private:
     /**
      * Computes the received power by applying the pathloss model described in
@@ -174,6 +182,20 @@
                                              ChannelCondition::LosConditionValue cond) const;
 
     /**
+     * \brief Indicates the condition of the o2i building penetration loss
+     *        (defined in 3GPP TR 38.901 7.4.3.1).
+     *        The default implementation returns the condition as set
+     *        (either based on the buildings materials, or if the probabilistic
+     *        model is used in the ThreeGppChannelConditionModel, then
+     *        based on the result of a random variable).
+     *        The derived classes can change the default behavior by overriding
+     *        this method.
+     * \param cond the ptr to the channel condition model
+     * \return True for low losses, false for high losses
+     */
+    virtual bool DoIsO2iLowPenetrationLoss(Ptr<const ChannelCondition> cond) const;
+
+    /**
      * \brief Computes the pathloss between a and b considering that the line of
      *        sight is obstructed
      * \param distance2D the 2D distance between tx and rx in meters
@@ -375,6 +397,14 @@
     double GetO2iDistance2dIn() const override;
 
     /**
+     * \brief Indicates the condition of the o2i building penetration loss
+     *        (defined in 3GPP TR 38.901 7.4.3.1).
+     * \param cond the ptr to the channel condition model
+     * \return True for low losses, false for high losses
+     */
+    bool DoIsO2iLowPenetrationLoss(Ptr<const ChannelCondition> cond) const override;
+
+    /**
      * \brief Computes the pathloss between a and b considering that the line of
      *        sight is obstructed
      * \param distance2D the 2D distance between tx and rx in meters
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/propagation/test/examples-to-run.py ns-3.39/src/propagation/test/examples-to-run.py
--- ns-3.38/src/propagation/test/examples-to-run.py	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/propagation/test/examples-to-run.py	2023-07-05 14:23:48.073428200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/sixlowpan/doc/sixlowpan.rst ns-3.39/src/sixlowpan/doc/sixlowpan.rst
--- ns-3.38/src/sixlowpan/doc/sixlowpan.rst	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/sixlowpan/doc/sixlowpan.rst	2023-07-05 14:23:48.073428200 -0700
@@ -112,8 +112,8 @@
 Scope and Limitations
 =====================
 
-Contex-based compression
-########################
+Context-based compression
+#########################
 
 IPHC sateful (context-based) compression is supported but, since :rfc:`6775`
 ("Neighbor Discovery Optimization for IPv6 over Low-Power Wireless Personal Area Networks (6LoWPANs)")
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/sixlowpan/model/sixlowpan-header.cc ns-3.39/src/sixlowpan/model/sixlowpan-header.cc
--- ns-3.38/src/sixlowpan/model/sixlowpan-header.cc	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/sixlowpan/model/sixlowpan-header.cc	2023-07-05 14:23:48.073428200 -0700
@@ -167,7 +167,7 @@
         break;
     }
 
-    if (m_tcflCompression == false)
+    if (!m_tcflCompression)
     {
         serializedSize += 4;
     }
@@ -253,7 +253,7 @@
         break;
     }
 
-    if (m_tcflCompression == false)
+    if (!m_tcflCompression)
     {
         i.WriteU8(m_trafficClass);
         uint8_t temp[3];
@@ -347,7 +347,7 @@
         break;
     }
 
-    if (m_tcflCompression == false)
+    if (!m_tcflCompression)
     {
         m_trafficClass = i.ReadU8();
         uint8_t temp[3];
@@ -938,7 +938,7 @@
     default:
         break;
     }
-    if (GetNh() == false)
+    if (!GetNh())
     {
         serializedSize++;
     }
@@ -949,7 +949,7 @@
     switch (GetSam())
     {
     case HC_INLINE:
-        if (GetSac() == false)
+        if (!GetSac())
         {
             serializedSize += 16;
         }
@@ -964,12 +964,12 @@
     default:
         break;
     }
-    if (GetM() == false)
+    if (!GetM())
     {
         switch (GetDam())
         {
         case HC_INLINE:
-            if (GetDac() == false)
+            if (!GetDac())
             {
                 serializedSize += 16;
             }
@@ -990,7 +990,7 @@
         switch (GetDam())
         {
         case HC_INLINE:
-            if (GetDac() == false)
+            if (!GetDac())
             {
                 serializedSize += 16;
             }
@@ -1000,20 +1000,20 @@
             }
             break;
         case HC_COMPR_64:
-            if (GetDac() == false)
+            if (!GetDac())
             {
                 serializedSize += 6;
             }
             break;
         case HC_COMPR_16:
-            if (GetDac() == false)
+            if (!GetDac())
             {
                 serializedSize += 4;
             }
             break;
         case HC_COMPR_0:
         default:
-            if (GetDac() == false)
+            if (!GetDac())
             {
                 serializedSize++;
             }
@@ -1065,7 +1065,7 @@
         break;
     }
     // Next Header
-    if (GetNh() == false)
+    if (!GetNh())
     {
         i.WriteU8(m_nextHeader);
     }
@@ -1078,7 +1078,7 @@
     switch (GetSam())
     {
     case HC_INLINE:
-        if (GetSac() == false)
+        if (!GetSac())
         {
             i.Write(m_srcInlinePart, 16);
         }
@@ -1094,7 +1094,7 @@
         break;
     }
     // Destination Address
-    if (GetM() == false)
+    if (!GetM())
     {
         // unicast
         switch (GetDam())
@@ -1184,7 +1184,7 @@
         break;
     }
     // Next Header
-    if (GetNh() == false)
+    if (!GetNh())
     {
         m_nextHeader = i.ReadU8();
     }
@@ -1210,7 +1210,7 @@
     switch (GetSam())
     {
     case HC_INLINE:
-        if (GetSac() == false)
+        if (!GetSac())
         {
             i.Read(m_srcInlinePart, 16);
         }
@@ -1228,7 +1228,7 @@
 
     // Destination Address
     memset(m_dstInlinePart, 0x00, sizeof(m_dstInlinePart));
-    if (GetM() == false)
+    if (!GetM())
     {
         // unicast
         switch (GetDam())
@@ -1552,7 +1552,7 @@
 SixLowPanNhcExtension::GetSerializedSize() const
 {
     uint32_t serializedSize = 2;
-    if (GetNh() == false)
+    if (!GetNh())
     {
         serializedSize++;
     }
@@ -1564,7 +1564,7 @@
 {
     Buffer::Iterator i = start;
     i.WriteU8(m_nhcExtensionHeader);
-    if (GetNh() == false)
+    if (!GetNh())
     {
         i.WriteU8(m_nhcNextHeader);
     }
@@ -1577,7 +1577,7 @@
 {
     Buffer::Iterator i = start;
     m_nhcExtensionHeader = i.ReadU8();
-    if (GetNh() == false)
+    if (!GetNh())
     {
         m_nhcNextHeader = i.ReadU8();
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/sixlowpan/model/sixlowpan-net-device.cc ns-3.39/src/sixlowpan/model/sixlowpan-net-device.cc
--- ns-3.38/src/sixlowpan/model/sixlowpan-net-device.cc	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/sixlowpan/model/sixlowpan-net-device.cc	2023-07-05 14:23:48.073428200 -0700
@@ -1095,7 +1095,6 @@
         uint8_t addressBuf[16];
 
         // This is just to limit the scope of some variables.
-        if (true)
         {
             Ipv6Address srcAddr = ipHeader.GetSource();
             uint8_t srcContextId;
@@ -1197,7 +1196,6 @@
         }
 
         // This is just to limit the scope of some variables.
-        if (true)
         {
             Ipv6Address dstAddr = ipHeader.GetDestination();
             dstAddr.GetBytes(addressBuf);
@@ -1427,8 +1425,8 @@
                 return true;
             }
 
-            uint8_t contexPrefix[16];
-            m_contextTable[contextId].contextPrefix.GetBytes(contexPrefix);
+            uint8_t contextPrefix[16];
+            m_contextTable[contextId].contextPrefix.GetBytes(contextPrefix);
             uint8_t contextLength = m_contextTable[contextId].contextPrefix.GetPrefixLength();
 
             uint8_t srcAddress[16] = {};
@@ -1453,13 +1451,13 @@
             // Do not combine the prefix - we want to override the bytes.
             for (uint8_t i = 0; i < bytesToCopy; i++)
             {
-                srcAddress[i] = contexPrefix[i];
+                srcAddress[i] = contextPrefix[i];
             }
             if (bitsToCopy)
             {
                 uint8_t addressBitMask = (1 << (8 - bitsToCopy)) - 1;
                 uint8_t prefixBitMask = ~addressBitMask;
-                srcAddress[bytesToCopy] = (contexPrefix[bytesToCopy] & prefixBitMask) |
+                srcAddress[bytesToCopy] = (contextPrefix[bytesToCopy] & prefixBitMask) |
                                           (srcAddress[bytesToCopy] & addressBitMask);
             }
             ipHeader.SetSource(Ipv6Address::Deserialize(srcAddress));
@@ -1524,11 +1522,11 @@
             return true;
         }
 
-        uint8_t contexPrefix[16];
-        m_contextTable[contextId].contextPrefix.GetBytes(contexPrefix);
+        uint8_t contextPrefix[16];
+        m_contextTable[contextId].contextPrefix.GetBytes(contextPrefix);
         uint8_t contextLength = m_contextTable[contextId].contextPrefix.GetPrefixLength();
 
-        if (encoding.GetM() == false)
+        if (!encoding.GetM())
         {
             // unicast
             uint8_t dstAddress[16] = {};
@@ -1553,13 +1551,13 @@
             // Do not combine the prefix - we want to override the bytes.
             for (uint8_t i = 0; i < bytesToCopy; i++)
             {
-                dstAddress[i] = contexPrefix[i];
+                dstAddress[i] = contextPrefix[i];
             }
             if (bitsToCopy)
             {
                 uint8_t addressBitMask = (1 << (8 - bitsToCopy)) - 1;
                 uint8_t prefixBitMask = ~addressBitMask;
-                dstAddress[bytesToCopy] = (contexPrefix[bytesToCopy] & prefixBitMask) |
+                dstAddress[bytesToCopy] = (contextPrefix[bytesToCopy] & prefixBitMask) |
                                           (dstAddress[bytesToCopy] & addressBitMask);
             }
             ipHeader.SetDestination(Ipv6Address::Deserialize(dstAddress));
@@ -1572,7 +1570,7 @@
             dstAddress[0] = 0xff;
             memcpy(dstAddress + 1, encoding.GetDstInlinePart(), 2);
             dstAddress[3] = contextLength;
-            memcpy(dstAddress + 4, contexPrefix, 8);
+            memcpy(dstAddress + 4, contextPrefix, 8);
             memcpy(dstAddress + 12, encoding.GetDstInlinePart() + 2, 4);
             ipHeader.SetDestination(Ipv6Address::Deserialize(dstAddress));
         }
@@ -1580,7 +1578,7 @@
     else
     {
         // Destination address compression uses stateless compression.
-        if (encoding.GetM() == false)
+        if (!encoding.GetM())
         {
             // unicast
             if (encoding.GetDam() == SixLowPanIphc::HC_INLINE)
@@ -1697,7 +1695,7 @@
                                                                   dst,
                                                                   ipHeader.GetSource(),
                                                                   ipHeader.GetDestination());
-            if (retval.second == true)
+            if (retval.second)
             {
                 return true;
             }
@@ -2485,7 +2483,7 @@
             FragmentsTimeoutsListI_t iter = m_timeoutEventList.begin();
             FragmentKey_t oldestKey = std::get<1>(*iter);
 
-            std::list<Ptr<Packet>> storedFragments = m_fragments[oldestKey]->GetFraments();
+            std::list<Ptr<Packet>> storedFragments = m_fragments[oldestKey]->GetFragments();
             for (std::list<Ptr<Packet>>::iterator fragIter = storedFragments.begin();
                  fragIter != storedFragments.end();
                  fragIter++)
@@ -2612,11 +2610,7 @@
         }
     }
 
-    if (ret && (lastEndOffset == m_packetSize))
-    {
-        return true;
-    }
-    return false;
+    return ret && lastEndOffset == m_packetSize;
 }
 
 Ptr<Packet>
@@ -2658,7 +2652,7 @@
 }
 
 std::list<Ptr<Packet>>
-SixLowPanNetDevice::Fragments::GetFraments() const
+SixLowPanNetDevice::Fragments::GetFragments() const
 {
     std::list<Ptr<Packet>> fragments;
     std::list<std::pair<Ptr<Packet>, uint16_t>>::const_iterator iter;
@@ -2687,7 +2681,7 @@
     NS_LOG_FUNCTION(this);
 
     MapFragments_t::iterator it = m_fragments.find(key);
-    std::list<Ptr<Packet>> storedFragments = it->second->GetFraments();
+    std::list<Ptr<Packet>> storedFragments = it->second->GetFragments();
     for (std::list<Ptr<Packet>>::iterator fragIter = storedFragments.begin();
          fragIter != storedFragments.end();
          fragIter++)
@@ -2873,7 +2867,7 @@
     {
         ContextEntry context = iter.second;
 
-        if ((context.compressionAllowed == true) && (context.validLifetime > Simulator::Now()))
+        if (context.compressionAllowed && context.validLifetime > Simulator::Now())
         {
             if (address.HasPrefix(context.contextPrefix))
             {
@@ -2902,7 +2896,7 @@
     {
         ContextEntry context = iter.second;
 
-        if ((context.compressionAllowed == true) && (context.validLifetime > Simulator::Now()))
+        if (context.compressionAllowed && context.validLifetime > Simulator::Now())
         {
             uint8_t contextLength = context.contextPrefix.GetPrefixLength();
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/sixlowpan/model/sixlowpan-net-device.h ns-3.39/src/sixlowpan/model/sixlowpan-net-device.h
--- ns-3.38/src/sixlowpan/model/sixlowpan-net-device.h	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/sixlowpan/model/sixlowpan-net-device.h	2023-07-05 14:23:48.073428200 -0700
@@ -510,7 +510,7 @@
          * \brief Get a list of the current stored fragments.
          * \returns The current stored fragments.
          */
-        std::list<Ptr<Packet>> GetFraments() const;
+        std::list<Ptr<Packet>> GetFragments() const;
 
         /**
          * \brief Set the Timeout iterator.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/sixlowpan/test/mock-net-device.cc ns-3.39/src/sixlowpan/test/mock-net-device.cc
--- ns-3.38/src/sixlowpan/test/mock-net-device.cc	2023-03-16 20:54:23.816869300 -0700
+++ ns-3.39/src/sixlowpan/test/mock-net-device.cc	2023-07-05 14:23:48.073428200 -0700
@@ -245,11 +245,7 @@
 MockNetDevice::IsPointToPoint() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_pointToPointMode)
-    {
-        return true;
-    }
-    return false;
+    return m_pointToPointMode;
 }
 
 bool
@@ -305,11 +301,7 @@
 MockNetDevice::NeedsArp() const
 {
     NS_LOG_FUNCTION(this);
-    if (m_pointToPointMode)
-    {
-        return false;
-    }
-    return true;
+    return !m_pointToPointMode;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/CMakeLists.txt ns-3.39/src/spectrum/CMakeLists.txt
--- ns-3.38/src/spectrum/CMakeLists.txt	2023-03-16 20:54:23.820869200 -0700
+++ ns-3.39/src/spectrum/CMakeLists.txt	2023-07-05 14:23:48.073428200 -0700
@@ -10,6 +10,7 @@
     model/friis-spectrum-propagation-loss.cc
     model/half-duplex-ideal-phy-signal-parameters.cc
     model/half-duplex-ideal-phy.cc
+    model/ism-spectrum-value-helper.cc
     model/matrix-based-channel-model.cc
     model/microwave-oven-spectrum-value-helper.cc
     model/two-ray-spectrum-propagation-loss-model.cc
@@ -26,6 +27,7 @@
     model/spectrum-model.cc
     model/spectrum-phy.cc
     model/spectrum-propagation-loss-model.cc
+    model/spectrum-transmit-filter.cc
     model/phased-array-spectrum-propagation-loss-model.cc
     model/spectrum-signal-parameters.cc
     model/spectrum-value.cc
@@ -49,6 +51,7 @@
     model/friis-spectrum-propagation-loss.h
     model/half-duplex-ideal-phy-signal-parameters.h
     model/half-duplex-ideal-phy.h
+    model/ism-spectrum-value-helper.h
     model/matrix-based-channel-model.h
     model/microwave-oven-spectrum-value-helper.h
     model/two-ray-spectrum-propagation-loss-model.h
@@ -65,6 +68,7 @@
     model/spectrum-model.h
     model/spectrum-phy.h
     model/spectrum-propagation-loss-model.h
+    model/spectrum-transmit-filter.h
     model/phased-array-spectrum-propagation-loss-model.h
     model/spectrum-signal-parameters.h
     model/spectrum-value.h
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/doc/spectrum.rst ns-3.39/src/spectrum/doc/spectrum.rst
--- ns-3.38/src/spectrum/doc/spectrum.rst	2023-03-16 20:54:23.820869200 -0700
+++ ns-3.39/src/spectrum/doc/spectrum.rst	2023-07-05 14:23:48.077428300 -0700
@@ -263,7 +263,7 @@
    is one of the main features of the
    spectrum module, to support inter-technology interference. For
    example, if you implement a WifiSpectrumPhy and a
-   BluetoohSpectrumPhy, and plug both on a SpectrumChannel, then you'll
+   BluetoothSpectrumPhy, and plug both on a SpectrumChannel, then you'll
    be able to simulate interference between wifi and bluetooth and
    vice versa.
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/examples/adhoc-aloha-ideal-phy.cc ns-3.39/src/spectrum/examples/adhoc-aloha-ideal-phy.cc
--- ns-3.38/src/spectrum/examples/adhoc-aloha-ideal-phy.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/examples/adhoc-aloha-ideal-phy.cc	2023-07-05 14:23:48.077428300 -0700
@@ -21,6 +21,7 @@
 #include <ns3/applications-module.h>
 #include <ns3/core-module.h>
 #include <ns3/friis-spectrum-propagation-loss.h>
+#include <ns3/ism-spectrum-value-helper.h>
 #include <ns3/log.h>
 #include <ns3/mobility-module.h>
 #include <ns3/network-module.h>
@@ -31,7 +32,6 @@
 #include <ns3/spectrum-model-300kHz-300GHz-log.h>
 #include <ns3/spectrum-model-ism2400MHz-res1MHz.h>
 #include <ns3/waveform-generator.h>
-#include <ns3/wifi-spectrum-value-helper.h>
 
 #include <iostream>
 #include <string>
@@ -176,7 +176,7 @@
     SpectrumChannelHelper channelHelper = SpectrumChannelHelper::Default();
     Ptr<SpectrumChannel> channel = channelHelper.Create();
 
-    WifiSpectrumValue5MhzFactory sf;
+    SpectrumValue5MhzFactory sf;
 
     double txPower = 0.1; // Watts
     uint32_t channelNumber = 1;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/examples/adhoc-aloha-ideal-phy-matrix-propagation-loss-model.cc ns-3.39/src/spectrum/examples/adhoc-aloha-ideal-phy-matrix-propagation-loss-model.cc
--- ns-3.38/src/spectrum/examples/adhoc-aloha-ideal-phy-matrix-propagation-loss-model.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/examples/adhoc-aloha-ideal-phy-matrix-propagation-loss-model.cc	2023-07-05 14:23:48.077428300 -0700
@@ -21,6 +21,7 @@
 #include <ns3/applications-module.h>
 #include <ns3/core-module.h>
 #include <ns3/friis-spectrum-propagation-loss.h>
+#include <ns3/ism-spectrum-value-helper.h>
 #include <ns3/log.h>
 #include <ns3/mobility-module.h>
 #include <ns3/network-module.h>
@@ -31,7 +32,6 @@
 #include <ns3/spectrum-model-300kHz-300GHz-log.h>
 #include <ns3/spectrum-model-ism2400MHz-res1MHz.h>
 #include <ns3/waveform-generator.h>
-#include <ns3/wifi-spectrum-value-helper.h>
 
 #include <iomanip>
 #include <iostream>
@@ -163,7 +163,7 @@
     channelHelper.AddPropagationLoss(propLoss);
     Ptr<SpectrumChannel> channel = channelHelper.Create();
 
-    WifiSpectrumValue5MhzFactory sf;
+    SpectrumValue5MhzFactory sf;
 
     uint32_t channelNumber = 1;
     Ptr<SpectrumValue> txPsd = sf.CreateTxPowerSpectralDensity(txPowerW, channelNumber);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/examples/adhoc-aloha-ideal-phy-with-microwave-oven.cc ns-3.39/src/spectrum/examples/adhoc-aloha-ideal-phy-with-microwave-oven.cc
--- ns-3.38/src/spectrum/examples/adhoc-aloha-ideal-phy-with-microwave-oven.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/examples/adhoc-aloha-ideal-phy-with-microwave-oven.cc	2023-07-05 14:23:48.077428300 -0700
@@ -21,6 +21,7 @@
 #include <ns3/applications-module.h>
 #include <ns3/core-module.h>
 #include <ns3/friis-spectrum-propagation-loss.h>
+#include <ns3/ism-spectrum-value-helper.h>
 #include <ns3/log.h>
 #include <ns3/microwave-oven-spectrum-value-helper.h>
 #include <ns3/mobility-module.h>
@@ -35,7 +36,6 @@
 #include <ns3/spectrum-model-ism2400MHz-res1MHz.h>
 #include <ns3/waveform-generator-helper.h>
 #include <ns3/waveform-generator.h>
-#include <ns3/wifi-spectrum-value-helper.h>
 
 #include <iostream>
 #include <string>
@@ -196,7 +196,7 @@
     // Configure ofdm nodes
     ////////////////////////
 
-    WifiSpectrumValue5MhzFactory sf;
+    SpectrumValue5MhzFactory sf;
 
     double txPower = 0.1; // Watts
     uint32_t channelNumber = 4;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/aloha-noack-net-device.cc ns-3.39/src/spectrum/model/aloha-noack-net-device.cc
--- ns-3.38/src/spectrum/model/aloha-noack-net-device.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/model/aloha-noack-net-device.cc	2023-07-05 14:23:48.081428300 -0700
@@ -374,7 +374,7 @@
         else
         {
             NS_LOG_LOGIC("enqueueing new packet");
-            if (m_queue->Enqueue(packet) == false)
+            if (!m_queue->Enqueue(packet))
             {
                 m_macTxDropTrace(packet);
                 sendOk = false;
@@ -385,7 +385,7 @@
     {
         NS_LOG_LOGIC("deferring TX, enqueueing new packet");
         NS_ASSERT(m_queue);
-        if (m_queue->Enqueue(packet) == false)
+        if (!m_queue->Enqueue(packet))
         {
             m_macTxDropTrace(packet);
             sendOk = false;
@@ -426,7 +426,7 @@
     NS_ASSERT_MSG(m_state == TX, "TX end notified while state != TX");
     m_state = IDLE;
     NS_ASSERT(m_queue);
-    if (m_queue->IsEmpty() == false)
+    if (!m_queue->IsEmpty())
     {
         Ptr<Packet> p = m_queue->Dequeue();
         NS_ASSERT(p);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/half-duplex-ideal-phy.cc ns-3.39/src/spectrum/model/half-duplex-ideal-phy.cc
--- ns-3.38/src/spectrum/model/half-duplex-ideal-phy.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/model/half-duplex-ideal-phy.cc	2023-07-05 14:23:48.081428300 -0700
@@ -302,9 +302,7 @@
     break;
 
     case TX:
-
         return true;
-        break;
     }
     return false;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/ism-spectrum-value-helper.cc ns-3.39/src/spectrum/model/ism-spectrum-value-helper.cc
--- ns-3.38/src/spectrum/model/ism-spectrum-value-helper.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/spectrum/model/ism-spectrum-value-helper.cc	2023-07-05 14:23:48.081428300 -0700
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2009 CTTC
+ * Copyright (c) 2010 TELEMATICS LAB, DEE - Politecnico di Bari
+ * Copyright (c) 2017 Orange Labs
+ *
+ * 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: Nicola Baldo <nbaldo@cttc.es>
+ *          Giuseppe Piro <g.piro@poliba.it>
+ */
+
+#include "ism-spectrum-value-helper.h"
+
+#include "ns3/assert.h"
+#include "ns3/log.h"
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("IsmSpectrumValueHelper");
+
+static Ptr<SpectrumModel> g_WifiSpectrumModel5Mhz; ///< static initializer for the class
+
+/**
+ * Static class to initialize the values for the 2.4 GHz Wi-Fi spectrum model
+ */
+static class WifiSpectrumModel5MhzInitializer
+{
+  public:
+    WifiSpectrumModel5MhzInitializer()
+    {
+        Bands bands;
+        for (int i = -4; i < 13 + 7; i++)
+        {
+            BandInfo bi;
+            bi.fl = 2407.0e6 + i * 5.0e6;
+            bi.fh = 2407.0e6 + (i + 1) * 5.0e6;
+            bi.fc = (bi.fl + bi.fh) / 2;
+            bands.push_back(bi);
+        }
+        g_WifiSpectrumModel5Mhz = Create<SpectrumModel>(bands);
+    }
+} g_WifiSpectrumModel5MhzInitializerInstance; //!< initialization instance for WifiSpectrumModel5Mhz
+
+Ptr<SpectrumValue>
+SpectrumValue5MhzFactory::CreateConstant(double v)
+{
+    Ptr<SpectrumValue> c = Create<SpectrumValue>(g_WifiSpectrumModel5Mhz);
+    (*c) = v;
+    return c;
+}
+
+Ptr<SpectrumValue>
+SpectrumValue5MhzFactory::CreateTxPowerSpectralDensity(double txPower, uint8_t channel)
+{
+    Ptr<SpectrumValue> txPsd = Create<SpectrumValue>(g_WifiSpectrumModel5Mhz);
+
+    // since the spectrum model has a resolution of 5 MHz, we model
+    // the transmitted signal with a constant density over a 20MHz
+    // bandwidth centered on the center frequency of the channel. The
+    // transmission power outside the transmission power density is
+    // calculated considering the transmit spectrum mask, see IEEE
+    // Std. 802.11-2007, Annex I
+
+    double txPowerDensity = txPower / 20e6;
+
+    NS_ASSERT(channel >= 1);
+    NS_ASSERT(channel <= 13);
+
+    (*txPsd)[channel - 1] = txPowerDensity * 1e-4;      // -40dB
+    (*txPsd)[channel] = txPowerDensity * 1e-4;          // -40dB
+    (*txPsd)[channel + 1] = txPowerDensity * 0.0015849; // -28dB
+    (*txPsd)[channel + 2] = txPowerDensity * 0.0015849; // -28dB
+    (*txPsd)[channel + 3] = txPowerDensity;
+    (*txPsd)[channel + 4] = txPowerDensity;
+    (*txPsd)[channel + 5] = txPowerDensity;
+    (*txPsd)[channel + 6] = txPowerDensity;
+    (*txPsd)[channel + 7] = txPowerDensity * 0.0015849; // -28dB
+    (*txPsd)[channel + 8] = txPowerDensity * 0.0015849; // -28dB
+    (*txPsd)[channel + 9] = txPowerDensity * 1e-4;      // -40dB
+    (*txPsd)[channel + 10] = txPowerDensity * 1e-4;     // -40dB
+
+    return txPsd;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/ism-spectrum-value-helper.h ns-3.39/src/spectrum/model/ism-spectrum-value-helper.h
--- ns-3.38/src/spectrum/model/ism-spectrum-value-helper.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/spectrum/model/ism-spectrum-value-helper.h	2023-07-05 14:23:48.081428300 -0700
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009 CTTC
+ * Copyright (c) 2017 Orange Labs
+ *
+ * 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: Nicola Baldo <nbaldo@cttc.es>
+ *          Giuseppe Piro <g.piro@poliba.it>
+ */
+
+#ifndef ISM_SPECTRUM_VALUE_HELPER_H
+#define ISM_SPECTRUM_VALUE_HELPER_H
+
+#include <ns3/spectrum-value.h>
+
+namespace ns3
+{
+
+/**
+ * \ingroup spectrum
+ *
+ * Implements Wifi SpectrumValue for the 2.4 GHz ISM band only, with a
+ * 5 MHz spectrum resolution.
+ *
+ */
+class SpectrumValue5MhzFactory
+{
+  public:
+    /**
+     * Destructor
+     */
+    virtual ~SpectrumValue5MhzFactory() = default;
+    /**
+     * Creates a SpectrumValue instance with a constant value for all frequencies
+     *
+     * @param psd the constant value
+     *
+     * @return a Ptr to a newly created SpectrumValue
+     */
+    virtual Ptr<SpectrumValue> CreateConstant(double psd);
+    /**
+     * Creates a SpectrumValue instance that represents the TX Power Spectral
+     * Density  of a wifi device corresponding to the provided parameters
+     *
+     * Since the spectrum model has a resolution of 5 MHz, we model
+     * the transmitted signal with a constant density over a 20MHz
+     * bandwidth centered on the center frequency of the channel. The
+     * transmission power outside the transmission power density is
+     * calculated considering the transmit spectrum mask, see IEEE
+     * Std. 802.11-2007, Annex I.  The two bands just outside of the main
+     * 20 MHz are allocated power at -28 dB down from the center 20 MHz,
+     * and the two bands outside of this are allocated power at -40 dB down
+     * (with a total bandwidth of 60 MHz containing non-zero power allocation).
+     *
+     * @param txPower the total TX power in W
+     * @param channel the number of the channel (1 <= channel <= 13)
+     *
+     * @return a Ptr to a newly created SpectrumValue
+     */
+    virtual Ptr<SpectrumValue> CreateTxPowerSpectralDensity(double txPower, uint8_t channel);
+};
+
+} // namespace ns3
+
+#endif /*  ISM_SPECTRUM_VALUE_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/multi-model-spectrum-channel.cc ns-3.39/src/spectrum/model/multi-model-spectrum-channel.cc
--- ns-3.38/src/spectrum/model/multi-model-spectrum-channel.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/model/multi-model-spectrum-channel.cc	2023-07-05 14:23:48.081428300 -0700
@@ -35,6 +35,7 @@
 #include <ns3/spectrum-converter.h>
 #include <ns3/spectrum-phy.h>
 #include <ns3/spectrum-propagation-loss-model.h>
+#include <ns3/spectrum-transmit-filter.h>
 
 #include <algorithm>
 #include <iostream>
@@ -308,6 +309,11 @@
                     }
                 }
 
+                if (m_filter && m_filter->Filter(txParams, *rxPhyIterator))
+                {
+                    continue;
+                }
+
                 NS_LOG_LOGIC("copying signal parameters " << txParams);
                 Ptr<SpectrumSignalParameters> rxParams = txParams->Copy();
                 rxParams->psd = Copy<SpectrumValue>(convertedTxPowerSpectrum);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/single-model-spectrum-channel.cc ns-3.39/src/spectrum/model/single-model-spectrum-channel.cc
--- ns-3.38/src/spectrum/model/single-model-spectrum-channel.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/model/single-model-spectrum-channel.cc	2023-07-05 14:23:48.081428300 -0700
@@ -34,6 +34,7 @@
 #include <ns3/simulator.h>
 #include <ns3/spectrum-phy.h>
 #include <ns3/spectrum-propagation-loss-model.h>
+#include <ns3/spectrum-transmit-filter.h>
 
 #include <algorithm>
 
@@ -135,6 +136,11 @@
             }
         }
 
+        if (m_filter && m_filter->Filter(txParams, *rxPhyIterator))
+        {
+            continue;
+        }
+
         if ((*rxPhyIterator) != txParams->txPhy)
         {
             Time delay = MicroSeconds(0);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/spectrum-channel.cc ns-3.39/src/spectrum/model/spectrum-channel.cc
--- ns-3.38/src/spectrum/model/spectrum-channel.cc	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/model/spectrum-channel.cc	2023-07-05 14:23:48.081428300 -0700
@@ -147,6 +147,24 @@
 }
 
 void
+SpectrumChannel::AddSpectrumTransmitFilter(Ptr<SpectrumTransmitFilter> filter)
+{
+    NS_LOG_FUNCTION(this << filter);
+
+    if (m_filter)
+    {
+        filter->SetNext(m_filter);
+    }
+    m_filter = filter;
+}
+
+Ptr<const SpectrumTransmitFilter>
+SpectrumChannel::GetSpectrumTransmitFilter() const
+{
+    return m_filter;
+}
+
+void
 SpectrumChannel::SetPropagationDelayModel(Ptr<PropagationDelayModel> delay)
 {
     NS_ASSERT(!m_propagationDelay);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/spectrum-channel.h ns-3.39/src/spectrum/model/spectrum-channel.h
--- ns-3.38/src/spectrum/model/spectrum-channel.h	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/model/spectrum-channel.h	2023-07-05 14:23:48.081428300 -0700
@@ -30,6 +30,7 @@
 #include <ns3/spectrum-phy.h>
 #include <ns3/spectrum-propagation-loss-model.h>
 #include <ns3/spectrum-signal-parameters.h>
+#include <ns3/spectrum-transmit-filter.h>
 #include <ns3/traced-callback.h>
 
 namespace ns3
@@ -115,6 +116,23 @@
     Ptr<PropagationLossModel> GetPropagationLossModel();
 
     /**
+     * Add the transmit filter to be used to filter possible signal receptions
+     * at the StartTx() time.  This method may be called multiple
+     * times to chain multiple filters together; the last filter added will
+     * be the first one used in the chain.
+     *
+     * \param filter an instance of a SpectrumTransmitFilter
+     */
+    void AddSpectrumTransmitFilter(Ptr<SpectrumTransmitFilter> filter);
+
+    /**
+     * Get the transmit filter, or first in a chain of transmit filters
+     * if more than one is present.
+     * \returns a pointer to the transmit filter.
+     */
+    Ptr<const SpectrumTransmitFilter> GetSpectrumTransmitFilter() const;
+
+    /**
      * Used by attached PHY instances to transmit signals on the channel
      *
      * \param params the parameters of the signals being transmitted
@@ -237,6 +255,11 @@
      * Frequency-dependent propagation loss model to be used with this channel.
      */
     Ptr<PhasedArraySpectrumPropagationLossModel> m_phasedArraySpectrumPropagationLoss;
+
+    /**
+     * Transmit filter to be used with this channel
+     */
+    Ptr<SpectrumTransmitFilter> m_filter{nullptr};
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/spectrum-model.h ns-3.39/src/spectrum/model/spectrum-model.h
--- ns-3.38/src/spectrum/model/spectrum-model.h	2023-03-16 20:54:23.824869200 -0700
+++ ns-3.39/src/spectrum/model/spectrum-model.h	2023-07-05 14:23:48.081428300 -0700
@@ -72,7 +72,7 @@
 {
   public:
     /**
-     * Comparison operator. Returns true if the two SpectumModels are identical
+     * Comparison operator. Returns true if the two SpectrumModels are identical
      * \param lhs left operand
      * \param rhs right operand
      * \returns true if the two operands are identical
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/spectrum-transmit-filter.cc ns-3.39/src/spectrum/model/spectrum-transmit-filter.cc
--- ns-3.38/src/spectrum/model/spectrum-transmit-filter.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/spectrum/model/spectrum-transmit-filter.cc	2023-07-05 14:23:48.081428300 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2022 University of Washington
+ *
+ * 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 "spectrum-transmit-filter.h"
+
+#include "spectrum-phy.h"
+#include "spectrum-signal-parameters.h"
+
+#include <ns3/log.h>
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("SpectrumTransmitFilter");
+
+NS_OBJECT_ENSURE_REGISTERED(SpectrumTransmitFilter);
+
+TypeId
+SpectrumTransmitFilter::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::SpectrumTransmitFilter").SetParent<Object>().SetGroupName("Spectrum");
+    return tid;
+}
+
+SpectrumTransmitFilter::SpectrumTransmitFilter()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+void
+SpectrumTransmitFilter::DoDispose()
+{
+    NS_LOG_FUNCTION(this);
+    if (m_next)
+    {
+        m_next->Dispose();
+    }
+    m_next = nullptr;
+    Object::DoDispose();
+}
+
+void
+SpectrumTransmitFilter::SetNext(Ptr<SpectrumTransmitFilter> next)
+{
+    m_next = next;
+}
+
+Ptr<const SpectrumTransmitFilter>
+SpectrumTransmitFilter::GetNext() const
+{
+    return m_next;
+}
+
+bool
+SpectrumTransmitFilter::Filter(Ptr<const SpectrumSignalParameters> params,
+                               Ptr<const SpectrumPhy> receiverPhy)
+{
+    NS_LOG_FUNCTION(this << params << receiverPhy);
+    bool result = DoFilter(params, receiverPhy);
+    if (result)
+    {
+        return true;
+    }
+    else if (m_next)
+    {
+        return m_next->Filter(params, receiverPhy);
+    }
+    else
+    {
+        return false;
+    }
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/spectrum-transmit-filter.h ns-3.39/src/spectrum/model/spectrum-transmit-filter.h
--- ns-3.38/src/spectrum/model/spectrum-transmit-filter.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/spectrum/model/spectrum-transmit-filter.h	2023-07-05 14:23:48.081428300 -0700
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2022 University of Washington
+ *
+ * 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 SPECTRUM_TRANSMIT_FILTER_H
+#define SPECTRUM_TRANSMIT_FILTER_H
+
+#include <ns3/object.h>
+
+namespace ns3
+{
+
+struct SpectrumSignalParameters;
+class SpectrumPhy;
+
+/**
+ * \ingroup spectrum
+ *
+ * \brief spectrum-aware transmit filter object
+ *
+ * Interface for transmit filters that permit an early discard of signal
+ * reception before propagation loss models or receiving Phy objects have
+ * to process the signal, for performance optimization purposes.
+ *
+ */
+class SpectrumTransmitFilter : public Object
+{
+  public:
+    SpectrumTransmitFilter();
+
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    /**
+     * Add a transmit filter to be consulted next if this filter does not
+     * filter the signal
+     *
+     * \param next next transmit filter to add to the chain
+     */
+    void SetNext(Ptr<SpectrumTransmitFilter> next);
+
+    /**
+     * Return the next transmit filter in the chain
+     *
+     * \return next transmit filter in the chain
+     */
+    Ptr<const SpectrumTransmitFilter> GetNext() const;
+
+    /**
+     * Evaluate whether the signal to be scheduled on the receiving Phy should
+     * instead be filtered (discarded) before being processed in this channel
+     * and on the receiving Phy.
+     *
+     * \param params the spectrum signal parameters.
+     * \param receiverPhy pointer to the receiving SpectrumPhy
+     *
+     * \return whether to perform filtering of the signal
+     */
+    bool Filter(Ptr<const SpectrumSignalParameters> params, Ptr<const SpectrumPhy> receiverPhy);
+
+  protected:
+    void DoDispose() override;
+
+  private:
+    /**
+     * Evaluate whether the signal to be scheduled on the receiving Phy should
+     * instead be filtered (discarded) before being processed in this channel
+     * and on the receiving Phy.
+     *
+     * \param params the spectrum signal parameters.
+     * \param receiverPhy pointer to the receiving SpectrumPhy
+     *
+     * \return whether to perform filtering of the signal
+     */
+    virtual bool DoFilter(Ptr<const SpectrumSignalParameters> params,
+                          Ptr<const SpectrumPhy> receiverPhy) = 0;
+
+    Ptr<SpectrumTransmitFilter> m_next{nullptr}; //!< SpectrumTransmitFilter chained to this one.
+};
+
+} // namespace ns3
+
+#endif /* SPECTRUM_TRANSMIT_FILTER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/three-gpp-channel-model.cc ns-3.39/src/spectrum/model/three-gpp-channel-model.cc
--- ns-3.38/src/spectrum/model/three-gpp-channel-model.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/model/three-gpp-channel-model.cc	2023-07-05 14:23:48.081428300 -0700
@@ -1083,14 +1083,7 @@
 ThreeGppChannelModel::ChannelMatrixNeedsUpdate(Ptr<const ThreeGppChannelParams> channelParams,
                                                Ptr<const ChannelMatrix> channelMatrix)
 {
-    if (channelParams->m_generatedTime > channelMatrix->m_generatedTime)
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return channelParams->m_generatedTime > channelMatrix->m_generatedTime;
 }
 
 Ptr<const MatrixBasedChannelModel::ChannelMatrix>
@@ -1972,7 +1965,7 @@
             // cache the component of the "rxPhaseDiff" terms which depend on the random angle of
             // arrivals only
             double sinRayZoa = sin(rayZoaRadian[nIndex][mIndex]);
-            double sinRayAoa = cos(rayAoaRadian[nIndex][mIndex]);
+            double sinRayAoa = sin(rayAoaRadian[nIndex][mIndex]);
             double cosRayAoa = cos(rayAoaRadian[nIndex][mIndex]);
             sinCosA[nIndex][mIndex] = sinRayZoa * cosRayAoa;
             sinSinA[nIndex][mIndex] = sinRayZoa * sinRayAoa;
@@ -1981,7 +1974,7 @@
             // cache the component of the "txPhaseDiff" terms which depend on the random angle of
             // departure only
             double sinRayZod = sin(rayZodRadian[nIndex][mIndex]);
-            double sinRayAod = cos(rayAodRadian[nIndex][mIndex]);
+            double sinRayAod = sin(rayAodRadian[nIndex][mIndex]);
             double cosRayAod = cos(rayAodRadian[nIndex][mIndex]);
             sinCosD[nIndex][mIndex] = sinRayZod * cosRayAod;
             sinSinD[nIndex][mIndex] = sinRayZod * sinRayAod;
@@ -2147,7 +2140,7 @@
                     sqrt(kLinear / (1 + kLinear)) * ray /
                         pow(10,
                             channelParams->m_attenuation_dB[0] / 10.0); //(7.5-30) for tau = tau1
-                for (uint16_t nIndex = 1; nIndex < hUsn.GetNumPages(); nIndex++)
+                for (size_t nIndex = 1; nIndex < hUsn.GetNumPages(); nIndex++)
                 {
                     hUsn(uIndex, sIndex, nIndex) *=
                         sqrt(1.0 / (kLinear + 1)); //(7.5-30) for tau = tau2...tauN
@@ -2157,11 +2150,11 @@
     }
 
     NS_LOG_DEBUG("Husn (sAntenna, uAntenna):" << sAntenna->GetId() << ", " << uAntenna->GetId());
-    for (uint16_t cIndex = 0; cIndex < hUsn.GetNumPages(); cIndex++)
+    for (size_t cIndex = 0; cIndex < hUsn.GetNumPages(); cIndex++)
     {
-        for (uint16_t rowIdx = 0; rowIdx < hUsn.GetNumRows(); rowIdx++)
+        for (size_t rowIdx = 0; rowIdx < hUsn.GetNumRows(); rowIdx++)
         {
-            for (uint16_t colIdx = 0; colIdx < hUsn.GetNumCols(); colIdx++)
+            for (size_t colIdx = 0; colIdx < hUsn.GetNumCols(); colIdx++)
             {
                 NS_LOG_DEBUG(" " << hUsn(rowIdx, colIdx, cIndex) << ",");
             }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/trace-fading-loss-model.cc ns-3.39/src/spectrum/model/trace-fading-loss-model.cc
--- ns-3.38/src/spectrum/model/trace-fading-loss-model.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/model/trace-fading-loss-model.cc	2023-07-05 14:23:48.081428300 -0700
@@ -84,7 +84,7 @@
                           "The number of RB the trace is made of (default 100)",
                           UintegerValue(100),
                           MakeUintegerAccessor(&TraceFadingLossModel::m_rbNum),
-                          MakeUintegerChecker<uint8_t>())
+                          MakeUintegerChecker<uint32_t>())
             .AddAttribute(
                 "RngStreamSetSize",
                 "The number of RNG streams reserved for the fading model. The maximum number of "
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/trace-fading-loss-model.h ns-3.39/src/spectrum/model/trace-fading-loss-model.h
--- ns-3.38/src/spectrum/model/trace-fading-loss-model.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/model/trace-fading-loss-model.h	2023-07-05 14:23:48.081428300 -0700
@@ -122,9 +122,9 @@
     Time m_traceLength;               ///< the trace time
     uint32_t m_samplesNum;            ///< number of samples
     Time m_windowSize;                ///< window size
-    uint8_t m_rbNum;                  ///< RB number
+    uint32_t m_rbNum;                 ///< RB number
     mutable Time m_lastWindowUpdate;  ///< time of last window update
-    uint8_t m_timeGranularity;        ///< time granularity
+    uint32_t m_timeGranularity;       ///< time granularity
     mutable uint64_t m_currentStream; ///< the current stream
     mutable uint64_t m_lastStream;    ///< the last stream
     uint64_t m_streamSetSize;         ///< stream set size
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h ns-3.39/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h
--- ns-3.38/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/model/two-ray-spectrum-propagation-loss-model.h	2023-07-05 14:23:48.081428300 -0700
@@ -23,6 +23,8 @@
 #include "ns3/phased-array-spectrum-propagation-loss-model.h"
 #include "ns3/spectrum-signal-parameters.h"
 
+#include <map>
+
 class FtrFadingModelAverageTest;
 class ArrayResponseTest;
 class OverallGainAverageTest;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/wifi-spectrum-value-helper.cc ns-3.39/src/spectrum/model/wifi-spectrum-value-helper.cc
--- ns-3.38/src/spectrum/model/wifi-spectrum-value-helper.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/model/wifi-spectrum-value-helper.cc	2023-07-05 14:23:48.081428300 -0700
@@ -39,29 +39,12 @@
 ///< Wifi Spectrum Model structure
 struct WifiSpectrumModelId
 {
-    /**
-     * Constructor
-     * \param f the frequency (in MHz)
-     * \param w the channel width (in MHz)
-     * \param b the width of each band (in Hz)
-     * \param g the guard band width (in MHz)
-     */
-    WifiSpectrumModelId(uint32_t f, uint16_t w, double b, uint16_t g);
     uint32_t m_centerFrequency; ///< center frequency (in MHz)
     uint16_t m_channelWidth;    ///< channel width (in MHz)
-    double m_bandBandwidth;     ///< width of each band (in Hz)
+    uint32_t m_carrierSpacing;  ///< carrier spacing (in Hz)
     uint16_t m_guardBandwidth;  ///< guard band width (in MHz)
 };
 
-WifiSpectrumModelId::WifiSpectrumModelId(uint32_t f, uint16_t w, double b, uint16_t g)
-    : m_centerFrequency(f),
-      m_channelWidth(w),
-      m_bandBandwidth(b),
-      m_guardBandwidth(g)
-{
-    NS_LOG_FUNCTION(this << f << w << b << g);
-}
-
 /**
  * Less than operator
  * \param a the first wifi spectrum to compare
@@ -75,9 +58,9 @@
         (a.m_centerFrequency < b.m_centerFrequency) ||
         ((a.m_centerFrequency == b.m_centerFrequency) && (a.m_channelWidth < b.m_channelWidth)) ||
         ((a.m_centerFrequency == b.m_centerFrequency) && (a.m_channelWidth == b.m_channelWidth) &&
-         (a.m_bandBandwidth < b.m_bandBandwidth)) // to cover coexistence of 11ax with legacy case
+         (a.m_carrierSpacing < b.m_carrierSpacing)) // to cover coexistence of 11ax with legacy case
         || ((a.m_centerFrequency == b.m_centerFrequency) &&
-            (a.m_channelWidth == b.m_channelWidth) && (a.m_bandBandwidth == b.m_bandBandwidth) &&
+            (a.m_channelWidth == b.m_channelWidth) && (a.m_carrierSpacing == b.m_carrierSpacing) &&
             (a.m_guardBandwidth <
              b.m_guardBandwidth))); // to cover 2.4 GHz case, where DSSS coexists with OFDM
 }
@@ -88,12 +71,12 @@
 Ptr<SpectrumModel>
 WifiSpectrumValueHelper::GetSpectrumModel(uint32_t centerFrequency,
                                           uint16_t channelWidth,
-                                          uint32_t bandBandwidth,
+                                          uint32_t carrierSpacing,
                                           uint16_t guardBandwidth)
 {
-    NS_LOG_FUNCTION(centerFrequency << channelWidth << bandBandwidth << guardBandwidth);
+    NS_LOG_FUNCTION(centerFrequency << channelWidth << carrierSpacing << guardBandwidth);
     Ptr<SpectrumModel> ret;
-    WifiSpectrumModelId key(centerFrequency, channelWidth, bandBandwidth, guardBandwidth);
+    WifiSpectrumModelId key{centerFrequency, channelWidth, carrierSpacing, guardBandwidth};
     std::map<WifiSpectrumModelId, Ptr<SpectrumModel>>::iterator it =
         g_wifiSpectrumModelMap.find(key);
     if (it != g_wifiSpectrumModelMap.end())
@@ -106,7 +89,7 @@
         double centerFrequencyHz = centerFrequency * 1e6;
         double bandwidth = (channelWidth + (2.0 * guardBandwidth)) * 1e6;
         // For OFDM, the center subcarrier is null (at center frequency)
-        uint32_t numBands = static_cast<uint32_t>((bandwidth / bandBandwidth) + 0.5);
+        uint32_t numBands = static_cast<uint32_t>((bandwidth / carrierSpacing) + 0.5);
         NS_ASSERT(numBands > 0);
         if (numBands % 2 == 0)
         {
@@ -115,19 +98,19 @@
             numBands += 1;
         }
         NS_ASSERT_MSG(numBands % 2 == 1, "Number of bands should be odd");
-        NS_LOG_DEBUG("Num bands " << numBands << " band bandwidth " << bandBandwidth);
+        NS_LOG_DEBUG("Num bands " << numBands << " band bandwidth " << carrierSpacing);
         // lay down numBands/2 bands symmetrically around center frequency
         // and place an additional band at center frequency
         double startingFrequencyHz =
-            centerFrequencyHz - (numBands / 2 * bandBandwidth) - bandBandwidth / 2;
+            centerFrequencyHz - (numBands / 2 * carrierSpacing) - carrierSpacing / 2;
         for (size_t i = 0; i < numBands; i++)
         {
             BandInfo info;
-            double f = startingFrequencyHz + (i * bandBandwidth);
+            double f = startingFrequencyHz + (i * carrierSpacing);
             info.fl = f;
-            f += bandBandwidth / 2;
+            f += carrierSpacing / 2;
             info.fc = f;
-            f += bandBandwidth / 2;
+            f += carrierSpacing / 2;
             info.fh = f;
             NS_LOG_DEBUG("creating band " << i << " (" << info.fl << ":" << info.fc << ":"
                                           << info.fh << ")");
@@ -148,14 +131,14 @@
 {
     NS_LOG_FUNCTION(centerFrequency << txPowerW << +guardBandwidth);
     uint16_t channelWidth = 22; // DSSS channels are 22 MHz wide
-    uint32_t bandBandwidth = 312500;
+    uint32_t carrierSpacing = 312500;
     Ptr<SpectrumValue> c = Create<SpectrumValue>(
-        GetSpectrumModel(centerFrequency, channelWidth, bandBandwidth, guardBandwidth));
+        GetSpectrumModel(centerFrequency, channelWidth, carrierSpacing, guardBandwidth));
     Values::iterator vit = c->ValuesBegin();
     Bands::const_iterator bit = c->ConstBandsBegin();
     uint32_t nGuardBands =
-        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / bandBandwidth) + 0.5);
-    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / bandBandwidth) + 0.5);
+        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
+    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
     NS_ASSERT(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1));
     // Evenly spread power across 22 MHz
     double txPowerPerBand = txPowerW / nAllocatedBands;
@@ -180,23 +163,23 @@
 {
     NS_LOG_FUNCTION(centerFrequency << channelWidth << txPowerW << guardBandwidth << minInnerBandDbr
                                     << minOuterBandDbr << lowestPointDbr);
-    uint32_t bandBandwidth = 0;
+    uint32_t carrierSpacing = 0;
     uint32_t innerSlopeWidth = 0;
     switch (channelWidth)
     {
     case 20:
-        bandBandwidth = 312500;
-        innerSlopeWidth = static_cast<uint32_t>((2e6 / bandBandwidth) + 0.5); // [-11;-9] & [9;11]
+        carrierSpacing = 312500;
+        innerSlopeWidth = static_cast<uint32_t>((2e6 / carrierSpacing) + 0.5); // [-11;-9] & [9;11]
         break;
     case 10:
-        bandBandwidth = 156250;
+        carrierSpacing = 156250;
         innerSlopeWidth =
-            static_cast<uint32_t>((1e6 / bandBandwidth) + 0.5); // [-5.5;-4.5] & [4.5;5.5]
+            static_cast<uint32_t>((1e6 / carrierSpacing) + 0.5); // [-5.5;-4.5] & [4.5;5.5]
         break;
     case 5:
-        bandBandwidth = 78125;
+        carrierSpacing = 78125;
         innerSlopeWidth =
-            static_cast<uint32_t>((5e5 / bandBandwidth) + 0.5); // [-2.75;-2.5] & [2.5;2.75]
+            static_cast<uint32_t>((5e5 / carrierSpacing) + 0.5); // [-2.75;-2.5] & [2.5;2.75]
         break;
     default:
         NS_FATAL_ERROR("Channel width " << channelWidth << " should be correctly set.");
@@ -204,10 +187,10 @@
     }
 
     Ptr<SpectrumValue> c = Create<SpectrumValue>(
-        GetSpectrumModel(centerFrequency, channelWidth, bandBandwidth, guardBandwidth));
+        GetSpectrumModel(centerFrequency, channelWidth, carrierSpacing, guardBandwidth));
     uint32_t nGuardBands =
-        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / bandBandwidth) + 0.5);
-    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / bandBandwidth) + 0.5);
+        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
+    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
     NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1),
                   "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
     // 52 subcarriers (48 data + 4 pilot)
@@ -222,11 +205,11 @@
     uint32_t stop2 = start2 + 26 - 1;
 
     // Build transmit spectrum mask
-    std::vector<WifiSpectrumBand> subBands{
+    std::vector<WifiSpectrumBandIndices> subBands{
         std::make_pair(start1, stop1),
         std::make_pair(start2, stop2),
     };
-    WifiSpectrumBand maskBand(0, nAllocatedBands + nGuardBands);
+    WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands);
     CreateSpectrumMaskForOfdm(c,
                               subBands,
                               maskBand,
@@ -254,12 +237,12 @@
 {
     NS_LOG_FUNCTION(centerFrequency << channelWidth << txPowerW << guardBandwidth << minInnerBandDbr
                                     << minOuterBandDbr << lowestPointDbr);
-    uint32_t bandBandwidth = 312500;
+    uint32_t carrierSpacing = 312500;
     Ptr<SpectrumValue> c = Create<SpectrumValue>(
-        GetSpectrumModel(centerFrequency, channelWidth, bandBandwidth, guardBandwidth));
+        GetSpectrumModel(centerFrequency, channelWidth, carrierSpacing, guardBandwidth));
     uint32_t nGuardBands =
-        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / bandBandwidth) + 0.5);
-    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / bandBandwidth) + 0.5);
+        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
+    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
     NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1),
                   "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
     std::size_t num20MhzBands = channelWidth / 20;
@@ -268,10 +251,11 @@
     double txPowerPerBandW = (txPowerW / numAllocatedSubcarriersPer20MHz) / num20MhzBands;
     NS_LOG_DEBUG("Power per band " << txPowerPerBandW << "W");
 
-    std::size_t numSubcarriersPer20MHz = (20 * 1e6) / bandBandwidth;
+    std::size_t numSubcarriersPer20MHz = (20 * 1e6) / carrierSpacing;
     std::size_t numUnallocatedSubcarriersPer20MHz =
         numSubcarriersPer20MHz - numAllocatedSubcarriersPer20MHz;
-    std::vector<WifiSpectrumBand> subBands; // list of data/pilot-containing subBands (sent at 0dBr)
+    std::vector<WifiSpectrumBandIndices>
+        subBands; // list of data/pilot-containing subBands (sent at 0dBr)
     subBands.resize(num20MhzBands *
                     2); // the center subcarrier is skipped, hence 2 subbands per 20 MHz subchannel
     uint32_t start = (nGuardBands / 2) + (numUnallocatedSubcarriersPer20MHz / 2);
@@ -298,9 +282,9 @@
 
     // Prepare spectrum mask specific variables
     uint32_t innerSlopeWidth = static_cast<uint32_t>(
-        (2e6 / bandBandwidth) +
+        (2e6 / carrierSpacing) +
         0.5); // size in number of subcarriers of the 0dBr<->20dBr slope (2MHz for HT/VHT)
-    WifiSpectrumBand maskBand(0, nAllocatedBands + nGuardBands);
+    WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands);
 
     // Build transmit spectrum mask
     CreateSpectrumMaskForOfdm(c,
@@ -328,12 +312,12 @@
 {
     NS_LOG_FUNCTION(centerFrequency << channelWidth << txPowerW << guardBandwidth << minInnerBandDbr
                                     << minOuterBandDbr << lowestPointDbr);
-    uint32_t bandBandwidth = 312500;
+    uint32_t carrierSpacing = 312500;
     Ptr<SpectrumValue> c = Create<SpectrumValue>(
-        GetSpectrumModel(centerFrequency, channelWidth, bandBandwidth, guardBandwidth));
+        GetSpectrumModel(centerFrequency, channelWidth, carrierSpacing, guardBandwidth));
     uint32_t nGuardBands =
-        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / bandBandwidth) + 0.5);
-    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / bandBandwidth) + 0.5);
+        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
+    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
     NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1),
                   "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
     std::size_t num20MhzBands = channelWidth / 20;
@@ -341,10 +325,11 @@
     double txPowerPerBandW = (txPowerW / numAllocatedSubcarriersPer20MHz) / num20MhzBands;
     NS_LOG_DEBUG("Power per band " << txPowerPerBandW << "W");
 
-    std::size_t numSubcarriersPer20MHz = (20 * 1e6) / bandBandwidth;
+    std::size_t numSubcarriersPer20MHz = (20 * 1e6) / carrierSpacing;
     std::size_t numUnallocatedSubcarriersPer20MHz =
         numSubcarriersPer20MHz - numAllocatedSubcarriersPer20MHz;
-    std::vector<WifiSpectrumBand> subBands; // list of data/pilot-containing subBands (sent at 0dBr)
+    std::vector<WifiSpectrumBandIndices>
+        subBands; // list of data/pilot-containing subBands (sent at 0dBr)
     subBands.resize(num20MhzBands *
                     2); // the center subcarrier is skipped, hence 2 subbands per 20 MHz subchannel
     uint32_t start = (nGuardBands / 2) + (numUnallocatedSubcarriersPer20MHz / 2);
@@ -363,9 +348,9 @@
 
     // Prepare spectrum mask specific variables
     uint32_t innerSlopeWidth = static_cast<uint32_t>(
-        (2e6 / bandBandwidth) +
+        (2e6 / carrierSpacing) +
         0.5); // size in number of subcarriers of the inner band (2MHz for HT/VHT)
-    WifiSpectrumBand maskBand(0, nAllocatedBands + nGuardBands);
+    WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands);
 
     // Build transmit spectrum mask
     CreateSpectrumMaskForOfdm(c,
@@ -395,12 +380,12 @@
 {
     NS_LOG_FUNCTION(centerFrequency << channelWidth << txPowerW << guardBandwidth << minInnerBandDbr
                                     << minOuterBandDbr << lowestPointDbr);
-    uint32_t bandBandwidth = 78125;
+    uint32_t carrierSpacing = 78125;
     Ptr<SpectrumValue> c = Create<SpectrumValue>(
-        GetSpectrumModel(centerFrequency, channelWidth, bandBandwidth, guardBandwidth));
+        GetSpectrumModel(centerFrequency, channelWidth, carrierSpacing, guardBandwidth));
     uint32_t nGuardBands =
-        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / bandBandwidth) + 0.5);
-    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / bandBandwidth) + 0.5);
+        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / carrierSpacing) + 0.5);
+    uint32_t nAllocatedBands = static_cast<uint32_t>(((channelWidth * 1e6) / carrierSpacing) + 0.5);
     NS_ASSERT_MSG(c->GetSpectrumModel()->GetNumBands() == (nAllocatedBands + nGuardBands + 1),
                   "Unexpected number of bands " << c->GetSpectrumModel()->GetNumBands());
     double txPowerPerBandW = 0.0;
@@ -414,16 +399,17 @@
     uint32_t stop4;
     // Prepare spectrum mask specific variables
     uint32_t innerSlopeWidth = static_cast<uint32_t>(
-        (1e6 / bandBandwidth) + 0.5);       // size in number of subcarriers of the inner band
-    std::vector<WifiSpectrumBand> subBands; // list of data/pilot-containing subBands (sent at 0dBr)
-    WifiSpectrumBand maskBand(0, nAllocatedBands + nGuardBands);
+        (1e6 / carrierSpacing) + 0.5); // size in number of subcarriers of the inner band
+    std::vector<WifiSpectrumBandIndices>
+        subBands; // list of data/pilot-containing subBands (sent at 0dBr)
+    WifiSpectrumBandIndices maskBand(0, nAllocatedBands + nGuardBands);
     switch (channelWidth)
     {
     case 20:
         // 242 subcarriers (234 data + 8 pilot)
         txPowerPerBandW = txPowerW / 242;
         innerSlopeWidth =
-            static_cast<uint32_t>((5e5 / bandBandwidth) + 0.5); // [-10.25;-9.75] & [9.75;10.25]
+            static_cast<uint32_t>((5e5 / carrierSpacing) + 0.5); // [-10.25;-9.75] & [9.75;10.25]
         // skip the guard band and 6 subbands, then place power in 121 subbands, then
         // skip 3 DC, then place power in 121 subbands, then skip
         // the final 5 subbands and the guard band.
@@ -482,10 +468,11 @@
     }
 
     // Create punctured bands
-    uint32_t puncturedSlopeWidth = static_cast<uint32_t>(
-        (500e3 / bandBandwidth) + 0.5); // size in number of subcarriers of the punctured slope band
-    std::vector<WifiSpectrumBand> puncturedBands;
-    std::size_t subcarriersPerSuband = (20 * 1e6 / bandBandwidth);
+    uint32_t puncturedSlopeWidth =
+        static_cast<uint32_t>((500e3 / carrierSpacing) +
+                              0.5); // size in number of subcarriers of the punctured slope band
+    std::vector<WifiSpectrumBandIndices> puncturedBands;
+    std::size_t subcarriersPerSuband = (20 * 1e6 / carrierSpacing);
     uint32_t start = (nGuardBands / 2);
     uint32_t stop = start + subcarriersPerSuband - 1;
     for (auto puncturedSubchannel : puncturedSubchannels)
@@ -520,13 +507,13 @@
                                                               uint16_t channelWidth,
                                                               double txPowerW,
                                                               uint16_t guardBandwidth,
-                                                              WifiSpectrumBand ru)
+                                                              const WifiSpectrumBandIndices& ru)
 {
     NS_LOG_FUNCTION(centerFrequency << channelWidth << txPowerW << guardBandwidth << ru.first
                                     << ru.second);
-    uint32_t bandBandwidth = 78125;
+    uint32_t carrierSpacing = 78125;
     Ptr<SpectrumValue> c = Create<SpectrumValue>(
-        GetSpectrumModel(centerFrequency, channelWidth, bandBandwidth, guardBandwidth));
+        GetSpectrumModel(centerFrequency, channelWidth, carrierSpacing, guardBandwidth));
 
     // Build spectrum mask
     Values::iterator vit = c->ValuesBegin();
@@ -551,12 +538,12 @@
 Ptr<SpectrumValue>
 WifiSpectrumValueHelper::CreateNoisePowerSpectralDensity(uint32_t centerFrequency,
                                                          uint16_t channelWidth,
-                                                         uint32_t bandBandwidth,
+                                                         uint32_t carrierSpacing,
                                                          double noiseFigure,
                                                          uint16_t guardBandwidth)
 {
     Ptr<SpectrumModel> model =
-        GetSpectrumModel(centerFrequency, channelWidth, bandBandwidth, guardBandwidth);
+        GetSpectrumModel(centerFrequency, channelWidth, carrierSpacing, guardBandwidth);
     return CreateNoisePowerSpectralDensity(noiseFigure, model);
 }
 
@@ -579,43 +566,18 @@
     return noisePsd;
 }
 
-Ptr<SpectrumValue>
-WifiSpectrumValueHelper::CreateRfFilter(uint32_t centerFrequency,
-                                        uint16_t totalChannelWidth,
-                                        uint32_t bandBandwidth,
-                                        uint16_t guardBandwidth,
-                                        WifiSpectrumBand band)
-{
-    uint32_t startIndex = band.first;
-    uint32_t stopIndex = band.second;
-    NS_LOG_FUNCTION(centerFrequency << totalChannelWidth << bandBandwidth << guardBandwidth
-                                    << startIndex << stopIndex);
-    Ptr<SpectrumValue> c = Create<SpectrumValue>(
-        GetSpectrumModel(centerFrequency, totalChannelWidth, bandBandwidth, guardBandwidth));
-    Bands::const_iterator bit = c->ConstBandsBegin();
-    Values::iterator vit = c->ValuesBegin();
-    vit += startIndex;
-    bit += startIndex;
-    for (size_t i = startIndex; i <= stopIndex; i++, vit++, bit++)
-    {
-        *vit = 1;
-    }
-    NS_LOG_LOGIC("Added subbands " << startIndex << " to " << stopIndex << " to filter");
-    return c;
-}
-
 void
 WifiSpectrumValueHelper::CreateSpectrumMaskForOfdm(
     Ptr<SpectrumValue> c,
-    const std::vector<WifiSpectrumBand>& allocatedSubBands,
-    WifiSpectrumBand maskBand,
+    const std::vector<WifiSpectrumBandIndices>& allocatedSubBands,
+    const WifiSpectrumBandIndices& maskBand,
     double txPowerPerBandW,
     uint32_t nGuardBands,
     uint32_t innerSlopeWidth,
     double minInnerBandDbr,
     double minOuterBandDbr,
     double lowestPointDbr,
-    const std::vector<WifiSpectrumBand>& puncturedBands,
+    const std::vector<WifiSpectrumBandIndices>& puncturedBands,
     uint32_t puncturedSlopeWidth)
 {
     NS_LOG_FUNCTION(c << allocatedSubBands.front().first << allocatedSubBands.back().second
@@ -640,25 +602,27 @@
         nGuardBands / 4; // nGuardBands is the total left+right guard band. The left/right outer
                          // part is half of the left/right guard band.
     uint32_t middleSlopeWidth = outerSlopeWidth - (innerSlopeWidth / 2);
-    WifiSpectrumBand outerBandLeft(maskBand.first, // to handle cases where allocated channel is
-                                                   // under WifiPhy configured channel width.
-                                   maskBand.first + outerSlopeWidth - 1);
-    WifiSpectrumBand middleBandLeft(outerBandLeft.second + 1,
-                                    outerBandLeft.second + middleSlopeWidth);
-    WifiSpectrumBand innerBandLeft(allocatedSubBands.front().first - innerSlopeWidth,
-                                   allocatedSubBands.front().first -
-                                       1); // better to place slope based on allocated subcarriers
-    WifiSpectrumBand flatJunctionLeft(middleBandLeft.second + 1,
-                                      innerBandLeft.first -
-                                          1); // in order to handle shift due to guard subcarriers
-    WifiSpectrumBand outerBandRight(
+    WifiSpectrumBandIndices outerBandLeft(
+        maskBand.first, // to handle cases where allocated channel is
+                        // under WifiPhy configured channel width.
+        maskBand.first + outerSlopeWidth - 1);
+    WifiSpectrumBandIndices middleBandLeft(outerBandLeft.second + 1,
+                                           outerBandLeft.second + middleSlopeWidth);
+    WifiSpectrumBandIndices innerBandLeft(
+        allocatedSubBands.front().first - innerSlopeWidth,
+        allocatedSubBands.front().first -
+            1); // better to place slope based on allocated subcarriers
+    WifiSpectrumBandIndices flatJunctionLeft(
+        middleBandLeft.second + 1,
+        innerBandLeft.first - 1); // in order to handle shift due to guard subcarriers
+    WifiSpectrumBandIndices outerBandRight(
         maskBand.second - outerSlopeWidth + 1,
         maskBand.second); // start from outer edge to be able to compute flat junction width
-    WifiSpectrumBand middleBandRight(outerBandRight.first - middleSlopeWidth,
-                                     outerBandRight.first - 1);
-    WifiSpectrumBand innerBandRight(allocatedSubBands.back().second + 1,
-                                    allocatedSubBands.back().second + innerSlopeWidth);
-    WifiSpectrumBand flatJunctionRight(innerBandRight.second + 1, middleBandRight.first - 1);
+    WifiSpectrumBandIndices middleBandRight(outerBandRight.first - middleSlopeWidth,
+                                            outerBandRight.first - 1);
+    WifiSpectrumBandIndices innerBandRight(allocatedSubBands.back().second + 1,
+                                           allocatedSubBands.back().second + innerSlopeWidth);
+    WifiSpectrumBandIndices flatJunctionRight(innerBandRight.second + 1, middleBandRight.first - 1);
     std::ostringstream ss;
     ss << "outerBandLeft=[" << outerBandLeft.first << ";" << outerBandLeft.second << "] "
        << "middleBandLeft=[" << middleBandLeft.first << ";" << middleBandLeft.second << "] "
@@ -830,7 +794,7 @@
 }
 
 double
-WifiSpectrumValueHelper::GetBandPowerW(Ptr<SpectrumValue> psd, const WifiSpectrumBand& band)
+WifiSpectrumValueHelper::GetBandPowerW(Ptr<SpectrumValue> psd, const WifiSpectrumBandIndices& band)
 {
     double powerWattPerHertz = 0.0;
     auto valueIt = psd->ConstValuesBegin() + band.first;
@@ -844,92 +808,29 @@
     return powerWattPerHertz * (bandIt->fh - bandIt->fl);
 }
 
-static Ptr<SpectrumModel> g_WifiSpectrumModel5Mhz; ///< static initializer for the class
-
-WifiSpectrumValueHelper::~WifiSpectrumValueHelper()
-{
-}
-
-WifiSpectrumValue5MhzFactory::~WifiSpectrumValue5MhzFactory()
+bool
+operator<(const FrequencyRange& left, const FrequencyRange& right)
 {
+    return left.minFrequency < right.minFrequency;
 }
 
-/**
- * Static class to initialize the values for the 2.4 GHz Wi-Fi spectrum model
- */
-static class WifiSpectrumModel5MhzInitializer
-{
-  public:
-    WifiSpectrumModel5MhzInitializer()
-    {
-        Bands bands;
-        for (int i = -4; i < 13 + 7; i++)
-        {
-            BandInfo bi;
-            bi.fl = 2407.0e6 + i * 5.0e6;
-            bi.fh = 2407.0e6 + (i + 1) * 5.0e6;
-            bi.fc = (bi.fl + bi.fh) / 2;
-            bands.push_back(bi);
-        }
-        g_WifiSpectrumModel5Mhz = Create<SpectrumModel>(bands);
-    }
-} g_WifiSpectrumModel5MhzInitializerInstance; //!< initialization instance for WifiSpectrumModel5Mhz
-
-Ptr<SpectrumValue>
-WifiSpectrumValue5MhzFactory::CreateConstant(double v)
+bool
+operator==(const FrequencyRange& left, const FrequencyRange& right)
 {
-    Ptr<SpectrumValue> c = Create<SpectrumValue>(g_WifiSpectrumModel5Mhz);
-    (*c) = v;
-    return c;
+    return (left.minFrequency == right.minFrequency) && (left.maxFrequency == right.maxFrequency);
 }
 
-Ptr<SpectrumValue>
-WifiSpectrumValue5MhzFactory::CreateTxPowerSpectralDensity(double txPower, uint8_t channel)
+bool
+operator!=(const FrequencyRange& left, const FrequencyRange& right)
 {
-    Ptr<SpectrumValue> txPsd = Create<SpectrumValue>(g_WifiSpectrumModel5Mhz);
-
-    // since the spectrum model has a resolution of 5 MHz, we model
-    // the transmitted signal with a constant density over a 20MHz
-    // bandwidth centered on the center frequency of the channel. The
-    // transmission power outside the transmission power density is
-    // calculated considering the transmit spectrum mask, see IEEE
-    // Std. 802.11-2007, Annex I
-
-    double txPowerDensity = txPower / 20e6;
-
-    NS_ASSERT(channel >= 1);
-    NS_ASSERT(channel <= 13);
-
-    (*txPsd)[channel - 1] = txPowerDensity * 1e-4;      // -40dB
-    (*txPsd)[channel] = txPowerDensity * 1e-4;          // -40dB
-    (*txPsd)[channel + 1] = txPowerDensity * 0.0015849; // -28dB
-    (*txPsd)[channel + 2] = txPowerDensity * 0.0015849; // -28dB
-    (*txPsd)[channel + 3] = txPowerDensity;
-    (*txPsd)[channel + 4] = txPowerDensity;
-    (*txPsd)[channel + 5] = txPowerDensity;
-    (*txPsd)[channel + 6] = txPowerDensity;
-    (*txPsd)[channel + 7] = txPowerDensity * 0.0015849; // -28dB
-    (*txPsd)[channel + 8] = txPowerDensity * 0.0015849; // -28dB
-    (*txPsd)[channel + 9] = txPowerDensity * 1e-4;      // -40dB
-    (*txPsd)[channel + 10] = txPowerDensity * 1e-4;     // -40dB
-
-    return txPsd;
+    return !(left == right);
 }
 
-Ptr<SpectrumValue>
-WifiSpectrumValue5MhzFactory::CreateRfFilter(uint8_t channel)
+std::ostream&
+operator<<(std::ostream& os, const FrequencyRange& freqRange)
 {
-    Ptr<SpectrumValue> rf = Create<SpectrumValue>(g_WifiSpectrumModel5Mhz);
-
-    NS_ASSERT(channel >= 1);
-    NS_ASSERT(channel <= 13);
-
-    (*rf)[channel + 3] = 1;
-    (*rf)[channel + 4] = 1;
-    (*rf)[channel + 5] = 1;
-    (*rf)[channel + 6] = 1;
-
-    return rf;
+    os << "[" << freqRange.minFrequency << " MHz - " << freqRange.maxFrequency << " MHz]";
+    return os;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/model/wifi-spectrum-value-helper.h ns-3.39/src/spectrum/model/wifi-spectrum-value-helper.h
--- ns-3.38/src/spectrum/model/wifi-spectrum-value-helper.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/model/wifi-spectrum-value-helper.h	2023-07-05 14:23:48.081428300 -0700
@@ -28,9 +28,9 @@
 {
 
 /**
- * typedef for a pair of start and stop sub-band indexes
+ * typedef for a pair of start and stop sub-band indices
  */
-typedef std::pair<uint32_t, uint32_t> WifiSpectrumBand;
+using WifiSpectrumBandIndices = std::pair<uint32_t, uint32_t>;
 
 /**
  * \ingroup spectrum
@@ -45,7 +45,7 @@
     /**
      * Destructor
      */
-    virtual ~WifiSpectrumValueHelper();
+    virtual ~WifiSpectrumValueHelper() = default;
 
     /**
      * Return a SpectrumModel instance corresponding to the center frequency
@@ -55,7 +55,7 @@
      *
      * \param centerFrequency center frequency (MHz)
      * \param channelWidth channel width (MHz)
-     * \param bandBandwidth width of each band (Hz)
+     * \param carrierSpacing carrier spacing (Hz)
      * \param guardBandwidth width of the guard band (MHz)
      *
      * \return the static SpectrumModel instance corresponding to the
@@ -63,7 +63,7 @@
      */
     static Ptr<SpectrumModel> GetSpectrumModel(uint32_t centerFrequency,
                                                uint16_t channelWidth,
-                                               uint32_t bandBandwidth,
+                                               uint32_t carrierSpacing,
                                                uint16_t guardBandwidth);
 
     /**
@@ -198,18 +198,19 @@
      * \return a pointer to a newly allocated SpectrumValue representing the HE OFDM Transmit Power
      * Spectral Density on the RU used by the STA in W/Hz for each Band
      */
-    static Ptr<SpectrumValue> CreateHeMuOfdmTxPowerSpectralDensity(uint32_t centerFrequency,
-                                                                   uint16_t channelWidth,
-                                                                   double txPowerW,
-                                                                   uint16_t guardBandwidth,
-                                                                   WifiSpectrumBand ru);
+    static Ptr<SpectrumValue> CreateHeMuOfdmTxPowerSpectralDensity(
+        uint32_t centerFrequency,
+        uint16_t channelWidth,
+        double txPowerW,
+        uint16_t guardBandwidth,
+        const WifiSpectrumBandIndices& ru);
 
     /**
      * Create a power spectral density corresponding to the noise
      *
      * \param centerFrequency center frequency (MHz)
      * \param channelWidth channel width (MHz)
-     * \param bandBandwidth width of each band (Hz)
+     * \param carrierSpacing carrier spacing (Hz)
      * \param noiseFigure the noise figure in dB w.r.t. a reference temperature of 290K
      * \param guardBandwidth width of the guard band (MHz)
      * \return a pointer to a newly allocated SpectrumValue representing the noise Power Spectral
@@ -217,7 +218,7 @@
      */
     static Ptr<SpectrumValue> CreateNoisePowerSpectralDensity(uint32_t centerFrequency,
                                                               uint16_t channelWidth,
-                                                              uint32_t bandBandwidth,
+                                                              uint32_t carrierSpacing,
                                                               double noiseFigure,
                                                               uint16_t guardBandwidth);
 
@@ -233,24 +234,6 @@
                                                               Ptr<SpectrumModel> spectrumModel);
 
     /**
-     * Create a spectral density corresponding to the RF filter
-     *
-     * \param centerFrequency the center frequency (MHz)
-     * \param totalChannelWidth the total channel width (MHz)
-     * \param bandBandwidth the width of each band (MHz)
-     * \param guardBandwidth the width of the guard band (MHz)
-     * \param band the pair of start and stop indexes that defines the band to be filtered
-     *
-     * \return a pointer to a SpectrumValue representing the RF filter applied
-     * to an received power spectral density
-     */
-    static Ptr<SpectrumValue> CreateRfFilter(uint32_t centerFrequency,
-                                             uint16_t totalChannelWidth,
-                                             uint32_t bandBandwidth,
-                                             uint16_t guardBandwidth,
-                                             WifiSpectrumBand band);
-
-    /**
      * Create a transmit power spectral density corresponding to OFDM
      * transmit spectrum mask requirements for 11a/11g/11n/11ac/11ax
      * Channel width may vary between 5, 10, 20, 40, 80, and 160 MHz.
@@ -299,15 +282,16 @@
      */
     static void CreateSpectrumMaskForOfdm(
         Ptr<SpectrumValue> c,
-        const std::vector<WifiSpectrumBand>& allocatedSubBands,
-        WifiSpectrumBand maskBand,
+        const std::vector<WifiSpectrumBandIndices>& allocatedSubBands,
+        const WifiSpectrumBandIndices& maskBand,
         double txPowerPerBandW,
         uint32_t nGuardBands,
         uint32_t innerSlopeWidth,
         double minInnerBandDbr,
         double minOuterbandDbr,
         double lowestPointDbr,
-        const std::vector<WifiSpectrumBand>& puncturedSubBands = std::vector<WifiSpectrumBand>{},
+        const std::vector<WifiSpectrumBandIndices>& puncturedSubBands =
+            std::vector<WifiSpectrumBandIndices>{},
         uint32_t puncturedSlopeWidth = 0);
 
     /**
@@ -338,63 +322,73 @@
      *
      * \return band power in W
      */
-    static double GetBandPowerW(Ptr<SpectrumValue> psd, const WifiSpectrumBand& band);
+    static double GetBandPowerW(Ptr<SpectrumValue> psd, const WifiSpectrumBandIndices& band);
 };
 
 /**
  * \ingroup spectrum
- *
- * Implements Wifi SpectrumValue for the 2.4 GHz ISM band only, with a
- * 5 MHz spectrum resolution.
- *
+ * Struct defining a frequency range between minFrequency (MHz) and maxFrequency (MHz).
  */
-class WifiSpectrumValue5MhzFactory
+struct FrequencyRange
 {
-  public:
-    /**
-     * Destructor
-     */
-    virtual ~WifiSpectrumValue5MhzFactory();
-    /**
-     * Creates a SpectrumValue instance with a constant value for all frequencies
-     *
-     * @param psd the constant value
-     *
-     * @return a Ptr to a newly created SpectrumValue
-     */
-    virtual Ptr<SpectrumValue> CreateConstant(double psd);
-    /**
-     * Creates a SpectrumValue instance that represents the TX Power Spectral
-     * Density  of a wifi device corresponding to the provided parameters
-     *
-     * Since the spectrum model has a resolution of 5 MHz, we model
-     * the transmitted signal with a constant density over a 20MHz
-     * bandwidth centered on the center frequency of the channel. The
-     * transmission power outside the transmission power density is
-     * calculated considering the transmit spectrum mask, see IEEE
-     * Std. 802.11-2007, Annex I.  The two bands just outside of the main
-     * 20 MHz are allocated power at -28 dB down from the center 20 MHz,
-     * and the two bands outside of this are allocated power at -40 dB down
-     * (with a total bandwidth of 60 MHz containing non-zero power allocation).
-     *
-     * @param txPower the total TX power in W
-     * @param channel the number of the channel (1 <= channel <= 13)
-     *
-     * @return a Ptr to a newly created SpectrumValue
-     */
-    virtual Ptr<SpectrumValue> CreateTxPowerSpectralDensity(double txPower, uint8_t channel);
-    /**
-     * Creates a SpectrumValue instance which
-     * represents the frequency response of the RF filter which is used
-     * by a wifi device to receive signals when tuned to a particular channel
-     *
-     * @param channel the number of the channel (1 <= channel <= 13)
-     *
-     * @return a Ptr to a newly created SpectrumValue
-     */
-    virtual Ptr<SpectrumValue> CreateRfFilter(uint8_t channel);
+    uint16_t minFrequency{0}; ///< the minimum frequency in MHz
+    uint16_t maxFrequency{0}; ///< the maximum frequency in MHz
 };
 
+/**
+ * Compare two FrequencyRange values
+ *
+ * \param lhs the FrequencyRange value on the left of operator
+ * \param rhs the FrequencyRange value on the right of operator
+ *
+ * \return true if minFrequency of left is less than minFrequency of right, false otherwise
+ */
+bool operator<(const FrequencyRange& lhs, const FrequencyRange& rhs);
+
+/**
+ * Compare two FrequencyRange values
+ *
+ * \param lhs the FrequencyRange value on the left of operator
+ * \param rhs the FrequencyRange value on the right of operator
+ *
+ * \return true if both minFrequency and maxFrequency of left are equal to minFrequency and
+ * maxFrequency of right respectively, false otherwise
+ */
+bool operator==(const FrequencyRange& lhs, const FrequencyRange& rhs);
+
+/**
+ * Compare two FrequencyRange values
+ *
+ * \param lhs the FrequencyRange value on the left of operator
+ * \param rhs the FrequencyRange value on the right of operator
+ *
+ * \return true if either minFrequency or maxFrequency of left different from minFrequency or
+ * maxFrequency of right respectively, false otherwise
+ */
+bool operator!=(const FrequencyRange& lhs, const FrequencyRange& rhs);
+
+/**
+ * Serialize FrequencyRange values to ostream (human-readable).
+ *
+ * \param os the output stream
+ * \param freqRange the FrequencyRange
+ *
+ * \return std::ostream
+ */
+std::ostream& operator<<(std::ostream& os, const FrequencyRange& freqRange);
+
+/// Identifier for the frequency range covering the whole wifi spectrum
+constexpr FrequencyRange WHOLE_WIFI_SPECTRUM = {2401, 7125};
+
+/// Identifier for the frequency range covering the wifi spectrum in the 2.4 GHz band
+constexpr FrequencyRange WIFI_SPECTRUM_2_4_GHZ = {2401, 2483};
+
+/// Identifier for the frequency range covering the wifi spectrum in the 5 GHz band
+constexpr FrequencyRange WIFI_SPECTRUM_5_GHZ = {5170, 5915};
+
+/// Identifier for the frequency range covering the wifi spectrum in the 6 GHz band
+constexpr FrequencyRange WIFI_SPECTRUM_6_GHZ = {5945, 7125};
+
 } // namespace ns3
 
 #endif /*  WIFI_SPECTRUM_VALUE_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/test/examples-to-run.py ns-3.39/src/spectrum/test/examples-to-run.py
--- ns-3.38/src/spectrum/test/examples-to-run.py	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/test/examples-to-run.py	2023-07-05 14:23:48.081428300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/test/spectrum-ideal-phy-test.cc ns-3.39/src/spectrum/test/spectrum-ideal-phy-test.cc
--- ns-3.38/src/spectrum/test/spectrum-ideal-phy-test.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/test/spectrum-ideal-phy-test.cc	2023-07-05 14:23:48.081428300 -0700
@@ -21,6 +21,7 @@
 #include <ns3/config.h>
 #include <ns3/data-rate.h>
 #include <ns3/friis-spectrum-propagation-loss.h>
+#include <ns3/ism-spectrum-value-helper.h>
 #include <ns3/log.h>
 #include <ns3/math.h>
 #include <ns3/mobility-helper.h>
@@ -43,7 +44,6 @@
 #include <ns3/test.h>
 #include <ns3/uinteger.h>
 #include <ns3/waveform-generator.h>
-#include <ns3/wifi-spectrum-value-helper.h>
 
 #include <iomanip>
 #include <iostream>
@@ -170,7 +170,7 @@
     channelHelper.AddPropagationLoss(propLoss);
     Ptr<SpectrumChannel> channel = channelHelper.Create();
 
-    WifiSpectrumValue5MhzFactory sf;
+    SpectrumValue5MhzFactory sf;
 
     uint32_t channelNumber = 1;
     Ptr<SpectrumValue> txPsd = sf.CreateTxPowerSpectralDensity(txPowerW, channelNumber);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/test/spectrum-interference-test.cc ns-3.39/src/spectrum/test/spectrum-interference-test.cc
--- ns-3.38/src/spectrum/test/spectrum-interference-test.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/test/spectrum-interference-test.cc	2023-07-05 14:23:48.081428300 -0700
@@ -62,7 +62,7 @@
   private:
     Ptr<SpectrumValue> m_s;                     //!< Spectrum value
     uint32_t m_txBytes;                         //!< number of bytes to transmit
-    uint32_t m_rxCorrectKnownOutcome;           //!< extected Rx bytes
+    uint32_t m_rxCorrectKnownOutcome;           //!< expected Rx bytes
     Ptr<const SpectrumModel> m_mySpectrumModel; //!< Spectrum model pointer
 };
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/test/spectrum-test.h ns-3.39/src/spectrum/test/spectrum-test.h
--- ns-3.38/src/spectrum/test/spectrum-test.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/test/spectrum-test.h	2023-07-05 14:23:48.081428300 -0700
@@ -19,6 +19,9 @@
  * part of the code copied from test.h
  */
 
+#ifndef SPECTRUM_TEST_H
+#define SPECTRUM_TEST_H
+
 #include <ns3/spectrum-value.h>
 #include <ns3/test.h>
 
@@ -155,3 +158,5 @@
                               (__LINE__));                                                         \
         }                                                                                          \
     } while (false);
+
+#endif // SPECTRUM_TEST_H
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/spectrum/test/three-gpp-channel-test-suite.cc ns-3.39/src/spectrum/test/three-gpp-channel-test-suite.cc
--- ns-3.38/src/spectrum/test/three-gpp-channel-test-suite.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/spectrum/test/three-gpp-channel-test-suite.cc	2023-07-05 14:23:48.081428300 -0700
@@ -21,6 +21,7 @@
 #include "ns3/config.h"
 #include "ns3/constant-position-mobility-model.h"
 #include "ns3/double.h"
+#include "ns3/ism-spectrum-value-helper.h"
 #include "ns3/isotropic-antenna-model.h"
 #include "ns3/log.h"
 #include "ns3/node-container.h"
@@ -34,7 +35,6 @@
 #include "ns3/three-gpp-spectrum-propagation-loss-model.h"
 #include "ns3/uinteger.h"
 #include "ns3/uniform-planar-array.h"
-#include "ns3/wifi-spectrum-value-helper.h"
 
 using namespace ns3;
 
@@ -636,7 +636,7 @@
     DoBeamforming(rxDev, rxAntenna, txDev, txAntenna);
 
     // create the tx psd
-    WifiSpectrumValue5MhzFactory sf;
+    SpectrumValue5MhzFactory sf;
     double txPower = 0.1; // Watts
     uint32_t channelNumber = 1;
     Ptr<SpectrumValue> txPsd = sf.CreateTxPowerSpectralDensity(txPower, channelNumber);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/helper/file-helper.cc ns-3.39/src/stats/helper/file-helper.cc
--- ns-3.38/src/stats/helper/file-helper.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/helper/file-helper.cc	2023-07-05 14:23:48.085428200 -0700
@@ -47,7 +47,7 @@
 }
 
 FileHelper::FileHelper(const std::string& outputFileNameWithoutExtension,
-                       enum FileAggregator::FileType fileType)
+                       FileAggregator::FileType fileType)
     : m_aggregator(nullptr),
       m_fileProbeCount(0),
       m_fileType(fileType),
@@ -67,7 +67,7 @@
 
 void
 FileHelper::ConfigureFile(const std::string& outputFileNameWithoutExtension,
-                          enum FileAggregator::FileType fileType)
+                          FileAggregator::FileType fileType)
 {
     NS_LOG_FUNCTION(this << outputFileNameWithoutExtension << fileType);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/helper/file-helper.h ns-3.39/src/stats/helper/file-helper.h
--- ns-3.38/src/stats/helper/file-helper.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/helper/file-helper.h	2023-07-05 14:23:48.085428200 -0700
@@ -56,7 +56,7 @@
      * specified by fileType.  The default file type is space-separated.
      */
     FileHelper(const std::string& outputFileNameWithoutExtension,
-               enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
+               FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
 
     virtual ~FileHelper();
 
@@ -72,7 +72,7 @@
      * is space-separated.
      */
     void ConfigureFile(const std::string& outputFileNameWithoutExtension,
-                       enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
+                       FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
 
     /**
      * \param typeId the type ID for the probe used when it is created.
@@ -298,7 +298,7 @@
     uint32_t m_fileProbeCount;
 
     /// Determines the kind of file written by the aggregator.
-    enum FileAggregator::FileType m_fileType;
+    FileAggregator::FileType m_fileType;
 
     /// The name of the output file to created without its extension.
     std::string m_outputFileNameWithoutExtension;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/helper/gnuplot-helper.cc ns-3.39/src/stats/helper/gnuplot-helper.cc
--- ns-3.38/src/stats/helper/gnuplot-helper.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/helper/gnuplot-helper.cc	2023-07-05 14:23:48.085428200 -0700
@@ -107,7 +107,7 @@
                          const std::string& path,
                          const std::string& probeTraceSource,
                          const std::string& title,
-                         enum GnuplotAggregator::KeyLocation keyLocation)
+                         GnuplotAggregator::KeyLocation keyLocation)
 {
     NS_LOG_FUNCTION(this << typeId << path << probeTraceSource << title << keyLocation);
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/helper/gnuplot-helper.h ns-3.39/src/stats/helper/gnuplot-helper.h
--- ns-3.38/src/stats/helper/gnuplot-helper.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/helper/gnuplot-helper.h	2023-07-05 14:23:48.085428200 -0700
@@ -124,7 +124,7 @@
                    const std::string& path,
                    const std::string& probeTraceSource,
                    const std::string& title,
-                   enum GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);
+                   GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);
 
     /**
      * \param adaptorName the timeSeriesAdaptor's name.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/get-wildcard-matches.cc ns-3.39/src/stats/model/get-wildcard-matches.cc
--- ns-3.38/src/stats/model/get-wildcard-matches.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/model/get-wildcard-matches.cc	2023-07-05 14:23:48.085428200 -0700
@@ -79,7 +79,7 @@
     tokenStart = 0;
     for (i = 0; i < nonWildcardTokenCount; i++)
     {
-        // Look for the non-wilcard token.
+        // Look for the non-wildcard token.
         token = matchedPath.find(nonWildcardTokens[i], tokenStart);
 
         // Make sure that the token is found.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/gnuplot-aggregator.cc ns-3.39/src/stats/model/gnuplot-aggregator.cc
--- ns-3.38/src/stats/model/gnuplot-aggregator.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/model/gnuplot-aggregator.cc	2023-07-05 14:23:48.085428200 -0700
@@ -274,14 +274,14 @@
 }
 
 void
-GnuplotAggregator::Set2dDatasetDefaultStyle(enum Gnuplot2dDataset::Style style)
+GnuplotAggregator::Set2dDatasetDefaultStyle(Gnuplot2dDataset::Style style)
 {
     NS_LOG_FUNCTION(style);
     Gnuplot2dDataset::SetDefaultStyle(style);
 }
 
 void
-GnuplotAggregator::Set2dDatasetStyle(const std::string& dataset, enum Gnuplot2dDataset::Style style)
+GnuplotAggregator::Set2dDatasetStyle(const std::string& dataset, Gnuplot2dDataset::Style style)
 {
     NS_LOG_FUNCTION(this << dataset << style);
     if (m_2dDatasetMap.count(dataset) == 0)
@@ -294,7 +294,7 @@
 }
 
 void
-GnuplotAggregator::Set2dDatasetDefaultErrorBars(enum Gnuplot2dDataset::ErrorBars errorBars)
+GnuplotAggregator::Set2dDatasetDefaultErrorBars(Gnuplot2dDataset::ErrorBars errorBars)
 {
     NS_LOG_FUNCTION(errorBars);
     Gnuplot2dDataset::SetDefaultErrorBars(errorBars);
@@ -302,7 +302,7 @@
 
 void
 GnuplotAggregator::Set2dDatasetErrorBars(const std::string& dataset,
-                                         enum Gnuplot2dDataset::ErrorBars errorBars)
+                                         Gnuplot2dDataset::ErrorBars errorBars)
 {
     NS_LOG_FUNCTION(this << dataset << errorBars);
     if (m_2dDatasetMap.count(dataset) == 0)
@@ -315,7 +315,7 @@
 }
 
 void
-GnuplotAggregator::SetKeyLocation(enum GnuplotAggregator::KeyLocation keyLocation)
+GnuplotAggregator::SetKeyLocation(GnuplotAggregator::KeyLocation keyLocation)
 {
     NS_LOG_FUNCTION(this << keyLocation);
     // Set the specified key location.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/gnuplot-aggregator.h ns-3.39/src/stats/model/gnuplot-aggregator.h
--- ns-3.38/src/stats/model/gnuplot-aggregator.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/model/gnuplot-aggregator.h	2023-07-05 14:23:48.085428200 -0700
@@ -210,7 +210,7 @@
      *
      * \brief Change default style for all newly created objects.
      */
-    static void Set2dDatasetDefaultStyle(enum Gnuplot2dDataset::Style style);
+    static void Set2dDatasetDefaultStyle(Gnuplot2dDataset::Style style);
 
     /**
      * \param dataset the gnuplot 2D dataset to be plotted.
@@ -221,14 +221,14 @@
      * The string in dataset should match the context for the Collector
      * it is connected to.
      */
-    void Set2dDatasetStyle(const std::string& dataset, enum Gnuplot2dDataset::Style style);
+    void Set2dDatasetStyle(const std::string& dataset, Gnuplot2dDataset::Style style);
 
     /**
      * \param errorBars the style of errorbars to use for newly created datasets.
      *
      * \brief Change default errorbars style for all newly created objects.
      */
-    static void Set2dDatasetDefaultErrorBars(enum Gnuplot2dDataset::ErrorBars errorBars);
+    static void Set2dDatasetDefaultErrorBars(Gnuplot2dDataset::ErrorBars errorBars);
 
     /**
      * \param dataset the gnuplot 2D dataset to be plotted.
@@ -244,8 +244,7 @@
      * The string in dataset should match the context for the Collector
      * it is connected to.
      */
-    void Set2dDatasetErrorBars(const std::string& dataset,
-                               enum Gnuplot2dDataset::ErrorBars errorBars);
+    void Set2dDatasetErrorBars(const std::string& dataset, Gnuplot2dDataset::ErrorBars errorBars);
 
     /**
      * \param keyLocation the location of the key in the plot.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/gnuplot.cc ns-3.39/src/stats/model/gnuplot.cc
--- ns-3.38/src/stats/model/gnuplot.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/model/gnuplot.cc	2023-07-05 14:23:48.085428200 -0700
@@ -109,7 +109,7 @@
 
 std::string GnuplotDataset::m_defaultExtra = "";
 
-GnuplotDataset::GnuplotDataset(struct Data* data)
+GnuplotDataset::GnuplotDataset(Data* data)
     : m_data(data)
 {
 }
@@ -340,9 +340,9 @@
 // --- Gnuplot2dDataset ---------------------------------------------------- //
 
 /// Default plot style static instance
-enum Gnuplot2dDataset::Style Gnuplot2dDataset::m_defaultStyle = LINES;
+Gnuplot2dDataset::Style Gnuplot2dDataset::m_defaultStyle = LINES;
 /// Default error bars type static instance
-enum Gnuplot2dDataset::ErrorBars Gnuplot2dDataset::m_defaultErrorBars = NONE;
+Gnuplot2dDataset::ErrorBars Gnuplot2dDataset::m_defaultErrorBars = NONE;
 
 Gnuplot2dDataset::Gnuplot2dDataset(const std::string& title)
     : GnuplotDataset(new Data2d(title))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/gnuplot.h ns-3.39/src/stats/model/gnuplot.h
--- ns-3.38/src/stats/model/gnuplot.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/model/gnuplot.h	2023-07-05 14:23:48.085428200 -0700
@@ -97,12 +97,12 @@
      * Called by constructors of derived classes.
      * \param data the reference counted data object representing this dataset.
      */
-    GnuplotDataset(struct Data* data);
+    GnuplotDataset(Data* data);
 
     /**
      * Reference counted data object.
      */
-    struct Data* m_data;
+    Data* m_data;
 };
 
 /**
@@ -223,7 +223,7 @@
     };
 
     /// The set of points in the dataset
-    typedef std::vector<struct Point> PointSet;
+    typedef std::vector<Point> PointSet;
 
     static Style m_defaultStyle;         //!< default plot style
     static ErrorBars m_defaultErrorBars; //!< default error bars type
@@ -318,7 +318,7 @@
     };
 
     /// The set of points in the dataset
-    typedef std::vector<struct Point> PointSet;
+    typedef std::vector<Point> PointSet;
 
     static std::string m_defaultStyle; //!< default plot style
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/histogram.cc ns-3.39/src/stats/model/histogram.cc
--- ns-3.38/src/stats/model/histogram.cc	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/model/histogram.cc	2023-07-05 14:23:48.085428200 -0700
@@ -71,7 +71,7 @@
 }
 
 uint32_t
-Histogram::GetBinCount(uint32_t index)
+Histogram::GetBinCount(uint32_t index) const
 {
     NS_ASSERT(index < m_histogram.size());
     return m_histogram[index];
@@ -92,6 +92,12 @@
     m_histogram[index]++;
 }
 
+void
+Histogram::Clear()
+{
+    m_histogram.clear();
+}
+
 Histogram::Histogram(double binWidth)
 {
     m_binWidth = binWidth;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/histogram.h ns-3.39/src/stats/model/histogram.h
--- ns-3.38/src/stats/model/histogram.h	2023-03-16 20:54:23.828869000 -0700
+++ ns-3.39/src/stats/model/histogram.h	2023-07-05 14:23:48.085428200 -0700
@@ -93,7 +93,7 @@
      * \param index the bin index
      * \return the number of data added to the bin
      */
-    uint32_t GetBinCount(uint32_t index);
+    uint32_t GetBinCount(uint32_t index) const;
 
     // Method for adding values
     /**
@@ -103,6 +103,11 @@
     void AddValue(double value);
 
     /**
+     * Clear the histogram content.
+     */
+    void Clear();
+
+    /**
      * \brief Serializes the results to an std::ostream in XML format.
      * \param os the output stream
      * \param indent number of spaces to use as base indentation level
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/model/sqlite-output.cc ns-3.39/src/stats/model/sqlite-output.cc
--- ns-3.38/src/stats/model/sqlite-output.cc	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/stats/model/sqlite-output.cc	2023-07-05 14:23:48.085428200 -0700
@@ -132,11 +132,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const Time& value) const
 {
-    if (sqlite3_bind_double(stmt, pos, value.GetSeconds()) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_double(stmt, pos, value.GetSeconds()) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -144,11 +140,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const double& value) const
 {
-    if (sqlite3_bind_double(stmt, pos, value) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_double(stmt, pos, value) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -156,11 +148,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const uint32_t& value) const
 {
-    if (sqlite3_bind_int(stmt, pos, static_cast<int>(value)) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_int(stmt, pos, static_cast<int>(value)) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -168,11 +156,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const long& value) const
 {
-    if (sqlite3_bind_int64(stmt, pos, value) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_int64(stmt, pos, value) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -180,11 +164,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const long long& value) const
 {
-    if (sqlite3_bind_int64(stmt, pos, value) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_int64(stmt, pos, value) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -192,11 +172,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const uint16_t& value) const
 {
-    if (sqlite3_bind_int(stmt, pos, static_cast<int>(value)) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_int(stmt, pos, static_cast<int>(value)) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -204,11 +180,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const uint8_t& value) const
 {
-    if (sqlite3_bind_int(stmt, pos, static_cast<int>(value)) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_int(stmt, pos, static_cast<int>(value)) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -216,11 +188,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const int& value) const
 {
-    if (sqlite3_bind_int(stmt, pos, value) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_int(stmt, pos, value) == SQLITE_OK;
 }
 
 //! \copydoc SQLiteOutput::Bind
@@ -228,11 +196,7 @@
 bool
 SQLiteOutput::Bind(sqlite3_stmt* stmt, int pos, const std::string& value) const
 {
-    if (sqlite3_bind_text(stmt, pos, value.c_str(), -1, SQLITE_STATIC) == SQLITE_OK)
-    {
-        return true;
-    }
-    return false;
+    return sqlite3_bind_text(stmt, pos, value.c_str(), -1, SQLITE_STATIC) == SQLITE_OK;
 }
 
 int
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/stats/test/examples-to-run.py ns-3.39/src/stats/test/examples-to-run.py
--- ns-3.38/src/stats/test/examples-to-run.py	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/stats/test/examples-to-run.py	2023-07-05 14:23:48.085428200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/tap-bridge/doc/tap.rst ns-3.39/src/tap-bridge/doc/tap.rst
--- ns-3.38/src/tap-bridge/doc/tap.rst	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/tap-bridge/doc/tap.rst	2023-07-05 14:23:48.085428200 -0700
@@ -147,9 +147,9 @@
 
 .. sourcecode:: bash
 
-  $ sudo tunctl -t tap0
-  $ sudo ifconfig tap0 hw ether 08:00:2e:00:00:01
-  $ sudo ifconfig tap0 10.1.1.1 netmask 255.255.255.0 up
+  $ sudo ip tuntap add mode tap tap0
+  $ sudo ip address add 10.1.1.1/24 dev tap0
+  $ sudo ip link set dev tap0 address 08:00:2e:00:00:01 up
 
 To tell the TapBridge what is going on, the user will set either directly
 into the TapBridge or via the TapBridgeHelper, the "DeviceName" attribute.
@@ -185,7 +185,7 @@
     +---------+ +--------+                  |  bridge  |     |   stack  |
         ||          ||                      | -------- |     | -------- |
     +--------------------+                  |   ns-3   |     |   ns-3   |
-    | OS (brctl) Bridge  |                  |   net    |     |   net    |
+    |     OS Bridge      |                  |   net    |     |   net    |
     +--------------------+                  |  device  |     |  device  |
                                             +----------+     +----------+
                                                  ||               ||
@@ -215,13 +215,13 @@
 
 .. sourcecode:: bash
 
-  $ sudo brctl addbr mybridge
-  $ sudo tunctl -t mytap
-  $ sudo ifconfig mytap hw ether 00:00:00:00:00:01
-  $ sudo ifconfig mytap 0.0.0.0 up
-  $ sudo brctl addif mybridge mytap
-  $ sudo brctl addif mybridge ...
-  $ sudo ifconfig mybridge 10.1.1.1 netmask 255.255.255.0 up
+  $ sudo ip link add mybridge type bridge
+  $ sudo ip address add 10.1.1.1/24 dev mybridge
+  $ sudo ip tuntap add mode tap mytap
+  $ sudo ip link set dev mytap address 00:00:00:00:00:01 up
+  $ sudo ip link set dev mytap master mybridge
+  $ sudo ip link set dev ... master mybridge
+  $ sudo ip link set dev mybridge up
 
 To tell the TapBridge what is going on, the user will set either directly
 into the TapBridge or via the TapBridgeHelper, the "DeviceName" attribute.
@@ -390,8 +390,8 @@
 
 In the other direction, a packet received by an ns-3 net device is hooked
 via callback to the TapBridge.  This must be done in promiscuous mode since
-the goal is to bridge the ns-3 net device onto the OS (brctl) bridge of
-which the TAP device is a part.
+the goal is to bridge the ns-3 net device onto the OS bridge of which the
+TAP device is a part.
 
 For these reasons, only ns-3 net devices that support SendFrom() and have a
 hookable promiscuous receive callback are allowed to participate in UseBridge
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/tap-bridge/examples/tap-wifi-dumbbell.cc ns-3.39/src/tap-bridge/examples/tap-wifi-dumbbell.cc
--- ns-3.38/src/tap-bridge/examples/tap-wifi-dumbbell.cc	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/tap-bridge/examples/tap-wifi-dumbbell.cc	2023-07-05 14:23:48.085428200 -0700
@@ -52,7 +52,7 @@
 //
 // 1) Ping one of the simulated nodes on the left side of the topology.
 //
-//    ./ns3 run tap-wifi-dumbbell&
+//    ./ns3 run --enable-sudo tap-wifi-dumbbell&
 //    ping 10.1.1.3
 //
 // 2) Configure a route in the linux host and ping once of the nodes on the
@@ -60,8 +60,8 @@
 //    delays due to CBR background traffic on the point-to-point (see next
 //    item).
 //
-//    ./ns3 run tap-wifi-dumbbell&
-//    sudo route add -net 10.1.3.0 netmask 255.255.255.0 dev thetap gw 10.1.1.2
+//    ./ns3 run --enable-sudo tap-wifi-dumbbell&
+//    sudo ip route add 10.1.3.0/24 dev thetap via 10.1.1.2
 //    ping 10.1.3.4
 //
 //    Take a look at the pcap traces and note that the timing reflects the
@@ -76,22 +76,25 @@
 //    reflected in large delays seen by ping.  You can crank down the CBR
 //    traffic data rate and watch the ping timing change dramatically.
 //
-//    ./ns3 run "tap-wifi-dumbbell --ns3::OnOffApplication::DataRate=100kb/s"&
-//    sudo route add -net 10.1.3.0 netmask 255.255.255.0 dev thetap gw 10.1.1.2
+//    ./ns3 run --enable-sudo "tap-wifi-dumbbell --ns3::OnOffApplication::DataRate=100kb/s"&
+//    sudo ip route add 10.1.3.0/24 dev thetap via 10.1.1.2
 //    ping 10.1.3.4
 //
 // 4) Try to run this in UseBridge mode.  This allows you to bridge an ns-3
 //    simulation to an existing pre-configured bridge.  This uses tap devices
 //    just for illustration, you can create your own bridge if you want.
+//    The "--enable-sudo" option to "./ns3 run" is not needed in this case
+//    because devices are being created outside of ns-3 execution.
 //
-//    sudo tunctl -t mytap1
-//    sudo ifconfig mytap1 0.0.0.0 promisc up
-//    sudo tunctl -t mytap2
-//    sudo ifconfig mytap2 0.0.0.0 promisc up
-//    sudo brctl addbr mybridge
-//    sudo brctl addif mybridge mytap1
-//    sudo brctl addif mybridge mytap2
-//    sudo ifconfig mybridge 10.1.1.5 netmask 255.255.255.0 up
+//    sudo ip tuntap add mode tap mytap1
+//    sudo ip link set mytap1 promisc on up
+//    sudo ip tuntap add mode tap mytap2
+//    sudo ip link set mytap2 promisc on up
+//    sudo ip link add mybridge type bridge
+//    sudo ip link set dev mytap1 master mybridge
+//    sudo ip link set dev mytap2 master mybridge
+//    sudo ip address add 10.1.1.5/24 dev mybridge
+//    sudo ip link set dev mybridge up
 //    ./ns3 run "tap-wifi-dumbbell --mode=UseBridge --tapName=mytap2"&
 //    ping 10.1.1.3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/tap-bridge/examples/virtual-network-setup.sh ns-3.39/src/tap-bridge/examples/virtual-network-setup.sh
--- ns-3.38/src/tap-bridge/examples/virtual-network-setup.sh	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/tap-bridge/examples/virtual-network-setup.sh	2023-07-05 14:23:48.085428200 -0700
@@ -1,14 +1,14 @@
 #!/bin/bash
-brctl addbr br-left
-brctl addbr br-right
-tunctl -t tap-left
-tunctl -t tap-right
-ifconfig tap-left 0.0.0.0 promisc up
-ifconfig tap-right 0.0.0.0 promisc up
-brctl addif br-left tap-left
-ifconfig br-left up
-brctl addif br-right tap-right
-ifconfig br-right up
+ip link add br-left type bridge
+ip link add br-right type bridge
+ip tuntap add mode tap tap-left
+ip tuntap add mode tap tap-right
+ip link set tap-left promisc on up
+ip link set tap-right promisc on up
+ip link set dev tap-left master br-left
+ip link set dev br-left up
+ip link set dev tap-right master br-right
+ip link set dev br-right up
 # Note:  you also need to have loaded br_netfilter module
 # ('modprobe br_netfilter') to enable /proc/sys/net/bridge
 pushd /proc/sys/net/bridge
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/tap-bridge/examples/virtual-network-teardown.sh ns-3.39/src/tap-bridge/examples/virtual-network-teardown.sh
--- ns-3.38/src/tap-bridge/examples/virtual-network-teardown.sh	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/tap-bridge/examples/virtual-network-teardown.sh	2023-07-05 14:23:48.085428200 -0700
@@ -3,13 +3,13 @@
 lxc-stop -n right
 lxc-destroy -n left
 lxc-destroy -n right
-ifconfig br-left down
-ifconfig br-right down
-brctl delif br-left tap-left
-brctl delif br-right tap-right
-brctl delbr br-left
-brctl delbr br-right
-ifconfig tap-left down
-ifconfig tap-right down
-tunctl -d tap-left
-tunctl -d tap-right
+ip link set dev br-left down
+ip link set dev br-right down
+ip link set dev tap-left nomaster
+ip link set dev tap-right nomaster
+ip link del br-left
+ip link del br-right
+ip link set dev tap-left down
+ip link set dev tap-right down
+ip tuntap del mode tap tap-left
+ip tuntap del mode tap tap-right
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/tap-bridge/model/tap-bridge.cc ns-3.39/src/tap-bridge/model/tap-bridge.cc
--- ns-3.38/src/tap-bridge/model/tap-bridge.cc	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/tap-bridge/model/tap-bridge.cc	2023-07-05 14:23:48.085428200 -0700
@@ -282,9 +282,9 @@
     // be configuring a tap device outside the scope of the ns-3 simulation and
     // will be expecting us to work with it.  The user will do something like:
     //
-    //   sudo tunctl -t tap0
-    //   sudo ifconfig tap0 hw ether 00:00:00:00:00:01
-    //   sudo ifconfig tap0 10.1.1.1 netmask 255.255.255.0 up
+    //   sudo ip tuntap add mode tap tap0
+    //   sudo ip address add 10.1.1.1/24 dev tap0
+    //   sudo ip link set dev tap0 address 00:00:00:00:00:01 up
     //
     // The user will then set the "Mode" Attribute of the TapBridge to "UseLocal"
     // and the "DeviceName" Attribute to "tap0" in this case.
@@ -300,13 +300,13 @@
     // net device and logically add it to the existing bridge.  We expect that
     // the user has done something like:
     //
-    //   sudo brctl addbr mybridge
-    //   sudo tunctl -t mytap
-    //   sudo ifconfig mytap hw ether 00:00:00:00:00:01
-    //   sudo ifconfig mytap 0.0.0.0 up
-    //   sudo brctl addif mybridge mytap
-    //   sudo brctl addif mybridge ...
-    //   sudo ifconfig mybridge 10.1.1.1 netmask 255.255.255.0 up
+    //   sudo ip link add mybridge type bridge
+    //   sudo ip tuntap add mode tap mytap
+    //   sudo ip link set dev mytap address 00:00:00:00:00:01 up
+    //   sudo ip link set dev mytap master mybridge
+    //   sudo ip link set dev ... master mybridge
+    //   sudo ip address add 10.1.1.1/24 dev mybridge
+    //   sudo ip link set dev mybridge up
     //
     // The bottom line at this point is that we want to either create or use a
     // tap device on the host based on the verb part "Use" or "Configure" of the
@@ -801,7 +801,7 @@
         //
         NS_ASSERT_MSG(Mac48Address::ConvertFrom(src) != Mac48Address("ff:ff:ff:ff:ff:ff"),
                       "TapBridge::ForwardToBridgedDevice:  Source addr is broadcast");
-        if (m_ns3AddressRewritten == false)
+        if (!m_ns3AddressRewritten)
         {
             //
             // Set the ns-3 device's mac address to the overlying container's
@@ -833,9 +833,9 @@
     // In Linux, "Other Device" and "Tap Device" are bridged together.  By this
     // we mean that a user has sone something in Linux like:
     //
-    //   brctl addbr mybridge
-    //   brctl addif other-device
-    //   brctl addif tap-device
+    //   ip link add mybridge type bridge
+    //   ip link set dev other-device master mybridge
+    //   ip link set dev tap-device master mybridge
     //
     // In USE_BRIDGE mode, we want to logically extend this Linux behavior to the
     // simulated ns3 device and make it appear as if it is connected to the Linux
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/tap-bridge/model/tap-encode-decode.h ns-3.39/src/tap-bridge/model/tap-encode-decode.h
--- ns-3.38/src/tap-bridge/model/tap-encode-decode.h	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/tap-bridge/model/tap-encode-decode.h	2023-07-05 14:23:48.089428200 -0700
@@ -18,6 +18,7 @@
 #ifndef TAP_ENCODE_DECODE_H
 #define TAP_ENCODE_DECODE_H
 
+#include <cstdint>
 #include <string>
 
 namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/tap-bridge/test/examples-to-run.py ns-3.39/src/tap-bridge/test/examples-to-run.py
--- ns-3.38/src/tap-bridge/test/examples-to-run.py	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/tap-bridge/test/examples-to-run.py	2023-07-05 14:23:48.089428200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/test/CMakeLists.txt ns-3.39/src/test/CMakeLists.txt
--- ns-3.38/src/test/CMakeLists.txt	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/test/CMakeLists.txt	2023-07-05 14:23:48.089428200 -0700
@@ -3,7 +3,8 @@
 # cmake-format: off
 if((dsr IN_LIST ns3-all-enabled-modules)
     AND (uan IN_LIST ns3-all-enabled-modules)
-    AND (olsr IN_LIST ns3-all-enabled-modules))
+    AND (olsr IN_LIST ns3-all-enabled-modules)
+    AND (lte IN_LIST ns3-all-enabled-modules))
   set(dsr_sources traced/traced-callback-typedef-test-suite.cc)
 endif()
 # cmake-format: on
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc ns-3.39/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc
--- ns-3.38/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/test/ns3tc/fq-cobalt-queue-disc-test-suite.cc	2023-07-05 14:23:48.089428200 -0700
@@ -1308,7 +1308,7 @@
  *
  * The variable outerhash stores the nearest multiple of 8 that is lesser than
  * the hash. When a flow hash of 20 arrives, the value of outerhash
- * is 16. Since m_flowIndices[16] wasnâ€™t previously allotted, a new flow
+ * is 16. Since m_flowIndices[16] wasn't previously allotted, a new flow
  * is created, and the tag corresponding to this queue is set to 20.
  */
 class FqCobaltQueueDiscSetLinearProbing : public TestCase
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/test/ns3tc/fq-codel-queue-disc-test-suite.cc ns-3.39/src/test/ns3tc/fq-codel-queue-disc-test-suite.cc
--- ns-3.38/src/test/ns3tc/fq-codel-queue-disc-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/test/ns3tc/fq-codel-queue-disc-test-suite.cc	2023-07-05 14:23:48.089428200 -0700
@@ -1372,7 +1372,7 @@
  *
  * The variable outerHash stores the nearest multiple of 8 that is lesser than
  * the hash. When a flow hash of 20 arrives, the value of outerHash
- * is 16. Since m_flowIndices[16] wasnâ€™t previously allotted, a new flow
+ * is 16. Since m_flowIndices[16] wasn't previously allotted, a new flow
  * is created, and the tag corresponding to this queue is set to 20.
  */
 class FqCoDelQueueDiscSetLinearProbing : public TestCase
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/test/traced/traced-callback-typedef-test-suite.cc ns-3.39/src/test/traced/traced-callback-typedef-test-suite.cc
--- ns-3.38/src/test/traced/traced-callback-typedef-test-suite.cc	2023-03-16 20:54:23.832869300 -0700
+++ ns-3.39/src/test/traced/traced-callback-typedef-test-suite.cc	2023-07-05 14:23:48.089428200 -0700
@@ -338,11 +338,13 @@
         std::cout << #U << " matches " << #T1 << std::endl;                                        \
     }                                                                                              \
     else                                                                                           \
+    {                                                                                              \
         NS_TEST_ASSERT_MSG_EQ(TypeName<U>(0),                                                      \
                               TypeName<T1>(0),                                                     \
                               "the typedef "                                                       \
                                   << #U << " used to match the typedef " << #T1                    \
-                                  << " but no longer does.  Please add a new CHECK call.")
+                                  << " but no longer does.  Please add a new CHECK call.");        \
+    }
 
 /**
  * \ingroup system-tests-traced
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/topology-read/model/rocketfuel-topology-reader.cc ns-3.39/src/topology-read/model/rocketfuel-topology-reader.cc
--- ns-3.38/src/topology-read/model/rocketfuel-topology-reader.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/topology-read/model/rocketfuel-topology-reader.cc	2023-07-05 14:23:48.089428200 -0700
@@ -319,7 +319,7 @@
     return nodes;
 }
 
-enum RocketfuelTopologyReader::RF_FileType
+RocketfuelTopologyReader::RF_FileType
 RocketfuelTopologyReader::GetFileType(const std::string& line)
 {
     int ret;
@@ -352,7 +352,7 @@
     std::istringstream lineBuffer;
     std::string line;
     int lineNumber = 0;
-    enum RF_FileType ftype = RF_UNKNOWN;
+    RF_FileType ftype = RF_UNKNOWN;
 
     if (!topgen.is_open())
     {
@@ -386,7 +386,7 @@
         if (ftype == RF_MAPS)
         {
             ret = std::regex_match(line, matches, rocketfuel_maps_regex);
-            if (ret != true || matches.empty())
+            if (!ret || matches.empty())
             {
                 NS_LOG_WARN("match failed (maps file): %s" << line);
                 break;
@@ -395,7 +395,7 @@
         else if (ftype == RF_WEIGHTS)
         {
             ret = std::regex_match(line, matches, rocketfuel_weights_regex);
-            if (ret != true || matches.empty())
+            if (!ret || matches.empty())
             {
                 NS_LOG_WARN("match failed (weights file): %s" << line);
                 break;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/topology-read/model/rocketfuel-topology-reader.h ns-3.39/src/topology-read/model/rocketfuel-topology-reader.h
--- ns-3.38/src/topology-read/model/rocketfuel-topology-reader.h	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/topology-read/model/rocketfuel-topology-reader.h	2023-07-05 14:23:48.089428200 -0700
@@ -111,7 +111,7 @@
      * \param buf the first line of the file being read
      * \return The file type (RF_MAPS, RF_WEIGHTS, or RF_UNKNOWN)
      */
-    enum RF_FileType GetFileType(const std::string& buf);
+    RF_FileType GetFileType(const std::string& buf);
 
     int m_linksNumber;                          //!< Number of links.
     int m_nodesNumber;                          //!< Number of nodes.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/topology-read/test/examples-to-run.py ns-3.39/src/topology-read/test/examples-to-run.py
--- ns-3.38/src/topology-read/test/examples-to-run.py	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/topology-read/test/examples-to-run.py	2023-07-05 14:23:48.089428200 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc ns-3.39/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc
--- ns-3.38/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/traffic-control/examples/codel-vs-pfifo-asymmetric.cc	2023-07-05 14:23:48.093428400 -0700
@@ -220,12 +220,12 @@
 static void
 DroppingStateTracer(Ptr<OutputStreamWrapper> stream, bool oldVal, bool newVal)
 {
-    if (oldVal == false && newVal == true)
+    if (!oldVal && newVal)
     {
         NS_LOG_INFO("Entering the dropping state");
         *stream->GetStream() << Simulator::Now().GetSeconds() << " ";
     }
-    else if (oldVal == true && newVal == false)
+    else if (oldVal && !newVal)
     {
         NS_LOG_INFO("Leaving the dropping state");
         *stream->GetStream() << Simulator::Now().GetSeconds() << std::endl;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/examples/fqcodel-l4s-example.cc ns-3.39/src/traffic-control/examples/fqcodel-l4s-example.cc
--- ns-3.38/src/traffic-control/examples/fqcodel-l4s-example.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/traffic-control/examples/fqcodel-l4s-example.cc	2023-07-05 14:23:48.093428400 -0700
@@ -775,7 +775,7 @@
         proto->SetAttribute("SocketType", TypeIdValue(n1TcpTypeId));
     }
 
-    // InternetStackHelper will install a base TrafficControLayer on the node,
+    // InternetStackHelper will install a base TrafficControlLayer on the node,
     // but the Ipv4AddressHelper below will install the default FqCoDelQueueDisc
     // on all single device nodes.  The below code overrides the configuration
     // that is normally done by the Ipv4AddressHelper::Install() method by
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/model/fq-cobalt-queue-disc.cc ns-3.39/src/traffic-control/model/fq-cobalt-queue-disc.cc
--- ns-3.38/src/traffic-control/model/fq-cobalt-queue-disc.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/traffic-control/model/fq-cobalt-queue-disc.cc	2023-07-05 14:23:48.093428400 -0700
@@ -469,7 +469,7 @@
     if (m_useL4s)
     {
         NS_ABORT_MSG_IF(m_ceThreshold == Time::Max(), "CE threshold not set");
-        if (m_useEcn == false)
+        if (!m_useEcn)
         {
             NS_LOG_WARN("Enabling ECN as L4S mode is enabled");
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/model/fq-codel-queue-disc.cc ns-3.39/src/traffic-control/model/fq-codel-queue-disc.cc
--- ns-3.38/src/traffic-control/model/fq-codel-queue-disc.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/traffic-control/model/fq-codel-queue-disc.cc	2023-07-05 14:23:48.093428400 -0700
@@ -440,7 +440,7 @@
     if (m_useL4s)
     {
         NS_ABORT_MSG_IF(m_ceThreshold == Time::Max(), "CE threshold not set");
-        if (m_useEcn == false)
+        if (!m_useEcn)
         {
             NS_LOG_WARN("Enabling ECN as L4S mode is enabled");
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/model/fq-pie-queue-disc.cc ns-3.39/src/traffic-control/model/fq-pie-queue-disc.cc
--- ns-3.38/src/traffic-control/model/fq-pie-queue-disc.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/traffic-control/model/fq-pie-queue-disc.cc	2023-07-05 14:23:48.093428400 -0700
@@ -497,7 +497,7 @@
     if (m_useL4s)
     {
         NS_ABORT_MSG_IF(m_ceThreshold == Time::Max(), "CE threshold not set");
-        if (m_useEcn == false)
+        if (!m_useEcn)
         {
             NS_LOG_WARN("Enabling ECN as L4S mode is enabled");
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/model/pie-queue-disc.cc ns-3.39/src/traffic-control/model/pie-queue-disc.cc
--- ns-3.38/src/traffic-control/model/pie-queue-disc.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/traffic-control/model/pie-queue-disc.cc	2023-07-05 14:23:48.093428400 -0700
@@ -328,12 +328,7 @@
     }
 
     double u = m_uv->GetValue();
-    if (u > p)
-    {
-        return false;
-    }
-
-    return true;
+    return u <= p;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/model/queue-disc.cc ns-3.39/src/traffic-control/model/queue-disc.cc
--- ns-3.38/src/traffic-control/model/queue-disc.cc	2023-03-16 20:54:23.836869200 -0700
+++ ns-3.39/src/traffic-control/model/queue-disc.cc	2023-07-05 14:23:48.093428400 -0700
@@ -1108,13 +1108,9 @@
 
     // if the queue disc is empty or the device queue is now stopped, return false so
     // that the Run method does not attempt to dequeue other packets and exits
-    if (GetNPackets() == 0 ||
-        (m_devQueueIface && m_devQueueIface->GetTxQueue(item->GetTxQueueIndex())->IsStopped()))
-    {
-        return false;
-    }
-
-    return true;
+    return !(
+        GetNPackets() == 0 ||
+        (m_devQueueIface && m_devQueueIface->GetTxQueue(item->GetTxQueueIndex())->IsStopped()));
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/model/tbf-queue-disc.cc ns-3.39/src/traffic-control/model/tbf-queue-disc.cc
--- ns-3.38/src/traffic-control/model/tbf-queue-disc.cc	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/traffic-control/model/tbf-queue-disc.cc	2023-07-05 14:23:48.093428400 -0700
@@ -261,7 +261,7 @@
         // A packet gets blocked if the above if() condition is not satisfied:
         // either or both btoks and ptoks are negative.  In that case, we have
         // to schedule the waking of queue when enough tokens are available.
-        if (m_id.IsExpired() == true)
+        if (m_id.IsExpired())
         {
             NS_ASSERT_MSG(m_rate.GetBitRate() > 0, "Rate must be positive");
             Time requiredDelayTime;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/model/traffic-control-layer.h ns-3.39/src/traffic-control/model/traffic-control-layer.h
--- ns-3.38/src/traffic-control/model/traffic-control-layer.h	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/traffic-control/model/traffic-control-layer.h	2023-07-05 14:23:48.093428400 -0700
@@ -234,7 +234,7 @@
     };
 
     /// Typedef for protocol handlers container
-    typedef std::vector<struct ProtocolHandlerEntry> ProtocolHandlerList;
+    typedef std::vector<ProtocolHandlerEntry> ProtocolHandlerList;
 
     /**
      * \brief Required by the object map accessor
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/test/cobalt-queue-disc-test-suite.cc ns-3.39/src/traffic-control/test/cobalt-queue-disc-test-suite.cc
--- ns-3.38/src/traffic-control/test/cobalt-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/traffic-control/test/cobalt-queue-disc-test-suite.cc	2023-07-05 14:23:48.093428400 -0700
@@ -82,11 +82,7 @@
 bool
 CobaltQueueDiscTestItem::Mark()
 {
-    if (m_ecnCapablePacket)
-    {
-        return true;
-    }
-    return false;
+    return m_ecnCapablePacket;
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/test/codel-queue-disc-test-suite.cc ns-3.39/src/traffic-control/test/codel-queue-disc-test-suite.cc
--- ns-3.38/src/traffic-control/test/codel-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/traffic-control/test/codel-queue-disc-test-suite.cc	2023-07-05 14:23:48.093428400 -0700
@@ -104,11 +104,7 @@
 bool
 CodelQueueDiscTestItem::Mark()
 {
-    if (m_ecnCapablePacket)
-    {
-        return true;
-    }
-    return false;
+    return m_ecnCapablePacket;
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/test/examples-to-run.py ns-3.39/src/traffic-control/test/examples-to-run.py
--- ns-3.38/src/traffic-control/test/examples-to-run.py	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/traffic-control/test/examples-to-run.py	2023-07-05 14:23:48.093428400 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/test/pie-queue-disc-test-suite.cc ns-3.39/src/traffic-control/test/pie-queue-disc-test-suite.cc
--- ns-3.38/src/traffic-control/test/pie-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/traffic-control/test/pie-queue-disc-test-suite.cc	2023-07-05 14:23:48.093428400 -0700
@@ -93,11 +93,7 @@
 bool
 PieQueueDiscTestItem::Mark()
 {
-    if (m_ecnCapablePacket)
-    {
-        return true;
-    }
-    return false;
+    return m_ecnCapablePacket;
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/traffic-control/test/red-queue-disc-test-suite.cc ns-3.39/src/traffic-control/test/red-queue-disc-test-suite.cc
--- ns-3.38/src/traffic-control/test/red-queue-disc-test-suite.cc	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/traffic-control/test/red-queue-disc-test-suite.cc	2023-07-05 14:23:48.093428400 -0700
@@ -73,11 +73,7 @@
 bool
 RedQueueDiscTestItem::Mark()
 {
-    if (m_ecnCapablePacket)
-    {
-        return true;
-    }
-    return false;
+    return m_ecnCapablePacket;
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/uan/model/uan-mac-cw.cc ns-3.39/src/uan/model/uan-mac-cw.cc
--- ns-3.38/src/uan/model/uan-mac-cw.cc	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/uan/model/uan-mac-cw.cc	2023-07-05 14:23:48.097428300 -0700
@@ -117,7 +117,7 @@
     case CCABUSY:
         NS_LOG_DEBUG("Time " << Now().As(Time::S) << " MAC " << GetAddress()
                              << " Starting enqueue CCABUSY");
-        if (m_txOngoing == true)
+        if (m_txOngoing)
         {
             NS_LOG_DEBUG("State is TX");
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/uan/test/examples-to-run.py ns-3.39/src/uan/test/examples-to-run.py
--- ns-3.38/src/uan/test/examples-to-run.py	2023-03-16 20:54:23.840869200 -0700
+++ ns-3.39/src/uan/test/examples-to-run.py	2023-07-05 14:23:48.097428300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/virtual-net-device/model/virtual-net-device.cc ns-3.39/src/virtual-net-device/model/virtual-net-device.cc
--- ns-3.38/src/virtual-net-device/model/virtual-net-device.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/virtual-net-device/model/virtual-net-device.cc	2023-07-05 14:23:48.097428300 -0700
@@ -255,11 +255,7 @@
 VirtualNetDevice::Send(Ptr<Packet> packet, const Address& dest, uint16_t protocolNumber)
 {
     m_macTxTrace(packet);
-    if (m_sendCb(packet, GetAddress(), dest, protocolNumber))
-    {
-        return true;
-    }
-    return false;
+    return m_sendCb(packet, GetAddress(), dest, protocolNumber);
 }
 
 bool
@@ -270,11 +266,7 @@
 {
     NS_ASSERT(m_supportsSendFrom);
     m_macTxTrace(packet);
-    if (m_sendCb(packet, source, dest, protocolNumber))
-    {
-        return true;
-    }
-    return false;
+    return m_sendCb(packet, source, dest, protocolNumber);
 }
 
 Ptr<Node>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/virtual-net-device/test/examples-to-run.py ns-3.39/src/virtual-net-device/test/examples-to-run.py
--- ns-3.38/src/virtual-net-device/test/examples-to-run.py	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/virtual-net-device/test/examples-to-run.py	2023-07-05 14:23:48.097428300 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/CMakeLists.txt ns-3.39/src/visualizer/CMakeLists.txt
--- ns-3.38/src/visualizer/CMakeLists.txt	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/CMakeLists.txt	2023-07-05 14:23:48.097428300 -0700
@@ -1,10 +1,23 @@
+# Embedding python isn't a good option, so just leave symbols undefined and hope
+# for the best https://github.com/pypa/manylinux/pull/1185
+# https://github.com/scikit-build/scikit-build/pull/47
+# https://9to5answer.com/can-gcc-not-complain-about-undefined-references
+set(python_libraries
+    ${Python3_LIBRARIES}
+)
+if(${NS3_PIP_PACKAGING})
+  set(python_libraries
+      -Wl,--allow-shlib-undefined
+  )
+endif()
+
 build_lib(
   LIBNAME visualizer
   SOURCE_FILES model/pyviz.cc
                model/visual-simulator-impl.cc
   HEADER_FILES model/pyviz.h
   LIBRARIES_TO_LINK
-    ${Python3_LIBRARIES}
+    ${python_libraries}
     ${libcore}
     ${libinternet}
     ${libwifi}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/model/pyviz.cc ns-3.39/src/visualizer/model/pyviz.cc
--- ns-3.38/src/visualizer/model/pyviz.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/model/pyviz.cc	2023-07-05 14:23:48.097428300 -0700
@@ -371,14 +371,7 @@
     {
         return false;
     }
-    if (this->channel < other.channel)
-    {
-        return true;
-    }
-    else
-    {
-        return false;
-    }
+    return this->channel < other.channel;
 }
 
 bool
@@ -457,14 +450,7 @@
                 missingHeaders.erase(missingIter);
             }
         }
-        if (missingHeaders.empty())
-        {
-            return true;
-        }
-        else
-        {
-            return false;
-        }
+        return missingHeaders.empty();
     }
 
     default:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/model/visual-simulator-impl.cc ns-3.39/src/visualizer/model/visual-simulator-impl.cc
--- ns-3.38/src/visualizer/model/visual-simulator-impl.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/model/visual-simulator-impl.cc	2023-07-05 14:23:48.101428300 -0700
@@ -119,9 +119,7 @@
 {
     if (!Py_IsInitialized())
     {
-        const wchar_t* argv[] = {L"python", nullptr};
         Py_Initialize();
-        PySys_SetArgv(1, (wchar_t**)argv);
         PyRun_SimpleString("import visualizer\n"
                            "visualizer.start();\n");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/visualizer/plugins/interface_statistics.py ns-3.39/src/visualizer/visualizer/plugins/interface_statistics.py
--- ns-3.38/src/visualizer/visualizer/plugins/interface_statistics.py	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/visualizer/plugins/interface_statistics.py	2023-07-05 14:23:48.101428300 -0700
@@ -1,5 +1,9 @@
 from gi.repository import Gtk
-from visualizer.base import InformationWindow
+
+try:
+    from ns3.visualizer.base import InformationWindow
+except ModuleNotFoundError:
+    from visualizer.base import InformationWindow
 
 NODE_STATISTICS_MEMORY = 10
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/visualizer/plugins/ipv4_routing_table.py ns-3.39/src/visualizer/visualizer/plugins/ipv4_routing_table.py
--- ns-3.38/src/visualizer/visualizer/plugins/ipv4_routing_table.py	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/visualizer/plugins/ipv4_routing_table.py	2023-07-05 14:23:48.101428300 -0700
@@ -1,6 +1,9 @@
 from gi.repository import Gtk
 
-from visualizer.base import InformationWindow
+try:
+    from ns3.visualizer.base import InformationWindow
+except ModuleNotFoundError:
+    from visualizer.base import InformationWindow
 
 ## ShowIpv4RoutingTable class
 class ShowIpv4RoutingTable(InformationWindow):
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/visualizer/plugins/olsr.py ns-3.39/src/visualizer/visualizer/plugins/olsr.py
--- ns-3.38/src/visualizer/visualizer/plugins/olsr.py	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/visualizer/plugins/olsr.py	2023-07-05 14:23:48.101428300 -0700
@@ -1,7 +1,10 @@
 from gi.repository import Gtk
 from gi.repository import Gdk
 
-from visualizer.base import InformationWindow
+try:
+    from ns3.visualizer.base import InformationWindow
+except ModuleNotFoundError:
+    from visualizer.base import InformationWindow
 
 ## ShowOlsrRoutingTable class
 class ShowOlsrRoutingTable(InformationWindow):
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/visualizer/plugins/show_last_packets.py ns-3.39/src/visualizer/visualizer/plugins/show_last_packets.py
--- ns-3.38/src/visualizer/visualizer/plugins/show_last_packets.py	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/visualizer/plugins/show_last_packets.py	2023-07-05 14:23:48.101428300 -0700
@@ -3,7 +3,11 @@
 
 from ns import ns
 
-from visualizer.base import InformationWindow
+try:
+    from ns3.visualizer.base import InformationWindow
+except ModuleNotFoundError:
+    from visualizer.base import InformationWindow
+
 from kiwi.ui.objectlist import ObjectList, Column
 
 ## ShowLastPackets class
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py ns-3.39/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py
--- ns-3.38/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/visualizer/visualizer/plugins/wifi_intrastructure_link.py	2023-07-05 14:23:48.101428300 -0700
@@ -1,7 +1,11 @@
 import math
 from ns import ns
 from gi.repository import GooCanvas
-from visualizer.base import Link, transform_distance_canvas_to_simulation
+
+try:
+    from ns3.visualizer.base import Link, transform_distance_canvas_to_simulation
+except ModuleNotFoundError:
+    from visualizer.base import Link, transform_distance_canvas_to_simulation
 
 ## WifiLink class
 class WifiLink(Link):
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/CMakeLists.txt ns-3.39/src/wave/CMakeLists.txt
--- ns-3.38/src/wave/CMakeLists.txt	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
@@ -1,44 +0,0 @@
-build_lib(
-  LIBNAME wave
-  SOURCE_FILES
-    helper/wave-bsm-helper.cc
-    helper/wave-bsm-stats.cc
-    helper/wave-helper.cc
-    helper/wave-mac-helper.cc
-    helper/wifi-80211p-helper.cc
-    model/bsm-application.cc
-    model/channel-coordinator.cc
-    model/channel-manager.cc
-    model/channel-scheduler.cc
-    model/default-channel-scheduler.cc
-    model/higher-tx-tag.cc
-    model/ocb-wifi-mac.cc
-    model/vendor-specific-action.cc
-    model/vsa-manager.cc
-    model/wave-frame-exchange-manager.cc
-    model/wave-net-device.cc
-  HEADER_FILES
-    helper/wave-bsm-helper.h
-    helper/wave-bsm-stats.h
-    helper/wave-helper.h
-    helper/wave-mac-helper.h
-    helper/wifi-80211p-helper.h
-    model/bsm-application.h
-    model/channel-coordinator.h
-    model/channel-manager.h
-    model/channel-scheduler.h
-    model/default-channel-scheduler.h
-    model/higher-tx-tag.h
-    model/ocb-wifi-mac.h
-    model/vendor-specific-action.h
-    model/vsa-manager.h
-    model/wave-frame-exchange-manager.h
-    model/wave-net-device.h
-  LIBRARIES_TO_LINK
-    ${libcore}
-    ${libpropagation}
-    ${libwifi}
-    ${libinternet}
-  TEST_SOURCES test/mac-extension-test-suite.cc
-               test/ocb-test-suite.cc
-)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/doc/wave.rst ns-3.39/src/wave/doc/wave.rst
--- ns-3.38/src/wave/doc/wave.rst	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/doc/wave.rst	1969-12-31 16:00:00.000000000 -0800
@@ -1,935 +0,0 @@
-WAVE models
------------
-WAVE is a system architecture for wireless-based vehicular communications,
-specified by the IEEE.  This chapter documents available models for WAVE
-within |ns3|.  The focus is on the MAC layer and MAC extension layer
-defined by [ieee80211p]_ and [ieee1609dot4]_.
-
-.. include:: replace.txt
-
-.. heading hierarchy:
-   ------------- Chapter
-   ************* Section (#.#)
-   ============= Subsection (#.#.#)
-   ############# Paragraph (no number)
-
-Model Description
-*****************
-
-WAVE is an overall system architecture for vehicular communications.
-The standards for specifying WAVE include a set of extensions to the IEEE
-802.11 standard, found in IEEE Std 802.11p-2010 [ieee80211p]_, and
-the IEEE 1609 standard set, consisting of four documents:
-resource manager:  IEEE 1609.1 [ieee1609dot1]_,
-security services:  IEEE 1609.2 [ieee1609dot2]_,
-network and transport layer services:  IEEE 1609.3 [ieee1609dot3]_,
-and multi-channel coordination:  IEEE 1609.4 [ieee1609dot4]_.
-Additionally, SAE standard J2735 [saej2735]_ describes a Dedicated
-Short Range Communications (DSRC) application message set that allows
-applications to transmit information using WAVE.
-
-In |ns3|, the focus of the ``wave`` module is on both the MAC layer and the
-multi-channel coordination layer.
-The key design aspect of 802.11p-compilant MAC layer is that they allow
-communications outside the context of a basic service set (BSS).
-The literature uses the acronym OCB to denote "outside the context
-of a BSS", and the class ``ns3::OcbWifiMac`` models this in |ns3|.
-This MAC does not require any association between devices (similar to an
-adhoc WiFi MAC). Many management frames will not be used, but when used, the BSSID field
-needs to be set to a wildcard BSSID value. Management information is
-transmitted by what is called a vendor specific action (VSA) frame. With these
-changes, the packet transmissions (for a moving vehicle) can be fast with
-small delay in the MAC layer. Users can create IEEE802.11p-compliant device
-(the object of the class ``ns3::WifiNetDevice`` associating with
-``ns3::OcbWifiMac``) .
-
-The key design aspect of the WAVE-compilant MAC layer (including 802.11p MAC
-layer and 1609.4 MAC extension layer) is that, based on OCB features, they
-provide devices with the capability of switching between control and service channels, using a single radio or using multiple radios.
-Therefore devices can communicate with others in single or multiple
-channels, which can
-support both safety related and non-safety related service for vehicular environments.
-
-At the physical layer, the biggest difference is the use of the 5.9 GHz band
-with a channel bandwidth of 10 MHz.  These physical
-layer changes can make the wireless signal relatively more stable,
-without degrading throughput too much (ranging from 3 Mbps to 27 Mbps).
-
-The source code for the WAVE MAC models lives in the directory
-``src/wave``.
-
-For better modeling WAVE and VANET, the WAVE models for high layers
-(mainly [ieee1609dot3]_ ) are planned for a later patch.
-
-Design
-======
-
-In |ns3|, support for 802.11p involves the MAC and PHY layers.
-To use an 802.11p NetDevice, ``ns3::Wifi80211pHelper`` is suggested.
-
-In |ns3|, support for WAVE involves the MAC, its MAC extension and PHY layers.
-To use a WAVE NetDevice, ``ns3::WaveHelper`` is suggested.
-
-MAC layer
-#########
-
-The classes used to model the MAC layer are ``ns3::OrganizationIdentifier``,
-``ns3::VendorSpecificActionHeader`` and ``ns3::OcbWifiMac``.
-
-The OrganizationIdentifier and VendorSpecificActionHeader are used to support
-the sending of a Vendor Specific Action frame.
-
-OcbWifiMac is very similar to AdhocWifiMac, with some modifications.
-The |ns3| AdhocWifiMac class is implemented very close to the 802.11p OCB
-mode rather than a real 802.11 ad-hoc mode. The AdhocWifiMac has no BSS
-context that is defined in 802.11 standard, so it will not take time to
-send beacons and to authenticate, making its behavior similar to that
-of an OcbWifiMac.
-
-1. SetBssid, GetBssid, SetSsid, GetSsid
-
-  These methods are related to 802.11 BSS context, and are unused in the OCB context.
-
-2. SetLinkUpCallback, SetLinkDownCallback
-
-   WAVE device can send packets directly, so the WiFi link is never down.
-
-3. SendVsc, AddReceiveVscCallback
-
-   WAVE management information shall be sent by vendor specific action frames,
-   sent by the upper layer 1609.4 standard as WSA
-   (WAVE Service Advertisement) packets or other vendor specific information.
-
-4. SendTimingAdvertisement (not implemented)
-
-   Although Timing Advertisement is very important and specifically defined in
-   802.11p standard, it is not useful in a simulation environment.
-   Every node in |ns3| vehicular simulation is assumed to be already time
-   synchronized (perhaps by GPS).
-
-5. ConfigureEdca
-
-   This method will allow the user to set EDCA parameters of WAVE channels
-   including CCH and SCHs. And the OcbWifiMac itself also uses this method
-   to configure default 802.11p EDCA parameters.
-
-6. Wildcard BSSID
-
-   The Wildcard BSSID is set to "ff:ff:ff:ff:ff:ff".
-   As defined in IEEE 802.11-2007, a wildcard BSSID shall not be used in the
-   BSSID field except for management frames of subtype probe request. But Adhoc
-   mode of |ns3| simplifies this mechanism:  when stations receive packets,
-   they will be forwarded up to the higher layer, regardless of BSSID.
-   This process is very close
-   to OCB mode as defined in 802.11p-2010, in which stations use the wildcard
-   BSSID to allow the higher layer of other stations to hear directly.
-
-7. Enqueue, Receive
-
-   The most important methods are send and receive methods. According to the
-   standard, we should filter the frames that are not permitted. Thus here we
-   just identify the frames we care about; the other frames will be discarded.
-
-MAC extension layer
-###################
-
-Although 1609.4 is still in the MAC layer, the implementation
-approach for |ns3| does not do much modification in the
-source code of the wifi module. Instead, if some feature is related
-to wifi MAC classes, then a relevant subclass is defined; if some
-feature has no relation to wifi MAC classes, then a new class
-will be defined. This approach was selected to be non-intrusive to the
-|ns3| wifi module. All of these classes will be hosted in a 'container'
-class called ``ns3:: WaveNetDevice``. This class is a subclass inheriting
-from ``ns3::NetDeivce``, composed of the objects of
-``ns3::ChannelScheduler``, ``ns3::ChannelManager``,
-``ns3::ChannelCoordinator`` and ``ns3::VsaManager``
-classes to provide the features described in 1609.4 while still
-containing the objects of ``ns3::OcbWifiMac`` and ``ns3::WifiPhy``
-classes.  Moreover, ``ns3::OcbWifiMac`` class is further extended with
-support for IEEE 1609.4 associating with ``ns3::HigherLayerTxVectorTag``
-and ``ns3::WaveMacLow``. The main work of the WaveNetDevice is to create
-objects, configure, check arguments and provide new APIs for multiple
-channel operation as follows:
-
-1. AddMac, GetMac and GetMacs
-
-  Different from ``ns3::WifiNetDevice``, the WAVE device will have
-  multiple internal MAC entities rather than a single one. Each MAC
-  entity is used to support each WAVE channel. Thus, when
-  devices switch from the current channel to the next channel in different
-  channel intervals, the packets in the internal queue will not be
-  flushed and the current MAC entity will perform a suspend operation
-  until woken up in next appropriate channel interval.
-
-2. AddPhy, GetPhy and GetPhys
-
-  Also in contrast to ``ns3::WifiNetDevice``,  the WAVE device here
-  can allow more than one PHY entity, which permits the use cases of
-  of single-PHY devices or multiple-PHY devices.
-
-3. SetChannelScheduler and GetChannelScheduler
-
-  How to deal with multiple MAC entities and PHY entities to assign
-  channel access for different requests from higher layer? IEEE
-  1609.4 [ieee1609dot4]_ does not seem to give a very clear and detailed
-  mechanism, deferring to the implementor. In this model, the class
-  ``ns3::ChannelScheduler`` provides the API and delegates to the subclasses
-  to implement the virtual methods. In the current implementation, the default
-  assignment mechanism for channel access,
-  called ``ns3::DefaultChannelScheduler``, gives a simple answer that only
-  deals with multiple channel operation in the context of a single-PHY device.
-  If users define their own different assignment mechanisms such as in the
-  context of two PHY entities, they can easily reuse models using AddPhy and
-  SetChannelScheduler methods to import a new assignment mechanism.
-
-4. SetChannelManager and GetChannelManager
-
-  class ``ns3::ChannelManager`` is a WAVE channel set which contains
-  valid WAVE channel numbers. Moreover, the tx information in this channel
-  set such as data rate and tx power level is used for transmitting management frames.
-
-5. SetVsaManager and GetVsaManager
-
-  class ``ns3::VsaManager`` is used to deal with sending and receiving
-  VSA frames. According to different request parameters from the higher layer,
-  this class may transmit VSA frames repeatedly in the appropriate channel
-  number and channel interval.
-
-6. SetChannelCoordinator and GetChannelCoordinator
-
-  class ``ns3::ChannelCoordinator`` is used to deal with channel coordination.
-  The WAVE device can be aware of the channel interval at the current time or
-  in the future.  It can also notify listeners about incoming channel coordination
-  events. Generally this class is used in the case of assigning alternating CCH and SCH access.
-
-7. StartSch and StopSch
-
-  In contrast to the basic 802.11p device that allow transmission packets
-  immediately after
-  the device is created, the WAVE device should assign channel access
-  for sending packets.  This method will call class ``ns3::ChannelScheduler``
-  to assign radio resources for the relevant channel.
-
-8. ChangeAddress
-
-  The WAVE device can support a change of address after devices are already
-  initialized, which will cause all of MAC entities reset their status.
-
-9. CancelTx
-
-  The WAVE device can support a request to cancel all transmissions associated
-  with the particular category and channel number, which will reset the
-  particular interval queue and drop all of the queued packets in this queue.
-
-10. RegisterTxProfile and DeleteTxProfile
-
-  After channel access is assigned, we still cannot send IP-based
-  (or other protocol) packets by the ``Send()`` method. A tx profile should
-  be registered to specify tx parameters before transmission.
-
-11. StartVsa, StopVsa and SetWaveVsaCallback
-
-  These methods will call an object from class ``ns3::VsaManager`` to send
-  and receive VSA frames.  Generally these methods are used by IEEE 1609.3
-  for WSA management information.
-
-12. SendX
-
-  After channel access is assigned, we can send WSMP (or other protocol)
-  packets via the ``SendX()`` method. We should specify the tx parameters for
-  each packet, e.g. the channel number for transmit.
-
-13. Send and SetReceiveCallback
-
-  This method is the abstract method defined in the parent class
-  ``ns3::NetDevice``, defined to allow the sending of IP-based packets.
-  The channel access should be already assigned and tx profile should
-  be registered, otherwise incoming packets from the higher layer will be
-  discarded. No matter whether packets are sent by Send method or SendX
-  method, the received packets will be only be delivered to the higher layer
-  by the registered ReceiveCallback.
-
-14. other methods from its parent class ``ns3::NetDevice``
-
-  These methods are implemented very similar to the code in ``ns3::WifiNetDevice``.
-
-In the above numbered list, we can categorize the methods into three types:
-the first type, from 1 to 6 and also 14, is the configuration for modeling and
-creating a WAVE device; the second type, from 7 to 11, is
-the management plane of the standard; and the third type, 12 and 13,
-is the data plane of the standard.
-
-Channel coordination
-
-The class ``ns3::ChannelCoordinator`` defines the CCH Interval, SCH Interval and GuardInteval. Users can be aware of which interval the current time or
-future time will be in. If channel access mode is assigned to
-alternating CCH and SCH access,
-channel interval events will be notified repeatedly for class
-``ns3::ChannelCoordinator`` to switch channels.  Current default values are
-for CCHI with 50ms interval, SCHI with 50ms interval, and GuardI with 4ms interval. Users can change these values by configuring the class attributes.
-
-Channel routing
-
-Channel routing service means different transmission approaches for WSMP data,
-IP datagram and management information.
-For WSMP data, the SendX () method implements the service primitive
-MA-UNITDATAX, and users can
-set transmission parameters for each individual packet. The parameters include
-channel number, priority,
-data rate and tx power level (expiration time is not supported now).
-For IP datagrams, the Send () method is a virtual method from ``ns3::NetDevice`` that implements the service primitive MA-UNITDATA.
-Users should insert QoS tags into packets themselves if they want to use QoS.
-Moreover, a tx profile should be registered
-before the Send method is called for transmit; the profile contains SCH number, data rate, power level and adaptable mode.
-For management information, StartVsa method implements the service primitive
-MLMEX-VSA. The tx information is already configured
-in ``ns3::ChannelManager``, including data rate, power level and adaptable mode.
-
-Channel access assignment
-
-The channel access assignment is done by class ``ns3::ChannelScheduler`` to assign ContinuousAccess, ExtendedAccess
-and AlternatingAccess. Besides that, immediate access is achieved by enabling
-the "immediate" parameter, in which case
-the request channel will be switched to immediately.  However this class is a
-virtual parent class.  The current module provides a
-subclass ``ns3::DefaultChannelScheduler`` to assign channel access in the context of a single-PHY device. In this subclass, if the channel
-access is already assigned for another request, the next coming request will
-fail until the previous channel access is released.
-Users can implement different assignment mechanisms to deal with multiple MAC entities and multiple PHY entities by
-inheriting from parent class ``ns3::ChannelScheduler``.
-An important point is that channel access should be assigned before sending
-routing packets, otherwise the packets will be discard.
-
-Vendor Specific Action frames
-
-When users want to send VSA repeatedly by calling WaveNetDevice::StartVsa, VSA will be sent repeatedly by
-``ns3::VsaManager``. It is worth noting that if the peer MAC address is a unicast address, the VSA can only
-be transmitted once even there is a repeat request. The tx parameters for VSA management frames can be obtained from the ``ns3::ChannelManager``.
-
-User priority and Multi-channel synchronization
-
-Since wifi module has already implemented a QoS mechanism, the wave module
-reuses the mechanism; VSA frames will be assigned the default value with the
-highest AC according to the standard.
-Multiple-channel synchronization is very important in practice for devices
-without a local timing source.
-However, in simulation, every node is supposed to have the same system clock, which could be provided by GPS devices in a real environment, so this feature is not modelled in |ns3|.
-During the guard interval, the device can only be in receive state, except
-for the switch state when the device does channel switching operation.
-
-PHY layer
-#########
-
-No modification or extension is made to the |ns3| PHY layer
-corresponding to this model.
-In the 802.11p standard, the PHY layer wireless technology is still 80211a OFDM with a 10MHz channel width,
-so Wifi80211pHelper will only allow the user to set the standard
-to WIFI_PHY_STANDARD_80211_10MHZ or WIFI_PHY_STANDARD_80211_20MHZ,
-while WaveHelper will only support WIFI_PHY_STANDARD_80211_10MHZ.
-The maximum station transmit power and maximum permitted EIRP defined in
-802.11p is larger
-than that of WiFi, so transmit range can normally become longer than the
-usual WiFi.  However, this feature will
-not be implemented. Users who want to obtain longer range should configure
-attributes "TxPowerStart",
-"TxPowerEnd" and "TxPowerLevels" of the YansWifiPhy class by themselves.
-
-Scope and Limitations
-=====================
-
-1. Does the model involve vehicular mobility of some sort?
-
-Vehicular networks involve not only communication protocols, but
-also a communication environment including vehicular mobility and
-propagation loss models. Because of specific features of the latter,
-the protocols need to change. The MAC layer model in this project just
-adapts MAC changes to vehicular environment. However this model
-does not involve any vehicular mobility with time limit. While users
-should understand that vehicular mobility is out of scope for the
-current WAVE module, they can use any mobility model in |ns3|.  For
-example, users may use a ``ns3::RandomWaypointMobilityModel`` (RWP)
-for node mobilty or may generate ns-2-style playback files using
-other third-party tools and then playback those mobility traces
-using ``ns3::Ns2MobilityHelper``.
-
-2. Does this model use different propagation models?
-
-Referring to the first issue, some more realistic propagation loss
-models for vehicualr environment are suggested and welcome.  Some
-existing propagation los models in |ns3| are also suitable.
-Normally, users can use Friis, Two-Ray Ground, and Nakagami models.
-The ``ns3::VanetRoutingExample`` example defaults to Two-Ray
-Ground propagation loss with no additional fading, although adding
-stochastic Nakagami-m fading is parametrically supported.
-
-3. Are there any vehicular application models to drive the code?
-
-About vehicular application models, SAE J2375 depends on WAVE
-architecture and is an application message set in US.  Cooperative Awareness
-Messages (CAM) and Decentralized Environment Notification Messages (DENM) can
-be sent Europe between network and application layer, and is
-very close to being an application model. The BSM in J2375 [saej2735] and CAM
-send alert messages that every vehicle node will sent periodically about
-its status information to cooperate with others. The
-``ns3::VanetRoutingExample`` example sets up a network of (vehicular)
-nodes that each broadcast BSMs at regular intervals and may additionally
-attempt to route non-BSM data through the network using select IP-based
-routing protocols.
-
-5. Why are there two kinds of NetDevice helpers?
-
-The current module provides two helpers to create two kinds of NetDevice.
-The first is an object of WifiNetDevice (802.11p device) which mainly
-contains class ``ns3::OcbWifiMac`` to enable OCB mode. The second
-is an object of WaveNetDevice (WAVE device) which contains additional
-classes ``ns3::ChannelScheduler``, ``ns3::ChannelManager``,
-``ns3::ChannelCoordinator`` and ``ns3::VsaManager`` to support
-multi-channel operation mode.  The reason to provide a special 802.11p
-device helper is that, considering the fact that many researchers are
-interested in routing protocols or other aspects of vehicular environment in
-a single channel context, they need neither multi-channel operation nor
-WAVE architectures.
-Besides that, the European standard may also reuse an 802.11p device in an
-modified ITS-G5 implementation (maybe called ItsG5NetDevice).  Hence,
-the model supports configuration of both types of devices.
-
-References
-==========
-
-.. [ieee80211p] IEEE Std 802.11p-2010 "IEEE Standard for Information technology-- Local and metropolitan area networks-- Specific requirements-- Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications Amendment 6: Wireless Access in Vehicular Environments"
-
-.. [ieee1609dot1] IEEE Std 1609.1-2010 "IEEE Standard for Wireless Access in Vehicular Environments (WAVE) - Resource Manager, 2010"
-
-.. [ieee1609dot2] IEEE Std 1609.2-2010 "IEEE Standard for Wireless Access in Vehicular Environments (WAVE) - Security Services for Applications and Management Messages, 2010"
-
-.. [ieee1609dot3] IEEE Std 1609.3-2010 "IEEE Standard for Wireless Access in Vehicular Environments (WAVE) - Networking Services, 2010"
-
-.. [ieee1609dot4] IEEE Std 1609.4-2010 "IEEE Standard for Wireless Access in Vehicular Environments (WAVE) - Multi-Channel Operation, 2010"
-
-.. [saej2735] SAE Std J2735 "J2735 dedicated short range communications (DSRC) message set dictionary. 2009"
-
-Usage
-*****
-
-Helpers
-=======
-
-The helpers include a) lower-level MAC and PHY channel helpers and
-b) higher-level application helpers that handle the sending and receiving
-of the Basic Safety Message (BSM).
-
-The lower-level helpers include ``ns3::YansWavePhyHelper``, ``ns3::NqosWaveMacHelper``, ``ns3::QosWaveMacHelper``,
-``ns3::Wifi80211pHelper`` and ``ns3::WaveHelper``.
-
-Wifi80211pHelper is used to create
-802.11p devices that follow the 802.11p-2010 standard. WaveHelper is
-used to create WAVE devices that follow both 802.11p-2010 and 1609.4-2010
-standards which are the MAC and PHY layers of the WAVE architecture.
-
-The relation of ``ns3::NqosWaveMacHelper``, ``ns3::QosWaveMacHelper`` and
-``ns3::Wifi80211pHelper`` is described as below:
-
-::
-
-    WifiHelper ------------use-------------->   WifiMacHelper
-        ^                                        ^         ^
-        |                                        |         |
-        |                                        |         |
-      inherit                                inherit      inherit
-        |                                        |         |
-    Wifi80211pHelper ------use----->  QosWaveMacHelper or NqosWaveHelper
-
-From the above diagram, there are two Mac helper classes that both
-inherit from the WifiMacHelper; when the WAVE module was originally
-written, there were specialized versions (QoS and Nqos) of WifiMacHelper
-that have since been removed from the Wifi codebase, but the distinction
-remains in the WAVE helpers.  The functions of WiFi 802.11p device can be achieved by
-WaveNetDevice's ContinuousAccess assignment, Wifi80211pHelper is recommended
-if there is no need for multiple channel operation.
-Usage is as follows:
-
-::
-
-    NodeContainer nodes;
-    NetDeviceContainer devices;
-    nodes.Create(2);
-    YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default();
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
-    wifiPhy.SetChannel(wifiChannel.Create());
-    NqosWave80211pMacHelper wifi80211pMac = NqosWaveMacHelper::Default();
-    Wifi80211pHelper 80211pHelper = Wifi80211pHelper::Default();
-    devices = 80211pHelper.Install(wifiPhy, wifi80211pMac, nodes);
-
-The relation of  ``ns3::YansWavePhyHelper``, ``ns3::QosWaveMacHelper`` and ``ns3::WaveHelper``
-is described as below:
-
-::
-
-                                            WifiMacHelper
-                                                  ^
-                                                  |
-                                                inherit
-                                                  |
-    WaveHelper -------- only use --------> QosWaveMacHelper
-
-From the above diagram, WaveHelper is not the subclass of WifiHelper and should only
-use QosWaveMacHelper because WAVE MAC layer is based on QoS mechanism. But
-the WaveHelper is recommended if there is a need for multiple channel operation.
-Usage is as follows:
-
-::
-
-    NodeContainer nodes;
-    NetDeviceContainer devices;
-    nodes.Create(2);
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
-    YansWavePhyHelper wavePhy =  YansWavePhyHelper::Default();
-    wavePhy.SetChannel(wifiChannel.Create());
-    QosWaveMacHelper waveMac = QosWaveMacHelper::Default();
-    WaveHelper waveHelper = WaveHelper::Default();
-    devices = waveHelper.Install(wavePhy, waveMac, nodes);
-
-The higher-level helpers include ``ns3::WaveBsmStats`` and ``ns3::WaveBsmHelper``.
-
-WaveBsmStats is used to collect and manage statistics, such as packet and byte
-counts and Packet Delivery Ratio (PDR), regarding the sending
-and receiving of WAVE BSM packets.  WaveBsmHelper is used by applications that
-wish to send and receive BSMs.
-
-The relation of ``ns3::WaveBsmHelper`` and ``WaveBsmStats`` is described
-below:
-
-::
-
-    <Your Vanet Routing Application> ----use----> WaveBsmHelper ----use----> WaveBsmStats
-
-From <Your Vanet Routing Application>, usage is as follows:
-
-    // declare WAVE BSM helper instance
-    WaveBsmHelper m_waveBsmHelper;
-
-    // the following are passed to the WaveBsmHelpe::Install()
-    // method, and they are thus assumed to be created and
-    // initialized themselves, based on the user's
-    // simulation setup criteria.
-    // container of network node
-    NodeContainer m_adhocTxNodes;
-    // (transmitting) devices (1 per node)
-    NetDeviceContainer m_adhocTxDevices;
-    // IPv4 interfaces (1 per device)
-    Ipv4InterfaceContainer m_adhocTxInterfaces;
-    // total simulation time (in seconds)
-    double m_TotalSimTime;
-    // WAVE BSM broadcast interval.  E.g., 100ms = 0.1 seconds
-    double m_waveInterval; // seconds
-    // time-synchronization accuracy of GPS devices.  E.g., +/- 40ns
-    double m_gpsAccuracyNs;
-    // array of distances (m) at which safety PDR shall be determined,
-    // e.g. 50m, 100m, 200m, 300m, 400m, 500m, 600m, 800m, 1000m, and 1500m
-    std::vector<double> m_txSafetyRanges;
-    // used to get consistent random numbers across scenarios
-    int64_t m_streamIndex;
-
-    m_waveBsmHelper.Install(m_adhocTxNodes,
-                            m_adhocTxDevices,
-                            m_adhocTxInterfaces,
-                            Seconds(m_TotalSimTime),
-                            m_wavePacketSize,
-                            Seconds(m_waveInterval),
-                            // convert GPS accuracy, in ns, to Time
-                            Seconds(m_gpsAccuracyNs / 1000000.0),
-                            m_txSafetyRanges);
-
-    // fix random number streams
-    m_streamIndex += m_waveBsmHelper.AssignStreams(m_streamIndex);
-
-Example usages of BSM statistics are as follows:
-
-    // Get the cumulative PDR of the first safety Tx range (i.e, 50m in the
-    // m_txSafetyRanges example above).
-    double bsm_pdr1 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(1);
-
-    // total WAVE BSM bytes sent
-    uint32_t cumulativeWaveBsmBytes = m_waveBsmHelper.GetWaveBsmStats()->GetTxByteCount();
-
-    // get number of WAVE BSM packets sent
-    int wavePktsSent = m_waveBsmHelper.GetWaveBsmStats()->GetTxPktCount();
-
-    // get number of WAVE BSM packets received
-    int wavePktsReceived = m_waveBsmHelper.GetWaveBsmStats()->GetRxPktCount();
-
-    // reset count of WAVE BSM packets received
-    m_waveBsmHelper.GetWaveBsmStats()->SetRxPktCount(0);
-
-    // reset count of WAVE BSM packets sent
-    m_waveBsmHelper.GetWaveBsmStats()->SetTxPktCount(0);
-
-    // indicate that a node (nodeId) is moving.  (set to 0 to "stop" node)
-    WaveBsmHelper::GetNodesMoving()[nodeId] = 1;
-
-APIs
-====
-
-MAC layer
-#########
-The 802.11p device can allow the upper layer to send different information
-over Vendor Specific Action management frames by using different
-OrganizationIdentifier fields to identify differences.
-
-1. create some Node objects and WifiNetDevice objects, e.g. one sender and one receiver.
-
-2. receiver defines an OrganizationIdentifier
-
-  ::
-
-   uint8_t oi_bytes[5] = {0x00, 0x50, 0xC2, 0x4A, 0x40};
-   OrganizationIdentifier oi(oi_bytes,5);
-
-3. receiver defines a Callback for the defined OrganizationIdentifier
-
-  ::
-
-    VscCallback vsccall = MakeCallback(&VsaExample::GetWsaAndOi, this);
-
-4. receiver registers this identifier and function
-
-  ::
-
-      Ptr<WifiNetDevice> device1 = DynamicCast<WifiNetDevice>(nodes.Get(i)->GetDevice(0));
-      Ptr<OcbWifiMac> ocb1 = DynamicCast<OcbWifiMac>(device->GetMac());
-      ocb1->AddReceiveVscCallback(oi, vsccall);
-
-5. sender transmits management information over VSA frames
-
-  ::
-
-      Ptr<Packet> vsc = Create<Packet>();
-      ocb2->SendVsc(vsc, Mac48Address::GetBroadcast(), m_16093oi);
-
-6. then registered callbacks in the receiver will be called.
-
-MAC extension layer
-###################
-
-The WAVE devices allow the upper layer to route packets in different control
-approaches.  However dedicated APIs and invocation sequences should be
-followed; otherwise, the packets may be discarded by devices.
-
-1. create some Node objects and WaveNetDevice objects by helpers, e.g. one sender and one receiver.
-
-2. receiver registers the receive callback if WSMP and IP-based packets are supposed to be received.
-
-  ::
-
-    // the class ``ns3::WaveNetDeviceExample``here will has a receive method "Receive" to be registered.
-    receiver->SetReceiveCallback(MakeCallback(&WaveNetDeviceExample::Receive, this));
-
-3. receiver registers the receive callback if WSA frames are supposed to be received.
-
-  ::
-
-    // the class ``ns3::WaveNetDeviceExample``here will has a receive method "ReceiveVsa" to be registered.
-    receiver->SetWaveVsaCallback(MakeCallback (&WaveNetDeviceExample::ReceiveVsa, this));
-
-4. sender and receiver assign channel access by StartSch method.
-
-  ::
-
-    // in this case that alternating access with non-immediate mode is assigned for sender and receiver devices.
-    const SchInfo schInfo = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, sender, schInfo);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, receiver, schInfo);
-
-  or
-
-  ::
-
-    // in this case that continuous access with immediate mode is assigned for sender and receiver devices.
-    const SchInfo schInfo = SchInfo(SCH1, true, EXTENDED_CONTINUOUS);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, sender, schInfo);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, receiver, schInfo)
-
-  or
-
-  ::
-
-    // in this case that extended access with non-immediate mode is assigned for sender and receiver devices.
-    const SchInfo schInfo = SchInfo(SCH1, false, 100);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, sender, schInfo);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, receiver, schInfo)
-
-5. sender registers a tx profile if IP-based packets are planned to be transmitted
-
-  ::
-
-    // the IP-based packets will be transmitted in SCH1 with 6Mbps and 4 txPowerLevel with adaptable mode.
-    const TxProfile txProfile = TxProfile(SCH1, true, 4, WifiMode("OfdmRate6MbpsBW10MHz"));
-    Simulator::Schedule(Seconds(2.0), &WaveNetDevice::RegisterTxProfile, sender, txProfile);
-
-6. sender transmits  WSMP packets by SendX method.
-
-  ::
-
-    // the data rate and txPowerLevel is controlled by the high layer which are 6Mbps and 0 level here.
-    const TxInfo txInfo = TxInfo(CCH, 7, WifiMode("OfdmRate6MbpsBW10MHz"),  0);
-    // this packet will contain WSMP header when IEEE 1609.3 model is implemented
-    const static uint16_t WSMP_PROT_NUMBER = 0x88DC;
-    Ptr<Packet> wsaPacket  = Create<Packet>(100);
-    const Address dest = receiver->GetAddress();
-    Simulator::Schedule(Seconds(2.0),  &WaveNetDevice::SendX, sender, wsaPacket, dest, WSMP_PROT_NUMBER, txInfo);
-
-  or
-
-  ::
-
-    // the data rate and txPowerLevel is controlled by the MAC layer which are decided by WifiRemoteStationManager
-    const TxInfo txInfo = TxInfo(CCH, 7, WifiMode(),  8);
-    // this packet will contain WSMP header when IEEE 1609.3 model is implemented
-    const static uint16_t WSMP_PROT_NUMBER = 0x88DC;
-    Ptr<Packet> wsaPacket  = Create<Packet>(100);
-    const Address dest = receiver->GetAddress();
-    Simulator::Schedule(Seconds(2.0),  &WaveNetDevice::SendX, sender, wsaPacket, dest, WSMP_PROT_NUMBER, txInfo);
-
-7. sender transmits IP-based packets by Send method.
-
-  ::
-
-    const static uint16_t IPv6_PROT_NUMBER = 0x86DD;
-    Ptr<Packet> packet  = Create<Packet>(100);
-    const Address dest = receiver->GetAddress();
-    Simulator::Schedule(Seconds(2.0),  &WaveNetDevice::Send, sender, packet, dest, IPv6_PROT_NUMBER);
-
-8. send transmits WSA frames repeatedly by StartVsa method.
-
-  ::
-
-     // this packet will contain WSA management information when IEEE 1609.3 model is implemented
-    Ptr<Packet> wsaPacket = Create<Packet>(100);
-    Mac48Address dest = Mac48Address::GetBroadcast();
-    const VsaInfo vsaInfo = VsaInfo(dest, OrganizationIdentifier(), 0, wsaPacket, SCH1, 100, VSA_TRANSMIT_IN_BOTHI);
-    Simulator::Schedule(Seconds(2.0), &WaveNetDevice::StartVsa, sender, vsaInfo);
-
-9. sender stops WSA frames repeatedly transmit by StopVsa method.
-
-  ::
-
-    Simulator::Schedule(Seconds(3.0), &WaveNetDevice::StopVsa, sender, SCH1);
-
-10. sender and receiver release assigned channel access by StopSch method.
-
-  ::
-
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::StopSch, sender, SCH1);
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::StopSch, receiver, SCH1);
-
-11. sender or receiver changes current MAC address by ChangeAddress method.
-
-  ::
-
-    Address newAddress = Mac48Address::Allocate();
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::ChangeAddress, sender, newAddress);
-
-12. sender cancels all transmissions with the particular category and channel number by CancelTx method.
-
-  ::
-
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::CancelTx, sender, CCH,  AC_BE);
-
-For transmitting and receiving these packets successfully,
-the normal and appropriate invocation procedures should be performed.
-
-(a) For WSMP, channel access should be assigned for transmit and receive.
-The channel access release operation may be optional if there is no need for
-transmission in another channel.
-
-::
-
-    StartSch -------------> SendX / ReceiveCallback -------------->  StopSch
-
-(b) For IP, a tx profile should be registered before transmit and receive
-operations. The delete operation of tx profile may be
-optional if there is no need for transmission with other tx parameters.
-The channel access assignment and release optional usage is the same with
-WSMP here.
-
-::
-
-    StartSch -------------> RegisterTxProfile ----------> Send / ReceiveCallback -------------->  DeleteTxProfile -------------> StopSch
-
-(c) For WSA, StartVsa is called to transmit while StopVsa is an optional
-operation for canceling repeat transmit. The channel
-access assignment and release optional usage is also the same with WSMP here.
-To receive VSA, WaveVsaCallback should
-be registered; otherwise, the received VSA frames will be discard by
-the MAC extension layer and not delivered to the higher layer.
-
-::
-
-    StartSch -------------> StartVsa / WaveVsaCallback -------------->  StopVsa ---------------> StopSch
-
-(d) Here an important point is that if the higher layer wants to transmit
-these packets in a control channel (the channel 178),
-there will be no need to request for CCH by the StartSch method, which means
-that StartSch can be optional or should be avoided
-here. The reason is that the default continuous CCH access has been assigned automatically after WAVE devices are created and initialized.
-Therefore, if calling StartSch and StopSch method with CCH as a parameter,
-the request will be discarded by devices and the method will return false to
-indicate failure.
-
-Attributes
-==========
-
-The channel interval duration's default value is defined in the standard.
-However, the current implementation allows users to configure these
-attributes with other values. These attributes are included in the class
-``ns3::ChannelCoodinator`` with config paths shown in the below. The method
-IsValidConfig is suggested to test whether new configuration follows the
-standard.
-
-::
-
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/ChannelCoordinator/$ns3::ChannelCoordinator/CchInterval
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/ChannelCoordinator/$ns3::ChannelCoordinator/SchInterval
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/ChannelCoordinator/$ns3::ChannelCoordinator/GuardInterval
-
-The ``ns3::WaveNetDevice`` is a wrapper class that contains those classes to support for multiple channel
-operation. To set or get the pointers of those objects, users can also
-use them by config paths shown in the below.
-
-::
-
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/Mtu
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/Channel
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/PhyEntities
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/MacEntities
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/ChannelScheduler
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/ChannelManager
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/ChannelCoordinator
-/NodeList/[i]/DeviceList/[i]/$ns3::WaveNetDevice/VsaManager
-
-Output
-======
-
-For the 802.11p device, current classes provide output of the same type as WiFi devices;
-namely, ASCII and pcap traces, and logging output.  The 802.11p logging
-components can be enabled globally via the call to
-
-::
-
-  Wifi80211pHelper::EnableLogComponents();
-
-For the WAVE device, current classes provide output of the same type as WiFi
-devices; namely, ASCII and pcap traces, and logging output. The WAVE logging
-components can be enabled globally via the call to
-
-::
-
-  WaveHelper::EnableLogComponents();
-
-
-Advanced Usage
-==============
-
-Advanced WaveHelper configuration
-#################################
-
-If users can make sure in which channel this WAVE device will work,
-they can set specific channel numbers to save resources of unused channels .
-Usage is as follows:
-
-::
-
-    // in this case, the MAC entities for SCH2 to SCH6 will not be created
-    WaveHelper helper = WaveHelper::Default();
-    uint32_t channels[] = {CCH, SCH1};
-    std::vector<uint32_t> channelsVector(channels, channels + 2);
-    helper.CreateMacForChannel(channelsVector);
-
-If users can create other channel access assignment mechanism, e.g.
-in the context of more PHY entities, which may be called
-"ns3::AnotherScheduler", they can use this helper to create WAVE devices
-with new assignment mechanisms.  Usage is as follows:
-
-::
-
-    WaveHelper helper = WaveHelper::Default();
-    helper.helper.CreateMacForChannel(ChannelManager::GetWaveChannels());    // create all 7 MAC entities for WAVE
-    helper.CreatePhys(2);        // or other number which should be less than 7
-    helper.SetChannelScheduler("ns3::AnotherScheduler");    // The AnotherScheduler should be implemented by users.
-    helper.SetRemoteStationManager("ns3::ConstantRateWifiManager");    // or other  rate control algorithms
-
-Examples
-========
-
-A basic example exists called ``wave-simple-80211p.cc``.
-This example shows basic construction of an 802.11p node.  Two nodes
-are constructed with 802.11p devices, and by default, one node sends a single
-packet to another node (the number of packets and interval between
-them can be configured by command-line arguments).  The example shows
-typical usage of the helper classes for this mode of WiFi.
-
-Another example exists called ``wave-simple-device.cc``. This
-example shows how to create WAVE devices by helpers and the routing service
-for different packets.
-After WAVE devices are configured and created by helpers, these packets are
-transmitted in different approaches.
-
-Another example exists called ``vanet-routing-compare.cc``. This
-example shows how to create mobility nodes in a VANET scenario and
-send Basic Safety Message (BSM) packets are regular intervals and/or
-additional data traffic to be routed between nodes.  BSMs are transmitted
-assuming the WAVE Short Message Protocol (WSMP), whereas non-BSM data
-packets are relayed by using one of several different IP-based routing
-protocols (e.g., AODV, OLSR, DSDV, or DSR).
-
-Troubleshooting
-===============
-
-To be defined.
-
-Validation
-**********
-
-A test suite named ``wifi-80211p-ocb`` is defined.  This test
-case consists of a stationary node and a mobile node.  The mobile
-node moves towards the stationary mode, and time points are checked
-at which time the physical layer starts to receive packets (and
-whether the MAC becomes associated, if applicable).  The same physical
-experiment is repeated for normal WiFi NetDevices in AP/STA mode, in
-Adhoc mode, and the new OCB mode.
-
-Another test suite named ``wave-mac-extension`` is defined. This test suite
-has four test cases, including ``channel-coordination``, ``channel-routing``,
-``channel-access`` and ``annex-c``. The first case is to test channel
-coordination
-feature. The second case is to test channel routing for three types of packets.
-The third case is to test four channel access assignments. And the fourth case
-is to test the implemented feature described in the Annex C of the standard.
-It is worth noting that the  ``channel-routing`` and ``channel-access`` test
-cases are both in the context of single-PHY device, which depends on the
-default channel
-access assignment mechanism ``ns3:DefaultChannelScheduler``, thus they may not
-be suitable for testing when other channel access assignment mechanisms are
-used.  Although they are test cases, they are also good examples to show
-usage.
-
-The ``ns3::VanetRoutingExample`` example was studied using mobility trace
-files in the Raleigh, NC USA area generated using Simulation for Urban
-Mobility (SUMO).  Three environments were studied:  a) an open highway
-scenario, b) a residential neighborhood scenario, and c) and urban downtown
-scenario.  For each environment, a constant number of 50-750 vehicles was
-maintained for 2000 simulation seconds (> 30 minutes).  The mobility trace
-file were played back using ``ns3::Ns2MobilityHelper``.  All vehicular nodes
-transmitted a 200-byte BSM at 10 Hz and the PDR was determined for
-transmission ranges of 50-1500m.  No additional non-BSM data was injected /
-routed through the network.  The default propagation loss model used
-was Two-Ray Ground.  Different fading / shadowing models were evaluated,
-including a) no fading, b) stochastic Nakagami-m fading, and c) an
-obstacle shadowing model (to be contributed to |ns3|).  30 trials of each
-scenario were run in the North Carolina State University (NCSU) High
-Performance Computing (HPC) center, with each trial requiring from
-8 hours to 6 days of CPU time to complete.  Preliminary results were
-presented at the PhD Forum, 22nd IEEE International Conference on
-Network Protocols (ICNP), October 24, 2014, Research Triangle Park, NC.
-See:  https://web.archive.org/web/20170808113648/http://www4.ncsu.edu/~scarpen/Research_files/Final-PHD_Forum_SE_Carpenter_2014.pdf
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/examples/CMakeLists.txt ns-3.39/src/wave/examples/CMakeLists.txt
--- ns-3.38/src/wave/examples/CMakeLists.txt	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/examples/CMakeLists.txt	1969-12-31 16:00:00.000000000 -0800
@@ -1,42 +0,0 @@
-build_lib_example(
-  NAME wave-simple-80211p
-  SOURCE_FILES wave-simple-80211p.cc
-  LIBRARIES_TO_LINK
-    ${libcore}
-    ${libapplications}
-    ${libmobility}
-    ${libnetwork}
-    ${libwifi}
-    ${libwave}
-)
-
-build_lib_example(
-  NAME wave-simple-device
-  SOURCE_FILES wave-simple-device.cc
-  LIBRARIES_TO_LINK
-    ${libcore}
-    ${libapplications}
-    ${libmobility}
-    ${libnetwork}
-    ${libwifi}
-    ${libwave}
-)
-
-build_lib_example(
-  NAME vanet-routing-compare
-  SOURCE_FILES vanet-routing-compare.cc
-  LIBRARIES_TO_LINK
-    ${libcore}
-    ${libaodv}
-    ${libapplications}
-    ${libconfig-store}
-    ${libdsr}
-    ${libdsdv}
-    ${libflow-monitor}
-    ${libmobility}
-    ${libnetwork}
-    ${libolsr}
-    ${libpropagation}
-    ${libwifi}
-    ${libwave}
-)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/examples/low99-ct-unterstrass-1day.filt.7.adj.mob ns-3.39/src/wave/examples/low99-ct-unterstrass-1day.filt.7.adj.mob
--- ns-3.38/src/wave/examples/low99-ct-unterstrass-1day.filt.7.adj.mob	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/examples/low99-ct-unterstrass-1day.filt.7.adj.mob	1969-12-31 16:00:00.000000000 -0800
@@ -1,1515 +0,0 @@
-# Statistics:
-# T_min:	0.000000	T_max:	300.010000
-# X_min:	10	X_max:	4660
-# Y_min:	10	Y_max:	3010
-# V_min:	9.937942	V_max:	32.054187	V_avg:	15.446680
-# nn:	99	area:	13950000.000000
-# events:	792	hosts/area:	0.070968
-# Last mov would end time at (time): 351.020
-#
-# Processed from file: ct-unterstrass-1day.filt.7.mov
-# ADJUSTMENT: X_min=679990.0 Y_min=247490.0 T_min=36610.980000 T_max= T_scalar=1
-#
-# Statistics:
-# T_min:	36610.980000	T_max:	36910.990000
-# X_min:	680000	X_max:	684650
-# Y_min:	247500	Y_max:	250500
-# V_min:	9.937942	V_max:	32.054187	V_avg:	15.446680
-# nn:	99	area:	13950000.000000
-# events:	792	hosts/area:	0.070968
-# Last mov would end time at (time): 36962.000
-#
-# Processed from file: ct-unterstrass-1day.filt.mov
-# FILTERING: X_min=0 X_max=999999999 Y_min=0 Y_max=999999999 T_min=36610.990000 T_max=36910.990000 V_min=0
-#
-# Statistics:
-# T_min:	35560.990000	T_max:	37461.000000
-# X_min:	680000	X_max:	684650
-# Y_min:	247500	Y_max:	250500
-# V_min:	7.806775	V_max:	32.386778	V_avg:	15.496693
-# nn:	505	area:	13950000.000000
-# events:	5174	hosts/area:	0.362007
-# Last mov would end time at (time): 37484.000
-#
-# Processed from file: ct-unterstrass-1day.mov
-# FILTERING: X_min=0 X_max=999999999 Y_min=0 Y_max=999999999 T_min=35561 T_max=37461 V_min=0
-#
-#
-# VANET traces; ns-2 movement file
-# Parser: car-xml2ns
-#
-# City region: Unterstrass
-# Statistics:
-# T_min:	16392.990000	T_max:	88387.766811
-# X_min:	680000	X_max:	685000
-# Y_min:	247500	Y_max:	250500
-# V_min:	1.279985	V_max:	32.641710	V_avg:	14.853009
-# nn:	96934	area:	15000000.000000
-# events:	1973502	hosts/area:	64.622667
-# Last mov would end time at (time): 88387.767
-#
-# Processed from file: VANET-traces-ns2.mov
-# X_min=680000 X_max=685000 Y_min=247500 Y_max=250500 T_min=0 T_max=999999999 V_min=0
-#
-#
-# VANET traces; ns-2 movement file
-# Parser: car-xml2ns
-#
-# Statistic for VANET-traces-ns2.mov
-# T_min: 14208.990000    T_max:  90811.692948
-# X_min: 486740  X_max:  840500
-# Y_min: 46000   Y_max:  309000
-# V_min: 0.787110        V_max:  32.679368       V_avg:  20.536439
-# nn:    259978
-# events:        27609858
-#
-# More information on VANET traces can be found in the MobiHoc 2006 paper:
-# Val Naumov, Rainer Baumann, Thomas Gross "An Evaluation of Inter-Vehicle Ad Hoc Networks Based on Realistic Vehicular Traces"
-# (http://www.lst.inf.ethz.ch/research)
-#
-# The speed of 10^9 m/s is used to initially place nodes
-# from (0,0) to their trip starting points, e.g.:
-# $node_(12345) set X_ 0.0
-# $node_(12345) set Y_ 0.0
-# $node_(12345) set Z_ 0.0
-# $ns_ at 10000.990000 "$node_(12345) setdest 710130.000000 91630.000000 1000000000.0"
-#
-
-$ns_ at 0.0 "$node_(0) switch OFF" ;# set_X,Y,Z
-$node_(0) set X_ 0.000000
-$node_(0) set Y_ 0.000000
-$node_(0) set Z_ 0.0
-$ns_ at 0.000000 "$node_(0) setdest 1335.996339 1004.578227 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(0) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(0) setdest 1290.000000 1090.000000 16.142807" ;#
-$ns_ at 0.0 "$node_(1) switch OFF" ;# set_X,Y,Z
-$node_(1) set X_ 0.000000
-$node_(1) set Y_ 0.000000
-$node_(1) set Z_ 0.0
-$ns_ at 0.000000 "$node_(1) setdest 3386.590740 102.881482 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(1) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(1) setdest 3310.000000 160.000000 13.629680" ;#
-$ns_ at 0.0 "$node_(2) switch OFF" ;# set_X,Y,Z
-$node_(2) set X_ 0.000000
-$node_(2) set Y_ 0.000000
-$node_(2) set Z_ 0.0
-$ns_ at 0.000000 "$node_(2) setdest 1151.671243 2088.378976 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(2) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(2) setdest 1071.000000 2103.000000 16.364369" ;#
-$ns_ at 0.0 "$node_(3) switch OFF" ;# set_X,Y,Z
-$node_(3) set X_ 0.000000
-$node_(3) set Y_ 0.000000
-$node_(3) set Z_ 0.0
-$ns_ at 0.000000 "$node_(3) setdest 2145.733295 1665.474912 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(3) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(3) setdest 2145.139452 1666.087027 12.762536" ;#
-$ns_ at 0.0 "$node_(4) switch OFF" ;# set_X,Y,Z
-$node_(4) set X_ 0.000000
-$node_(4) set Y_ 0.000000
-$node_(4) set Z_ 0.0
-$ns_ at 0.000000 "$node_(4) setdest 1344.895727 2053.358569 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(4) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(4) setdest 1071.000000 2103.000000 16.364369" ;#
-$ns_ at 0.0 "$node_(5) switch OFF" ;# set_X,Y,Z
-$node_(5) set X_ 0.000000
-$node_(5) set Y_ 0.000000
-$node_(5) set Z_ 0.0
-$ns_ at 0.000000 "$node_(5) setdest 1302.721238 2548.142173 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(5) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(5) setdest 1265.525646 2567.811046 15.943000" ;#
-$ns_ at 0.0 "$node_(6) switch OFF" ;# set_X,Y,Z
-$node_(6) set X_ 0.000000
-$node_(6) set Y_ 0.000000
-$node_(6) set Z_ 0.0
-$ns_ at 0.000000 "$node_(6) setdest 3014.330992 373.464793 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(6) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(6) setdest 2560.000000 10.000000 13.527727" ;#
-$ns_ at 0.0 "$node_(7) switch OFF" ;# set_X,Y,Z
-$node_(7) set X_ 0.000000
-$node_(7) set Y_ 0.000000
-$node_(7) set Z_ 0.0
-$ns_ at 0.000000 "$node_(7) setdest 3827.411664 1295.726554 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(7) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(7) setdest 3810.000000 1480.000000 13.211580" ;#
-$ns_ at 0.0 "$node_(8) switch OFF" ;# set_X,Y,Z
-$node_(8) set X_ 0.000000
-$node_(8) set Y_ 0.000000
-$node_(8) set Z_ 0.0
-$ns_ at 0.000000 "$node_(8) setdest 3902.621739 499.753269 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(8) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(8) setdest 3878.703961 752.883083 13.865834" ;#
-$ns_ at 0.0 "$node_(9) switch OFF" ;# set_X,Y,Z
-$node_(9) set X_ 0.000000
-$node_(9) set Y_ 0.000000
-$node_(9) set Z_ 0.0
-$ns_ at 0.000000 "$node_(9) setdest 2967.771730 91.554346 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(9) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(9) setdest 2560.000000 10.000000 16.627238" ;#
-$ns_ at 0.0 "$node_(10) switch OFF" ;# set_X,Y,Z
-$node_(10) set X_ 0.000000
-$node_(10) set Y_ 0.000000
-$node_(10) set Z_ 0.0
-$ns_ at 0.000000 "$node_(10) setdest 2318.919116 1486.960295 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(10) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(10) setdest 2291.086492 1515.649308 13.727766" ;#
-$ns_ at 0.0 "$node_(11) switch OFF" ;# set_X,Y,Z
-$node_(11) set X_ 0.000000
-$node_(11) set Y_ 0.000000
-$node_(11) set Z_ 0.0
-$ns_ at 0.000000 "$node_(11) setdest 1419.066610 2039.915684 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(11) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(11) setdest 1384.989853 2046.091826 15.647451" ;#
-$ns_ at 0.0 "$node_(12) switch OFF" ;# set_X,Y,Z
-$node_(12) set X_ 0.000000
-$node_(12) set Y_ 0.000000
-$node_(12) set Z_ 0.0
-$ns_ at 0.000000 "$node_(12) setdest 3124.161401 1857.484928 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(12) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(12) setdest 3111.196216 1922.959110 13.728154" ;#
-$ns_ at 0.0 "$node_(13) switch OFF" ;# set_X,Y,Z
-$node_(13) set X_ 0.000000
-$node_(13) set Y_ 0.000000
-$node_(13) set Z_ 0.0
-$ns_ at 0.000000 "$node_(13) setdest 1765.904200 206.177913 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(13) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(13) setdest 1575.313011 560.132979 16.252872" ;#
-$ns_ at 0.0 "$node_(14) switch OFF" ;# set_X,Y,Z
-$node_(14) set X_ 0.000000
-$node_(14) set Y_ 0.000000
-$node_(14) set Z_ 0.0
-$ns_ at 0.000000 "$node_(14) setdest 2644.602103 77.681682 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(14) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(14) setdest 2560.000000 10.000000 13.526031" ;#
-$ns_ at 0.0 "$node_(15) switch OFF" ;# set_X,Y,Z
-$node_(15) set X_ 0.000000
-$node_(15) set Y_ 0.000000
-$node_(15) set Z_ 0.0
-$ns_ at 0.000000 "$node_(15) setdest 1412.404730 1269.781947 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(15) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(15) setdest 1450.191974 1325.281961 13.601995" ;#
-$ns_ at 0.0 "$node_(16) switch OFF" ;# set_X,Y,Z
-$node_(16) set X_ 0.000000
-$node_(16) set Y_ 0.000000
-$node_(16) set Z_ 0.0
-$ns_ at 0.000000 "$node_(16) setdest 3808.631535 1683.901279 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(16) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(16) setdest 3802.025034 2668.269892 12.752087" ;#
-$ns_ at 0.0 "$node_(17) switch OFF" ;# set_X,Y,Z
-$node_(17) set X_ 0.000000
-$node_(17) set Y_ 0.000000
-$node_(17) set Z_ 0.0
-$ns_ at 0.000000 "$node_(17) setdest 2168.625696 1641.878129 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(17) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(17) setdest 2077.481811 1735.826441 12.043499" ;#
-$ns_ at 0.0 "$node_(18) switch OFF" ;# set_X,Y,Z
-$node_(18) set X_ 0.000000
-$node_(18) set Y_ 0.000000
-$node_(18) set Z_ 0.0
-$ns_ at 0.000000 "$node_(18) setdest 1487.721647 2027.472503 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(18) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(18) setdest 1444.642737 2035.280214 15.606334" ;#
-$ns_ at 0.0 "$node_(19) switch OFF" ;# set_X,Y,Z
-$node_(19) set X_ 0.000000
-$node_(19) set Y_ 0.000000
-$node_(19) set Z_ 0.0
-$ns_ at 0.000000 "$node_(19) setdest 2522.490892 1279.382742 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(19) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(19) setdest 2510.000000 1290.000000 16.231244" ;#
-$ns_ at 0.0 "$node_(20) switch OFF" ;# set_X,Y,Z
-$node_(20) set X_ 0.000000
-$node_(20) set Y_ 0.000000
-$node_(20) set Z_ 0.0
-$ns_ at 0.000000 "$node_(20) setdest 3808.901377 1643.694814 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(20) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(20) setdest 3805.915125 2088.646417 11.701108" ;#
-$ns_ at 0.0 "$node_(21) switch OFF" ;# set_X,Y,Z
-$node_(21) set X_ 0.000000
-$node_(21) set Y_ 0.000000
-$node_(21) set Z_ 0.0
-$ns_ at 0.000000 "$node_(21) setdest 3310.000000 343.829579 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(21) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(21) setdest 3310.000000 417.405846 13.140070" ;#
-$ns_ at 0.0 "$node_(22) switch OFF" ;# set_X,Y,Z
-$node_(22) set X_ 0.000000
-$node_(22) set Y_ 0.000000
-$node_(22) set Z_ 0.0
-$ns_ at 0.000000 "$node_(22) setdest 2855.036555 1507.937286 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(22) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(22) setdest 2651.364951 1666.692719 16.038021" ;#
-$ns_ at 0.0 "$node_(23) switch OFF" ;# set_X,Y,Z
-$node_(23) set X_ 0.000000
-$node_(23) set Y_ 0.000000
-$node_(23) set Z_ 0.0
-$ns_ at 0.000000 "$node_(23) setdest 1592.382893 1534.124874 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(23) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(23) setdest 1610.000000 1560.000000 15.573683" ;#
-$ns_ at 0.0 "$node_(24) switch OFF" ;# set_X,Y,Z
-$node_(24) set X_ 0.000000
-$node_(24) set Y_ 0.000000
-$node_(24) set Z_ 0.0
-$ns_ at 8.316123 "$node_(24) setdest 3917.755102 10.000000 1000000000.000000" ;# init_node
-$ns_ at 8.326123 "$node_(24) switch ON" ;# inside
-$ns_ at 8.326123 "$node_(24) setdest 3930.000000 210.000000 13.636597" ;#
-$ns_ at 0.0 "$node_(25) switch OFF" ;# set_X,Y,Z
-$node_(25) set X_ 0.000000
-$node_(25) set Y_ 0.000000
-$node_(25) set Z_ 0.0
-$ns_ at 0.000000 "$node_(25) setdest 2260.037210 1971.720046 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(25) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(25) setdest 1980.000000 2190.000000 16.131718" ;#
-$ns_ at 0.0 "$node_(26) switch OFF" ;# set_X,Y,Z
-$node_(26) set X_ 0.000000
-$node_(26) set Y_ 0.000000
-$node_(26) set Z_ 0.0
-$ns_ at 0.000000 "$node_(26) setdest 3296.991650 876.671168 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(26) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(26) setdest 3290.000000 1020.000000 13.033539" ;#
-$ns_ at 0.0 "$node_(27) switch OFF" ;# set_X,Y,Z
-$node_(27) set X_ 0.000000
-$node_(27) set Y_ 0.000000
-$node_(27) set Z_ 0.0
-$ns_ at 0.000000 "$node_(27) setdest 3175.428887 1175.425929 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(27) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(27) setdest 3032.000000 1370.000000 16.104256" ;#
-$ns_ at 0.0 "$node_(28) switch OFF" ;# set_X,Y,Z
-$node_(28) set X_ 0.000000
-$node_(28) set Y_ 0.000000
-$node_(28) set Z_ 0.0
-$ns_ at 0.000000 "$node_(28) setdest 2409.695914 1393.390366 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(28) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(28) setdest 2179.032149 1631.151477 13.107394" ;#
-$ns_ at 0.0 "$node_(29) switch OFF" ;# set_X,Y,Z
-$node_(29) set X_ 0.000000
-$node_(29) set Y_ 0.000000
-$node_(29) set Z_ 0.0
-$ns_ at 0.000000 "$node_(29) setdest 3452.084028 54.039030 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(29) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(29) setdest 3431.218482 69.599776 13.629680" ;#
-$ns_ at 0.0 "$node_(30) switch OFF" ;# set_X,Y,Z
-$node_(30) set X_ 0.000000
-$node_(30) set Y_ 0.000000
-$node_(30) set Z_ 0.0
-$ns_ at 0.000000 "$node_(30) setdest 1115.272435 1128.723407 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(30) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(30) setdest 1049.135457 1143.380791 17.914625" ;#
-$ns_ at 0.0 "$node_(31) switch OFF" ;# set_X,Y,Z
-$node_(31) set X_ 0.000000
-$node_(31) set Y_ 0.000000
-$node_(31) set Z_ 0.0
-$ns_ at 0.000000 "$node_(31) setdest 3917.389929 343.456587 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(31) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(31) setdest 3854.700880 1006.915687 13.418520" ;#
-$ns_ at 0.0 "$node_(32) switch OFF" ;# set_X,Y,Z
-$node_(32) set X_ 0.000000
-$node_(32) set Y_ 0.000000
-$node_(32) set Z_ 0.0
-$ns_ at 0.000000 "$node_(32) setdest 1916.905265 2069.068425 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(32) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(32) setdest 1860.000000 1960.000000 13.653812" ;#
-$ns_ at 0.0 "$node_(33) switch OFF" ;# set_X,Y,Z
-$node_(33) set X_ 0.000000
-$node_(33) set Y_ 0.000000
-$node_(33) set Z_ 0.0
-$ns_ at 25.600909 "$node_(33) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 25.610909 "$node_(33) switch ON" ;# inside
-$ns_ at 25.610909 "$node_(33) setdest 3310.000000 160.000000 13.629680" ;#
-$ns_ at 0.0 "$node_(34) switch OFF" ;# set_X,Y,Z
-$node_(34) set X_ 0.000000
-$node_(34) set Y_ 0.000000
-$node_(34) set Z_ 0.0
-$ns_ at 0.000000 "$node_(34) setdest 3322.430436 150.729845 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(34) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(34) setdest 3310.000000 160.000000 15.352977" ;#
-$ns_ at 0.0 "$node_(35) switch OFF" ;# set_X,Y,Z
-$node_(35) set X_ 0.000000
-$node_(35) set Y_ 0.000000
-$node_(35) set Z_ 0.0
-$ns_ at 0.000000 "$node_(35) setdest 1489.808529 718.927017 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(35) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(35) setdest 1290.000000 1090.000000 16.203316" ;#
-$ns_ at 0.0 "$node_(36) switch OFF" ;# set_X,Y,Z
-$node_(36) set X_ 0.000000
-$node_(36) set Y_ 0.000000
-$node_(36) set Z_ 0.0
-$ns_ at 0.000000 "$node_(36) setdest 3217.224801 688.858919 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(36) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(36) setdest 2696.443341 1131.523160 13.544152" ;#
-$ns_ at 0.0 "$node_(37) switch OFF" ;# set_X,Y,Z
-$node_(37) set X_ 0.000000
-$node_(37) set Y_ 0.000000
-$node_(37) set Z_ 0.0
-$ns_ at 0.000000 "$node_(37) setdest 3310.000000 367.414167 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(37) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(37) setdest 3310.000000 610.000000 13.469508" ;#
-$ns_ at 0.0 "$node_(38) switch OFF" ;# set_X,Y,Z
-$node_(38) set X_ 0.000000
-$node_(38) set Y_ 0.000000
-$node_(38) set Z_ 0.0
-$ns_ at 0.000000 "$node_(38) setdest 3304.673333 719.196665 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(38) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(38) setdest 3290.000000 1020.000000 13.682917" ;#
-$ns_ at 0.0 "$node_(39) switch OFF" ;# set_X,Y,Z
-$node_(39) set X_ 0.000000
-$node_(39) set Y_ 0.000000
-$node_(39) set Z_ 0.0
-$ns_ at 26.010001 "$node_(39) setdest 2560.000000 10.000000 1000000000.000000" ;# init_node
-$ns_ at 26.020001 "$node_(39) switch ON" ;# inside
-$ns_ at 26.020001 "$node_(39) setdest 2560.000000 10.000000 16.187162" ;#
-$ns_ at 0.0 "$node_(40) switch OFF" ;# set_X,Y,Z
-$node_(40) set X_ 0.000000
-$node_(40) set Y_ 0.000000
-$node_(40) set Z_ 0.0
-$ns_ at 0.000000 "$node_(40) setdest 3447.779989 57.248822 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(40) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(40) setdest 3310.000000 160.000000 13.211017" ;#
-$ns_ at 0.0 "$node_(41) switch OFF" ;# set_X,Y,Z
-$node_(41) set X_ 0.000000
-$node_(41) set Y_ 0.000000
-$node_(41) set Z_ 0.0
-$ns_ at 13.039465 "$node_(41) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 13.049465 "$node_(41) switch ON" ;# inside
-$ns_ at 13.049465 "$node_(41) setdest 3310.000000 160.000000 13.226301" ;#
-$ns_ at 0.0 "$node_(42) switch OFF" ;# set_X,Y,Z
-$node_(42) set X_ 0.000000
-$node_(42) set Y_ 0.000000
-$node_(42) set Z_ 0.0
-$ns_ at 4.811641 "$node_(42) setdest 3917.755102 10.000000 1000000000.000000" ;# init_node
-$ns_ at 4.821641 "$node_(42) switch ON" ;# inside
-$ns_ at 4.821641 "$node_(42) setdest 3930.000000 210.000000 12.370050" ;#
-$ns_ at 0.0 "$node_(43) switch OFF" ;# set_X,Y,Z
-$node_(43) set X_ 0.000000
-$node_(43) set Y_ 0.000000
-$node_(43) set Z_ 0.0
-$ns_ at 0.000000 "$node_(43) setdest 3926.546213 153.588154 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(43) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(43) setdest 3930.000000 210.000000 11.280936" ;#
-$ns_ at 0.0 "$node_(44) switch OFF" ;# set_X,Y,Z
-$node_(44) set X_ 0.000000
-$node_(44) set Y_ 0.000000
-$node_(44) set Z_ 0.0
-$ns_ at 0.000000 "$node_(44) setdest 3237.376624 671.729869 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(44) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(44) setdest 2887.075383 969.485924 13.635745" ;#
-$ns_ at 0.0 "$node_(45) switch OFF" ;# set_X,Y,Z
-$node_(45) set X_ 0.000000
-$node_(45) set Y_ 0.000000
-$node_(45) set Z_ 0.0
-$ns_ at 0.000000 "$node_(45) setdest 2447.488785 1354.434637 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(45) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(45) setdest 2140.491793 1670.877690 12.843287" ;#
-$ns_ at 0.0 "$node_(46) switch OFF" ;# set_X,Y,Z
-$node_(46) set X_ 0.000000
-$node_(46) set Y_ 0.000000
-$node_(46) set Z_ 0.0
-$ns_ at 0.000000 "$node_(46) setdest 607.861722 2915.580975 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(46) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(46) setdest 429.306931 3010.000000 16.007155" ;#
-$ns_ at 12.628238 "$node_(46) switch OFF" ;# leaving_area
-$ns_ at 0.0 "$node_(47) switch OFF" ;# set_X,Y,Z
-$node_(47) set X_ 0.000000
-$node_(47) set Y_ 0.000000
-$node_(47) set Z_ 0.0
-$ns_ at 0.000000 "$node_(47) setdest 3247.678772 560.143018 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(47) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(47) setdest 2934.323476 309.458781 13.323892" ;#
-$ns_ at 0.0 "$node_(48) switch OFF" ;# set_X,Y,Z
-$node_(48) set X_ 0.000000
-$node_(48) set Y_ 0.000000
-$node_(48) set Z_ 0.0
-$ns_ at 0.000000 "$node_(48) setdest 3230.522293 1783.588111 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(48) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(48) setdest 3432.713282 2204.819337 15.937765" ;#
-$ns_ at 0.0 "$node_(49) switch OFF" ;# set_X,Y,Z
-$node_(49) set X_ 0.000000
-$node_(49) set Y_ 0.000000
-$node_(49) set Z_ 0.0
-$ns_ at 0.000000 "$node_(49) setdest 2880.947177 974.694900 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(49) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(49) setdest 2510.000000 1290.000000 12.480033" ;#
-$ns_ at 0.0 "$node_(50) switch OFF" ;# set_X,Y,Z
-$node_(50) set X_ 0.000000
-$node_(50) set Y_ 0.000000
-$node_(50) set Z_ 0.0
-$ns_ at 0.000000 "$node_(50) setdest 3263.335772 649.664594 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(50) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(50) setdest 2799.701274 1043.753917 12.273351" ;#
-$ns_ at 0.0 "$node_(51) switch OFF" ;# set_X,Y,Z
-$node_(51) set X_ 0.000000
-$node_(51) set Y_ 0.000000
-$node_(51) set Z_ 0.0
-$ns_ at 0.000000 "$node_(51) setdest 474.355810 2204.848463 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(51) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(51) setdest 682.726230 2169.279179 15.059437" ;#
-$ns_ at 0.0 "$node_(52) switch OFF" ;# set_X,Y,Z
-$node_(52) set X_ 0.000000
-$node_(52) set Y_ 0.000000
-$node_(52) set Z_ 0.0
-$ns_ at 0.000000 "$node_(52) setdest 2947.761957 2261.550302 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(52) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(52) setdest 2789.626910 2518.979448 9.937942" ;#
-$ns_ at 0.0 "$node_(53) switch OFF" ;# set_X,Y,Z
-$node_(53) set X_ 0.000000
-$node_(53) set Y_ 0.000000
-$node_(53) set Z_ 0.0
-$ns_ at 0.000000 "$node_(53) setdest 2192.087846 1617.694066 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(53) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(53) setdest 2038.935574 1775.558716 13.528813" ;#
-$ns_ at 0.0 "$node_(54) switch OFF" ;# set_X,Y,Z
-$node_(54) set X_ 0.000000
-$node_(54) set Y_ 0.000000
-$node_(54) set Z_ 0.0
-$ns_ at 0.000000 "$node_(54) setdest 3199.841733 2703.083238 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(54) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(54) setdest 3161.675427 2757.606532 17.588560" ;#
-$ns_ at 0.0 "$node_(55) switch OFF" ;# set_X,Y,Z
-$node_(55) set X_ 0.000000
-$node_(55) set Y_ 0.000000
-$node_(55) set Z_ 0.0
-$ns_ at 0.000000 "$node_(55) setdest 3295.830375 900.477312 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(55) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(55) setdest 3290.000000 1020.000000 14.939426" ;#
-$ns_ at 0.0 "$node_(56) switch OFF" ;# set_X,Y,Z
-$node_(56) set X_ 0.000000
-$node_(56) set Y_ 0.000000
-$node_(56) set Z_ 0.0
-$ns_ at 27.328226 "$node_(56) setdest 3511.136363 10.000000 1000000000.000000" ;# init_node
-$ns_ at 27.338226 "$node_(56) switch ON" ;# inside
-$ns_ at 27.338226 "$node_(56) setdest 3310.000000 160.000000 13.430737" ;#
-$ns_ at 0.0 "$node_(57) switch OFF" ;# set_X,Y,Z
-$node_(57) set X_ 0.000000
-$node_(57) set Y_ 0.000000
-$node_(57) set Z_ 0.0
-$ns_ at 34.730846 "$node_(57) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 34.740846 "$node_(57) switch ON" ;# inside
-$ns_ at 34.740846 "$node_(57) setdest 3479.270091 33.764678 13.210281" ;#
-$ns_ at 0.0 "$node_(58) switch OFF" ;# set_X,Y,Z
-$node_(58) set X_ 0.000000
-$node_(58) set Y_ 0.000000
-$node_(58) set Z_ 0.0
-$ns_ at 0.000000 "$node_(58) setdest 2502.168218 1298.072760 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(58) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(58) setdest 2113.048299 1699.165600 11.361111" ;#
-$ns_ at 0.0 "$node_(59) switch OFF" ;# set_X,Y,Z
-$node_(59) set X_ 0.000000
-$node_(59) set Y_ 0.000000
-$node_(59) set Z_ 0.0
-$ns_ at 0.000000 "$node_(59) setdest 3180.760450 2438.422024 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(59) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(59) setdest 3230.000000 2660.000000 12.603172" ;#
-$ns_ at 0.0 "$node_(60) switch OFF" ;# set_X,Y,Z
-$node_(60) set X_ 0.000000
-$node_(60) set Y_ 0.000000
-$node_(60) set Z_ 0.0
-$ns_ at 0.000000 "$node_(60) setdest 3298.426977 847.246971 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(60) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(60) setdest 3290.000000 1020.000000 13.294269" ;#
-$ns_ at 0.0 "$node_(61) switch OFF" ;# set_X,Y,Z
-$node_(61) set X_ 0.000000
-$node_(61) set Y_ 0.000000
-$node_(61) set Z_ 0.0
-$ns_ at 0.000000 "$node_(61) setdest 1517.875494 666.802655 1000000000.000000" ;# init_node
-$ns_ at 0.010000 "$node_(61) switch ON" ;# inside
-$ns_ at 0.010000 "$node_(61) setdest 1417.806992 852.644157 17.696076" ;#
-$ns_ at 0.076824 "$node_(3) setdest 1860.000000 1960.000000 15.198613" ;#
-$ns_ at 1.919728 "$node_(29) setdest 3310.000000 160.000000 14.971426" ;#
-$ns_ at 2.020000 "$node_(34) setdest 3060.137172 110.027434 18.021479" ;#
-$ns_ at 2.020000 "$node_(19) setdest 2050.787795 1763.341811 11.429272" ;#
-$ns_ at 2.223263 "$node_(11) setdest 1178.615011 2083.495632 17.638126" ;#
-$ns_ at 2.649143 "$node_(5) setdest 655.054915 2890.625411 16.620014" ;#
-$ns_ at 2.815319 "$node_(18) setdest 1272.682302 2066.446680 18.024390" ;#
-$ns_ at 2.921721 "$node_(10) setdest 2125.369766 1686.465011 15.334893" ;#
-$ns_ at 3.020000 "$node_(23) setdest 1727.986402 1748.778243 13.637945" ;#
-$ns_ at 3.791364 "$node_(30) setdest 400.780217 1287.070330 16.187840" ;#
-$ns_ at 3.793946 "$node_(54) setdest 3090.000000 2860.000000 15.194059" ;#
-$ns_ at 4.871944 "$node_(12) setdest 3090.000000 2030.000000 15.265594" ;#
-$ns_ at 4.946232 "$node_(15) setdest 1565.819542 1495.109953 13.868165" ;#
-$ns_ at 5.609381 "$node_(21) setdest 3310.000000 520.726879 13.636364" ;#
-$ns_ at 6.020000 "$node_(2) setdest 986.595609 2117.408013 17.746939" ;#
-$ns_ at 6.020000 "$node_(43) setdest 4184.308366 276.189849 10.624238" ;#
-$ns_ at 0.0 "$node_(62) switch OFF" ;# set_X,Y,Z
-$node_(62) set X_ 0.000000
-$node_(62) set Y_ 0.000000
-$node_(62) set Z_ 0.0
-$ns_ at 21.663345 "$node_(62) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 21.673345 "$node_(62) switch ON" ;# inside
-$ns_ at 21.673345 "$node_(62) setdest 1624.591870 468.615099 15.962482" ;#
-$ns_ at 7.020000 "$node_(0) setdest 1144.244259 860.324287 13.686075" ;#
-$ns_ at 8.020000 "$node_(1) setdest 3310.000000 406.390980 13.636364" ;#
-$ns_ at 9.020000 "$node_(14) setdest 2560.000000 10.000000 12.232374" ;#
-$ns_ at 9.020000 "$node_(14) switch OFF" ;# leaving_area
-$ns_ at 9.020000 "$node_(55) setdest 3267.610092 1133.069036 15.548435" ;#
-#$ns_ at 10.020000 $node_(32) arrived_to 1860.000000 1960.000000
-$ns_ at 10.020000 "$node_(32) switch OFF" ;# arrived_to
-$ns_ at 11.020000 "$node_(32) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 11.030000 "$node_(32) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 10.844793 "$node_(2) setdest 245.000000 2244.000000 16.292787" ;#
-$ns_ at 10.878517 "$node_(17) setdest 1944.619748 1872.776568 14.878609" ;#
-$ns_ at 11.937533 "$node_(61) setdest 1290.000000 1090.000000 15.780990" ;#
-$ns_ at 12.020000 "$node_(26) setdest 3184.747598 1551.524630 13.728154" ;#
-$ns_ at 12.511178 "$node_(18) setdest 1071.000000 2103.000000 15.172901" ;#
-$ns_ at 13.020000 "$node_(54) setdest 2856.135371 2789.296740 15.963316" ;#
-$ns_ at 13.020000 "$node_(12) setdest 2660.000000 2730.000000 10.953640" ;#
-$ns_ at 13.020000 "$node_(29) setdest 3310.000000 610.000000 13.636364" ;#
-$ns_ at 13.186257 "$node_(21) setdest 3310.000000 610.000000 15.302890" ;#
-$ns_ at 0.0 "$node_(63) switch OFF" ;# set_X,Y,Z
-$node_(63) set X_ 0.000000
-$node_(63) set Y_ 0.000000
-$node_(63) set Z_ 0.0
-$ns_ at 40.472571 "$node_(63) setdest 3511.136363 10.000000 1000000000.000000" ;# init_node
-$ns_ at 40.482571 "$node_(63) switch ON" ;# inside
-$ns_ at 40.482571 "$node_(63) setdest 3506.004290 13.827309 13.160959" ;#
-$ns_ at 14.020000 "$node_(60) setdest 3095.122467 1284.368746 15.380819" ;#
-$ns_ at 14.020000 "$node_(40) setdest 3310.000000 610.000000 13.636364" ;#
-$ns_ at 14.046680 "$node_(51) setdest 809.510897 2147.636760 17.612676" ;#
-$ns_ at 14.114380 "$node_(11) setdest 1071.000000 2103.000000 18.519346" ;#
-$ns_ at 15.020000 "$node_(7) setdest 3806.412901 2014.477749 13.272601" ;#
-$ns_ at 0.0 "$node_(64) switch OFF" ;# set_X,Y,Z
-$node_(64) set X_ 0.000000
-$node_(64) set Y_ 0.000000
-$node_(64) set Z_ 0.0
-$ns_ at 19.911426 "$node_(64) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 19.921426 "$node_(64) switch ON" ;# inside
-$ns_ at 19.921426 "$node_(64) setdest 3362.802632 120.621766 13.629680" ;#
-$ns_ at 16.020000 "$node_(27) setdest 2490.547415 1792.044791 15.855134" ;#
-$ns_ at 0.0 "$node_(65) switch OFF" ;# set_X,Y,Z
-$node_(65) set X_ 0.000000
-$node_(65) set Y_ 0.000000
-$node_(65) set Z_ 0.0
-$ns_ at 51.600909 "$node_(65) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 51.610909 "$node_(65) switch ON" ;# inside
-$ns_ at 51.610909 "$node_(65) setdest 3310.000000 160.000000 13.629680" ;#
-$ns_ at 16.111438 "$node_(22) setdest 2371.972360 1884.470213 14.981457" ;#
-$ns_ at 16.159300 "$node_(34) setdest 2560.000000 10.000000 16.008495" ;#
-$ns_ at 16.267702 "$node_(53) setdest 1860.000000 1960.000000 15.339731" ;#
-$ns_ at 16.433257 "$node_(55) setdest 3195.265097 1498.411260 13.248144" ;#
-$ns_ at 18.020000 "$node_(4) setdest 675.293496 2170.547962 18.050376" ;#
-$ns_ at 18.346962 "$node_(8) setdest 3850.356305 1052.895768 15.199614" ;#
-$ns_ at 18.441338 "$node_(10) setdest 1860.000000 1960.000000 12.884523" ;#
-$ns_ at 19.020000 "$node_(37) setdest 3019.273244 857.117743 12.352381" ;#
-$ns_ at 19.020000 "$node_(59) setdest 3090.000000 2860.000000 16.275407" ;#
-$ns_ at 19.343302 "$node_(23) setdest 1822.317642 1899.708228 13.873502" ;#
-$ns_ at 19.761014 "$node_(15) setdest 1610.000000 1560.000000 14.927294" ;#
-$ns_ at 20.020000 "$node_(21) setdest 3044.211745 835.920017 10.938056" ;#
-$ns_ at 0.0 "$node_(66) switch OFF" ;# set_X,Y,Z
-$node_(66) set X_ 0.000000
-$node_(66) set Y_ 0.000000
-$node_(66) set Z_ 0.0
-$ns_ at 35.071538 "$node_(66) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 35.081538 "$node_(66) switch ON" ;# inside
-$ns_ at 35.081538 "$node_(66) setdest 1740.185930 253.940416 16.589689" ;#
-$ns_ at 0.0 "$node_(67) switch OFF" ;# set_X,Y,Z
-$node_(67) set X_ 0.000000
-$node_(67) set Y_ 0.000000
-$node_(67) set Z_ 0.0
-$ns_ at 55.600909 "$node_(67) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 55.610909 "$node_(67) switch ON" ;# inside
-$ns_ at 55.610909 "$node_(67) setdest 3310.000000 160.000000 13.629680" ;#
-$ns_ at 21.020000 "$node_(11) setdest 622.932312 2179.486131 16.430355" ;#
-$ns_ at 21.349295 "$node_(51) setdest 1071.000000 2103.000000 15.912438" ;#
-#$ns_ at 22.020000 $node_(42) arrived_to 3930.000000 210.000000
-$ns_ at 22.020000 "$node_(42) switch OFF" ;# arrived_to
-$ns_ at 23.020000 "$node_(42) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 23.030000 "$node_(42) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 23.020000 $node_(25) arrived_to 1980.000000 2190.000000
-$ns_ at 23.020000 "$node_(25) switch OFF" ;# arrived_to
-$ns_ at 24.020000 "$node_(25) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 24.030000 "$node_(25) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 23.020000 "$node_(38) setdest 3213.718018 1405.224010 13.728154" ;#
-$ns_ at 23.702829 "$node_(17) setdest 1860.000000 1960.000000 14.611387" ;#
-$ns_ at 24.020000 "$node_(24) setdest 3886.630839 668.990288 13.427965" ;#
-$ns_ at 24.744488 "$node_(13) setdest 1485.096514 727.677903 19.049920" ;#
-$ns_ at 25.283097 "$node_(28) setdest 1999.747898 1815.952167 13.727766" ;#
-$ns_ at 26.020000 "$node_(15) setdest 1319.230885 1852.926956 15.221304" ;#
-#$ns_ at 26.020000 $node_(9) arrived_to 2560.000000 10.000000
-$ns_ at 26.020000 "$node_(9) switch OFF" ;# arrived_to
-$ns_ at 27.020000 "$node_(9) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 27.030000 "$node_(9) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 26.088671 "$node_(1) setdest 3310.000000 501.409247 15.578368" ;#
-$ns_ at 26.895783 "$node_(0) setdest 1111.751489 809.123558 14.424040" ;#
-$ns_ at 27.020000 "$node_(18) setdest 793.293376 2150.405126 16.430355" ;#
-$ns_ at 27.020000 "$node_(35) setdest 1218.992860 978.109962 14.247854" ;#
-$ns_ at 27.020000 "$node_(39) setdest 2945.432575 318.346060 13.527727" ;#
-$ns_ at 0.0 "$node_(68) switch OFF" ;# set_X,Y,Z
-$node_(68) set X_ 0.000000
-$node_(68) set Y_ 0.000000
-$node_(68) set Z_ 0.0
-$ns_ at 43.372761 "$node_(68) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 43.382761 "$node_(68) switch ON" ;# inside
-$ns_ at 43.382761 "$node_(68) setdest 1626.443588 465.176194 16.264410" ;#
-#$ns_ at 28.020000 $node_(3) arrived_to 1860.000000 1960.000000
-$ns_ at 28.020000 "$node_(3) switch OFF" ;# arrived_to
-$ns_ at 29.020000 "$node_(3) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 29.030000 "$node_(3) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 28.325008 "$node_(54) setdest 2728.508244 2750.711795 16.637835" ;#
-$ns_ at 0.0 "$node_(69) switch OFF" ;# set_X,Y,Z
-$node_(69) set X_ 0.000000
-$node_(69) set Y_ 0.000000
-$node_(69) set Z_ 0.0
-$ns_ at 29.354827 "$node_(69) setdest 3511.136363 10.000000 1000000000.000000" ;# init_node
-$ns_ at 29.364827 "$node_(69) switch ON" ;# inside
-$ns_ at 29.364827 "$node_(69) setdest 3310.000000 160.000000 12.765598" ;#
-$ns_ at 29.326782 "$node_(48) setdest 3579.061480 2509.711417 18.214880" ;#
-$ns_ at 30.020000 "$node_(61) setdest 1344.994298 1170.772875 16.003152" ;#
-$ns_ at 30.128119 "$node_(47) setdest 2671.108075 98.886460 13.527727" ;#
-$ns_ at 30.410654 "$node_(52) setdest 2760.657979 2566.138174 11.977805" ;#
-$ns_ at 30.754101 "$node_(43) setdest 4346.804596 318.483388 11.379102" ;#
-$ns_ at 31.099924 "$node_(0) setdest 960.000000 570.000000 13.537772" ;#
-$ns_ at 32.172350 "$node_(23) setdest 1860.000000 1960.000000 14.666683" ;#
-$ns_ at 32.188044 "$node_(1) setdest 3310.000000 610.000000 12.295211" ;#
-$ns_ at 33.020000 "$node_(41) setdest 3310.000000 368.122809 14.621366" ;#
-#$ns_ at 33.020000 $node_(17) arrived_to 1860.000000 1960.000000
-$ns_ at 33.020000 "$node_(17) switch OFF" ;# arrived_to
-$ns_ at 34.020000 "$node_(17) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 34.030000 "$node_(17) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 33.497734 "$node_(64) setdest 3310.000000 160.000000 14.565550" ;#
-$ns_ at 33.726495 "$node_(44) setdest 2825.150685 1022.121917 15.219462" ;#
-$ns_ at 34.020000 "$node_(53) setdest 1444.491111 2035.307695 15.517953" ;#
-$ns_ at 34.338383 "$node_(45) setdest 1983.675546 1832.519053 13.147719" ;#
-$ns_ at 34.733511 "$node_(13) setdest 1290.000000 1090.000000 15.654787" ;#
-#$ns_ at 35.020000 $node_(59) arrived_to 3090.000000 2860.000000
-$ns_ at 35.020000 "$node_(59) switch OFF" ;# arrived_to
-$ns_ at 36.020000 "$node_(59) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 36.030000 "$node_(59) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 35.031340 "$node_(52) setdest 2660.000000 2730.000000 10.690566" ;#
-$ns_ at 35.373400 "$node_(60) setdest 3032.000000 1370.000000 18.840035" ;#
-$ns_ at 36.126115 "$node_(61) setdest 1610.000000 1560.000000 13.494562" ;#
-$ns_ at 36.321008 "$node_(35) setdest 960.000000 570.000000 13.170769" ;#
-$ns_ at 36.338804 "$node_(54) setdest 2660.000000 2730.000000 19.442226" ;#
-$ns_ at 37.750015 "$node_(57) setdest 3310.000000 160.000000 14.797356" ;#
-$ns_ at 38.020000 "$node_(23) setdest 1980.000000 2190.000000 13.653812" ;#
-$ns_ at 38.037306 "$node_(20) setdest 3802.957225 2529.373416 12.845117" ;#
-$ns_ at 38.173057 "$node_(8) setdest 3810.000000 1480.000000 13.060778" ;#
-$ns_ at 39.020000 "$node_(51) setdest 1437.648683 2036.547831 15.741164" ;#
-$ns_ at 39.020000 "$node_(64) setdest 3310.000000 610.000000 13.636364" ;#
-$ns_ at 39.066531 "$node_(44) setdest 2510.000000 1290.000000 13.362525" ;#
-$ns_ at 0.0 "$node_(70) switch OFF" ;# set_X,Y,Z
-$node_(70) set X_ 0.000000
-$node_(70) set Y_ 0.000000
-$node_(70) set Z_ 0.0
-$ns_ at 40.803139 "$node_(70) setdest 3917.755102 10.000000 1000000000.000000" ;# init_node
-$ns_ at 40.813139 "$node_(70) switch ON" ;# inside
-$ns_ at 40.813139 "$node_(70) setdest 3921.897255 77.655162 14.231013" ;#
-$ns_ at 39.756789 "$node_(22) setdest 2132.313140 2071.276830 18.619493" ;#
-$ns_ at 40.020000 "$node_(49) setdest 2411.643775 1391.382570 14.280623" ;#
-$ns_ at 40.259448 "$node_(4) setdest 245.000000 2244.000000 15.177654" ;#
-$ns_ at 40.969015 "$node_(63) setdest 3379.387957 108.253049 14.844106" ;#
-#$ns_ at 41.020000 $node_(54) arrived_to 2660.000000 2730.000000
-$ns_ at 41.020000 "$node_(54) switch OFF" ;# arrived_to
-$ns_ at 42.020000 "$node_(54) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 42.030000 "$node_(54) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 42.020000 "$node_(60) setdest 2648.452720 1668.962709 15.306413" ;#
-$ns_ at 42.020000 "$node_(1) setdest 2860.842523 991.783855 12.842619" ;#
-$ns_ at 44.020000 "$node_(6) setdest 2560.000000 10.000000 15.881382" ;#
-$ns_ at 44.020000 "$node_(6) switch OFF" ;# leaving_area
-$ns_ at 44.038988 "$node_(28) setdest 1860.000000 1960.000000 15.460807" ;#
-$ns_ at 44.166535 "$node_(18) setdest 705.713161 2165.355259 18.400794" ;#
-$ns_ at 44.199506 "$node_(5) setdest 443.743582 3002.365959 18.819432" ;#
-$ns_ at 44.545590 "$node_(55) setdest 3177.366049 1588.801450 14.749930" ;#
-$ns_ at 0.0 "$node_(71) switch OFF" ;# set_X,Y,Z
-$node_(71) set X_ 0.000000
-$node_(71) set Y_ 0.000000
-$node_(71) set Z_ 0.0
-$ns_ at 48.463739 "$node_(71) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 48.473739 "$node_(71) switch ON" ;# inside
-$ns_ at 48.473739 "$node_(71) setdest 1290.000000 1090.000000 16.454428" ;#
-$ns_ at 44.815166 "$node_(30) setdest 10.000000 1373.675675 19.086673" ;#
-$ns_ at 65.785924 "$node_(30) switch OFF" ;# leaving_area
-$ns_ at 45.020000 "$node_(33) setdest 3310.000000 435.072554 13.066308" ;#
-$ns_ at 45.510099 "$node_(43) setdest 4660.000000 400.000000 10.270737" ;#
-$ns_ at 45.576106 "$node_(70) setdest 3930.000000 210.000000 12.695710" ;#
-$ns_ at 0.0 "$node_(72) switch OFF" ;# set_X,Y,Z
-$node_(72) set X_ 0.000000
-$node_(72) set Y_ 0.000000
-$node_(72) set Z_ 0.0
-$ns_ at 67.289997 "$node_(72) setdest 10.000000 518.541114 1000000000.000000" ;# init_node
-$ns_ at 67.299997 "$node_(72) switch ON" ;# inside
-$ns_ at 67.299997 "$node_(72) setdest 100.000000 470.000000 13.245560" ;#
-$ns_ at 47.020000 "$node_(56) setdest 3100.811579 118.162316 14.610681" ;#
-$ns_ at 47.020000 "$node_(29) setdest 2871.215338 982.966963 12.885176" ;#
-$ns_ at 47.254156 "$node_(41) setdest 3310.000000 610.000000 12.889226" ;#
-$ns_ at 0.0 "$node_(73) switch OFF" ;# set_X,Y,Z
-$node_(73) set X_ 0.000000
-$node_(73) set Y_ 0.000000
-$node_(73) set Z_ 0.0
-$ns_ at 50.986223 "$node_(73) setdest 3511.136363 10.000000 1000000000.000000" ;# init_node
-$ns_ at 50.996223 "$node_(73) switch ON" ;# inside
-$ns_ at 50.996223 "$node_(73) setdest 3310.000000 160.000000 14.738798" ;#
-$ns_ at 47.893835 "$node_(48) setdest 3800.000000 2970.000000 15.892582" ;#
-#$ns_ at 48.020000 $node_(40) arrived_to 3310.000000 610.000000
-$ns_ at 48.020000 "$node_(40) switch OFF" ;# arrived_to
-$ns_ at 49.020000 "$node_(40) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 49.030000 "$node_(40) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 48.685196 "$node_(11) setdest 494.489546 2201.411591 17.638474" ;#
-$ns_ at 48.994971 "$node_(18) setdest 245.000000 2244.000000 16.102564" ;#
-$ns_ at 49.020000 "$node_(34) setdest 2560.000000 10.000000 14.572952" ;#
-$ns_ at 49.020000 "$node_(34) switch OFF" ;# leaving_area
-$ns_ at 49.020000 "$node_(10) setdest 1071.000000 2103.000000 16.364369" ;#
-$ns_ at 49.197879 "$node_(58) setdest 1860.000000 1960.000000 14.073656" ;#
-$ns_ at 49.588366 "$node_(50) setdest 2510.000000 1290.000000 13.372981" ;#
-$ns_ at 49.673763 "$node_(31) setdest 3810.000000 1480.000000 14.690782" ;#
-$ns_ at 49.909718 "$node_(37) setdest 2895.038572 962.717214 13.832905" ;#
-$ns_ at 49.911226 "$node_(49) setdest 2167.765222 1642.765079 12.740107" ;#
-$ns_ at 50.020000 "$node_(69) setdest 3310.000000 394.728837 13.636364" ;#
-$ns_ at 50.474190 "$node_(36) setdest 2510.000000 1290.000000 14.788989" ;#
-$ns_ at 50.792760 "$node_(55) setdest 3090.000000 2030.000000 13.536046" ;#
-$ns_ at 0.0 "$node_(74) switch OFF" ;# set_X,Y,Z
-$node_(74) set X_ 0.000000
-$node_(74) set Y_ 0.000000
-$node_(74) set Z_ 0.0
-$ns_ at 66.494791 "$node_(74) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 66.504791 "$node_(74) switch ON" ;# inside
-$ns_ at 66.504791 "$node_(74) setdest 1473.664290 748.909176 16.136236" ;#
-$ns_ at 51.467543 "$node_(45) setdest 1860.000000 1960.000000 14.149804" ;#
-$ns_ at 51.489652 "$node_(26) setdest 3150.170530 1726.138824 15.687276" ;#
-$ns_ at 51.609540 "$node_(63) setdest 3310.000000 160.000000 15.998430" ;#
-$ns_ at 51.625744 "$node_(38) setdest 3166.147644 1645.454398 15.199707" ;#
-$ns_ at 51.782074 "$node_(66) setdest 1604.241888 506.407922 17.882141" ;#
-$ns_ at 51.911523 "$node_(21) setdest 2745.055228 1090.203056 11.797218" ;#
-#$ns_ at 53.020000 $node_(0) arrived_to 960.000000 570.000000
-$ns_ at 53.020000 "$node_(0) switch OFF" ;# arrived_to
-$ns_ at 54.020000 "$node_(0) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 54.030000 "$node_(0) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 53.020000 "$node_(57) setdest 3310.000000 610.000000 13.636364" ;#
-$ns_ at 53.135829 "$node_(15) setdest 1071.000000 2103.000000 18.658828" ;#
-$ns_ at 54.020000 "$node_(52) setdest 2682.314562 2813.307698 18.212495" ;#
-$ns_ at 54.304528 "$node_(62) setdest 1421.963170 844.925541 16.589689" ;#
-$ns_ at 0.0 "$node_(75) switch OFF" ;# set_X,Y,Z
-$node_(75) set X_ 0.000000
-$node_(75) set Y_ 0.000000
-$node_(75) set Z_ 0.0
-$ns_ at 89.010001 "$node_(75) setdest 2560.000000 10.000000 1000000000.000000" ;# init_node
-$ns_ at 89.020001 "$node_(75) switch ON" ;# inside
-$ns_ at 89.020001 "$node_(75) setdest 2560.000000 10.000000 16.187162" ;#
-$ns_ at 55.045844 "$node_(47) setdest 2560.000000 10.000000 14.265643" ;#
-$ns_ at 55.290162 "$node_(7) setdest 3803.495480 2449.173521 12.392857" ;#
-$ns_ at 56.072495 "$node_(11) setdest 245.000000 2244.000000 15.870723" ;#
-$ns_ at 56.076454 "$node_(22) setdest 1980.000000 2190.000000 17.646729" ;#
-$ns_ at 56.901083 "$node_(5) setdest 429.306930 3010.000000 15.589882" ;#
-$ns_ at 57.948609 "$node_(5) switch OFF" ;# leaving_area
-#$ns_ at 57.020000 $node_(70) arrived_to 3930.000000 210.000000
-$ns_ at 57.020000 "$node_(70) switch OFF" ;# arrived_to
-$ns_ at 58.020000 "$node_(70) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 58.030000 "$node_(70) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 0.0 "$node_(76) switch OFF" ;# set_X,Y,Z
-$node_(76) set X_ 0.000000
-$node_(76) set Y_ 0.000000
-$node_(76) set Z_ 0.0
-$ns_ at 68.010000 "$node_(76) setdest 3838.461538 3010.000000 1000000000.000000" ;# init_node
-$ns_ at 68.020000 "$node_(76) switch ON" ;# inside
-$ns_ at 68.020000 "$node_(76) setdest 3800.000000 2970.000000 27.745675" ;#
-$ns_ at 58.020000 "$node_(2) setdest 10.000000 2297.153203 27.467800" ;#
-$ns_ at 66.791588 "$node_(2) switch OFF" ;# leaving_area
-$ns_ at 58.020000 "$node_(28) setdest 1749.929190 1783.886705 13.873502" ;#
-$ns_ at 58.020000 "$node_(63) setdest 3310.000000 327.330076 13.636364" ;#
-$ns_ at 58.020000 "$node_(23) setdest 2196.975090 2362.303748 14.877269" ;#
-$ns_ at 58.353920 "$node_(24) setdest 3860.098415 949.791769 15.165341" ;#
-$ns_ at 58.755457 "$node_(52) setdest 2735.000000 3010.000000 16.082464" ;#
-$ns_ at 71.416836 "$node_(52) switch OFF" ;# leaving_area
-$ns_ at 0.0 "$node_(77) switch OFF" ;# set_X,Y,Z
-$node_(77) set X_ 0.000000
-$node_(77) set Y_ 0.000000
-$node_(77) set Z_ 0.0
-$ns_ at 86.590419 "$node_(77) setdest 3511.136363 10.000000 1000000000.000000" ;# init_node
-$ns_ at 86.600419 "$node_(77) switch ON" ;# inside
-$ns_ at 86.600419 "$node_(77) setdest 3507.896464 12.416196 13.163994" ;#
-$ns_ at 59.318759 "$node_(27) setdest 2209.659280 2010.988013 16.467044" ;#
-$ns_ at 59.721921 "$node_(19) setdest 1860.000000 1960.000000 13.498673" ;#
-$ns_ at 0.0 "$node_(78) switch OFF" ;# set_X,Y,Z
-$node_(78) set X_ 0.000000
-$node_(78) set Y_ 0.000000
-$node_(78) set Z_ 0.0
-$ns_ at 76.468432 "$node_(78) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 76.478432 "$node_(78) switch ON" ;# inside
-$ns_ at 76.478432 "$node_(78) setdest 3310.000000 160.000000 12.839810" ;#
-$ns_ at 61.232236 "$node_(53) setdest 1071.000000 2103.000000 17.421516" ;#
-$ns_ at 61.621042 "$node_(56) setdest 2898.815759 77.763152 19.400182" ;#
-$ns_ at 61.696874 "$node_(37) setdest 2510.000000 1290.000000 11.940049" ;#
-$ns_ at 62.020000 "$node_(13) setdest 1100.568594 791.502028 13.103689" ;#
-$ns_ at 62.691820 "$node_(51) setdest 1742.898093 1981.223793 16.364369" ;#
-$ns_ at 62.836730 "$node_(26) setdest 3090.000000 2030.000000 12.808911" ;#
-$ns_ at 63.507618 "$node_(39) setdest 3058.719372 408.975498 14.499594" ;#
-$ns_ at 0.0 "$node_(79) switch OFF" ;# set_X,Y,Z
-$node_(79) set X_ 0.000000
-$node_(79) set Y_ 0.000000
-$node_(79) set Z_ 0.0
-$ns_ at 73.701755 "$node_(79) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 73.711755 "$node_(79) switch ON" ;# inside
-$ns_ at 73.711755 "$node_(79) setdest 3310.000000 160.000000 12.355081" ;#
-$ns_ at 65.020000 "$node_(45) setdest 1433.581205 2037.285029 16.364369" ;#
-#$ns_ at 66.020000 $node_(47) arrived_to 2560.000000 10.000000
-$ns_ at 66.020000 "$node_(47) switch OFF" ;# arrived_to
-$ns_ at 67.020000 "$node_(47) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 67.030000 "$node_(47) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 66.072048 "$node_(33) setdest 3310.000000 610.000000 14.640790" ;#
-$ns_ at 67.020000 "$node_(41) setdest 3303.787354 737.359240 13.682917" ;#
-$ns_ at 67.233448 "$node_(69) setdest 3310.000000 480.277751 15.860949" ;#
-$ns_ at 67.737571 "$node_(38) setdest 3090.000000 2030.000000 12.945212" ;#
-$ns_ at 67.817144 "$node_(66) setdest 1290.000000 1090.000000 16.086701" ;#
-#$ns_ at 68.020000 $node_(22) arrived_to 1980.000000 2190.000000
-$ns_ at 68.020000 "$node_(22) switch OFF" ;# arrived_to
-$ns_ at 69.020000 "$node_(22) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 69.030000 "$node_(22) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 68.020000 "$node_(36) setdest 2035.070740 1779.542468 13.169265" ;#
-$ns_ at 69.020000 "$node_(73) setdest 3310.000000 610.000000 13.636364" ;#
-$ns_ at 70.020000 "$node_(4) setdest 10.000000 2297.153203 26.882564" ;#
-$ns_ at 78.982546 "$node_(4) switch OFF" ;# leaving_area
-$ns_ at 70.290872 "$node_(63) setdest 3310.000000 393.291200 15.612614" ;#
-$ns_ at 71.020000 "$node_(76) setdest 3682.872943 2725.985298 18.171700" ;#
-$ns_ at 71.020000 "$node_(65) setdest 3310.000000 249.628909 15.700741" ;#
-$ns_ at 71.020000 "$node_(44) setdest 1860.000000 1960.000000 13.727766" ;#
-$ns_ at 0.0 "$node_(80) switch OFF" ;# set_X,Y,Z
-$node_(80) set X_ 0.000000
-$node_(80) set Y_ 0.000000
-$node_(80) set Z_ 0.0
-$ns_ at 77.881324 "$node_(80) setdest 3511.136363 10.000000 1000000000.000000" ;# init_node
-$ns_ at 77.891324 "$node_(80) switch ON" ;# inside
-$ns_ at 77.891324 "$node_(80) setdest 3310.000000 160.000000 14.648535" ;#
-$ns_ at 72.020000 "$node_(8) setdest 3803.933314 2383.936182 12.715764" ;#
-$ns_ at 72.020000 "$node_(61) setdest 1071.000000 2103.000000 16.632495" ;#
-$ns_ at 72.239299 "$node_(56) setdest 2849.588535 67.917707 19.419495" ;#
-$ns_ at 72.348937 "$node_(20) setdest 3800.000000 2970.000000 14.850716" ;#
-$ns_ at 72.627130 "$node_(69) setdest 3310.000000 610.000000 12.481851" ;#
-$ns_ at 72.989630 "$node_(28) setdest 1710.732502 1721.172003 15.240545" ;#
-#$ns_ at 73.020000 $node_(15) arrived_to 1071.000000 2103.000000
-$ns_ at 73.020000 "$node_(15) switch OFF" ;# arrived_to
-$ns_ at 74.020000 "$node_(15) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 74.030000 "$node_(15) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 73.020000 "$node_(11) setdest 10.000000 2297.153203 32.054187" ;#
-$ns_ at 80.536529 "$node_(11) switch OFF" ;# leaving_area
-#$ns_ at 73.020000 $node_(64) arrived_to 3310.000000 610.000000
-$ns_ at 73.020000 "$node_(64) switch OFF" ;# arrived_to
-$ns_ at 74.020000 "$node_(64) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 74.030000 "$node_(64) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 73.513269 "$node_(39) setdest 3310.000000 610.000000 13.130931" ;#
-$ns_ at 73.790965 "$node_(60) setdest 2288.942757 1949.189106 16.467044" ;#
-#$ns_ at 74.020000 $node_(35) arrived_to 960.000000 570.000000
-$ns_ at 74.020000 "$node_(35) switch OFF" ;# arrived_to
-$ns_ at 75.020000 "$node_(35) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 75.030000 "$node_(35) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 74.515733 "$node_(63) setdest 3310.000000 610.000000 13.130472" ;#
-$ns_ at 74.824440 "$node_(56) setdest 2560.000000 10.000000 16.230527" ;#
-$ns_ at 75.020000 "$node_(67) setdest 3310.000000 377.483559 13.636364" ;#
-$ns_ at 75.168046 "$node_(68) setdest 1429.744513 830.474476 16.589689" ;#
-#$ns_ at 76.020000 $node_(72) arrived_to 100.000000 470.000000
-$ns_ at 76.020000 "$node_(72) switch OFF" ;# arrived_to
-$ns_ at 77.020000 "$node_(72) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 77.030000 "$node_(72) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 76.020000 "$node_(58) setdest 1071.000000 2103.000000 16.364369" ;#
-$ns_ at 76.338969 "$node_(41) setdest 3298.983616 835.835864 15.904662" ;#
-$ns_ at 76.643591 "$node_(23) setdest 2382.278109 2509.456145 18.587284" ;#
-$ns_ at 76.728578 "$node_(65) setdest 3310.000000 610.000000 13.204556" ;#
-$ns_ at 76.952393 "$node_(24) setdest 3810.000000 1480.000000 12.659855" ;#
-$ns_ at 77.204488 "$node_(16) setdest 3800.000000 2970.000000 13.831303" ;#
-$ns_ at 77.402569 "$node_(49) setdest 2082.185204 1730.978328 15.039381" ;#
-$ns_ at 77.842223 "$node_(28) setdest 1610.000000 1560.000000 13.405608" ;#
-#$ns_ at 78.020000 $node_(43) arrived_to 4660.000000 400.000000
-$ns_ at 78.020000 "$node_(43) switch OFF" ;# arrived_to
-$ns_ at 79.020000 "$node_(43) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 79.030000 "$node_(43) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 79.020000 $node_(18) arrived_to 245.000000 2244.000000
-$ns_ at 79.020000 "$node_(18) switch OFF" ;# arrived_to
-$ns_ at 80.020000 "$node_(18) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 80.030000 "$node_(18) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 79.020000 "$node_(50) setdest 2232.243741 1576.302606 12.801209" ;#
-$ns_ at 79.020000 "$node_(33) setdest 3068.928899 814.910436 12.447342" ;#
-$ns_ at 80.067320 "$node_(62) setdest 1290.000000 1090.000000 18.615038" ;#
-$ns_ at 0.0 "$node_(81) switch OFF" ;# set_X,Y,Z
-$node_(81) set X_ 0.000000
-$node_(81) set Y_ 0.000000
-$node_(81) set Z_ 0.0
-$ns_ at 81.503502 "$node_(81) setdest 3917.755102 10.000000 1000000000.000000" ;# init_node
-$ns_ at 81.513502 "$node_(81) switch ON" ;# inside
-$ns_ at 81.513502 "$node_(81) setdest 3930.000000 210.000000 12.921969" ;#
-$ns_ at 80.946077 "$node_(27) setdest 1980.000000 2190.000000 18.115363" ;#
-$ns_ at 81.020000 "$node_(48) setdest 3838.461538 3010.000000 29.888688" ;#
-$ns_ at 82.876600 "$node_(48) switch OFF" ;# leaving_area
-$ns_ at 81.020000 "$node_(19) setdest 1071.000000 2103.000000 16.364369" ;#
-$ns_ at 81.649008 "$node_(51) setdest 1860.000000 1960.000000 18.679926" ;#
-$ns_ at 82.538014 "$node_(41) setdest 3290.000000 1020.000000 12.731894" ;#
-#$ns_ at 83.020000 $node_(31) arrived_to 3810.000000 1480.000000
-$ns_ at 83.020000 "$node_(31) switch OFF" ;# arrived_to
-$ns_ at 84.020000 "$node_(31) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 84.030000 "$node_(31) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 0.0 "$node_(82) switch OFF" ;# set_X,Y,Z
-$node_(82) set X_ 0.000000
-$node_(82) set Y_ 0.000000
-$node_(82) set Z_ 0.0
-$ns_ at 89.213655 "$node_(82) setdest 3511.136363 10.000000 1000000000.000000" ;# init_node
-$ns_ at 89.223655 "$node_(82) switch ON" ;# inside
-$ns_ at 89.223655 "$node_(82) setdest 3310.000000 160.000000 13.348873" ;#
-$ns_ at 0.0 "$node_(83) switch OFF" ;# set_X,Y,Z
-$node_(83) set X_ 0.000000
-$node_(83) set Y_ 0.000000
-$node_(83) set Z_ 0.0
-$ns_ at 111.088639 "$node_(83) setdest 565.476191 10.000000 1000000000.000000" ;# init_node
-$ns_ at 111.098639 "$node_(83) switch ON" ;# inside
-$ns_ at 111.098639 "$node_(83) setdest 100.000000 470.000000 12.851619" ;#
-$ns_ at 84.020000 "$node_(69) setdest 2946.763505 918.751021 13.460928" ;#
-$ns_ at 84.020000 "$node_(53) setdest 759.665364 2156.145501 18.893733" ;#
-$ns_ at 85.020000 "$node_(55) setdest 2660.000000 2730.000000 11.101662" ;#
-$ns_ at 85.192685 "$node_(21) setdest 2510.000000 1290.000000 12.947158" ;#
-$ns_ at 85.574748 "$node_(49) setdest 1860.000000 1960.000000 12.540179" ;#
-$ns_ at 85.915106 "$node_(76) setdest 3471.121930 2284.837355 16.045433" ;#
-$ns_ at 86.907442 "$node_(77) setdest 3429.356254 70.988556 15.578326" ;#
-$ns_ at 87.020000 "$node_(57) setdest 2948.388016 917.370186 13.815163" ;#
-$ns_ at 87.921266 "$node_(1) setdest 2510.000000 1290.000000 14.806388" ;#
-#$ns_ at 88.020000 $node_(26) arrived_to 3090.000000 2030.000000
-$ns_ at 88.020000 "$node_(26) switch OFF" ;# arrived_to
-$ns_ at 89.020000 "$node_(26) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 89.030000 "$node_(26) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 88.999624 "$node_(13) setdest 1031.332476 682.402690 14.209679" ;#
-#$ns_ at 89.020000 $node_(12) arrived_to 2660.000000 2730.000000
-$ns_ at 89.020000 "$node_(12) switch OFF" ;# arrived_to
-$ns_ at 90.020000 "$node_(12) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 90.030000 "$node_(12) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 89.020000 "$node_(51) setdest 1980.000000 2190.000000 13.653812" ;#
-$ns_ at 89.374028 "$node_(23) setdest 2507.500076 2608.897119 18.743439" ;#
-$ns_ at 90.020000 "$node_(75) setdest 3064.341849 413.473479 13.129175" ;#
-$ns_ at 90.367268 "$node_(7) setdest 3801.239648 2785.292469 16.164570" ;#
-$ns_ at 90.968794 "$node_(67) setdest 3310.000000 427.728101 15.991769" ;#
-$ns_ at 91.502283 "$node_(45) setdest 1369.887024 2048.829094 18.088682" ;#
-$ns_ at 91.713123 "$node_(29) setdest 2510.000000 1290.000000 14.674077" ;#
-$ns_ at 92.020000 "$node_(63) setdest 3044.881012 835.351140 13.124405" ;#
-$ns_ at 0.0 "$node_(84) switch OFF" ;# set_X,Y,Z
-$node_(84) set X_ 0.000000
-$node_(84) set Y_ 0.000000
-$node_(84) set Z_ 0.0
-$ns_ at 98.711006 "$node_(84) setdest 3511.136364 10.000000 1000000000.000000" ;# init_node
-$ns_ at 98.721006 "$node_(84) switch ON" ;# inside
-$ns_ at 98.721006 "$node_(84) setdest 3466.202399 43.510075 15.509769" ;#
-#$ns_ at 93.020000 $node_(28) arrived_to 1610.000000 1560.000000
-$ns_ at 93.020000 "$node_(28) switch OFF" ;# arrived_to
-$ns_ at 94.020000 "$node_(28) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 94.030000 "$node_(28) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 93.196690 "$node_(77) setdest 3310.000000 160.000000 12.593125" ;#
-#$ns_ at 94.020000 $node_(56) arrived_to 2560.000000 10.000000
-$ns_ at 94.020000 "$node_(56) switch OFF" ;# arrived_to
-$ns_ at 95.020000 "$node_(56) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 95.030000 "$node_(56) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 94.110694 "$node_(67) setdest 3310.000000 610.000000 13.104314" ;#
-$ns_ at 95.020000 "$node_(79) setdest 3310.000000 326.832286 12.456113" ;#
-$ns_ at 95.080867 "$node_(45) setdest 1071.000000 2103.000000 16.038557" ;#
-$ns_ at 0.0 "$node_(85) switch OFF" ;# set_X,Y,Z
-$node_(85) set X_ 0.000000
-$node_(85) set Y_ 0.000000
-$node_(85) set Z_ 0.0
-$ns_ at 107.010001 "$node_(85) setdest 2560.000000 10.000000 1000000000.000000" ;# init_node
-$ns_ at 107.020001 "$node_(85) switch ON" ;# inside
-$ns_ at 107.020001 "$node_(85) setdest 2560.000000 10.000000 15.432186" ;#
-$ns_ at 96.020000 "$node_(80) setdest 3310.000000 264.084004 14.479894" ;#
-#$ns_ at 96.020000 $node_(62) arrived_to 1290.000000 1090.000000
-$ns_ at 96.020000 "$node_(62) switch OFF" ;# arrived_to
-$ns_ at 97.020000 "$node_(62) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 97.030000 "$node_(62) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 97.020000 "$node_(78) setdest 2873.391011 72.678202 15.284245" ;#
-$ns_ at 97.905187 "$node_(23) setdest 2660.000000 2730.000000 14.848559" ;#
-$ns_ at 98.020000 "$node_(41) setdest 3207.358318 1437.340495 15.535574" ;#
-$ns_ at 98.020000 "$node_(27) setdest 851.301630 2786.850971 16.620014" ;#
-$ns_ at 98.020000 "$node_(81) setdest 4246.010292 292.249254 10.932187" ;#
-$ns_ at 98.093016 "$node_(13) setdest 960.000000 570.000000 12.183288" ;#
-$ns_ at 99.020000 "$node_(10) setdest 825.195870 2144.959301 18.438249" ;#
-$ns_ at 99.020000 "$node_(38) setdest 2867.393542 2392.382607 10.669130" ;#
-$ns_ at 99.020000 "$node_(39) setdest 3290.000000 1020.000000 13.682917" ;#
-$ns_ at 100.020000 "$node_(16) setdest 3802.644628 3010.000000 14.410845" ;#
-$ns_ at 102.801747 "$node_(16) switch OFF" ;# leaving_area
-$ns_ at 100.176928 "$node_(68) setdest 1290.000000 1090.000000 17.500228" ;#
-$ns_ at 100.736553 "$node_(53) setdest 245.000000 2244.000000 15.229216" ;#
-$ns_ at 101.471866 "$node_(60) setdest 1980.000000 2190.000000 18.178300" ;#
-$ns_ at 102.335078 "$node_(84) setdest 3310.000000 160.000000 12.423179" ;#
-$ns_ at 103.020000 "$node_(20) setdest 3838.461538 3010.000000 31.691224" ;#
-$ns_ at 104.771001 "$node_(20) switch OFF" ;# leaving_area
-$ns_ at 103.020000 "$node_(73) setdest 3301.533898 783.555084 13.682917" ;#
-$ns_ at 103.208174 "$node_(80) setdest 3310.000000 610.000000 13.401455" ;#
-$ns_ at 104.438397 "$node_(33) setdest 2879.338395 976.062364 14.543216" ;#
-$ns_ at 105.020000 "$node_(37) setdest 2261.715038 1545.924499 12.202857" ;#
-$ns_ at 105.020000 "$node_(65) setdest 3301.141878 791.591494 13.682917" ;#
-$ns_ at 106.020000 "$node_(77) setdest 3310.000000 406.827472 12.451045" ;#
-$ns_ at 108.020000 "$node_(85) setdest 3055.111374 406.089099 12.993422" ;#
-$ns_ at 108.413608 "$node_(79) setdest 3310.000000 478.314095 13.636364" ;#
-$ns_ at 109.020000 "$node_(51) setdest 2123.704561 2304.118328 17.343151" ;#
-$ns_ at 109.020000 "$node_(67) setdest 3300.899735 796.555442 13.682917" ;#
-$ns_ at 109.020000 "$node_(82) setdest 3310.000000 510.344385 13.220467" ;#
-$ns_ at 110.020000 "$node_(21) setdest 2052.051993 1762.038715 13.218839" ;#
-#$ns_ at 110.020000 $node_(13) arrived_to 960.000000 570.000000
-$ns_ at 110.020000 "$node_(13) switch OFF" ;# arrived_to
-$ns_ at 111.020000 "$node_(13) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 111.030000 "$node_(13) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 110.020000 "$node_(66) setdest 1390.631497 1237.802511 13.489251" ;#
-$ns_ at 110.180777 "$node_(50) setdest 2018.905962 1796.204624 13.528813" ;#
-$ns_ at 0.0 "$node_(86) switch OFF" ;# set_X,Y,Z
-$node_(86) set X_ 0.000000
-$node_(86) set Y_ 0.000000
-$node_(86) set Z_ 0.0
-$ns_ at 116.580034 "$node_(86) setdest 1871.538461 10.000000 1000000000.000000" ;# init_node
-$ns_ at 116.590034 "$node_(86) switch ON" ;# inside
-$ns_ at 116.590034 "$node_(86) setdest 1595.895451 521.908448 16.360244" ;#
-$ns_ at 111.161296 "$node_(7) setdest 3800.000000 2970.000000 15.576043" ;#
-$ns_ at 112.020000 "$node_(49) setdest 1447.048442 2034.844199 16.364369" ;#
-$ns_ at 112.020000 "$node_(23) setdest 2735.000000 3010.000000 16.564038" ;#
-$ns_ at 129.520000 "$node_(23) switch OFF" ;# leaving_area
-$ns_ at 112.544044 "$node_(10) setdest 245.000000 2244.000000 15.705761" ;#
-$ns_ at 115.020000 "$node_(45) setdest 800.877206 2149.110550 15.532960" ;#
-$ns_ at 115.719153 "$node_(73) setdest 3298.548961 844.746298 14.406345" ;#
-$ns_ at 116.412026 "$node_(76) setdest 3315.478202 1960.579587 18.536554" ;#
-$ns_ at 118.020000 "$node_(68) setdest 398.108304 1287.662484 16.621823" ;#
-$ns_ at 118.307183 "$node_(65) setdest 3299.114853 833.145514 14.465543" ;#
-$ns_ at 118.513219 "$node_(74) setdest 1290.000000 1090.000000 18.012730" ;#
-$ns_ at 118.531898 "$node_(63) setdest 2958.817947 908.504745 14.362559" ;#
-$ns_ at 119.020000 "$node_(61) setdest 747.339233 2158.249598 16.430355" ;#
-#$ns_ at 119.020000 $node_(84) arrived_to 3310.000000 160.000000
-$ns_ at 119.020000 "$node_(84) switch OFF" ;# arrived_to
-$ns_ at 120.020000 "$node_(84) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 120.030000 "$node_(84) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 119.435558 "$node_(69) setdest 2773.538937 1065.991904 14.458335" ;#
-$ns_ at 119.522273 "$node_(79) setdest 3310.000000 610.000000 15.496606" ;#
-$ns_ at 119.600819 "$node_(51) setdest 2660.000000 2730.000000 16.144277" ;#
-$ns_ at 119.812016 "$node_(36) setdest 1860.000000 1960.000000 15.512436" ;#
-$ns_ at 119.971721 "$node_(73) setdest 3290.000000 1020.000000 13.447144" ;#
-$ns_ at 120.020000 "$node_(24) setdest 3808.480944 1706.339332 14.637236" ;#
-$ns_ at 120.020000 "$node_(1) setdest 2405.157699 1398.068218 14.457381" ;#
-$ns_ at 121.183219 "$node_(65) setdest 3290.000000 1020.000000 13.520245" ;#
-$ns_ at 121.373138 "$node_(57) setdest 2805.464211 1038.855421 15.476236" ;#
-$ns_ at 121.547835 "$node_(33) setdest 2831.663988 1016.585611 14.964355" ;#
-$ns_ at 122.670398 "$node_(67) setdest 3297.875780 858.546515 15.283891" ;#
-$ns_ at 123.275587 "$node_(66) setdest 1517.151500 1423.628766 12.841373" ;#
-$ns_ at 124.020000 "$node_(60) setdest 1269.458678 2565.731275 14.975446" ;#
-#$ns_ at 124.020000 $node_(71) arrived_to 1290.000000 1090.000000
-$ns_ at 124.020000 "$node_(71) switch OFF" ;# arrived_to
-$ns_ at 125.020000 "$node_(71) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 125.030000 "$node_(71) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 124.020000 "$node_(7) setdest 3838.461538 3010.000000 27.745675" ;#
-$ns_ at 126.020000 "$node_(7) switch OFF" ;# leaving_area
-$ns_ at 125.020000 "$node_(29) setdest 2314.893745 1491.109525 12.787508" ;#
-$ns_ at 125.405158 "$node_(41) setdest 3090.000000 2030.000000 12.688636" ;#
-$ns_ at 125.729092 "$node_(33) setdest 2510.000000 1290.000000 13.073798" ;#
-$ns_ at 125.843836 "$node_(77) setdest 3310.000000 610.000000 15.419704" ;#
-$ns_ at 126.020000 "$node_(58) setdest 764.196652 2155.372000 16.430355" ;#
-$ns_ at 126.151669 "$node_(78) setdest 2560.000000 10.000000 18.946593" ;#
-$ns_ at 126.396280 "$node_(63) setdest 2510.000000 1290.000000 12.910978" ;#
-$ns_ at 126.731196 "$node_(67) setdest 3290.000000 1020.000000 13.153880" ;#
-$ns_ at 0.0 "$node_(87) switch OFF" ;# set_X,Y,Z
-$node_(87) set X_ 0.000000
-$node_(87) set Y_ 0.000000
-$node_(87) set Z_ 0.0
-$ns_ at 140.829201 "$node_(87) setdest 1871.538461 10.000000 1000000000.000000" ;# init_node
-$ns_ at 140.839201 "$node_(87) switch ON" ;# inside
-$ns_ at 140.839201 "$node_(87) setdest 1864.515162 23.043270 18.081091" ;#
-$ns_ at 127.889465 "$node_(81) setdest 4348.345188 318.884364 12.272889" ;#
-$ns_ at 129.020000 "$node_(79) setdest 3296.280996 891.239581 13.369011" ;#
-$ns_ at 130.020000 "$node_(80) setdest 2871.249270 982.938121 13.608044" ;#
-$ns_ at 130.020000 "$node_(39) setdest 3195.904728 1495.181122 15.338174" ;#
-$ns_ at 130.434594 "$node_(1) setdest 1860.000000 1960.000000 13.363744" ;#
-$ns_ at 131.020000 "$node_(19) setdest 245.000000 2244.000000 16.430355" ;#
-$ns_ at 132.661848 "$node_(45) setdest 576.051317 2187.488819 17.655915" ;#
-$ns_ at 132.827402 "$node_(50) setdest 1860.000000 1960.000000 15.021165" ;#
-$ns_ at 0.0 "$node_(88) switch OFF" ;# set_X,Y,Z
-$node_(88) set X_ 0.000000
-$node_(88) set Y_ 0.000000
-$node_(88) set Z_ 0.0
-$ns_ at 157.137696 "$node_(88) setdest 565.476191 10.000000 1000000000.000000" ;# init_node
-$ns_ at 157.147696 "$node_(88) switch ON" ;# inside
-$ns_ at 157.147696 "$node_(88) setdest 447.380636 126.706195 12.862592" ;#
-$ns_ at 133.493591 "$node_(57) setdest 2510.000000 1290.000000 13.133300" ;#
-$ns_ at 134.020000 "$node_(73) setdest 3245.511748 1080.352279 17.620032" ;#
-$ns_ at 134.240277 "$node_(37) setdest 2044.074180 1770.261999 16.010145" ;#
-$ns_ at 135.159840 "$node_(69) setdest 2510.000000 1290.000000 12.877033" ;#
-$ns_ at 135.483604 "$node_(24) setdest 3805.479210 2153.597670 12.883208" ;#
-$ns_ at 135.520151 "$node_(82) setdest 3310.000000 610.000000 15.331992" ;#
-$ns_ at 135.815726 "$node_(76) setdest 3032.000000 1370.000000 15.898616" ;#
-$ns_ at 136.020000 "$node_(53) setdest 10.000000 2297.153203 27.467800" ;#
-$ns_ at 144.791588 "$node_(53) switch OFF" ;# leaving_area
-$ns_ at 136.020000 "$node_(65) setdest 3201.793172 1465.444479 13.547521" ;#
-$ns_ at 136.505555 "$node_(81) setdest 4660.000000 400.000000 10.553625" ;#
-$ns_ at 137.020000 "$node_(36) setdest 1533.412123 2019.191466 16.364369" ;#
-$ns_ at 137.665915 "$node_(49) setdest 1399.568575 2043.449549 17.235207" ;#
-$ns_ at 138.275233 "$node_(73) setdest 3032.000000 1370.000000 15.820675" ;#
-$ns_ at 138.882085 "$node_(38) setdest 2791.267225 2516.309168 11.748791" ;#
-$ns_ at 139.003897 "$node_(61) setdest 532.623433 2194.902053 18.223602" ;#
-$ns_ at 139.213699 "$node_(75) setdest 3310.000000 610.000000 15.120226" ;#
-$ns_ at 140.020000 "$node_(44) setdest 1271.772161 2066.611636 15.916303" ;#
-$ns_ at 140.020000 "$node_(77) setdest 2885.061120 971.198048 13.815163" ;#
-$ns_ at 140.020000 "$node_(67) setdest 3207.216219 1438.058096 13.371579" ;#
-$ns_ at 140.465614 "$node_(49) setdest 1071.000000 2103.000000 16.245753" ;#
-$ns_ at 140.782139 "$node_(66) setdest 1586.789557 1525.909662 16.225247" ;#
-#$ns_ at 141.020000 $node_(74) arrived_to 1290.000000 1090.000000
-$ns_ at 141.020000 "$node_(74) switch OFF" ;# arrived_to
-$ns_ at 142.020000 "$node_(74) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 142.030000 "$node_(74) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 141.658508 "$node_(87) setdest 1290.000000 1090.000000 16.296098" ;#
-$ns_ at 143.020000 "$node_(82) setdest 3032.404943 845.955798 13.815163" ;#
-$ns_ at 143.109439 "$node_(8) setdest 3800.000000 2970.000000 15.062159" ;#
-#$ns_ at 144.020000 $node_(78) arrived_to 2560.000000 10.000000
-$ns_ at 144.020000 "$node_(78) switch OFF" ;# arrived_to
-$ns_ at 145.020000 "$node_(78) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 145.030000 "$node_(78) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 0.0 "$node_(89) switch OFF" ;# set_X,Y,Z
-$node_(89) set X_ 0.000000
-$node_(89) set Y_ 0.000000
-$node_(89) set Z_ 0.0
-$ns_ at 153.222693 "$node_(89) setdest 565.476191 10.000000 1000000000.000000" ;# init_node
-$ns_ at 153.232693 "$node_(89) switch ON" ;# inside
-$ns_ at 153.232693 "$node_(89) setdest 344.127931 228.744163 13.735993" ;#
-$ns_ at 144.963064 "$node_(58) setdest 611.323732 2181.467741 17.409286" ;#
-$ns_ at 145.579785 "$node_(45) setdest 496.265112 2201.108498 18.301437" ;#
-$ns_ at 146.931933 "$node_(29) setdest 2187.073134 1622.863078 14.945523" ;#
-$ns_ at 148.408339 "$node_(66) setdest 1610.000000 1560.000000 15.791359" ;#
-$ns_ at 149.020000 "$node_(50) setdest 1566.539209 2013.187444 15.277260" ;#
-$ns_ at 150.002406 "$node_(45) setdest 245.000000 2244.000000 15.913730" ;#
-$ns_ at 150.081692 "$node_(79) setdest 3290.000000 1020.000000 14.422586" ;#
-$ns_ at 150.956620 "$node_(61) setdest 245.000000 2244.000000 15.305991" ;#
-$ns_ at 151.020000 "$node_(10) setdest 10.000000 2297.153203 27.031537" ;#
-$ns_ at 159.933153 "$node_(10) switch OFF" ;# leaving_area
-$ns_ at 151.261294 "$node_(38) setdest 2660.000000 2730.000000 10.129306" ;#
-$ns_ at 152.020000 "$node_(66) setdest 1686.208867 1681.934187 14.721891" ;#
-$ns_ at 152.127578 "$node_(86) setdest 1424.637256 839.959382 15.416120" ;#
-$ns_ at 0.0 "$node_(90) switch OFF" ;# set_X,Y,Z
-$node_(90) set X_ 0.000000
-$node_(90) set Y_ 0.000000
-$node_(90) set Z_ 0.0
-$ns_ at 169.168944 "$node_(90) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 169.178944 "$node_(90) switch ON" ;# inside
-$ns_ at 169.178944 "$node_(90) setdest 1629.907162 458.743842 15.463030" ;#
-$ns_ at 153.763009 "$node_(37) setdest 1981.588388 1834.670431 14.570408" ;#
-$ns_ at 153.871196 "$node_(58) setdest 245.000000 2244.000000 16.053642" ;#
-$ns_ at 0.0 "$node_(91) switch OFF" ;# set_X,Y,Z
-$node_(91) set X_ 0.000000
-$node_(91) set Y_ 0.000000
-$node_(91) set Z_ 0.0
-$ns_ at 170.418030 "$node_(91) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 170.428030 "$node_(91) switch ON" ;# inside
-$ns_ at 170.428030 "$node_(91) setdest 1630.537227 457.573722 16.216624" ;#
-$ns_ at 156.817916 "$node_(85) setdest 3310.000000 610.000000 14.702074" ;#
-$ns_ at 157.302391 "$node_(36) setdest 1393.080306 2044.625496 17.971882" ;#
-$ns_ at 159.020000 "$node_(33) setdest 1860.000000 1960.000000 13.528813" ;#
-$ns_ at 159.214389 "$node_(29) setdest 1860.000000 1960.000000 12.104470" ;#
-$ns_ at 159.772887 "$node_(21) setdest 1860.000000 1960.000000 15.115415" ;#
-$ns_ at 159.921934 "$node_(37) setdest 1860.000000 1960.000000 13.331542" ;#
-#$ns_ at 160.020000 $node_(55) arrived_to 2660.000000 2730.000000
-$ns_ at 160.020000 "$node_(55) switch OFF" ;# arrived_to
-$ns_ at 161.020000 "$node_(55) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 161.030000 "$node_(55) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 160.020000 "$node_(79) setdest 3244.927850 1081.144389 17.520934" ;#
-$ns_ at 161.020000 "$node_(75) setdest 3298.796385 839.674115 13.400949" ;#
-$ns_ at 161.601850 "$node_(39) setdest 3090.000000 2030.000000 12.557045" ;#
-$ns_ at 161.787129 "$node_(66) setdest 1860.000000 1960.000000 13.531556" ;#
-$ns_ at 162.020000 "$node_(73) setdest 2448.642014 1824.708696 16.241331" ;#
-$ns_ at 162.020000 "$node_(49) setdest 245.000000 2244.000000 16.430355" ;#
-$ns_ at 163.020000 "$node_(51) setdest 2841.276329 2784.804471 15.762089" ;#
-$ns_ at 163.020000 "$node_(69) setdest 2192.066548 1617.716019 12.965112" ;#
-#$ns_ at 163.020000 $node_(83) arrived_to 100.000000 470.000000
-$ns_ at 163.020000 "$node_(83) switch OFF" ;# arrived_to
-$ns_ at 164.020000 "$node_(83) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 164.030000 "$node_(83) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 164.020000 "$node_(57) setdest 2196.107831 1613.550389 14.321469" ;#
-$ns_ at 164.355465 "$node_(79) setdest 3032.000000 1370.000000 15.833262" ;#
-$ns_ at 165.238012 "$node_(36) setdest 1071.000000 2103.000000 15.750539" ;#
-#$ns_ at 167.020000 $node_(45) arrived_to 245.000000 2244.000000
-$ns_ at 167.020000 "$node_(45) switch OFF" ;# arrived_to
-$ns_ at 168.020000 "$node_(45) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 168.030000 "$node_(45) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 168.020000 $node_(81) arrived_to 4660.000000 400.000000
-$ns_ at 168.020000 "$node_(81) switch OFF" ;# arrived_to
-$ns_ at 169.020000 "$node_(81) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 169.030000 "$node_(81) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 168.541939 "$node_(50) setdest 1303.139455 2060.926563 17.773443" ;#
-$ns_ at 169.391530 "$node_(82) setdest 2894.107066 963.508994 14.864930" ;#
-$ns_ at 169.538594 "$node_(65) setdest 3168.634794 1632.894289 14.854433" ;#
-$ns_ at 170.055887 "$node_(88) setdest 359.817155 213.239517 15.485932" ;#
-$ns_ at 170.200765 "$node_(24) setdest 3803.601631 2433.357008 15.356627" ;#
-#$ns_ at 171.020000 $node_(61) arrived_to 245.000000 2244.000000
-$ns_ at 171.020000 "$node_(61) switch OFF" ;# arrived_to
-$ns_ at 172.020000 "$node_(61) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 172.030000 "$node_(61) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 0.0 "$node_(92) switch OFF" ;# set_X,Y,Z
-$node_(92) set X_ 0.000000
-$node_(92) set Y_ 0.000000
-$node_(92) set Z_ 0.0
-$ns_ at 186.522795 "$node_(92) setdest 1871.538461 10.000000 1000000000.000000" ;# init_node
-$ns_ at 186.532795 "$node_(92) switch ON" ;# inside
-$ns_ at 186.532795 "$node_(92) setdest 1757.062373 222.598450 16.107106" ;#
-$ns_ at 171.891756 "$node_(67) setdest 3158.620839 1683.464762 14.102316" ;#
-$ns_ at 172.335722 "$node_(80) setdest 2604.125521 1209.993307 13.815163" ;#
-$ns_ at 172.979814 "$node_(68) setdest 40.534746 1366.908516 18.195136" ;#
-$ns_ at 173.020000 "$node_(63) setdest 2347.931038 1457.055700 14.766618" ;#
-$ns_ at 0.0 "$node_(93) switch OFF" ;# set_X,Y,Z
-$node_(93) set X_ 0.000000
-$node_(93) set Y_ 0.000000
-$node_(93) set Z_ 0.0
-$ns_ at 207.010001 "$node_(93) setdest 2560.000000 10.000000 1000000000.000000" ;# init_node
-$ns_ at 207.020001 "$node_(93) switch ON" ;# inside
-$ns_ at 207.020001 "$node_(93) setdest 2560.000000 10.000000 16.187162" ;#
-#$ns_ at 174.020000 $node_(41) arrived_to 3090.000000 2030.000000
-$ns_ at 174.020000 "$node_(41) switch OFF" ;# arrived_to
-$ns_ at 175.020000 "$node_(41) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 175.030000 "$node_(41) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 174.020000 $node_(37) arrived_to 1860.000000 1960.000000
-$ns_ at 174.020000 "$node_(37) switch OFF" ;# arrived_to
-$ns_ at 175.020000 "$node_(37) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 175.030000 "$node_(37) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 174.842400 "$node_(27) setdest 625.319656 2906.349292 19.152866" ;#
-$ns_ at 175.034880 "$node_(51) setdest 3007.934440 2835.189482 16.637835" ;#
-$ns_ at 175.559423 "$node_(86) setdest 1290.000000 1090.000000 18.368335" ;#
-$ns_ at 175.888338 "$node_(89) setdest 100.000000 470.000000 15.508288" ;#
-#$ns_ at 177.020000 $node_(38) arrived_to 2660.000000 2730.000000
-$ns_ at 177.020000 "$node_(38) switch OFF" ;# arrived_to
-$ns_ at 178.020000 "$node_(38) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 178.030000 "$node_(38) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 177.579667 "$node_(44) setdest 1071.000000 2103.000000 17.835412" ;#
-$ns_ at 177.692389 "$node_(60) setdest 651.781274 2892.356499 16.620014" ;#
-$ns_ at 178.005501 "$node_(88) setdest 100.000000 470.000000 13.039032" ;#
-$ns_ at 178.020000 "$node_(76) setdest 3152.346236 1206.739603 16.104256" ;#
-$ns_ at 178.020000 "$node_(58) setdest 46.380318 2288.924563 31.751156" ;#
-$ns_ at 178.179025 "$node_(75) setdest 3293.177795 954.855207 13.682917" ;#
-$ns_ at 179.020000 "$node_(21) setdest 1734.363809 1982.770564 17.710129" ;#
-$ns_ at 180.020000 "$node_(85) setdest 2946.994541 918.554640 13.815163" ;#
-$ns_ at 180.389193 "$node_(77) setdest 2733.002404 1100.447957 15.201584" ;#
-$ns_ at 181.030197 "$node_(65) setdest 3090.000000 2030.000000 13.062893" ;#
-$ns_ at 181.601997 "$node_(82) setdest 2510.000000 1290.000000 13.472597" ;#
-$ns_ at 183.020000 "$node_(8) setdest 3802.644628 3010.000000 14.845359" ;#
-$ns_ at 185.720327 "$node_(8) switch OFF" ;# leaving_area
-$ns_ at 183.020000 "$node_(19) setdest 10.000000 2297.153203 32.044586" ;#
-$ns_ at 190.538781 "$node_(19) switch OFF" ;# leaving_area
-$ns_ at 183.603229 "$node_(50) setdest 1235.530927 2073.180073 17.527362" ;#
-$ns_ at 184.433528 "$node_(58) setdest 10.000000 2297.153203 24.610297" ;#
-$ns_ at 185.949125 "$node_(58) switch OFF" ;# leaving_area
-$ns_ at 185.499459 "$node_(51) setdest 3090.000000 2860.000000 18.965428" ;#
-$ns_ at 0.0 "$node_(94) switch OFF" ;# set_X,Y,Z
-$node_(94) set X_ 0.000000
-$node_(94) set Y_ 0.000000
-$node_(94) set Z_ 0.0
-$ns_ at 200.082699 "$node_(94) setdest 1871.538462 10.000000 1000000000.000000" ;# init_node
-$ns_ at 200.092699 "$node_(94) switch ON" ;# inside
-$ns_ at 200.092699 "$node_(94) setdest 1847.554641 54.541381 17.755388" ;#
-$ns_ at 186.229604 "$node_(21) setdest 1071.000000 2103.000000 16.132201" ;#
-$ns_ at 186.606909 "$node_(75) setdest 3290.000000 1020.000000 14.779268" ;#
-$ns_ at 187.020000 "$node_(66) setdest 1900.692120 2037.993230 15.154980" ;#
-$ns_ at 187.020000 "$node_(36) setdest 719.708692 2162.966192 17.382919" ;#
-$ns_ at 187.523384 "$node_(50) setdest 1071.000000 2103.000000 15.930028" ;#
-$ns_ at 188.020000 "$node_(79) setdest 2665.592034 1655.603167 16.467044" ;#
-$ns_ at 188.189331 "$node_(27) setdest 429.306931 3010.000000 15.771276" ;#
-$ns_ at 202.248472 "$node_(27) switch OFF" ;# leaving_area
-$ns_ at 188.418674 "$node_(24) setdest 3800.000000 2970.000000 12.597146" ;#
-$ns_ at 188.782106 "$node_(63) setdest 2146.724027 1664.453696 12.991574" ;#
-$ns_ at 189.631525 "$node_(67) setdest 3090.000000 2030.000000 12.898274" ;#
-$ns_ at 190.020000 "$node_(44) setdest 776.761613 2153.227134 16.430355" ;#
-$ns_ at 190.020000 "$node_(1) setdest 1071.000000 2103.000000 16.364369" ;#
-$ns_ at 190.614373 "$node_(76) setdest 3199.271293 1143.081579 18.258249" ;#
-#$ns_ at 191.020000 $node_(51) arrived_to 3090.000000 2860.000000
-$ns_ at 191.020000 "$node_(51) switch OFF" ;# arrived_to
-$ns_ at 192.020000 "$node_(51) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 192.030000 "$node_(51) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 192.020000 "$node_(75) setdest 3216.505296 1119.702118 17.758994" ;#
-$ns_ at 192.020000 "$node_(86) setdest 1610.000000 1560.000000 13.868165" ;#
-$ns_ at 192.824720 "$node_(66) setdest 1980.000000 2190.000000 12.993435" ;#
-$ns_ at 193.108798 "$node_(68) setdest 10.000000 1373.675676 15.807939" ;#
-$ns_ at 195.087274 "$node_(68) switch OFF" ;# leaving_area
-$ns_ at 193.517299 "$node_(77) setdest 2510.000000 1290.000000 13.006323" ;#
-$ns_ at 194.945794 "$node_(76) setdest 3290.000000 1020.000000 15.178143" ;#
-$ns_ at 195.496637 "$node_(57) setdest 1860.000000 1960.000000 11.624694" ;#
-$ns_ at 0.0 "$node_(95) switch OFF" ;# set_X,Y,Z
-$node_(95) set X_ 0.000000
-$node_(95) set Y_ 0.000000
-$node_(95) set Z_ 0.0
-$ns_ at 200.540141 "$node_(95) setdest 3917.755102 10.000000 1000000000.000000" ;# init_node
-$ns_ at 200.550141 "$node_(95) switch ON" ;# inside
-$ns_ at 200.550141 "$node_(95) setdest 3930.000000 210.000000 11.469727" ;#
-$ns_ at 197.712478 "$node_(80) setdest 2510.000000 1290.000000 14.870156" ;#
-$ns_ at 198.237245 "$node_(69) setdest 2043.885022 1770.456977 15.035831" ;#
-$ns_ at 198.994650 "$node_(75) setdest 3032.000000 1370.000000 15.527926" ;#
-$ns_ at 199.020000 "$node_(50) setdest 851.594504 2140.452996 18.543827" ;#
-#$ns_ at 199.020000 $node_(89) arrived_to 100.000000 470.000000
-$ns_ at 199.020000 "$node_(89) switch OFF" ;# arrived_to
-$ns_ at 200.020000 "$node_(89) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 200.030000 "$node_(89) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 199.020000 "$node_(29) setdest 1776.442635 1975.144111 19.043520" ;#
-$ns_ at 201.523686 "$node_(92) setdest 1522.902019 657.467680 15.764942" ;#
-$ns_ at 201.774529 "$node_(91) setdest 1388.150347 907.720784 16.589689" ;#
-$ns_ at 202.139067 "$node_(90) setdest 1290.000000 1090.000000 17.977331" ;#
-$ns_ at 202.941870 "$node_(94) setdest 1290.000000 1090.000000 16.316016" ;#
-$ns_ at 203.479189 "$node_(29) setdest 1506.457950 2024.076696 15.681582" ;#
-#$ns_ at 206.020000 $node_(76) arrived_to 3290.000000 1020.000000
-$ns_ at 206.020000 "$node_(76) switch OFF" ;# arrived_to
-$ns_ at 207.020000 "$node_(76) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 207.030000 "$node_(76) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 206.020000 $node_(39) arrived_to 3090.000000 2030.000000
-$ns_ at 206.020000 "$node_(39) switch OFF" ;# arrived_to
-$ns_ at 207.020000 "$node_(39) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 207.030000 "$node_(39) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 207.020000 $node_(66) arrived_to 1980.000000 2190.000000
-$ns_ at 207.020000 "$node_(66) switch OFF" ;# arrived_to
-$ns_ at 208.020000 "$node_(66) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 208.030000 "$node_(66) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 207.020000 "$node_(80) setdest 2324.258074 1481.457062 11.783104" ;#
-#$ns_ at 207.020000 $node_(88) arrived_to 100.000000 470.000000
-$ns_ at 207.020000 "$node_(88) switch OFF" ;# arrived_to
-$ns_ at 208.020000 "$node_(88) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 208.030000 "$node_(88) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 207.521317 "$node_(36) setdest 245.000000 2244.000000 15.790038" ;#
-$ns_ at 207.560570 "$node_(73) setdest 2119.973365 2080.895286 16.467044" ;#
-$ns_ at 208.020000 "$node_(93) setdest 2989.140807 353.312646 13.088646" ;#
-$ns_ at 208.187261 "$node_(44) setdest 623.790512 2179.339634 18.470348" ;#
-$ns_ at 211.022873 "$node_(50) setdest 245.000000 2244.000000 15.779852" ;#
-$ns_ at 211.024255 "$node_(63) setdest 2056.747297 1757.198940 14.688254" ;#
-$ns_ at 212.390686 "$node_(69) setdest 1860.000000 1960.000000 11.669987" ;#
-#$ns_ at 213.020000 $node_(65) arrived_to 3090.000000 2030.000000
-$ns_ at 213.020000 "$node_(65) switch OFF" ;# arrived_to
-$ns_ at 214.020000 "$node_(65) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 214.030000 "$node_(65) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 214.020000 "$node_(49) setdest 10.000000 2297.153203 25.061238" ;#
-$ns_ at 223.633899 "$node_(49) switch OFF" ;# leaving_area
-$ns_ at 214.505518 "$node_(85) setdest 2880.706633 974.899362 15.931866" ;#
-$ns_ at 216.232020 "$node_(79) setdest 2341.346270 1908.342261 17.761994" ;#
-$ns_ at 216.589042 "$node_(44) setdest 245.000000 2244.000000 15.728803" ;#
-$ns_ at 217.020000 "$node_(87) setdest 1440.191417 1310.593644 15.030777" ;#
-$ns_ at 217.020000 "$node_(77) setdest 1860.000000 1960.000000 13.528813" ;#
-$ns_ at 218.020000 "$node_(67) setdest 2974.599224 2217.861728 11.270777" ;#
-$ns_ at 219.020000 "$node_(95) setdest 4660.000000 400.000000 10.932187" ;#
-$ns_ at 219.733258 "$node_(60) setdest 429.306931 3010.000000 19.194417" ;#
-$ns_ at 232.844577 "$node_(60) switch OFF" ;# leaving_area
-$ns_ at 219.821678 "$node_(63) setdest 1860.000000 1960.000000 13.329158" ;#
-$ns_ at 219.966205 "$node_(85) setdest 2510.000000 1290.000000 13.494568" ;#
-$ns_ at 220.020000 "$node_(75) setdest 3335.449131 2002.185689 16.743154" ;#
-$ns_ at 220.020000 "$node_(82) setdest 2225.249532 1583.512021 13.528813" ;#
-$ns_ at 220.976351 "$node_(29) setdest 1447.052342 2034.843492 19.129141" ;#
-$ns_ at 224.132448 "$node_(29) setdest 1071.000000 2103.000000 15.999079" ;#
-$ns_ at 229.020000 "$node_(21) setdest 892.985050 2133.387540 18.885850" ;#
-$ns_ at 229.020000 "$node_(33) setdest 1598.308715 2007.429472 15.682055" ;#
-$ns_ at 229.658395 "$node_(80) setdest 2153.802027 1657.157910 13.260012" ;#
-$ns_ at 232.020000 "$node_(24) setdest 3838.461538 3010.000000 30.389856" ;#
-$ns_ at 233.845983 "$node_(24) switch OFF" ;# leaving_area
-$ns_ at 232.592289 "$node_(91) setdest 1290.000000 1090.000000 18.116021" ;#
-$ns_ at 232.853023 "$node_(92) setdest 1356.793484 965.954958 17.910555" ;#
-$ns_ at 232.866807 "$node_(73) setdest 1980.000000 2190.000000 17.479445" ;#
-$ns_ at 234.020000 "$node_(86) setdest 1786.643491 1842.629586 13.424032" ;#
-$ns_ at 234.774832 "$node_(87) setdest 1610.000000 1560.000000 12.980153" ;#
-$ns_ at 236.020000 "$node_(69) setdest 1071.000000 2103.000000 16.364369" ;#
-$ns_ at 237.581680 "$node_(67) setdest 2818.392033 2472.152504 10.264343" ;#
-$ns_ at 238.020000 "$node_(57) setdest 1707.400414 1987.657466 19.200321" ;#
-$ns_ at 238.582182 "$node_(21) setdest 685.403262 2168.822203 14.782163" ;#
-$ns_ at 239.020000 "$node_(36) setdest 10.000000 2297.153204 29.240031" ;#
-$ns_ at 247.259944 "$node_(36) switch OFF" ;# leaving_area
-$ns_ at 239.377571 "$node_(79) setdest 1980.000000 2190.000000 15.455914" ;#
-$ns_ at 240.020000 "$node_(1) setdest 602.456318 2182.981427 16.430355" ;#
-$ns_ at 242.020000 "$node_(44) setdest 10.000000 2297.153203 26.264286" ;#
-$ns_ at 251.193530 "$node_(44) switch OFF" ;# leaving_area
-#$ns_ at 242.020000 $node_(63) arrived_to 1860.000000 1960.000000
-$ns_ at 242.020000 "$node_(63) switch OFF" ;# arrived_to
-$ns_ at 243.020000 "$node_(63) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 243.030000 "$node_(63) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 243.020000 $node_(90) arrived_to 1290.000000 1090.000000
-$ns_ at 243.020000 "$node_(90) switch OFF" ;# arrived_to
-$ns_ at 244.020000 "$node_(90) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 244.030000 "$node_(90) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-#$ns_ at 244.020000 $node_(73) arrived_to 1980.000000 2190.000000
-$ns_ at 244.020000 "$node_(73) switch OFF" ;# arrived_to
-$ns_ at 245.020000 "$node_(73) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 245.030000 "$node_(73) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 245.020000 "$node_(91) setdest 652.346233 1231.317862 16.621823" ;#
-$ns_ at 245.979172 "$node_(33) setdest 1347.377906 2052.908694 15.278138" ;#
-$ns_ at 246.097244 "$node_(57) setdest 1477.003253 2029.415127 14.906121" ;#
-$ns_ at 248.119765 "$node_(80) setdest 2022.303833 1792.702203 13.262811" ;#
-$ns_ at 249.020000 "$node_(29) setdest 788.024513 2151.304532 17.646027" ;#
-$ns_ at 250.008177 "$node_(93) setdest 3310.000000 610.000000 14.668817" ;#
-$ns_ at 250.247357 "$node_(82) setdest 2091.946353 1720.916836 15.145760" ;#
-$ns_ at 251.020000 "$node_(50) setdest 10.000000 2297.153203 31.914843" ;#
-$ns_ at 258.569347 "$node_(50) switch OFF" ;# leaving_area
-$ns_ at 252.415016 "$node_(92) setdest 1290.000000 1090.000000 18.525334" ;#
-$ns_ at 252.828031 "$node_(21) setdest 444.675750 2209.914914 15.036167" ;#
-#$ns_ at 257.020000 $node_(85) arrived_to 2510.000000 1290.000000
-$ns_ at 257.020000 "$node_(85) switch OFF" ;# arrived_to
-$ns_ at 258.020000 "$node_(85) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 258.030000 "$node_(85) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 258.847881 "$node_(86) setdest 1860.000000 1960.000000 15.090166" ;#
-$ns_ at 259.020000 "$node_(87) setdest 1294.979871 1877.357940 16.121572" ;#
-#$ns_ at 261.020000 $node_(92) arrived_to 1290.000000 1090.000000
-$ns_ at 261.020000 "$node_(92) switch OFF" ;# arrived_to
-$ns_ at 262.020000 "$node_(92) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 262.030000 "$node_(92) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 261.805603 "$node_(57) setdest 1282.120421 2064.736096 17.465197" ;#
-$ns_ at 261.902303 "$node_(75) setdest 3558.437585 2466.744969 17.642329" ;#
-$ns_ at 262.358770 "$node_(80) setdest 1860.000000 1960.000000 11.855327" ;#
-$ns_ at 262.670923 "$node_(33) setdest 1148.444158 2088.963860 17.976508" ;#
-$ns_ at 262.887292 "$node_(82) setdest 1860.000000 1960.000000 12.746724" ;#
-$ns_ at 265.288179 "$node_(29) setdest 245.000000 2244.000000 15.860941" ;#
-$ns_ at 300.010000 "$node_(29) switch OFF" ;# T_max_OFF
-$ns_ at 266.656773 "$node_(67) setdest 2784.048966 2528.059823 11.611108" ;#
-$ns_ at 0.0 "$node_(96) switch OFF" ;# set_X,Y,Z
-$node_(96) set X_ 0.000000
-$node_(96) set Y_ 0.000000
-$node_(96) set Z_ 0.0
-$ns_ at 272.965033 "$node_(96) setdest 3838.461539 3010.000000 1000000000.000000" ;# init_node
-$ns_ at 272.975033 "$node_(96) switch ON" ;# inside
-$ns_ at 272.975033 "$node_(96) setdest 3800.000000 2970.000000 27.135580" ;#
-$ns_ at 268.949452 "$node_(1) setdest 557.162097 2190.713250 17.426686" ;#
-$ns_ at 269.020000 "$node_(86) setdest 1949.861359 2132.234271 13.400689" ;#
-$ns_ at 269.069512 "$node_(21) setdest 245.000000 2244.000000 18.498178" ;#
-$ns_ at 270.020000 "$node_(79) setdest 1527.825258 2429.108110 15.340410" ;#
-$ns_ at 300.010000 "$node_(79) switch OFF" ;# T_max_OFF
-$ns_ at 271.586178 "$node_(1) setdest 245.000000 2244.000000 16.295176" ;#
-$ns_ at 272.307661 "$node_(67) setdest 2660.000000 2730.000000 10.434763" ;#
-$ns_ at 273.145744 "$node_(57) setdest 1071.000000 2103.000000 15.464606" ;#
-$ns_ at 0.0 "$node_(97) switch OFF" ;# set_X,Y,Z
-$node_(97) set X_ 0.000000
-$node_(97) set Y_ 0.000000
-$node_(97) set Z_ 0.0
-$ns_ at 279.010000 "$node_(97) setdest 2560.000000 10.000000 1000000000.000000" ;# init_node
-$ns_ at 279.020000 "$node_(97) switch ON" ;# inside
-$ns_ at 279.020000 "$node_(97) setdest 2560.000000 10.000000 14.938944" ;#
-$ns_ at 273.917529 "$node_(33) setdest 1071.000000 2103.000000 19.184982" ;#
-$ns_ at 0.0 "$node_(98) switch OFF" ;# set_X,Y,Z
-$node_(98) set X_ 0.000000
-$node_(98) set Y_ 0.000000
-$node_(98) set Z_ 0.0
-$ns_ at 282.925931 "$node_(98) setdest 3838.461539 3010.000000 1000000000.000000" ;# init_node
-$ns_ at 282.935931 "$node_(98) switch ON" ;# inside
-$ns_ at 282.935931 "$node_(98) setdest 3800.000000 2970.000000 26.626446" ;#
-$ns_ at 276.020000 "$node_(96) setdest 3474.475390 2291.823728 16.199655" ;#
-$ns_ at 300.010000 "$node_(96) switch OFF" ;# T_max_OFF
-$ns_ at 276.020000 "$node_(94) setdest 1339.605990 1162.858798 15.675509" ;#
-$ns_ at 279.020000 "$node_(93) setdest 3298.774462 840.123521 13.682917" ;#
-$ns_ at 279.020000 "$node_(33) setdest 758.868100 2156.281595 15.403528" ;#
-$ns_ at 280.020000 "$node_(97) setdest 3310.000000 610.000000 13.527727" ;#
-$ns_ at 300.010000 "$node_(97) switch OFF" ;# T_max_OFF
-$ns_ at 281.020000 "$node_(21) setdest 10.000000 2297.153203 30.398557" ;#
-$ns_ at 288.945910 "$node_(21) switch OFF" ;# leaving_area
-$ns_ at 281.642965 "$node_(94) setdest 1484.258241 1375.316791 13.340685" ;#
-$ns_ at 300.010000 "$node_(94) switch OFF" ;# T_max_OFF
-$ns_ at 283.020000 "$node_(80) setdest 1502.174391 2024.853057 16.364369" ;#
-$ns_ at 300.010000 "$node_(80) switch OFF" ;# T_max_OFF
-$ns_ at 283.516799 "$node_(86) setdest 1980.000000 2190.000000 14.468669" ;#
-$ns_ at 284.313260 "$node_(91) setdest 431.300048 1280.306476 17.867474" ;#
-$ns_ at 286.020000 "$node_(98) setdest 3801.473672 2750.422828 15.669625" ;#
-$ns_ at 300.010000 "$node_(98) switch OFF" ;# T_max_OFF
-$ns_ at 286.020000 "$node_(69) setdest 918.365533 2129.055036 18.384042" ;#
-$ns_ at 286.756865 "$node_(87) setdest 1142.462024 2031.007645 16.632495" ;#
-$ns_ at 287.020000 "$node_(77) setdest 1551.772884 2015.863723 14.961985" ;#
-$ns_ at 300.010000 "$node_(77) switch OFF" ;# T_max_OFF
-$ns_ at 288.020000 "$node_(57) setdest 803.748295 2148.620448 15.729994" ;#
-$ns_ at 300.010000 "$node_(57) switch OFF" ;# T_max_OFF
-$ns_ at 289.020000 "$node_(86) setdest 2353.960580 2486.968696 16.383618" ;#
-$ns_ at 300.010000 "$node_(86) switch OFF" ;# T_max_OFF
-#$ns_ at 289.020000 $node_(95) arrived_to 4660.000000 400.000000
-$ns_ at 289.020000 "$node_(95) switch OFF" ;# arrived_to
-$ns_ at 290.020000 "$node_(95) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 290.030000 "$node_(95) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 290.020000 "$node_(82) setdest 1540.540627 2017.899481 16.364369" ;#
-$ns_ at 300.010000 "$node_(82) switch OFF" ;# T_max_OFF
-$ns_ at 291.110744 "$node_(75) setdest 3800.000000 2970.000000 15.990818" ;#
-$ns_ at 300.010000 "$node_(75) switch OFF" ;# T_max_OFF
-$ns_ at 292.020000 "$node_(1) setdest 10.000000 2297.153204 31.068618" ;#
-$ns_ at 299.774971 "$node_(1) switch OFF" ;# leaving_area
-$ns_ at 294.442649 "$node_(69) setdest 667.501399 2171.878090 15.432507" ;#
-$ns_ at 300.010000 "$node_(69) switch OFF" ;# T_max_OFF
-$ns_ at 295.858307 "$node_(93) setdest 3296.273248 891.398413 15.927207" ;#
-#$ns_ at 296.020000 $node_(67) arrived_to 2660.000000 2730.000000
-$ns_ at 296.020000 "$node_(67) switch OFF" ;# arrived_to
-$ns_ at 297.020000 "$node_(67) setdest 0.010000 0.010000 1000000001.000000" ;# remove_from_area_upon__arrived_to
-$ns_ at 297.030000 "$node_(67) setdest 0.020000 0.020000 1000000001.000000" ;# nam_hack__force_remove
-$ns_ at 296.984866 "$node_(91) setdest 10.000000 1373.675676 16.367380" ;#
-$ns_ at 300.010000 "$node_(91) switch OFF" ;# T_max_OFF
-$ns_ at 299.081462 "$node_(93) setdest 3290.000000 1020.000000 12.955074" ;#
-$ns_ at 300.010000 "$node_(93) switch OFF" ;# T_max_OFF
-$ns_ at 299.576777 "$node_(33) setdest 597.299018 2183.861790 18.859074" ;#
-$ns_ at 300.010000 "$node_(33) switch OFF" ;# T_max_OFF
-$ns_ at 299.773230 "$node_(87) setdest 1071.000000 2103.000000 19.333470" ;#
-$ns_ at 300.010000 "$node_(87) switch OFF" ;# T_max_OFF
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/examples/vanet-routing-compare.cc ns-3.39/src/wave/examples/vanet-routing-compare.cc
--- ns-3.38/src/wave/examples/vanet-routing-compare.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/examples/vanet-routing-compare.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,2500 +0,0 @@
-/*
- * Copyright (c) 2014 North Carolina State 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: Scott E. Carpenter <scarpen@ncsu.edu>
- *
- */
-
-/*
- * This example program allows one to run vehicular ad hoc
- * network (VANET) simulation scenarios in ns-3 to assess
- * performance by evaluating different 802.11p MAC/PHY
- * characteristics, propagation loss models (e.g. Friss,
- * Two-Ray Ground, or ITU R-P.1411), and application traffic
- * (e.g. Basic Safety Message) and/or routing traffic (e.g.
- * DSDV, AODV, OLSR, or DSR) under either a synthetic highway
- * scenario (i.e. a random waypoint mobility model) or by
- * playing back mobility trace files (i.e. ns-2 movement files).
- *
- * The script draws from several ns-3 examples, including:
- * /examples/routing/manet-routing-compare.cc
- * /src/propagation/model/itu-r-1411-los-propagation-loss-model.cc
- * /src/mobility/examples/ns2-mobility-trace.cc
- * /src/wave/examples/wave-simple-80211p.cc
- *
- * The script allows many parameters to be modified and
- * includes two predefined scenarios.  By default
- * scenario=1 runs for 10 simulated seconds with 40 nodes
- * (i.e. vehicles) moving according to RandomWaypointMobilityModel
- * with a speed of 20 m/s and no pause time within a 300x1500 m
- * region.  The WiFi is 802.11p with continuous access to a 10 MHz
- * Control Channel (CH) for all traffic.  All nodes transmit a
- * 200-byte safety message 10 times per second at 6 Mbps.
- * Additionally, all nodes (optionally) attempt to
- * continuously route 64-byte packets at an application
- * rate of 2.048 Kbps to one of 10 other nodes,
- * selected as sink nodes. The default routing protocol is AODV
- * and the Two-Ray Ground loss model is used.
- * The transmit power is set to 20 dBm and the transmission range
- * for safety message packet delivery is 145 m.
- *
- * Scenario 2 plays back vehicular trace files in
- * ns-2 movement format, and are taken from:
- * https://web.archive.org/web/20150218095728/http://www.lst.inf.ethz.ch/research/ad-hoc/car-traces
- * This scenario is 300 simulation seconds of 99
- * vehicles respectively within the Unterstrass
- * section of Zurich Switzerland that travel based on
- * models derived from real traffic data.  Note that these
- * scenarios can require a lot of clock time to complete.
- *
- * All parameters can be changed from their defaults (see
- * --help) and changing simulation parameters can have dramatic
- * impact on network performance.
- *
- * Several items can be output:
- * - a CSV file of data reception statistics, output once per
- *   second
- * - final statistics, in a CSV file
- * - dump of routing tables at 5 seconds into the simulation
- * - ASCII trace file
- * - PCAP trace files for each node
- *
- * Simulation scenarios can be defined and configuration
- * settings can be saved using config-store (raw text)
- * which can they be replayed again.  This is an easy way
- * to define and save the settings for a scenario, and then
- * re-execute the same scenario exactly, or to set up
- * several different simulation scenarios.
- * For example, to set up a scenario and save the configuration
- * as "scenario1.txt":
- *   ./ns3 run "vanet-routing-compare --scenario=1 --saveconfig=scenario1.txt"
- * Then, to re-play the scenario using the save configuration
- * settings:
- *   ./ns3 run "vanet-routing-compare --loadconfig=scenario1.txt"
- *
- * Class Diagram:
- *   main()
- *     +--uses-- VanetRoutingExperiment
- *                 +--is_a--- WifiApp
- *                 +--uses--- ConfigStoreHelper
- *                 +--has_a-- WaveBsmHelper
- *                 |            +--has_a-- WaveBsmStats
- *                 +--has_a-- RoutingHelper
- *                 |            +--has_a--RoutingStats
- *                 +--has_a-- WifiPhyStats
- *
- */
-
-#include "ns3/aodv-module.h"
-#include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
-#include "ns3/core-module.h"
-#include "ns3/dsdv-module.h"
-#include "ns3/dsr-module.h"
-#include "ns3/flow-monitor-module.h"
-#include "ns3/integer.h"
-#include "ns3/internet-module.h"
-#include "ns3/itu-r-1411-los-propagation-loss-model.h"
-#include "ns3/mobility-module.h"
-#include "ns3/network-module.h"
-#include "ns3/ocb-wifi-mac.h"
-#include "ns3/olsr-module.h"
-#include "ns3/wave-bsm-helper.h"
-#include "ns3/wave-helper.h"
-#include "ns3/wave-mac-helper.h"
-#include "ns3/wifi-80211p-helper.h"
-#include "ns3/yans-wifi-helper.h"
-
-#include <fstream>
-#include <iostream>
-
-using namespace ns3;
-using namespace dsr;
-
-NS_LOG_COMPONENT_DEFINE("vanet-routing-compare");
-
-/**
- * \ingroup wave
- * \brief The RoutingStats class manages collects statistics
- * on routing data (application-data packet and byte counts)
- * for the vehicular network
- */
-class RoutingStats
-{
-  public:
-    /**
-     * \brief Constructor
-     */
-    RoutingStats();
-
-    /**
-     * \brief Returns the number of bytes received
-     * \return the number of bytes received
-     */
-    uint32_t GetRxBytes() const;
-
-    /**
-     * \brief Returns the cumulative number of bytes received
-     * \return the cumulative number of bytes received
-     */
-    uint32_t GetCumulativeRxBytes() const;
-
-    /**
-     * \brief Returns the count of packets received
-     * \return the count of packets received
-     */
-    uint32_t GetRxPkts() const;
-
-    /**
-     * \brief Returns the cumulative count of packets received
-     * \return the cumulative count of packets received
-     */
-    uint32_t GetCumulativeRxPkts() const;
-
-    /**
-     * \brief Increments the number of (application-data)
-     * bytes received, not including MAC/PHY overhead
-     * \param rxBytes the number of bytes received
-     */
-    void IncRxBytes(uint32_t rxBytes);
-
-    /**
-     * \brief Increments the count of packets received
-     */
-    void IncRxPkts();
-
-    /**
-     * \brief Sets the number of bytes received.
-     * \param rxBytes the number of bytes received
-     */
-    void SetRxBytes(uint32_t rxBytes);
-
-    /**
-     * \brief Sets the number of packets received
-     * \param rxPkts the number of packets received
-     */
-    void SetRxPkts(uint32_t rxPkts);
-
-    /**
-     * \brief Returns the number of bytes transmitted
-     * \return the number of bytes transmitted
-     */
-    uint32_t GetTxBytes() const;
-
-    /**
-     * \brief Returns the cumulative number of bytes transmitted
-     * \return the cumulative number of bytes transmitted
-     */
-    uint32_t GetCumulativeTxBytes() const;
-
-    /**
-     * \brief Returns the number of packets transmitted
-     * \return the number of packets transmitted
-     */
-    uint32_t GetTxPkts() const;
-
-    /**
-     * \brief Returns the cumulative number of packets transmitted
-     * \return the cumulative number of packets transmitted
-     */
-    uint32_t GetCumulativeTxPkts() const;
-
-    /**
-     * \brief Increment the number of bytes transmitted
-     * \param txBytes the number of additional bytes transmitted
-     */
-    void IncTxBytes(uint32_t txBytes);
-
-    /**
-     * \brief Increment the count of packets transmitted
-     */
-    void IncTxPkts();
-
-    /**
-     * \brief Sets the number of bytes transmitted
-     * \param txBytes the number of bytes transmitted
-     */
-    void SetTxBytes(uint32_t txBytes);
-
-    /**
-     * \brief Sets the number of packets transmitted
-     * \param txPkts the number of packets transmitted
-     */
-    void SetTxPkts(uint32_t txPkts);
-
-  private:
-    uint32_t m_RxBytes;           ///< receive bytes
-    uint32_t m_cumulativeRxBytes; ///< cumulative receive bytes
-    uint32_t m_RxPkts;            ///< receive packets
-    uint32_t m_cumulativeRxPkts;  ///< cumulative receive packets
-    uint32_t m_TxBytes;           ///< transmit bytes
-    uint32_t m_cumulativeTxBytes; ///< cumulative transmit bytes
-    uint32_t m_TxPkts;            ///< transmit packets
-    uint32_t m_cumulativeTxPkts;  ///< cumulative transmit packets
-};
-
-RoutingStats::RoutingStats()
-    : m_RxBytes(0),
-      m_cumulativeRxBytes(0),
-      m_RxPkts(0),
-      m_cumulativeRxPkts(0),
-      m_TxBytes(0),
-      m_cumulativeTxBytes(0),
-      m_TxPkts(0),
-      m_cumulativeTxPkts(0)
-{
-}
-
-uint32_t
-RoutingStats::GetRxBytes() const
-{
-    return m_RxBytes;
-}
-
-uint32_t
-RoutingStats::GetCumulativeRxBytes() const
-{
-    return m_cumulativeRxBytes;
-}
-
-uint32_t
-RoutingStats::GetRxPkts() const
-{
-    return m_RxPkts;
-}
-
-uint32_t
-RoutingStats::GetCumulativeRxPkts() const
-{
-    return m_cumulativeRxPkts;
-}
-
-void
-RoutingStats::IncRxBytes(uint32_t rxBytes)
-{
-    m_RxBytes += rxBytes;
-    m_cumulativeRxBytes += rxBytes;
-}
-
-void
-RoutingStats::IncRxPkts()
-{
-    m_RxPkts++;
-    m_cumulativeRxPkts++;
-}
-
-void
-RoutingStats::SetRxBytes(uint32_t rxBytes)
-{
-    m_RxBytes = rxBytes;
-}
-
-void
-RoutingStats::SetRxPkts(uint32_t rxPkts)
-{
-    m_RxPkts = rxPkts;
-}
-
-uint32_t
-RoutingStats::GetTxBytes() const
-{
-    return m_TxBytes;
-}
-
-uint32_t
-RoutingStats::GetCumulativeTxBytes() const
-{
-    return m_cumulativeTxBytes;
-}
-
-uint32_t
-RoutingStats::GetTxPkts() const
-{
-    return m_TxPkts;
-}
-
-uint32_t
-RoutingStats::GetCumulativeTxPkts() const
-{
-    return m_cumulativeTxPkts;
-}
-
-void
-RoutingStats::IncTxBytes(uint32_t txBytes)
-{
-    m_TxBytes += txBytes;
-    m_cumulativeTxBytes += txBytes;
-}
-
-void
-RoutingStats::IncTxPkts()
-{
-    m_TxPkts++;
-    m_cumulativeTxPkts++;
-}
-
-void
-RoutingStats::SetTxBytes(uint32_t txBytes)
-{
-    m_TxBytes = txBytes;
-}
-
-void
-RoutingStats::SetTxPkts(uint32_t txPkts)
-{
-    m_TxPkts = txPkts;
-}
-
-/**
- * \ingroup wave
- * \brief The RoutingHelper class generates routing data between
- * nodes (vehicles) and uses the RoutingStats class to collect statistics
- * on routing data (application-data packet and byte counts).
- * A routing protocol is configured, and all nodes attempt to send
- * (i.e. route) small packets to another node, which acts as
- * data sinks.  Not all nodes act as data sinks.
- * for the vehicular network
- */
-class RoutingHelper : public Object
-{
-  public:
-    /**
-     * \brief Get class TypeId
-     * \return the TypeId for the class
-     */
-    static TypeId GetTypeId();
-
-    /**
-     * \brief Constructor
-     */
-    RoutingHelper();
-
-    /**
-     * \brief Destructor
-     */
-    ~RoutingHelper() override;
-
-    /**
-     * \brief Installs routing functionality on nodes and their
-     * devices and interfaces.
-     * \param c node container
-     * \param d net device container
-     * \param i IPv4 interface container
-     * \param totalTime the total time that nodes should attempt to
-     * route data
-     * \param protocol the routing protocol (1=OLSR;2=AODV;3=DSDV;4=DSR)
-     * \param nSinks the number of nodes which will act as data sinks
-     * \param routingTables whether to dump routing tables at t=5 seconds
-     */
-    void Install(NodeContainer& c,
-                 NetDeviceContainer& d,
-                 Ipv4InterfaceContainer& i,
-                 double totalTime,
-                 int protocol,
-                 uint32_t nSinks,
-                 bool routingTables);
-
-    /**
-     * \brief Trace the receipt of an on-off-application generated packet
-     * \param context this object
-     * \param packet a received packet
-     */
-    void OnOffTrace(std::string context, Ptr<const Packet> packet);
-
-    /**
-     * \brief Returns the RoutingStats instance
-     * \return the RoutingStats instance
-     */
-    RoutingStats& GetRoutingStats();
-
-    /**
-     * \brief Enable/disable logging
-     * \param log whether to enable logging
-     */
-    void SetLogging(bool log);
-
-  private:
-    /**
-     * \brief Sets up the protocol protocol on the nodes
-     * \param c node container
-     */
-    void SetupRoutingProtocol(NodeContainer& c);
-
-    /**
-     * \brief Assigns IPv4 addresses to net devices and their interfaces
-     * \param d net device container
-     * \param adhocTxInterfaces IPv4 interface container
-     */
-    void AssignIpAddresses(NetDeviceContainer& d, Ipv4InterfaceContainer& adhocTxInterfaces);
-
-    /**
-     * \brief Sets up routing messages on the nodes and their interfaces
-     * \param c node container
-     * \param adhocTxInterfaces IPv4 interface container
-     */
-    void SetupRoutingMessages(NodeContainer& c, Ipv4InterfaceContainer& adhocTxInterfaces);
-
-    /**
-     * \brief Sets up a routing packet for transmission
-     * \param addr destination address
-     * \param node source node
-     * \return Socket to be used for sending/receiving a routed data packet
-     */
-    Ptr<Socket> SetupRoutingPacketReceive(Ipv4Address addr, Ptr<Node> node);
-
-    /**
-     * \brief Process a received routing packet
-     * \param socket the receiving socket
-     */
-    void ReceiveRoutingPacket(Ptr<Socket> socket);
-
-    double m_TotalSimTime;      ///< seconds
-    uint32_t m_protocol;        ///< routing protocol; 0=NONE, 1=OLSR, 2=AODV, 3=DSDV, 4=DSR
-    uint32_t m_port;            ///< port
-    uint32_t m_nSinks;          ///< number of sink nodes (< all nodes)
-    bool m_routingTables;       ///< dump routing table (at t=5 sec).  0=No, 1=Yes
-    RoutingStats routingStats;  ///< routing statistics
-    std::string m_protocolName; ///< protocol name
-    bool m_log;                 ///< log
-};
-
-NS_OBJECT_ENSURE_REGISTERED(RoutingHelper);
-
-TypeId
-RoutingHelper::GetTypeId()
-{
-    static TypeId tid =
-        TypeId("ns3::RoutingHelper").SetParent<Object>().AddConstructor<RoutingHelper>();
-    return tid;
-}
-
-RoutingHelper::RoutingHelper()
-    : m_TotalSimTime(300.01),
-      m_protocol(0),
-      m_port(9),
-      m_nSinks(0),
-      m_routingTables(false),
-      m_log(false)
-{
-}
-
-RoutingHelper::~RoutingHelper()
-{
-}
-
-void
-RoutingHelper::Install(NodeContainer& c,
-                       NetDeviceContainer& d,
-                       Ipv4InterfaceContainer& i,
-                       double totalTime,
-                       int protocol,
-                       uint32_t nSinks,
-                       bool routingTables)
-{
-    m_TotalSimTime = totalTime;
-    m_protocol = protocol;
-    m_nSinks = nSinks;
-    m_routingTables = routingTables;
-
-    SetupRoutingProtocol(c);
-    AssignIpAddresses(d, i);
-    SetupRoutingMessages(c, i);
-}
-
-Ptr<Socket>
-RoutingHelper::SetupRoutingPacketReceive(Ipv4Address addr, Ptr<Node> node)
-{
-    TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
-    Ptr<Socket> sink = Socket::CreateSocket(node, tid);
-    InetSocketAddress local = InetSocketAddress(addr, m_port);
-    sink->Bind(local);
-    sink->SetRecvCallback(MakeCallback(&RoutingHelper::ReceiveRoutingPacket, this));
-
-    return sink;
-}
-
-void
-RoutingHelper::SetupRoutingProtocol(NodeContainer& c)
-{
-    AodvHelper aodv;
-    OlsrHelper olsr;
-    DsdvHelper dsdv;
-    DsrHelper dsr;
-    DsrMainHelper dsrMain;
-    Ipv4ListRoutingHelper list;
-    InternetStackHelper internet;
-
-    Time rtt = Time(5.0);
-    AsciiTraceHelper ascii;
-    Ptr<OutputStreamWrapper> rtw = ascii.CreateFileStream("routing_table");
-
-    switch (m_protocol)
-    {
-    case 0:
-        m_protocolName = "NONE";
-        break;
-    case 1:
-        if (m_routingTables)
-        {
-            Ipv4RoutingHelper::PrintRoutingTableAllAt(rtt, rtw);
-        }
-        list.Add(olsr, 100);
-        m_protocolName = "OLSR";
-        break;
-    case 2:
-        if (m_routingTables)
-        {
-            Ipv4RoutingHelper::PrintRoutingTableAllAt(rtt, rtw);
-        }
-        list.Add(aodv, 100);
-        m_protocolName = "AODV";
-        break;
-    case 3:
-        if (m_routingTables)
-        {
-            ns3::DsdvHelper::PrintRoutingTableAllAt(rtt, rtw);
-        }
-        list.Add(dsdv, 100);
-        m_protocolName = "DSDV";
-        break;
-    case 4:
-        // setup is later
-        m_protocolName = "DSR";
-        break;
-    default:
-        NS_FATAL_ERROR("No such protocol:" << m_protocol);
-        break;
-    }
-
-    if (m_protocol < 4)
-    {
-        internet.SetRoutingHelper(list);
-        internet.Install(c);
-    }
-    else if (m_protocol == 4)
-    {
-        internet.Install(c);
-        dsrMain.Install(dsr, c);
-    }
-
-    if (m_log)
-    {
-        NS_LOG_UNCOND("Routing Setup for " << m_protocolName);
-    }
-}
-
-void
-RoutingHelper::AssignIpAddresses(NetDeviceContainer& d, Ipv4InterfaceContainer& adhocTxInterfaces)
-{
-    NS_LOG_INFO("Assigning IP addresses");
-    Ipv4AddressHelper addressAdhoc;
-    // we may have a lot of nodes, and want them all
-    // in same subnet, to support broadcast
-    addressAdhoc.SetBase("10.1.0.0", "255.255.0.0");
-    adhocTxInterfaces = addressAdhoc.Assign(d);
-}
-
-void
-RoutingHelper::SetupRoutingMessages(NodeContainer& c, Ipv4InterfaceContainer& adhocTxInterfaces)
-{
-    // Setup routing transmissions
-    OnOffHelper onoff1("ns3::UdpSocketFactory", Address());
-    onoff1.SetAttribute("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1.0]"));
-    onoff1.SetAttribute("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0.0]"));
-
-    Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable>();
-    int64_t stream = 2;
-    var->SetStream(stream);
-    for (uint32_t i = 0; i < m_nSinks; i++)
-    {
-        // protocol == 0 means no routing data, WAVE BSM only
-        // so do not set up sink
-        if (m_protocol != 0)
-        {
-            Ptr<Socket> sink = SetupRoutingPacketReceive(adhocTxInterfaces.GetAddress(i), c.Get(i));
-        }
-
-        AddressValue remoteAddress(InetSocketAddress(adhocTxInterfaces.GetAddress(i), m_port));
-        onoff1.SetAttribute("Remote", remoteAddress);
-
-        ApplicationContainer temp = onoff1.Install(c.Get(i + m_nSinks));
-        temp.Start(Seconds(var->GetValue(1.0, 2.0)));
-        temp.Stop(Seconds(m_TotalSimTime));
-    }
-}
-
-/**
- * Print a received routing packet on a string
- * \param socket Rx socket
- * \param packet Rx packet
- * \param srcAddress source address
- * \return the built string
- */
-static inline std::string
-PrintReceivedRoutingPacket(Ptr<Socket> socket, Ptr<Packet> packet, Address srcAddress)
-{
-    std::ostringstream oss;
-
-    oss << Simulator::Now().As(Time::S) << " " << socket->GetNode()->GetId();
-
-    if (InetSocketAddress::IsMatchingType(srcAddress))
-    {
-        InetSocketAddress addr = InetSocketAddress::ConvertFrom(srcAddress);
-        oss << " received one packet from " << addr.GetIpv4();
-    }
-    else
-    {
-        oss << " received one packet!";
-    }
-    return oss.str();
-}
-
-void
-RoutingHelper::ReceiveRoutingPacket(Ptr<Socket> socket)
-{
-    Ptr<Packet> packet;
-    Address srcAddress;
-    while ((packet = socket->RecvFrom(srcAddress)))
-    {
-        // application data, for goodput
-        uint32_t RxRoutingBytes = packet->GetSize();
-        GetRoutingStats().IncRxBytes(RxRoutingBytes);
-        GetRoutingStats().IncRxPkts();
-        if (m_log)
-        {
-            NS_LOG_UNCOND(m_protocolName + " " +
-                          PrintReceivedRoutingPacket(socket, packet, srcAddress));
-        }
-    }
-}
-
-void
-RoutingHelper::OnOffTrace(std::string context, Ptr<const Packet> packet)
-{
-    uint32_t pktBytes = packet->GetSize();
-    routingStats.IncTxBytes(pktBytes);
-}
-
-RoutingStats&
-RoutingHelper::GetRoutingStats()
-{
-    return routingStats;
-}
-
-void
-RoutingHelper::SetLogging(bool log)
-{
-    m_log = log;
-}
-
-/**
- * \ingroup wave
- * \brief The WifiPhyStats class collects Wifi MAC/PHY statistics
- */
-class WifiPhyStats : public Object
-{
-  public:
-    /**
-     * \brief Gets the class TypeId
-     * \return the class TypeId
-     */
-    static TypeId GetTypeId();
-
-    /**
-     * \brief Constructor
-     */
-    WifiPhyStats();
-
-    /**
-     * \brief Destructor
-     */
-    ~WifiPhyStats() override;
-
-    /**
-     * \brief Returns the number of bytes that have been transmitted
-     * (this includes MAC/PHY overhead)
-     * \return the number of bytes transmitted
-     */
-    uint32_t GetTxBytes() const;
-
-    /**
-     * \brief Callback signature for Phy/Tx trace
-     * \param context this object
-     * \param packet packet transmitted
-     * \param mode wifi mode
-     * \param preamble wifi preamble
-     * \param txPower transmission power
-     */
-    void PhyTxTrace(std::string context,
-                    Ptr<const Packet> packet,
-                    WifiMode mode,
-                    WifiPreamble preamble,
-                    uint8_t txPower);
-
-    /**
-     * \brief Callback signature for Phy/TxDrop
-     * \param context this object
-     * \param packet the tx packet being dropped
-     */
-    void PhyTxDrop(std::string context, Ptr<const Packet> packet);
-
-    /**
-     * \brief Callback signature for Phy/RxDrop
-     * \param context this object
-     * \param packet the rx packet being dropped
-     * \param reason the reason for the drop
-     */
-    void PhyRxDrop(std::string context, Ptr<const Packet> packet, WifiPhyRxfailureReason reason);
-
-  private:
-    uint32_t m_phyTxPkts;  ///< phy transmit packets
-    uint32_t m_phyTxBytes; ///< phy transmit bytes
-};
-
-NS_OBJECT_ENSURE_REGISTERED(WifiPhyStats);
-
-TypeId
-WifiPhyStats::GetTypeId()
-{
-    static TypeId tid =
-        TypeId("ns3::WifiPhyStats").SetParent<Object>().AddConstructor<WifiPhyStats>();
-    return tid;
-}
-
-WifiPhyStats::WifiPhyStats()
-    : m_phyTxPkts(0),
-      m_phyTxBytes(0)
-{
-}
-
-WifiPhyStats::~WifiPhyStats()
-{
-}
-
-void
-WifiPhyStats::PhyTxTrace(std::string context,
-                         Ptr<const Packet> packet,
-                         WifiMode mode,
-                         WifiPreamble preamble,
-                         uint8_t txPower)
-{
-    NS_LOG_FUNCTION(this << context << packet << "PHYTX mode=" << mode);
-    ++m_phyTxPkts;
-    uint32_t pktSize = packet->GetSize();
-    m_phyTxBytes += pktSize;
-
-    // NS_LOG_UNCOND ("Received PHY size=" << pktSize);
-}
-
-void
-WifiPhyStats::PhyTxDrop(std::string context, Ptr<const Packet> packet)
-{
-    NS_LOG_UNCOND("PHY Tx Drop");
-}
-
-void
-WifiPhyStats::PhyRxDrop(std::string context,
-                        Ptr<const Packet> packet,
-                        WifiPhyRxfailureReason reason)
-{
-    NS_LOG_UNCOND("PHY Rx Drop");
-}
-
-uint32_t
-WifiPhyStats::GetTxBytes() const
-{
-    return m_phyTxBytes;
-}
-
-/**
- * \ingroup wave
- * \brief The WifiApp class enforces program flow for ns-3 wifi applications
- */
-class WifiApp
-{
-  public:
-    /**
-     * \brief Constructor
-     */
-    WifiApp();
-
-    /**
-     * \brief Destructor
-     */
-    virtual ~WifiApp();
-
-    /**
-     * \brief Enacts simulation of an ns-3 wifi application
-     * \param argc program arguments count
-     * \param argv program arguments
-     */
-    void Simulate(int argc, char** argv);
-
-  protected:
-    /**
-     * \brief Sets default attribute values
-     */
-    virtual void SetDefaultAttributeValues();
-
-    /**
-     * \brief Process command line arguments
-     * \param argc program arguments count
-     * \param argv program arguments
-     */
-    virtual void ParseCommandLineArguments(int argc, char** argv);
-
-    /**
-     * \brief Configure nodes
-     */
-    virtual void ConfigureNodes();
-
-    /**
-     * \brief Configure channels
-     */
-    virtual void ConfigureChannels();
-
-    /**
-     * \brief Configure devices
-     */
-    virtual void ConfigureDevices();
-
-    /**
-     * \brief Configure mobility
-     */
-    virtual void ConfigureMobility();
-
-    /**
-     * \brief Configure applications
-     */
-    virtual void ConfigureApplications();
-
-    /**
-     * \brief Configure tracing
-     */
-    virtual void ConfigureTracing();
-
-    /**
-     * \brief Run the simulation
-     */
-    virtual void RunSimulation();
-
-    /**
-     * \brief Process outputs
-     */
-    virtual void ProcessOutputs();
-};
-
-WifiApp::WifiApp()
-{
-}
-
-WifiApp::~WifiApp()
-{
-}
-
-void
-WifiApp::Simulate(int argc, char** argv)
-{
-    // Simulator Program Flow:
-    // (source:  NS-3 Annual Meeting, May, 2014, session 2 slides 6, 28)
-    //   (HandleProgramInputs:)
-    //   SetDefaultAttributeValues
-    //   ParseCommandLineArguments
-    //   (ConfigureTopology:)
-    //   ConfigureNodes
-    //   ConfigureChannels
-    //   ConfigureDevices
-    //   ConfigureMobility
-    //   ConfigureApplications
-    //     e.g AddInternetStackToNodes
-    //         ConfigureIpAddressingAndRouting
-    //         configureSendMessages
-    //   ConfigureTracing
-    //   RunSimulation
-    //   ProcessOutputs
-
-    SetDefaultAttributeValues();
-    ParseCommandLineArguments(argc, argv);
-    ConfigureNodes();
-    ConfigureChannels();
-    ConfigureDevices();
-    ConfigureMobility();
-    ConfigureApplications();
-    ConfigureTracing();
-    RunSimulation();
-    ProcessOutputs();
-}
-
-void
-WifiApp::SetDefaultAttributeValues()
-{
-}
-
-void
-WifiApp::ParseCommandLineArguments(int argc, char** argv)
-{
-}
-
-void
-WifiApp::ConfigureNodes()
-{
-}
-
-void
-WifiApp::ConfigureChannels()
-{
-}
-
-void
-WifiApp::ConfigureDevices()
-{
-}
-
-void
-WifiApp::ConfigureMobility()
-{
-}
-
-void
-WifiApp::ConfigureApplications()
-{
-}
-
-void
-WifiApp::ConfigureTracing()
-{
-}
-
-void
-WifiApp::RunSimulation()
-{
-}
-
-void
-WifiApp::ProcessOutputs()
-{
-}
-
-/**
- * \ingroup wave
- * \brief The ConfigStoreHelper class simplifies config-store raw text load and save
- */
-class ConfigStoreHelper
-{
-  public:
-    /**
-     * \brief Constructor
-     */
-    ConfigStoreHelper();
-
-    /**
-     * \brief Loads a saved config-store raw text configuration from a given named file
-     * \param configFilename the name of the config-store raw text file
-     */
-    void LoadConfig(std::string configFilename);
-
-    /**
-     * \brief Saves a configuration to a given named config-store raw text configuration file
-     * \param configFilename the name of the config-store raw text file
-     */
-    void SaveConfig(std::string configFilename);
-};
-
-ConfigStoreHelper::ConfigStoreHelper()
-{
-}
-
-void
-ConfigStoreHelper::LoadConfig(std::string configFilename)
-{
-    // Input config store from txt format
-    Config::SetDefault("ns3::ConfigStore::Filename", StringValue(configFilename));
-    Config::SetDefault("ns3::ConfigStore::FileFormat", StringValue("RawText"));
-    Config::SetDefault("ns3::ConfigStore::Mode", StringValue("Load"));
-    ConfigStore inputConfig;
-    inputConfig.ConfigureDefaults();
-    // inputConfig.ConfigureAttributes ();
-}
-
-void
-ConfigStoreHelper::SaveConfig(std::string configFilename)
-{
-    // only save if a non-empty filename has been specified
-    if (!configFilename.empty())
-    {
-        // Output config store to txt format
-        Config::SetDefault("ns3::ConfigStore::Filename", StringValue(configFilename));
-        Config::SetDefault("ns3::ConfigStore::FileFormat", StringValue("RawText"));
-        Config::SetDefault("ns3::ConfigStore::Mode", StringValue("Save"));
-        ConfigStore outputConfig;
-        outputConfig.ConfigureDefaults();
-        // outputConfig.ConfigureAttributes ();
-    }
-}
-
-/**
- * \ingroup wave
- * \brief The VanetRoutingExperiment class implements a wifi app that
- * allows VANET routing experiments to be simulated
- */
-class VanetRoutingExperiment : public WifiApp
-{
-  public:
-    /**
-     * \brief Constructor
-     */
-    VanetRoutingExperiment();
-
-  protected:
-    /**
-     * \brief Sets default attribute values
-     */
-    void SetDefaultAttributeValues() override;
-
-    /**
-     * \brief Process command line arguments
-     * \param argc program arguments count
-     * \param argv program arguments
-     */
-    void ParseCommandLineArguments(int argc, char** argv) override;
-
-    /**
-     * \brief Configure nodes
-     */
-    void ConfigureNodes() override;
-
-    /**
-     * \brief Configure channels
-     */
-    void ConfigureChannels() override;
-
-    /**
-     * \brief Configure devices
-     */
-    void ConfigureDevices() override;
-
-    /**
-     * \brief Configure mobility
-     */
-    void ConfigureMobility() override;
-
-    /**
-     * \brief Configure applications
-     */
-    void ConfigureApplications() override;
-
-    /**
-     * \brief Configure tracing
-     */
-    void ConfigureTracing() override;
-
-    /**
-     * \brief Run the simulation
-     */
-    void RunSimulation() override;
-
-    /**
-     * \brief Process outputs
-     */
-    void ProcessOutputs() override;
-
-  private:
-    /**
-     * \brief Run the simulation
-     */
-    void Run();
-
-    /**
-     * \brief Run the simulation
-     * \param argc command line argument count
-     * \param argv command line parameters
-     */
-    void CommandSetup(int argc, char** argv);
-
-    /**
-     * \brief Checks the throughput and outputs summary to CSV file1.
-     * This is scheduled and called once per second
-     */
-    void CheckThroughput();
-
-    /**
-     * \brief Set up log file
-     */
-    void SetupLogFile();
-
-    /**
-     * \brief Set up logging
-     */
-    void SetupLogging();
-
-    /**
-     * \brief Configure default attributes
-     */
-    void ConfigureDefaults();
-
-    /**
-     * \brief Set up the adhoc mobility nodes
-     */
-    void SetupAdhocMobilityNodes();
-
-    /**
-     * \brief Set up the adhoc devices
-     */
-    void SetupAdhocDevices();
-
-    /**
-     * \brief Set up generation of IEEE 1609 WAVE messages,
-     * as a Basic Safety Message (BSM).  The BSM is typically
-     * a ~200-byte packets broadcast by all vehicles at a nominal
-     * rate of 10 Hz
-     */
-    void SetupWaveMessages();
-
-    /**
-     * \brief Set up generation of packets to be routed
-     * through the vehicular network
-     */
-    void SetupRoutingMessages();
-
-    /**
-     * \brief Set up a prescribed scenario
-     */
-    void SetupScenario();
-
-    /**
-     * \brief Write the header line to the CSV file1
-     */
-    void WriteCsvHeader();
-
-    /**
-     * \brief Set up configuration parameter from the global variables
-     */
-    void SetConfigFromGlobals();
-
-    /**
-     * \brief Set up the global variables from the configuration parameters
-     */
-    void SetGlobalsFromConfig();
-
-    /**
-     * Course change function
-     * \param os the output stream
-     * \param context trace source context (unused)
-     * \param mobility the mobility model
-     */
-    static void CourseChange(std::ostream* os,
-                             std::string context,
-                             Ptr<const MobilityModel> mobility);
-
-    uint32_t m_port;            ///< port
-    std::string m_CSVfileName;  ///< CSV file name
-    std::string m_CSVfileName2; ///< CSV file name
-    uint32_t m_nSinks;          ///< number of sinks
-    std::string m_protocolName; ///< protocol name
-    double m_txp;               ///< distance
-    bool m_traceMobility;       ///< trace mobility
-    uint32_t m_protocol;        ///< protocol
-
-    uint32_t m_lossModel;        ///< loss model
-    uint32_t m_fading;           ///< fading
-    std::string m_lossModelName; ///< loss model name
-
-    std::string m_phyMode; ///< phy mode
-    uint32_t m_80211mode;  ///< 80211 mode
-
-    std::string m_traceFile;                    ///< trace file
-    std::string m_logFile;                      ///< log file
-    uint32_t m_mobility;                        ///< mobility
-    uint32_t m_nNodes;                          ///< number of nodes
-    double m_TotalSimTime;                      ///< total sim time
-    std::string m_rate;                         ///< rate
-    std::string m_phyModeB;                     ///< phy mode
-    std::string m_trName;                       ///< trace file name
-    int m_nodeSpeed;                            ///< in m/s
-    int m_nodePause;                            ///< in s
-    uint32_t m_wavePacketSize;                  ///< bytes
-    double m_waveInterval;                      ///< seconds
-    bool m_verbose;                             ///< verbose
-    std::ofstream m_os;                         ///< output stream
-    NetDeviceContainer m_adhocTxDevices;        ///< adhoc transmit devices
-    Ipv4InterfaceContainer m_adhocTxInterfaces; ///< adhoc transmit interfaces
-    uint32_t m_scenario;                        ///< scenario
-    double m_gpsAccuracyNs;                     ///< GPS accuracy
-    double m_txMaxDelayMs;                      ///< transmit maximum delay
-    bool m_routingTables;                       ///< routing tables
-    bool m_asciiTrace;                          ///< ascii trace
-    bool m_pcap;                                ///< PCAP
-    std::string m_loadConfigFilename;           ///< load config file name
-    std::string m_saveConfigFilename;           ///< save config file name
-
-    WaveBsmHelper m_waveBsmHelper;      ///< helper
-    Ptr<RoutingHelper> m_routingHelper; ///< routing helper
-    Ptr<WifiPhyStats> m_wifiPhyStats;   ///< wifi phy statistics
-    bool m_log;                         ///< log
-    int64_t m_streamIndex;              ///< used to get consistent random numbers across scenarios
-    NodeContainer m_adhocTxNodes;       ///< adhoc transmit nodes
-    double m_txSafetyRange1;            ///< range 1
-    double m_txSafetyRange2;            ///< range 2
-    double m_txSafetyRange3;            ///< range 3
-    double m_txSafetyRange4;            ///< range 4
-    double m_txSafetyRange5;            ///< range 5
-    double m_txSafetyRange6;            ///< range 6
-    double m_txSafetyRange7;            ///< range 7
-    double m_txSafetyRange8;            ///< range 8
-    double m_txSafetyRange9;            ///< range 9
-    double m_txSafetyRange10;           ///< range 10
-    std::vector<double> m_txSafetyRanges; ///< list of ranges
-    std::string m_exp;                    ///< exp
-    Time m_cumulativeBsmCaptureStart;     ///< capture start
-};
-
-VanetRoutingExperiment::VanetRoutingExperiment()
-    : m_port(9),
-      m_CSVfileName("vanet-routing.output.csv"),
-      m_CSVfileName2("vanet-routing.output2.csv"),
-      m_nSinks(10),
-      m_protocolName("protocol"),
-      m_txp(20),
-      m_traceMobility(false),
-      // AODV
-      m_protocol(2),
-      // Two-Ray ground
-      m_lossModel(3),
-      m_fading(0),
-      m_lossModelName(""),
-      m_phyMode("OfdmRate6MbpsBW10MHz"),
-      // 1=802.11p
-      m_80211mode(1),
-      m_traceFile(""),
-      m_logFile("low99-ct-unterstrass-1day.filt.7.adj.log"),
-      m_mobility(1),
-      m_nNodes(156),
-      m_TotalSimTime(300.01),
-      m_rate("2048bps"),
-      m_phyModeB("DsssRate11Mbps"),
-      m_trName("vanet-routing-compare"),
-      m_nodeSpeed(20),
-      m_nodePause(0),
-      m_wavePacketSize(200),
-      m_waveInterval(0.1),
-      m_verbose(false),
-      m_scenario(1),
-      m_gpsAccuracyNs(40),
-      m_txMaxDelayMs(10),
-      m_routingTables(false),
-      m_asciiTrace(false),
-      m_pcap(false),
-      m_loadConfigFilename("load-config.txt"),
-      m_saveConfigFilename(""),
-      m_log(false),
-      m_streamIndex(0),
-      m_adhocTxNodes(),
-      m_txSafetyRange1(50.0),
-      m_txSafetyRange2(100.0),
-      m_txSafetyRange3(150.0),
-      m_txSafetyRange4(200.0),
-      m_txSafetyRange5(250.0),
-      m_txSafetyRange6(300.0),
-      m_txSafetyRange7(350.0),
-      m_txSafetyRange8(400.0),
-      m_txSafetyRange9(450.0),
-      m_txSafetyRange10(500.0),
-      m_txSafetyRanges(),
-      m_exp(""),
-      m_cumulativeBsmCaptureStart(0)
-{
-    m_wifiPhyStats = CreateObject<WifiPhyStats>();
-    m_routingHelper = CreateObject<RoutingHelper>();
-
-    // simply uncond logging during simulation run
-    m_log = true;
-}
-
-void
-VanetRoutingExperiment::SetDefaultAttributeValues()
-{
-    // handled in constructor
-}
-
-// important configuration items stored in global values
-
-/// Port
-static ns3::GlobalValue g_port("VRCport",
-                               "Port",
-                               ns3::UintegerValue(9),
-                               ns3::MakeUintegerChecker<uint32_t>());
-
-/// Number of sink nodes for routing non-BSM traffic
-static ns3::GlobalValue g_nSinks("VRCnSinks",
-                                 "Number of sink nodes for routing non-BSM traffic",
-                                 ns3::UintegerValue(10),
-                                 ns3::MakeUintegerChecker<uint32_t>());
-
-/// Trace mobility 1=yes;0=no
-static ns3::GlobalValue g_traceMobility("VRCtraceMobility",
-                                        "Enable trace mobility",
-                                        ns3::BooleanValue(false),
-                                        ns3::MakeBooleanChecker());
-
-/// Routing protocol
-static ns3::GlobalValue g_protocol("VRCprotocol",
-                                   "Routing protocol",
-                                   ns3::UintegerValue(2),
-                                   ns3::MakeUintegerChecker<uint32_t>());
-
-/// Propagation Loss Model
-static ns3::GlobalValue g_lossModel("VRClossModel",
-                                    "Propagation Loss Model",
-                                    ns3::UintegerValue(3),
-                                    ns3::MakeUintegerChecker<uint32_t>());
-
-/// Fast Fading Model
-static ns3::GlobalValue g_fading("VRCfading",
-                                 "Fast Fading Model",
-                                 ns3::UintegerValue(0),
-                                 ns3::MakeUintegerChecker<uint32_t>());
-
-/// 802.11 mode (0=802.11a;1=802.11p)
-static ns3::GlobalValue g_80211mode("VRC80211mode",
-                                    "802.11 mode (0=802.11a;1=802.11p)",
-                                    ns3::UintegerValue(1),
-                                    ns3::MakeUintegerChecker<uint32_t>());
-
-/// Mobility mode 0=random waypoint;1=mobility trace file
-static ns3::GlobalValue g_mobility("VRCmobility",
-                                   "Mobility mode 0=random waypoint;1=mobility trace file",
-                                   ns3::UintegerValue(1),
-                                   ns3::MakeUintegerChecker<uint32_t>());
-
-/// Number of nodes (vehicles)
-static ns3::GlobalValue g_nNodes("VRCnNodes",
-                                 "Number of nodes (vehicles)",
-                                 ns3::UintegerValue(156),
-                                 ns3::MakeUintegerChecker<uint32_t>());
-
-/// Node speed (m/s) for RWP model
-static ns3::GlobalValue g_nodeSpeed("VRCnodeSpeed",
-                                    "Node speed (m/s) for RWP model",
-                                    ns3::UintegerValue(20),
-                                    ns3::MakeUintegerChecker<uint32_t>());
-
-/// Node pause time (s) for RWP model
-static ns3::GlobalValue g_nodePause("VRCnodePause",
-                                    "Node pause time (s) for RWP model",
-                                    ns3::UintegerValue(0),
-                                    ns3::MakeUintegerChecker<uint32_t>());
-
-/// Size in bytes of WAVE BSM
-static ns3::GlobalValue g_wavePacketSize("VRCwavePacketSize",
-                                         "Size in bytes of WAVE BSM",
-                                         ns3::UintegerValue(200),
-                                         ns3::MakeUintegerChecker<uint32_t>());
-
-/// Verbose 0=no;1=yes
-static ns3::GlobalValue g_verbose("VRCverbose",
-                                  "Enable verbose",
-                                  ns3::BooleanValue(false),
-                                  ns3::MakeBooleanChecker());
-
-/// Scenario
-static ns3::GlobalValue g_scenario("VRCscenario",
-                                   "Scenario",
-                                   ns3::UintegerValue(1),
-                                   ns3::MakeUintegerChecker<uint32_t>());
-
-/// Dump routing tables at t=5 seconds 0=no;1=yes
-static ns3::GlobalValue g_routingTables("VRCroutingTables",
-                                        "Dump routing tables at t=5 seconds",
-                                        ns3::BooleanValue(false),
-                                        ns3::MakeBooleanChecker());
-
-/// Dump ASCII trace 0=no;1=yes
-static ns3::GlobalValue g_asciiTrace("VRCasciiTrace",
-                                     "Dump ASCII trace",
-                                     ns3::BooleanValue(false),
-                                     ns3::MakeBooleanChecker());
-
-/// Generate PCAP files 0=no;1=yes
-static ns3::GlobalValue g_pcap("VRCpcap",
-                               "Generate PCAP files",
-                               ns3::BooleanValue(false),
-                               ns3::MakeBooleanChecker());
-
-/// Simulation start time for capturing cumulative BSM
-static ns3::GlobalValue g_cumulativeBsmCaptureStart(
-    "VRCcumulativeBsmCaptureStart",
-    "Simulation start time for capturing cumulative BSM",
-    ns3::TimeValue(Seconds(0)),
-    ns3::MakeTimeChecker());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange1("VRCtxSafetyRange1",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(50.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange2("VRCtxSafetyRange2",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(100.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange3("VRCtxSafetyRange3",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(150.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange4("VRCtxSafetyRange4",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(200.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange5("VRCtxSafetyRange5",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(250.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange6("VRCtxSafetyRange6",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(300.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange7("VRCtxSafetyRange7",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(350.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange8("VRCtxSafetyRange8",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(400.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange9("VRCtxSafetyRange9",
-                                         "BSM range for PDR inclusion",
-                                         ns3::DoubleValue(450.0),
-                                         ns3::MakeDoubleChecker<double>());
-
-/// BSM range for PDR inclusion
-static ns3::GlobalValue g_txSafetyRange10("VRCtxSafetyRange10",
-                                          "BSM range for PDR inclusion",
-                                          ns3::DoubleValue(500.0),
-                                          ns3::MakeDoubleChecker<double>());
-
-/// Transmission power dBm
-static ns3::GlobalValue g_txp("VRCtxp",
-                              "Transmission power dBm",
-                              ns3::DoubleValue(7.5),
-                              ns3::MakeDoubleChecker<double>());
-
-/// Total simulation time (s)
-static ns3::GlobalValue g_totalTime("VRCtotalTime",
-                                    "Total simulation time (s)",
-                                    ns3::DoubleValue(300.01),
-                                    ns3::MakeDoubleChecker<double>());
-
-/// Interval (s) between WAVE BSMs
-static ns3::GlobalValue g_waveInterval("VRCwaveInterval",
-                                       "Interval (s) between WAVE BSMs",
-                                       ns3::DoubleValue(0.1),
-                                       ns3::MakeDoubleChecker<double>());
-
-/// GPS sync accuracy (ns)
-static ns3::GlobalValue g_gpsAccuracyNs("VRCgpsAccuracyNs",
-                                        "GPS sync accuracy (ns)",
-                                        ns3::DoubleValue(40),
-                                        ns3::MakeDoubleChecker<double>());
-
-/// Tx May Delay (ms)
-static ns3::GlobalValue g_txMaxDelayMs("VRCtxMaxDelayMs",
-                                       "Tx May Delay (ms)",
-                                       ns3::DoubleValue(10),
-                                       ns3::MakeDoubleChecker<double>());
-
-/// CSV filename (for time series data)
-static ns3::GlobalValue g_CSVfileName("VRCCSVfileName",
-                                      "CSV filename (for time series data)",
-                                      ns3::StringValue("vanet-routing.output.csv"),
-                                      ns3::MakeStringChecker());
-
-/// CSV filename 2 (for overall simulation scenario results)
-static ns3::GlobalValue g_CSVfileName2("VRCCSVfileName2",
-                                       "CSV filename 2 (for overall simulation scenario results)",
-                                       ns3::StringValue("vanet-routing.output2.csv"),
-                                       ns3::MakeStringChecker());
-
-/// PHY mode (802.11p)
-static ns3::GlobalValue g_phyMode("VRCphyMode",
-                                  "PHY mode (802.11p)",
-                                  ns3::StringValue("OfdmRate6MbpsBW10MHz"),
-                                  ns3::MakeStringChecker());
-
-/// Mobility trace filename
-static ns3::GlobalValue g_traceFile(
-    "VRCtraceFile",
-    "Mobility trace filename",
-    ns3::StringValue("./src/wave/examples/low99-ct-unterstrass-1day.filt.7.adj.mob"),
-    ns3::MakeStringChecker());
-
-/// Log filename
-static ns3::GlobalValue g_logFile("VRClogFile",
-                                  "Log filename",
-                                  ns3::StringValue("low99-ct-unterstrass-1day.filt.7.adj.log"),
-                                  ns3::MakeStringChecker());
-
-/// Data rate
-static ns3::GlobalValue g_rate("VRCrate",
-                               "Data rate",
-                               ns3::StringValue("2048bps"),
-                               ns3::MakeStringChecker());
-
-/// PHY mode (802.11a)
-static ns3::GlobalValue g_phyModeB("VRCphyModeB",
-                                   "PHY mode (802.11a)",
-                                   ns3::StringValue("DsssRate11Mbps"),
-                                   ns3::MakeStringChecker());
-
-/// Trace name)
-static ns3::GlobalValue g_trName("VRCtrName",
-                                 "Trace name",
-                                 ns3::StringValue("vanet-routing-compare"),
-                                 ns3::MakeStringChecker());
-
-void
-VanetRoutingExperiment::ParseCommandLineArguments(int argc, char** argv)
-{
-    CommandSetup(argc, argv);
-    SetupScenario();
-
-    // user may specify up to 10 different tx distances
-    // to be used for calculating different values of Packet
-    // Delivery Ratio (PDR). Used to see the effects of
-    // fading over distance
-    m_txSafetyRanges.resize(10, 0);
-    m_txSafetyRanges[0] = m_txSafetyRange1;
-    m_txSafetyRanges[1] = m_txSafetyRange2;
-    m_txSafetyRanges[2] = m_txSafetyRange3;
-    m_txSafetyRanges[3] = m_txSafetyRange4;
-    m_txSafetyRanges[4] = m_txSafetyRange5;
-    m_txSafetyRanges[5] = m_txSafetyRange6;
-    m_txSafetyRanges[6] = m_txSafetyRange7;
-    m_txSafetyRanges[7] = m_txSafetyRange8;
-    m_txSafetyRanges[8] = m_txSafetyRange9;
-    m_txSafetyRanges[9] = m_txSafetyRange10;
-
-    ConfigureDefaults();
-
-    // we are done with all configuration
-    // save config-store, if requested
-    SetGlobalsFromConfig();
-    ConfigStoreHelper configStoreHelper;
-    configStoreHelper.SaveConfig(m_saveConfigFilename);
-
-    m_waveBsmHelper.GetWaveBsmStats()->SetLogging(m_log);
-    m_routingHelper->SetLogging(m_log);
-}
-
-void
-VanetRoutingExperiment::ConfigureNodes()
-{
-    m_adhocTxNodes.Create(m_nNodes);
-}
-
-void
-VanetRoutingExperiment::ConfigureChannels()
-{
-    // set up channel and devices
-    SetupAdhocDevices();
-}
-
-void
-VanetRoutingExperiment::ConfigureDevices()
-{
-    // devices are set up in SetupAdhocDevices(),
-    // called by ConfigureChannels()
-
-    // use a PHY callback for tracing
-    // to determine the total amount of
-    // data transmitted, and then used to calculate
-    // the MAC/PHY overhead beyond the app-data
-    if (m_80211mode == 3)
-    {
-        // WAVE
-        Config::Connect("/NodeList/*/DeviceList/*/$ns3::WaveNetDevice/PhyEntities/*/State/Tx",
-                        MakeCallback(&WifiPhyStats::PhyTxTrace, m_wifiPhyStats));
-        Config::Connect("/NodeList/*/DeviceList/*/$ns3::WaveNetDevice/PhyEntities/*/PhyTxDrop",
-                        MakeCallback(&WifiPhyStats::PhyTxDrop, m_wifiPhyStats));
-        Config::Connect("/NodeList/*/DeviceList/*/$ns3::WaveNetDevice/PhyEntities/*/PhyRxDrop",
-                        MakeCallback(&WifiPhyStats::PhyRxDrop, m_wifiPhyStats));
-    }
-    else
-    {
-        Config::Connect("/NodeList/*/DeviceList/*/Phy/State/Tx",
-                        MakeCallback(&WifiPhyStats::PhyTxTrace, m_wifiPhyStats));
-        Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyTxDrop",
-                        MakeCallback(&WifiPhyStats::PhyTxDrop, m_wifiPhyStats));
-        Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phy/PhyRxDrop",
-                        MakeCallback(&WifiPhyStats::PhyRxDrop, m_wifiPhyStats));
-    }
-}
-
-void
-VanetRoutingExperiment::ConfigureMobility()
-{
-    SetupAdhocMobilityNodes();
-}
-
-void
-VanetRoutingExperiment::ConfigureApplications()
-{
-    // Traffic mix consists of:
-    // 1. routing data
-    // 2. Broadcasting of Basic Safety Message (BSM)
-    SetupRoutingMessages();
-    SetupWaveMessages();
-
-    // config trace to capture app-data (bytes) for
-    // routing data, subtracted and used for
-    // routing overhead
-    std::ostringstream oss;
-    oss.str("");
-    oss << "/NodeList/*/ApplicationList/*/$ns3::OnOffApplication/Tx";
-    Config::Connect(oss.str(), MakeCallback(&RoutingHelper::OnOffTrace, m_routingHelper));
-}
-
-void
-VanetRoutingExperiment::ConfigureTracing()
-{
-    WriteCsvHeader();
-    SetupLogFile();
-    SetupLogging();
-
-    AsciiTraceHelper ascii;
-    MobilityHelper::EnableAsciiAll(ascii.CreateFileStream(m_trName + ".mob"));
-}
-
-void
-VanetRoutingExperiment::RunSimulation()
-{
-    Run();
-}
-
-void
-VanetRoutingExperiment::ProcessOutputs()
-{
-    // calculate and output final results
-    double bsm_pdr1 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(1);
-    double bsm_pdr2 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(2);
-    double bsm_pdr3 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(3);
-    double bsm_pdr4 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(4);
-    double bsm_pdr5 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(5);
-    double bsm_pdr6 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(6);
-    double bsm_pdr7 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(7);
-    double bsm_pdr8 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(8);
-    double bsm_pdr9 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(9);
-    double bsm_pdr10 = m_waveBsmHelper.GetWaveBsmStats()->GetCumulativeBsmPdr(10);
-
-    double averageRoutingGoodputKbps = 0.0;
-    uint32_t totalBytesTotal = m_routingHelper->GetRoutingStats().GetCumulativeRxBytes();
-    averageRoutingGoodputKbps = (((double)totalBytesTotal * 8.0) / m_TotalSimTime) / 1000.0;
-
-    // calculate MAC/PHY overhead (mac-phy-oh)
-    // total WAVE BSM bytes sent
-    uint32_t cumulativeWaveBsmBytes = m_waveBsmHelper.GetWaveBsmStats()->GetTxByteCount();
-    uint32_t cumulativeRoutingBytes = m_routingHelper->GetRoutingStats().GetCumulativeTxBytes();
-    uint32_t totalAppBytes = cumulativeWaveBsmBytes + cumulativeRoutingBytes;
-    uint32_t totalPhyBytes = m_wifiPhyStats->GetTxBytes();
-    // mac-phy-oh = (total-phy-bytes - total-app-bytes) / total-phy-bytes
-    double mac_phy_oh = 0.0;
-    if (totalPhyBytes > 0)
-    {
-        mac_phy_oh = (double)(totalPhyBytes - totalAppBytes) / (double)totalPhyBytes;
-    }
-
-    if (m_log)
-    {
-        NS_LOG_UNCOND("BSM_PDR1=" << bsm_pdr1 << " BSM_PDR2=" << bsm_pdr2
-                                  << " BSM_PDR3=" << bsm_pdr3 << " BSM_PDR4=" << bsm_pdr4
-                                  << " BSM_PDR5=" << bsm_pdr5 << " BSM_PDR6=" << bsm_pdr6
-                                  << " BSM_PDR7=" << bsm_pdr7 << " BSM_PDR8=" << bsm_pdr8
-                                  << " BSM_PDR9=" << bsm_pdr9 << " BSM_PDR10=" << bsm_pdr10
-                                  << " Goodput=" << averageRoutingGoodputKbps
-                                  << "Kbps MAC/PHY-oh=" << mac_phy_oh);
-    }
-
-    std::ofstream out(m_CSVfileName2, std::ios::app);
-
-    out << bsm_pdr1 << "," << bsm_pdr2 << "," << bsm_pdr3 << "," << bsm_pdr4 << "," << bsm_pdr5
-        << "," << bsm_pdr6 << "," << bsm_pdr7 << "," << bsm_pdr8 << "," << bsm_pdr9 << ","
-        << bsm_pdr10 << "," << averageRoutingGoodputKbps << "," << mac_phy_oh << "" << std::endl;
-
-    out.close();
-
-    m_os.close(); // close log file
-}
-
-void
-VanetRoutingExperiment::Run()
-{
-    NS_LOG_INFO("Run Simulation.");
-
-    CheckThroughput();
-
-    Simulator::Stop(Seconds(m_TotalSimTime));
-    Simulator::Run();
-    Simulator::Destroy();
-}
-
-// Prints actual position and velocity when a course change event occurs
-void
-VanetRoutingExperiment::CourseChange(std::ostream* os,
-                                     std::string context,
-                                     Ptr<const MobilityModel> mobility)
-{
-    Vector pos = mobility->GetPosition(); // Get position
-    Vector vel = mobility->GetVelocity(); // Get velocity
-
-    pos.z = 1.5;
-
-    int nodeId = mobility->GetObject<Node>()->GetId();
-    double t = (Simulator::Now()).GetSeconds();
-    if (t >= 1.0)
-    {
-        WaveBsmHelper::GetNodesMoving()[nodeId] = 1;
-    }
-
-    // NS_LOG_UNCOND ("Changing pos for node=" << nodeId << " at " << Simulator::Now () );
-
-    // Prints position and velocities
-    *os << Simulator::Now() << " POS: x=" << pos.x << ", y=" << pos.y << ", z=" << pos.z
-        << "; VEL:" << vel.x << ", y=" << vel.y << ", z=" << vel.z << std::endl;
-}
-
-void
-VanetRoutingExperiment::CheckThroughput()
-{
-    uint32_t bytesTotal = m_routingHelper->GetRoutingStats().GetRxBytes();
-    uint32_t packetsReceived = m_routingHelper->GetRoutingStats().GetRxPkts();
-    double kbps = (bytesTotal * 8.0) / 1000;
-    double wavePDR = 0.0;
-    int wavePktsSent = m_waveBsmHelper.GetWaveBsmStats()->GetTxPktCount();
-    int wavePktsReceived = m_waveBsmHelper.GetWaveBsmStats()->GetRxPktCount();
-    if (wavePktsSent > 0)
-    {
-        int wavePktsReceived = m_waveBsmHelper.GetWaveBsmStats()->GetRxPktCount();
-        wavePDR = (double)wavePktsReceived / (double)wavePktsSent;
-    }
-
-    int waveExpectedRxPktCount = m_waveBsmHelper.GetWaveBsmStats()->GetExpectedRxPktCount(1);
-    int waveRxPktInRangeCount = m_waveBsmHelper.GetWaveBsmStats()->GetRxPktInRangeCount(1);
-    double wavePDR1_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(1);
-    double wavePDR2_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(2);
-    double wavePDR3_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(3);
-    double wavePDR4_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(4);
-    double wavePDR5_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(5);
-    double wavePDR6_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(6);
-    double wavePDR7_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(7);
-    double wavePDR8_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(8);
-    double wavePDR9_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(9);
-    double wavePDR10_2 = m_waveBsmHelper.GetWaveBsmStats()->GetBsmPdr(10);
-
-    // calculate MAC/PHY overhead (mac-phy-oh)
-    // total WAVE BSM bytes sent
-    uint32_t cumulativeWaveBsmBytes = m_waveBsmHelper.GetWaveBsmStats()->GetTxByteCount();
-    uint32_t cumulativeRoutingBytes = m_routingHelper->GetRoutingStats().GetCumulativeTxBytes();
-    uint32_t totalAppBytes = cumulativeWaveBsmBytes + cumulativeRoutingBytes;
-    uint32_t totalPhyBytes = m_wifiPhyStats->GetTxBytes();
-    // mac-phy-oh = (total-phy-bytes - total-app-bytes) / total-phy-bytes
-    double mac_phy_oh = 0.0;
-    if (totalPhyBytes > 0)
-    {
-        mac_phy_oh = (double)(totalPhyBytes - totalAppBytes) / (double)totalPhyBytes;
-    }
-
-    std::ofstream out(m_CSVfileName, std::ios::app);
-
-    if (m_log)
-    {
-        NS_LOG_UNCOND("At t=" << (Simulator::Now()).As(Time::S) << " BSM_PDR1=" << wavePDR1_2
-                              << " BSM_PDR1=" << wavePDR2_2 << " BSM_PDR3=" << wavePDR3_2
-                              << " BSM_PDR4=" << wavePDR4_2 << " BSM_PDR5=" << wavePDR5_2
-                              << " BSM_PDR6=" << wavePDR6_2 << " BSM_PDR7=" << wavePDR7_2
-                              << " BSM_PDR8=" << wavePDR8_2 << " BSM_PDR9=" << wavePDR9_2
-                              << " BSM_PDR10=" << wavePDR10_2 << " Goodput=" << kbps
-                              << "Kbps" /*<< " MAC/PHY-OH=" << mac_phy_oh*/);
-    }
-
-    out << (Simulator::Now()).As(Time::S) << "," << kbps << "," << packetsReceived << ","
-        << m_nSinks << "," << m_protocolName << "," << m_txp << "," << wavePktsSent << ","
-        << wavePktsReceived << "," << wavePDR << "," << waveExpectedRxPktCount << ","
-        << waveRxPktInRangeCount << "," << wavePDR1_2 << "," << wavePDR2_2 << "," << wavePDR3_2
-        << "," << wavePDR4_2 << "," << wavePDR5_2 << "," << wavePDR6_2 << "," << wavePDR7_2 << ","
-        << wavePDR8_2 << "," << wavePDR9_2 << "," << wavePDR10_2 << "," << mac_phy_oh << ""
-        << std::endl;
-
-    out.close();
-
-    m_routingHelper->GetRoutingStats().SetRxBytes(0);
-    m_routingHelper->GetRoutingStats().SetRxPkts(0);
-    m_waveBsmHelper.GetWaveBsmStats()->SetRxPktCount(0);
-    m_waveBsmHelper.GetWaveBsmStats()->SetTxPktCount(0);
-    for (int index = 1; index <= 10; index++)
-    {
-        m_waveBsmHelper.GetWaveBsmStats()->SetExpectedRxPktCount(index, 0);
-        m_waveBsmHelper.GetWaveBsmStats()->SetRxPktInRangeCount(index, 0);
-    }
-
-    Time currentTime = Simulator::Now();
-    if (currentTime <= m_cumulativeBsmCaptureStart)
-    {
-        for (int index = 1; index <= 10; index++)
-        {
-            m_waveBsmHelper.GetWaveBsmStats()->ResetTotalRxPktCounts(index);
-        }
-    }
-
-    Simulator::Schedule(Seconds(1.0), &VanetRoutingExperiment::CheckThroughput, this);
-}
-
-void
-VanetRoutingExperiment::SetConfigFromGlobals()
-{
-    // get settings saved from config-store
-    UintegerValue uintegerValue;
-    DoubleValue doubleValue;
-    StringValue stringValue;
-    TimeValue timeValue;
-    BooleanValue booleanValue;
-
-    // This may not be the best way to manage program configuration
-    // (directing them through global values), but management
-    // through the config-store here is copied from
-    // src/lte/examples/lena-dual-stripe.cc
-
-    GlobalValue::GetValueByName("VRCport", uintegerValue);
-    m_port = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCnSinks", uintegerValue);
-    m_nSinks = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCtraceMobility", booleanValue);
-    m_traceMobility = booleanValue.Get();
-    GlobalValue::GetValueByName("VRCprotocol", uintegerValue);
-    m_protocol = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRClossModel", uintegerValue);
-    m_lossModel = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCfading", uintegerValue);
-    m_fading = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRC80211mode", uintegerValue);
-    m_80211mode = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCmobility", uintegerValue);
-    m_mobility = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCnNodes", uintegerValue);
-    m_nNodes = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCnodeSpeed", uintegerValue);
-    m_nodeSpeed = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCnodePause", uintegerValue);
-    m_nodePause = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCwavePacketSize", uintegerValue);
-    m_wavePacketSize = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCverbose", booleanValue);
-    m_verbose = booleanValue.Get();
-    GlobalValue::GetValueByName("VRCscenario", uintegerValue);
-    m_scenario = uintegerValue.Get();
-    GlobalValue::GetValueByName("VRCroutingTables", booleanValue);
-    m_routingTables = booleanValue.Get();
-    GlobalValue::GetValueByName("VRCasciiTrace", booleanValue);
-    m_asciiTrace = booleanValue.Get();
-    GlobalValue::GetValueByName("VRCpcap", booleanValue);
-    m_pcap = booleanValue.Get();
-    GlobalValue::GetValueByName("VRCcumulativeBsmCaptureStart", timeValue);
-    m_cumulativeBsmCaptureStart = timeValue.Get();
-
-    GlobalValue::GetValueByName("VRCtxSafetyRange1", doubleValue);
-    m_txSafetyRange1 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange2", doubleValue);
-    m_txSafetyRange2 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange3", doubleValue);
-    m_txSafetyRange3 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange4", doubleValue);
-    m_txSafetyRange4 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange5", doubleValue);
-    m_txSafetyRange5 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange6", doubleValue);
-    m_txSafetyRange6 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange7", doubleValue);
-    m_txSafetyRange7 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange8", doubleValue);
-    m_txSafetyRange8 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange9", doubleValue);
-    m_txSafetyRange9 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxSafetyRange10", doubleValue);
-    m_txSafetyRange10 = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxp", doubleValue);
-    m_txp = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtotalTime", doubleValue);
-    m_TotalSimTime = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCwaveInterval", doubleValue);
-    m_waveInterval = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCgpsAccuracyNs", doubleValue);
-    m_gpsAccuracyNs = doubleValue.Get();
-    GlobalValue::GetValueByName("VRCtxMaxDelayMs", doubleValue);
-    m_txMaxDelayMs = doubleValue.Get();
-
-    GlobalValue::GetValueByName("VRCCSVfileName", stringValue);
-    m_CSVfileName = stringValue.Get();
-    GlobalValue::GetValueByName("VRCCSVfileName2", stringValue);
-    m_CSVfileName2 = stringValue.Get();
-    GlobalValue::GetValueByName("VRCphyMode", stringValue);
-    m_phyMode = stringValue.Get();
-    GlobalValue::GetValueByName("VRCtraceFile", stringValue);
-    m_traceFile = stringValue.Get();
-    GlobalValue::GetValueByName("VRClogFile", stringValue);
-    m_logFile = stringValue.Get();
-    GlobalValue::GetValueByName("VRCrate", stringValue);
-    m_rate = stringValue.Get();
-    GlobalValue::GetValueByName("VRCphyModeB", stringValue);
-    m_phyModeB = stringValue.Get();
-    GlobalValue::GetValueByName("VRCtrName", stringValue);
-    m_trName = stringValue.Get();
-}
-
-void
-VanetRoutingExperiment::SetGlobalsFromConfig()
-{
-    // get settings saved from config-store
-    UintegerValue uintegerValue;
-    DoubleValue doubleValue;
-    StringValue stringValue;
-
-    g_port.SetValue(UintegerValue(m_port));
-    g_nSinks.SetValue(UintegerValue(m_nSinks));
-    g_traceMobility.SetValue(BooleanValue(m_traceMobility));
-    g_protocol.SetValue(UintegerValue(m_protocol));
-    g_lossModel.SetValue(UintegerValue(m_lossModel));
-    g_fading.SetValue(UintegerValue(m_fading));
-    g_80211mode.SetValue(UintegerValue(m_80211mode));
-    g_mobility.SetValue(UintegerValue(m_mobility));
-    g_nNodes.SetValue(UintegerValue(m_nNodes));
-    g_nodeSpeed.SetValue(UintegerValue(m_nodeSpeed));
-    g_nodePause.SetValue(UintegerValue(m_nodePause));
-    g_wavePacketSize.SetValue(UintegerValue(m_wavePacketSize));
-    g_verbose.SetValue(BooleanValue(m_verbose));
-    g_scenario.SetValue(UintegerValue(m_scenario));
-    g_routingTables.SetValue(BooleanValue(m_routingTables));
-    g_asciiTrace.SetValue(BooleanValue(m_asciiTrace));
-    g_pcap.SetValue(BooleanValue(m_pcap));
-    g_cumulativeBsmCaptureStart.SetValue(TimeValue(m_cumulativeBsmCaptureStart));
-
-    g_txSafetyRange1.SetValue(DoubleValue(m_txSafetyRange1));
-    g_txSafetyRange2.SetValue(DoubleValue(m_txSafetyRange2));
-    g_txSafetyRange3.SetValue(DoubleValue(m_txSafetyRange3));
-    g_txSafetyRange4.SetValue(DoubleValue(m_txSafetyRange4));
-    g_txSafetyRange5.SetValue(DoubleValue(m_txSafetyRange5));
-    g_txSafetyRange6.SetValue(DoubleValue(m_txSafetyRange6));
-    g_txSafetyRange7.SetValue(DoubleValue(m_txSafetyRange7));
-    g_txSafetyRange8.SetValue(DoubleValue(m_txSafetyRange8));
-    g_txSafetyRange9.SetValue(DoubleValue(m_txSafetyRange9));
-    g_txSafetyRange10.SetValue(DoubleValue(m_txSafetyRange10));
-    g_txp.SetValue(DoubleValue(m_txp));
-    g_totalTime.SetValue(DoubleValue(m_TotalSimTime));
-    g_waveInterval.SetValue(DoubleValue(m_waveInterval));
-    g_gpsAccuracyNs.SetValue(DoubleValue(m_gpsAccuracyNs));
-    g_txMaxDelayMs.SetValue(DoubleValue(m_txMaxDelayMs));
-
-    g_CSVfileName.SetValue(StringValue(m_CSVfileName));
-    g_CSVfileName2.SetValue(StringValue(m_CSVfileName2));
-    g_phyMode.SetValue(StringValue(m_phyMode));
-    g_traceFile.SetValue(StringValue(m_traceFile));
-    g_logFile.SetValue(StringValue(m_logFile));
-    g_rate.SetValue(StringValue(m_rate));
-    g_phyModeB.SetValue(StringValue(m_phyModeB));
-    g_trName.SetValue(StringValue(m_trName));
-    GlobalValue::GetValueByName("VRCtrName", stringValue);
-    m_trName = stringValue.Get();
-}
-
-void
-VanetRoutingExperiment::CommandSetup(int argc, char** argv)
-{
-    CommandLine cmd(__FILE__);
-    double txDist1 = 50.0;
-    double txDist2 = 100.0;
-    double txDist3 = 150.0;
-    double txDist4 = 200.0;
-    double txDist5 = 250.0;
-    double txDist6 = 300.0;
-    double txDist7 = 350.0;
-    double txDist8 = 350.0;
-    double txDist9 = 350.0;
-    double txDist10 = 350.0;
-
-    // allow command line overrides
-    cmd.AddValue("CSVfileName", "The name of the CSV output file name", m_CSVfileName);
-    cmd.AddValue("CSVfileName2", "The name of the CSV output file name2", m_CSVfileName2);
-    cmd.AddValue("totaltime", "Simulation end time", m_TotalSimTime);
-    cmd.AddValue("nodes", "Number of nodes (i.e. vehicles)", m_nNodes);
-    cmd.AddValue("sinks", "Number of routing sinks", m_nSinks);
-    cmd.AddValue("txp", "Transmit power (dB), e.g. txp=7.5", m_txp);
-    cmd.AddValue("traceMobility", "Enable mobility tracing", m_traceMobility);
-    cmd.AddValue("protocol", "1=OLSR;2=AODV;3=DSDV;4=DSR", m_protocol);
-    cmd.AddValue("lossModel", "1=Friis;2=ItuR1411Los;3=TwoRayGround;4=LogDistance", m_lossModel);
-    cmd.AddValue("fading", "0=None;1=Nakagami;(buildings=1 overrides)", m_fading);
-    cmd.AddValue("phyMode", "Wifi Phy mode", m_phyMode);
-    cmd.AddValue("80211Mode", "1=802.11p; 2=802.11b; 3=WAVE-PHY", m_80211mode);
-    cmd.AddValue("traceFile", "Ns2 movement trace file", m_traceFile);
-    cmd.AddValue("logFile", "Log file", m_logFile);
-    cmd.AddValue("mobility", "1=trace;2=RWP", m_mobility);
-    cmd.AddValue("rate", "Rate", m_rate);
-    cmd.AddValue("phyModeB", "Phy mode 802.11b", m_phyModeB);
-    cmd.AddValue("speed", "Node speed (m/s)", m_nodeSpeed);
-    cmd.AddValue("pause", "Node pause (s)", m_nodePause);
-    cmd.AddValue("verbose", "Enable verbose output", m_verbose);
-    cmd.AddValue("bsm", "(WAVE) BSM size (bytes)", m_wavePacketSize);
-    cmd.AddValue("interval", "(WAVE) BSM interval (s)", m_waveInterval);
-    cmd.AddValue("scenario", "1=synthetic, 2=playback-trace", m_scenario);
-    // User is allowed to have up to 10 different PDRs (Packet
-    // Delivery Ratios) calculate, and so can specify up to
-    // 10 different tx distances.
-    cmd.AddValue("txdist1", "Expected BSM tx range, m", txDist1);
-    cmd.AddValue("txdist2", "Expected BSM tx range, m", txDist2);
-    cmd.AddValue("txdist3", "Expected BSM tx range, m", txDist3);
-    cmd.AddValue("txdist4", "Expected BSM tx range, m", txDist4);
-    cmd.AddValue("txdist5", "Expected BSM tx range, m", txDist5);
-    cmd.AddValue("txdist6", "Expected BSM tx range, m", txDist6);
-    cmd.AddValue("txdist7", "Expected BSM tx range, m", txDist7);
-    cmd.AddValue("txdist8", "Expected BSM tx range, m", txDist8);
-    cmd.AddValue("txdist9", "Expected BSM tx range, m", txDist9);
-    cmd.AddValue("txdist10", "Expected BSM tx range, m", txDist10);
-    cmd.AddValue("gpsaccuracy", "GPS time accuracy, in ns", m_gpsAccuracyNs);
-    cmd.AddValue("txmaxdelay", "Tx max delay, in ms", m_txMaxDelayMs);
-    cmd.AddValue("routingTables", "Whether to dump routing tables at t=5 seconds", m_routingTables);
-    cmd.AddValue("asciiTrace", "Whether to dump ASCII Trace data", m_asciiTrace);
-    cmd.AddValue("pcap", "Whether to create PCAP files for all nodes", m_pcap);
-    cmd.AddValue("loadconfig", "Config-store filename to load", m_loadConfigFilename);
-    cmd.AddValue("saveconfig", "Config-store filename to save", m_saveConfigFilename);
-    cmd.AddValue("exp", "Experiment", m_exp);
-    cmd.AddValue("BsmCaptureStart",
-                 "Start time to begin capturing pkts for cumulative Bsm",
-                 m_cumulativeBsmCaptureStart);
-    cmd.Parse(argc, argv);
-
-    m_txSafetyRange1 = txDist1;
-    m_txSafetyRange2 = txDist2;
-    m_txSafetyRange3 = txDist3;
-    m_txSafetyRange4 = txDist4;
-    m_txSafetyRange5 = txDist5;
-    m_txSafetyRange6 = txDist6;
-    m_txSafetyRange7 = txDist7;
-    m_txSafetyRange8 = txDist8;
-    m_txSafetyRange9 = txDist9;
-    m_txSafetyRange10 = txDist10;
-    // load configuration info from config-store
-    ConfigStoreHelper configStoreHelper;
-    configStoreHelper.LoadConfig(m_loadConfigFilename);
-    // transfer config-store values to config parameters
-    SetConfigFromGlobals();
-
-    // parse again so you can override input file default values via command line
-    cmd.Parse(argc, argv);
-
-    m_txSafetyRange1 = txDist1;
-    m_txSafetyRange2 = txDist2;
-    m_txSafetyRange3 = txDist3;
-    m_txSafetyRange4 = txDist4;
-    m_txSafetyRange5 = txDist5;
-    m_txSafetyRange6 = txDist6;
-    m_txSafetyRange7 = txDist7;
-    m_txSafetyRange8 = txDist8;
-    m_txSafetyRange9 = txDist9;
-    m_txSafetyRange10 = txDist10;
-}
-
-void
-VanetRoutingExperiment::SetupLogFile()
-{
-    // open log file for output
-    m_os.open(m_logFile);
-}
-
-void
-VanetRoutingExperiment::SetupLogging()
-{
-    // Enable logging from the ns2 helper
-    LogComponentEnable("Ns2MobilityHelper", LOG_LEVEL_DEBUG);
-
-    Packet::EnablePrinting();
-}
-
-void
-VanetRoutingExperiment::ConfigureDefaults()
-{
-    Config::SetDefault("ns3::OnOffApplication::PacketSize", StringValue("64"));
-    Config::SetDefault("ns3::OnOffApplication::DataRate", StringValue(m_rate));
-
-    // Set Non-unicastMode rate to unicast mode
-    if (m_80211mode == 2)
-    {
-        Config::SetDefault("ns3::WifiRemoteStationManager::NonUnicastMode",
-                           StringValue(m_phyModeB));
-    }
-    else
-    {
-        Config::SetDefault("ns3::WifiRemoteStationManager::NonUnicastMode", StringValue(m_phyMode));
-    }
-}
-
-void
-VanetRoutingExperiment::SetupAdhocMobilityNodes()
-{
-    if (m_mobility == 1)
-    {
-        // Create Ns2MobilityHelper with the specified trace log file as parameter
-        Ns2MobilityHelper ns2 = Ns2MobilityHelper(m_traceFile);
-        ns2.Install(); // configure movements for each node, while reading trace file
-        // initially assume all nodes are not moving
-        WaveBsmHelper::GetNodesMoving().resize(m_nNodes, 0);
-    }
-    else if (m_mobility == 2)
-    {
-        MobilityHelper mobilityAdhoc;
-
-        ObjectFactory pos;
-        pos.SetTypeId("ns3::RandomBoxPositionAllocator");
-        pos.Set("X", StringValue("ns3::UniformRandomVariable[Min=0.0|Max=1500.0]"));
-        pos.Set("Y", StringValue("ns3::UniformRandomVariable[Min=0.0|Max=300.0]"));
-        // we need antenna height uniform [1.0 .. 2.0] for loss model
-        pos.Set("Z", StringValue("ns3::UniformRandomVariable[Min=1.0|Max=2.0]"));
-
-        Ptr<PositionAllocator> taPositionAlloc = pos.Create()->GetObject<PositionAllocator>();
-        m_streamIndex += taPositionAlloc->AssignStreams(m_streamIndex);
-
-        std::stringstream ssSpeed;
-        ssSpeed << "ns3::UniformRandomVariable[Min=0.0|Max=" << m_nodeSpeed << "]";
-        std::stringstream ssPause;
-        ssPause << "ns3::ConstantRandomVariable[Constant=" << m_nodePause << "]";
-        mobilityAdhoc.SetMobilityModel("ns3::RandomWaypointMobilityModel",
-                                       "Speed",
-                                       StringValue(ssSpeed.str()),
-                                       "Pause",
-                                       StringValue(ssPause.str()),
-                                       "PositionAllocator",
-                                       PointerValue(taPositionAlloc));
-        mobilityAdhoc.SetPositionAllocator(taPositionAlloc);
-        mobilityAdhoc.Install(m_adhocTxNodes);
-        m_streamIndex += mobilityAdhoc.AssignStreams(m_adhocTxNodes, m_streamIndex);
-
-        // initially assume all nodes are moving
-        WaveBsmHelper::GetNodesMoving().resize(m_nNodes, 1);
-    }
-
-    // Configure callback for logging
-    Config::Connect("/NodeList/*/$ns3::MobilityModel/CourseChange",
-                    MakeBoundCallback(&VanetRoutingExperiment::CourseChange, &m_os));
-}
-
-void
-VanetRoutingExperiment::SetupAdhocDevices()
-{
-    if (m_lossModel == 1)
-    {
-        m_lossModelName = "ns3::FriisPropagationLossModel";
-    }
-    else if (m_lossModel == 2)
-    {
-        m_lossModelName = "ns3::ItuR1411LosPropagationLossModel";
-    }
-    else if (m_lossModel == 3)
-    {
-        m_lossModelName = "ns3::TwoRayGroundPropagationLossModel";
-    }
-    else if (m_lossModel == 4)
-    {
-        m_lossModelName = "ns3::LogDistancePropagationLossModel";
-    }
-    else
-    {
-        // Unsupported propagation loss model.
-        // Treating as ERROR
-        NS_LOG_ERROR("Invalid propagation loss model specified.  Values must be [1-4], where "
-                     "1=Friis;2=ItuR1411Los;3=TwoRayGround;4=LogDistance");
-    }
-
-    // frequency
-    double freq = 0.0;
-    if ((m_80211mode == 1) || (m_80211mode == 3))
-    {
-        // 802.11p 5.9 GHz
-        freq = 5.9e9;
-    }
-    else
-    {
-        // 802.11b 2.4 GHz
-        freq = 2.4e9;
-    }
-
-    // Setup propagation models
-    YansWifiChannelHelper wifiChannel;
-    wifiChannel.SetPropagationDelay("ns3::ConstantSpeedPropagationDelayModel");
-    if (m_lossModel == 3)
-    {
-        // two-ray requires antenna height (else defaults to Friis)
-        wifiChannel.AddPropagationLoss(m_lossModelName,
-                                       "Frequency",
-                                       DoubleValue(freq),
-                                       "HeightAboveZ",
-                                       DoubleValue(1.5));
-    }
-    else
-    {
-        wifiChannel.AddPropagationLoss(m_lossModelName, "Frequency", DoubleValue(freq));
-    }
-
-    // Propagation loss models are additive.
-    if (m_fading != 0)
-    {
-        // if no obstacle model, then use Nakagami fading if requested
-        wifiChannel.AddPropagationLoss("ns3::NakagamiPropagationLossModel");
-    }
-
-    // the channel
-    Ptr<YansWifiChannel> channel = wifiChannel.Create();
-
-    // The below set of helpers will help us to put together the wifi NICs we want
-    YansWifiPhyHelper wifiPhy;
-    wifiPhy.SetChannel(channel);
-    // ns-3 supports generate a pcap trace
-    wifiPhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11);
-
-    YansWavePhyHelper wavePhy = YansWavePhyHelper::Default();
-    wavePhy.SetChannel(channel);
-    wavePhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11);
-
-    // Setup WAVE PHY and MAC
-    NqosWaveMacHelper wifi80211pMac = NqosWaveMacHelper::Default();
-    WaveHelper waveHelper = WaveHelper::Default();
-    Wifi80211pHelper wifi80211p = Wifi80211pHelper::Default();
-    if (m_verbose)
-    {
-        Wifi80211pHelper::EnableLogComponents(); // Turn on all Wifi 802.11p logging
-        // likewise, turn on WAVE PHY logging
-        WaveHelper::EnableLogComponents();
-    }
-
-    WifiHelper wifi;
-
-    // Setup 802.11b stuff
-    wifi.SetStandard(WIFI_STANDARD_80211b);
-
-    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                 "DataMode",
-                                 StringValue(m_phyModeB),
-                                 "ControlMode",
-                                 StringValue(m_phyModeB));
-
-    // Setup 802.11p stuff
-    wifi80211p.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                       "DataMode",
-                                       StringValue(m_phyMode),
-                                       "ControlMode",
-                                       StringValue(m_phyMode));
-
-    // Setup WAVE-PHY stuff
-    waveHelper.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                       "DataMode",
-                                       StringValue(m_phyMode),
-                                       "ControlMode",
-                                       StringValue(m_phyMode));
-
-    // Set Tx Power
-    wifiPhy.Set("TxPowerStart", DoubleValue(m_txp));
-    wifiPhy.Set("TxPowerEnd", DoubleValue(m_txp));
-    wavePhy.Set("TxPowerStart", DoubleValue(m_txp));
-    wavePhy.Set("TxPowerEnd", DoubleValue(m_txp));
-
-    // Add an upper mac and disable rate control
-    WifiMacHelper wifiMac;
-    wifiMac.SetType("ns3::AdhocWifiMac");
-    QosWaveMacHelper waveMac = QosWaveMacHelper::Default();
-
-    // Setup net devices
-
-    if (m_80211mode == 3)
-    {
-        m_adhocTxDevices = waveHelper.Install(wavePhy, waveMac, m_adhocTxNodes);
-    }
-    else if (m_80211mode == 1)
-    {
-        m_adhocTxDevices = wifi80211p.Install(wifiPhy, wifi80211pMac, m_adhocTxNodes);
-    }
-    else
-    {
-        m_adhocTxDevices = wifi.Install(wifiPhy, wifiMac, m_adhocTxNodes);
-    }
-
-    if (m_asciiTrace)
-    {
-        AsciiTraceHelper ascii;
-        Ptr<OutputStreamWrapper> osw = ascii.CreateFileStream(m_trName + ".tr");
-        wifiPhy.EnableAsciiAll(osw);
-        wavePhy.EnableAsciiAll(osw);
-    }
-    if (m_pcap)
-    {
-        wifiPhy.EnablePcapAll("vanet-routing-compare-pcap");
-        wavePhy.EnablePcapAll("vanet-routing-compare-pcap");
-    }
-}
-
-void
-VanetRoutingExperiment::SetupWaveMessages()
-{
-    // WAVE PHY mode
-    // 0=continuous channel; 1=channel-switching
-    int chAccessMode = 0;
-    if (m_80211mode == 3)
-    {
-        chAccessMode = 1;
-    }
-
-    m_waveBsmHelper.Install(m_adhocTxInterfaces,
-                            Seconds(m_TotalSimTime),
-                            m_wavePacketSize,
-                            Seconds(m_waveInterval),
-                            // GPS accuracy (i.e, clock drift), in number of ns
-                            m_gpsAccuracyNs,
-                            m_txSafetyRanges,
-                            chAccessMode,
-                            // tx max delay before transmit, in ms
-                            MilliSeconds(m_txMaxDelayMs));
-
-    // fix random number streams
-    m_streamIndex += m_waveBsmHelper.AssignStreams(m_adhocTxNodes, m_streamIndex);
-}
-
-void
-VanetRoutingExperiment::SetupRoutingMessages()
-{
-    m_routingHelper->Install(m_adhocTxNodes,
-                             m_adhocTxDevices,
-                             m_adhocTxInterfaces,
-                             m_TotalSimTime,
-                             m_protocol,
-                             m_nSinks,
-                             m_routingTables);
-}
-
-void
-VanetRoutingExperiment::SetupScenario()
-{
-    // member variable parameter use
-    // defaults or command line overrides,
-    // except where scenario={1,2,3,...}
-    // have been specified, in which case
-    // specify parameters are overwritten
-    // here to setup for specific scenarios
-
-    // certain parameters may be further overridden
-    // i.e. specify a scenario, override tx power.
-
-    if (m_scenario == 1)
-    {
-        // 40 nodes in RWP 300 m x 1500 m synthetic highway, 10s
-        m_traceFile = "";
-        m_logFile = "";
-        m_mobility = 2;
-        if (m_nNodes == 156)
-        {
-            m_nNodes = 40;
-        }
-        if (m_TotalSimTime == 300.01)
-        {
-            m_TotalSimTime = 10.0;
-        }
-    }
-    else if (m_scenario == 2)
-    {
-        // Realistic vehicular trace in 4.6 km x 3.0 km suburban Zurich
-        // "low density, 99 total vehicles"
-        m_traceFile = "src/wave/examples/low99-ct-unterstrass-1day.filt.7.adj.mob";
-        m_logFile = "low99-ct-unterstrass-1day.filt.7.adj.log";
-        m_mobility = 1;
-        m_nNodes = 99;
-        m_TotalSimTime = 300.01;
-        m_nodeSpeed = 0;
-        m_nodePause = 0;
-        m_CSVfileName = "low_vanet-routing-compare.csv";
-        m_CSVfileName = "low_vanet-routing-compare2.csv";
-    }
-}
-
-void
-VanetRoutingExperiment::WriteCsvHeader()
-{
-    // blank out the last output file and write the column headers
-    std::ofstream out(m_CSVfileName);
-    out << "SimulationSecond,"
-        << "ReceiveRate,"
-        << "PacketsReceived,"
-        << "NumberOfSinks,"
-        << "RoutingProtocol,"
-        << "TransmissionPower,"
-        << "WavePktsSent,"
-        << "WavePtksReceived,"
-        << "WavePktsPpr,"
-        << "ExpectedWavePktsReceived,"
-        << "ExpectedWavePktsInCoverageReceived,"
-        << "BSM_PDR1,"
-        << "BSM_PDR2,"
-        << "BSM_PDR3,"
-        << "BSM_PDR4,"
-        << "BSM_PDR5,"
-        << "BSM_PDR6,"
-        << "BSM_PDR7,"
-        << "BSM_PDR8,"
-        << "BSM_PDR9,"
-        << "BSM_PDR10,"
-        << "MacPhyOverhead" << std::endl;
-    out.close();
-
-    std::ofstream out2(m_CSVfileName2);
-    out2 << "BSM_PDR1,"
-         << "BSM_PDR2,"
-         << "BSM_PDR3,"
-         << "BSM_PDR4,"
-         << "BSM_PDR5,"
-         << "BSM_PDR6,"
-         << "BSM_PDR7,"
-         << "BSM_PDR8,"
-         << "BSM_PDR9,"
-         << "BSM_PDR10,"
-         << "AverageRoutingGoodputKbps,"
-         << "MacPhyOverhead" << std::endl;
-    out2.close();
-}
-
-int
-main(int argc, char* argv[])
-{
-    VanetRoutingExperiment experiment;
-    experiment.Simulate(argc, argv);
-
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/examples/wave-simple-80211p.cc ns-3.39/src/wave/examples/wave-simple-80211p.cc
--- ns-3.38/src/wave/examples/wave-simple-80211p.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/examples/wave-simple-80211p.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,201 +0,0 @@
-/*
- * Copyright (c) 2005,2006,2007 INRIA
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- * Author: Junling Bu <linlinjavaer@gmail.com>
- *
- */
-/**
- * This example shows basic construction of an 802.11p node.  Two nodes
- * are constructed with 802.11p devices, and by default, one node sends a single
- * packet to another node (the number of packets and interval between
- * them can be configured by command-line arguments).  The example shows
- * typical usage of the helper classes for this mode of WiFi (where "OCB" refers
- * to "Outside the Context of a BSS")."
- */
-
-#include "ns3/command-line.h"
-#include "ns3/config.h"
-#include "ns3/double.h"
-#include "ns3/internet-stack-helper.h"
-#include "ns3/ipv4-address-helper.h"
-#include "ns3/ipv4-interface-container.h"
-#include "ns3/log.h"
-#include "ns3/mobility-helper.h"
-#include "ns3/mobility-model.h"
-#include "ns3/ocb-wifi-mac.h"
-#include "ns3/position-allocator.h"
-#include "ns3/socket.h"
-#include "ns3/string.h"
-#include "ns3/vector.h"
-#include "ns3/wave-mac-helper.h"
-#include "ns3/wifi-80211p-helper.h"
-#include "ns3/yans-wifi-helper.h"
-
-#include <iostream>
-
-using namespace ns3;
-
-NS_LOG_COMPONENT_DEFINE("WifiSimpleOcb");
-
-/*
- * In WAVE module, there is no net device class named like "Wifi80211pNetDevice",
- * instead, we need to use Wifi80211pHelper to create an object of
- * WifiNetDevice class.
- *
- * usage:
- *  NodeContainer nodes;
- *  NetDeviceContainer devices;
- *  nodes.Create (2);
- *  YansWifiPhyHelper wifiPhy;
- *  YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
- *  wifiPhy.SetChannel (wifiChannel.Create ());
- *  NqosWaveMacHelper wifi80211pMac = NqosWave80211pMacHelper::Default();
- *  Wifi80211pHelper wifi80211p = Wifi80211pHelper::Default ();
- *  devices = wifi80211p.Install (wifiPhy, wifi80211pMac, nodes);
- *
- * The reason of not providing a 802.11p class is that most of modeling
- * 802.11p standard has been done in wifi module, so we only need a high
- * MAC class that enables OCB mode.
- */
-
-/**
- * Receive a packet
- * \param socket Rx socket
- */
-void
-ReceivePacket(Ptr<Socket> socket)
-{
-    while (socket->Recv())
-    {
-        NS_LOG_UNCOND("Received one packet!");
-    }
-}
-
-/**
- * Geerate traffic
- * \param socket Tx socket
- * \param pktSize packet size
- * \param pktCount number of packets
- * \param pktInterval interval between packet generation
- */
-static void
-GenerateTraffic(Ptr<Socket> socket, uint32_t pktSize, uint32_t pktCount, Time pktInterval)
-{
-    if (pktCount > 0)
-    {
-        socket->Send(Create<Packet>(pktSize));
-        Simulator::Schedule(pktInterval,
-                            &GenerateTraffic,
-                            socket,
-                            pktSize,
-                            pktCount - 1,
-                            pktInterval);
-    }
-    else
-    {
-        socket->Close();
-    }
-}
-
-int
-main(int argc, char* argv[])
-{
-    std::string phyMode("OfdmRate6MbpsBW10MHz");
-    uint32_t packetSize = 1000; // bytes
-    uint32_t numPackets = 1;
-    double interval = 1.0; // seconds
-    bool verbose = false;
-
-    CommandLine cmd(__FILE__);
-
-    cmd.AddValue("phyMode", "Wifi Phy mode", phyMode);
-    cmd.AddValue("packetSize", "size of application packet sent", packetSize);
-    cmd.AddValue("numPackets", "number of packets generated", numPackets);
-    cmd.AddValue("interval", "interval (seconds) between packets", interval);
-    cmd.AddValue("verbose", "turn on all WifiNetDevice log components", verbose);
-    cmd.Parse(argc, argv);
-    // Convert to time object
-    Time interPacketInterval = Seconds(interval);
-
-    NodeContainer c;
-    c.Create(2);
-
-    // The below set of helpers will help us to put together the wifi NICs we want
-    YansWifiPhyHelper wifiPhy;
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
-    Ptr<YansWifiChannel> channel = wifiChannel.Create();
-    wifiPhy.SetChannel(channel);
-    // ns-3 supports generate a pcap trace
-    wifiPhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11);
-    NqosWaveMacHelper wifi80211pMac = NqosWaveMacHelper::Default();
-    Wifi80211pHelper wifi80211p = Wifi80211pHelper::Default();
-    if (verbose)
-    {
-        Wifi80211pHelper::EnableLogComponents(); // Turn on all Wifi 802.11p logging
-    }
-
-    wifi80211p.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                       "DataMode",
-                                       StringValue(phyMode),
-                                       "ControlMode",
-                                       StringValue(phyMode));
-    NetDeviceContainer devices = wifi80211p.Install(wifiPhy, wifi80211pMac, c);
-
-    // Tracing
-    wifiPhy.EnablePcap("wave-simple-80211p", devices);
-
-    MobilityHelper mobility;
-    Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
-    positionAlloc->Add(Vector(0.0, 0.0, 0.0));
-    positionAlloc->Add(Vector(5.0, 0.0, 0.0));
-    mobility.SetPositionAllocator(positionAlloc);
-    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
-    mobility.Install(c);
-
-    InternetStackHelper internet;
-    internet.Install(c);
-
-    Ipv4AddressHelper ipv4;
-    NS_LOG_INFO("Assign IP Addresses.");
-    ipv4.SetBase("10.1.1.0", "255.255.255.0");
-    Ipv4InterfaceContainer i = ipv4.Assign(devices);
-
-    TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
-    Ptr<Socket> recvSink = Socket::CreateSocket(c.Get(0), tid);
-    InetSocketAddress local = InetSocketAddress(Ipv4Address::GetAny(), 80);
-    recvSink->Bind(local);
-    recvSink->SetRecvCallback(MakeCallback(&ReceivePacket));
-
-    Ptr<Socket> source = Socket::CreateSocket(c.Get(1), tid);
-    InetSocketAddress remote = InetSocketAddress(Ipv4Address("255.255.255.255"), 80);
-    source->SetAllowBroadcast(true);
-    source->Connect(remote);
-
-    Simulator::ScheduleWithContext(source->GetNode()->GetId(),
-                                   Seconds(1.0),
-                                   &GenerateTraffic,
-                                   source,
-                                   packetSize,
-                                   numPackets,
-                                   interPacketInterval);
-
-    Simulator::Run();
-    Simulator::Destroy();
-
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/examples/wave-simple-device.cc ns-3.39/src/wave/examples/wave-simple-device.cc
--- ns-3.38/src/wave/examples/wave-simple-device.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/examples/wave-simple-device.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,302 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "ns3/command-line.h"
-#include "ns3/mobility-helper.h"
-#include "ns3/net-device-container.h"
-#include "ns3/node-container.h"
-#include "ns3/node.h"
-#include "ns3/packet.h"
-#include "ns3/seq-ts-header.h"
-#include "ns3/simulator.h"
-#include "ns3/wave-helper.h"
-#include "ns3/wave-mac-helper.h"
-#include "ns3/wave-net-device.h"
-#include "ns3/yans-wifi-helper.h"
-
-using namespace ns3;
-
-/**
- * This simulation is to show the routing service of WaveNetDevice described in IEEE 09.4.
- *
- * note: although txPowerLevel is supported now, the "TxPowerLevels"
- * attribute of YansWifiPhy is 1 which means phy devices only support 1
- * levels. Thus, if users want to control txPowerLevel, they should set
- * these attributes of YansWifiPhy by themselves..
- */
-class WaveNetDeviceExample
-{
-  public:
-    /// Send WSMP example function
-    void SendWsmpExample();
-
-    /// Send IP example function
-    void SendIpExample();
-
-    /// Send WSA example
-    void SendWsaExample();
-
-  private:
-    /**
-     * Send one WSMP packet function
-     * \param channel the channel to use
-     * \param seq the sequence
-     */
-    void SendOneWsmpPacket(uint32_t channel, uint32_t seq);
-    /**
-     * Send IP packet function
-     * \param seq the sequence
-     * \param ipv6 true if IPV6
-     */
-    void SendIpPacket(uint32_t seq, bool ipv6);
-    /**
-     * Receive function
-     * \param dev the device
-     * \param pkt the packet
-     * \param mode the mode
-     * \param sender the sender address
-     * \returns true if successful
-     */
-    bool Receive(Ptr<NetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender);
-    /**
-     * Receive VSA function
-     * \param pkt the packet
-     * \param address the address
-     * \returns true if successful
-     */
-    bool ReceiveVsa(Ptr<const Packet> pkt, const Address& address, uint32_t, uint32_t);
-    /// Create WAVE nodes function
-    void CreateWaveNodes();
-
-    NodeContainer nodes;        ///< the nodes
-    NetDeviceContainer devices; ///< the devices
-};
-
-void
-WaveNetDeviceExample::CreateWaveNodes()
-{
-    nodes = NodeContainer();
-    nodes.Create(2);
-
-    MobilityHelper mobility;
-    Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
-    positionAlloc->Add(Vector(0.0, 0.0, 0.0));
-    positionAlloc->Add(Vector(5.0, 0.0, 0.0));
-    mobility.SetPositionAllocator(positionAlloc);
-    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
-    mobility.Install(nodes);
-
-    YansWifiChannelHelper waveChannel = YansWifiChannelHelper::Default();
-    YansWavePhyHelper wavePhy = YansWavePhyHelper::Default();
-    wavePhy.SetChannel(waveChannel.Create());
-    wavePhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11);
-    QosWaveMacHelper waveMac = QosWaveMacHelper::Default();
-    WaveHelper waveHelper = WaveHelper::Default();
-    devices = waveHelper.Install(wavePhy, waveMac, nodes);
-
-    for (uint32_t i = 0; i != devices.GetN(); ++i)
-    {
-        Ptr<WaveNetDevice> device = DynamicCast<WaveNetDevice>(devices.Get(i));
-        device->SetReceiveCallback(MakeCallback(&WaveNetDeviceExample::Receive, this));
-        device->SetWaveVsaCallback(MakeCallback(&WaveNetDeviceExample::ReceiveVsa, this));
-    }
-
-    // Tracing
-    wavePhy.EnablePcap("wave-simple-device", devices);
-}
-
-bool
-WaveNetDeviceExample::Receive(Ptr<NetDevice> dev,
-                              Ptr<const Packet> pkt,
-                              uint16_t mode,
-                              const Address& sender)
-{
-    SeqTsHeader seqTs;
-    pkt->PeekHeader(seqTs);
-    std::cout << "receive a packet: " << std::endl
-              << "  sequence = " << seqTs.GetSeq() << "," << std::endl
-              << "  sendTime = " << seqTs.GetTs().As(Time::S) << "," << std::endl
-              << "  recvTime = " << Now().As(Time::S) << "," << std::endl
-              << "  protocol = 0x" << std::hex << mode << std::dec << std::endl;
-    return true;
-}
-
-void
-WaveNetDeviceExample::SendOneWsmpPacket(uint32_t channel, uint32_t seq)
-{
-    Ptr<WaveNetDevice> sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-    Ptr<WaveNetDevice> receiver = DynamicCast<WaveNetDevice>(devices.Get(1));
-    const static uint16_t WSMP_PROT_NUMBER = 0x88DC;
-    Mac48Address bssWildcard = Mac48Address::GetBroadcast();
-
-    const TxInfo txInfo = TxInfo(channel);
-    Ptr<Packet> p = Create<Packet>(100);
-    SeqTsHeader seqTs;
-    seqTs.SetSeq(seq);
-    p->AddHeader(seqTs);
-    sender->SendX(p, bssWildcard, WSMP_PROT_NUMBER, txInfo);
-}
-
-void
-WaveNetDeviceExample::SendWsmpExample()
-{
-    CreateWaveNodes();
-    Ptr<WaveNetDevice> sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-    Ptr<WaveNetDevice> receiver = DynamicCast<WaveNetDevice>(devices.Get(1));
-
-    // Alternating access without immediate channel switch
-    const SchInfo schInfo = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, sender, schInfo);
-    // An important point is that the receiver should also be assigned channel
-    // access for the same channel to receive packets.
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, receiver, schInfo);
-
-    // send WSMP packets
-    // the first packet will be queued currently and be transmitted in next SCH interval
-    Simulator::Schedule(Seconds(1.0), &WaveNetDeviceExample::SendOneWsmpPacket, this, SCH1, 1);
-    // the second packet will be queued currently and then be transmitted , because of in the CCH
-    // interval.
-    Simulator::Schedule(Seconds(1.0), &WaveNetDeviceExample::SendOneWsmpPacket, this, CCH, 2);
-    // the third packet will be dropped because of no channel access for SCH2.
-    Simulator::Schedule(Seconds(1.0), &WaveNetDeviceExample::SendOneWsmpPacket, this, SCH2, 3);
-
-    // release SCH access
-    Simulator::Schedule(Seconds(2.0), &WaveNetDevice::StopSch, sender, SCH1);
-    Simulator::Schedule(Seconds(2.0), &WaveNetDevice::StopSch, receiver, SCH1);
-    // the fourth packet will be queued and be transmitted because of default CCH access assigned
-    // automatically.
-    Simulator::Schedule(Seconds(3.0), &WaveNetDeviceExample::SendOneWsmpPacket, this, CCH, 4);
-    // the fifth packet will be dropped because of no SCH1 access assigned
-    Simulator::Schedule(Seconds(3.0), &WaveNetDeviceExample::SendOneWsmpPacket, this, SCH1, 5);
-
-    Simulator::Stop(Seconds(5.0));
-    Simulator::Run();
-    Simulator::Destroy();
-}
-
-void
-WaveNetDeviceExample::SendIpPacket(uint32_t seq, bool ipv6)
-{
-    Ptr<WaveNetDevice> sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-    Ptr<WaveNetDevice> receiver = DynamicCast<WaveNetDevice>(devices.Get(1));
-    const Address dest = receiver->GetAddress();
-    // send IPv4 packet or IPv6 packet
-    const static uint16_t IPv4_PROT_NUMBER = 0x0800;
-    const static uint16_t IPv6_PROT_NUMBER = 0x86DD;
-    uint16_t protocol = ipv6 ? IPv6_PROT_NUMBER : IPv4_PROT_NUMBER;
-    Ptr<Packet> p = Create<Packet>(100);
-    SeqTsHeader seqTs;
-    seqTs.SetSeq(seq);
-    p->AddHeader(seqTs);
-    sender->Send(p, dest, protocol);
-}
-
-void
-WaveNetDeviceExample::SendIpExample()
-{
-    CreateWaveNodes();
-    Ptr<WaveNetDevice> sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-    Ptr<WaveNetDevice> receiver = DynamicCast<WaveNetDevice>(devices.Get(1));
-
-    // Alternating access without immediate channel switch
-    const SchInfo schInfo = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, sender, schInfo);
-    // An important point is that the receiver should also be assigned channel
-    // access for the same channel to receive packets.
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, receiver, schInfo);
-
-    // both IPv4 and IPv6 packets below will not be inserted to internal queue because of no tx
-    // profile registered
-    Simulator::Schedule(Seconds(1.0), &WaveNetDeviceExample::SendIpPacket, this, 1, true);
-    Simulator::Schedule(Seconds(1.050), &WaveNetDeviceExample::SendIpPacket, this, 2, false);
-    // register txprofile
-    //  IP packets will automatically be sent with txprofile parameter
-    const TxProfile txProfile = TxProfile(SCH1);
-    Simulator::Schedule(Seconds(2.0), &WaveNetDevice::RegisterTxProfile, sender, txProfile);
-    // both IPv4 and IPv6 packet are transmitted successfully
-    Simulator::Schedule(Seconds(3.0), &WaveNetDeviceExample::SendIpPacket, this, 3, true);
-    Simulator::Schedule(Seconds(3.050), &WaveNetDeviceExample::SendIpPacket, this, 4, false);
-    // unregister TxProfile or release channel access
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::DeleteTxProfile, sender, SCH1);
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::StopSch, sender, SCH1);
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::StopSch, receiver, SCH1);
-    // these packets will be dropped again because of no channel access assigned and no tx profile
-    // registered
-    Simulator::Schedule(Seconds(5.0), &WaveNetDeviceExample::SendIpPacket, this, 5, true);
-    Simulator::Schedule(Seconds(5.050), &WaveNetDeviceExample::SendIpPacket, this, 6, false);
-
-    Simulator::Stop(Seconds(6.0));
-    Simulator::Run();
-    Simulator::Destroy();
-}
-
-bool
-WaveNetDeviceExample::ReceiveVsa(Ptr<const Packet> pkt, const Address& address, uint32_t, uint32_t)
-{
-    std::cout << "receive a VSA management frame: recvTime = " << Now().As(Time::S) << "."
-              << std::endl;
-    return true;
-}
-
-void
-WaveNetDeviceExample::SendWsaExample()
-{
-    CreateWaveNodes();
-    Ptr<WaveNetDevice> sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-    Ptr<WaveNetDevice> receiver = DynamicCast<WaveNetDevice>(devices.Get(1));
-
-    // Alternating access without immediate channel switch for sender and receiver
-    const SchInfo schInfo = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, sender, schInfo);
-    Simulator::Schedule(Seconds(0.0), &WaveNetDevice::StartSch, receiver, schInfo);
-
-    // the peer address of VSA is broadcast address, and the repeat rate
-    // of VsaInfo is 100 per 5s, the VSA frame will be sent repeatedly.
-    Ptr<Packet> wsaPacket = Create<Packet>(100);
-    Mac48Address dest = Mac48Address::GetBroadcast();
-    const VsaInfo vsaInfo =
-        VsaInfo(dest, OrganizationIdentifier(), 0, wsaPacket, SCH1, 100, VSA_TRANSMIT_IN_BOTHI);
-    Simulator::Schedule(Seconds(1.0), &WaveNetDevice::StartVsa, sender, vsaInfo);
-    Simulator::Schedule(Seconds(3.0), &WaveNetDevice::StopVsa, sender, SCH1);
-
-    // release alternating access
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::StopSch, sender, SCH1);
-    Simulator::Schedule(Seconds(4.0), &WaveNetDevice::StopSch, receiver, SCH1);
-
-    // these WSA packets cannot be transmitted because of no channel access assigned
-    Simulator::Schedule(Seconds(5.0), &WaveNetDevice::StartVsa, sender, vsaInfo);
-    Simulator::Schedule(Seconds(6.0), &WaveNetDevice::StopVsa, sender, SCH1);
-
-    Simulator::Stop(Seconds(6.0));
-    Simulator::Run();
-    Simulator::Destroy();
-}
-
-int
-main(int argc, char* argv[])
-{
-    CommandLine cmd(__FILE__);
-    cmd.Parse(argc, argv);
-
-    WaveNetDeviceExample example;
-    std::cout << "run WAVE WSMP routing service case:" << std::endl;
-    example.SendWsmpExample();
-    std::cout << "run WAVE IP routing service case:" << std::endl;
-    // example.SendIpExample ();
-    std::cout << "run WAVE WSA routing service case:" << std::endl;
-    // example.SendWsaExample ();
-    return 0;
-}
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-bsm-helper.cc ns-3.39/src/wave/helper/wave-bsm-helper.cc
--- ns-3.38/src/wave/helper/wave-bsm-helper.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-bsm-helper.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,165 +0,0 @@
-/*
- * Copyright (c) 2014 North Carolina State 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: Scott E. Carpenter <scarpen@ncsu.edu>
- *
- */
-
-#include "ns3/wave-bsm-helper.h"
-
-#include "ns3/log.h"
-
-NS_LOG_COMPONENT_DEFINE("WaveBsmHelper");
-
-namespace ns3
-{
-
-std::vector<int> WaveBsmHelper::nodesMoving;
-
-WaveBsmHelper::WaveBsmHelper()
-    : m_waveBsmStats()
-{
-    m_txSafetyRangesSq.resize(10, 0);
-    m_txSafetyRangesSq[0] = 50.0 * 50.0;
-    m_txSafetyRangesSq[1] = 100.0 * 100.0;
-    m_txSafetyRangesSq[2] = 200.0 * 200.0;
-    m_txSafetyRangesSq[3] = 300.0 * 300.0;
-    m_txSafetyRangesSq[4] = 400.0 * 400.0;
-    m_txSafetyRangesSq[5] = 500.0 * 500.0;
-    m_txSafetyRangesSq[6] = 600.0 * 600.0;
-    m_txSafetyRangesSq[7] = 800.0 * 800.0;
-    m_txSafetyRangesSq[8] = 1000.0 * 1000.0;
-    m_txSafetyRangesSq[9] = 1500.0 * 1500.0;
-
-    m_factory.SetTypeId("ns3::BsmApplication");
-}
-
-void
-WaveBsmHelper::SetAttribute(std::string name, const AttributeValue& value)
-{
-    m_factory.Set(name, value);
-}
-
-ApplicationContainer
-WaveBsmHelper::Install(Ptr<Node> node) const
-{
-    return ApplicationContainer(InstallPriv(node));
-}
-
-ApplicationContainer
-WaveBsmHelper::Install(Ipv4InterfaceContainer i) const
-{
-    ApplicationContainer apps;
-    for (Ipv4InterfaceContainer::Iterator itr = i.Begin(); itr != i.End(); ++itr)
-    {
-        std::pair<Ptr<Ipv4>, uint32_t> interface = (*itr);
-        Ptr<Ipv4> pp = interface.first;
-        Ptr<Node> node = pp->GetObject<Node>();
-        apps.Add(InstallPriv(node));
-    }
-
-    return apps;
-}
-
-Ptr<Application>
-WaveBsmHelper::InstallPriv(Ptr<Node> node) const
-{
-    Ptr<Application> app = m_factory.Create<Application>();
-    node->AddApplication(app);
-
-    return app;
-}
-
-void
-WaveBsmHelper::Install(Ipv4InterfaceContainer& i,
-                       Time totalTime,             // seconds
-                       uint32_t wavePacketSize,    // bytes
-                       Time waveInterval,          // seconds
-                       double gpsAccuracyNs,       // clock drift range in number of ns
-                       std::vector<double> ranges, // m
-                       int chAccessMode,           // channel access mode
-                       Time txMaxDelay)            // max delay prior to transmit
-{
-    int size = ranges.size();
-    m_txSafetyRangesSq.clear();
-    m_txSafetyRangesSq.resize(size, 0);
-    for (int index = 0; index < size; index++)
-    {
-        // stored as square of value, for optimization
-        m_txSafetyRangesSq[index] = ranges[index] * ranges[index];
-    }
-
-    // install a BsmApplication on each node
-    ApplicationContainer bsmApps = Install(i);
-    // start BSM app immediately (BsmApplication will
-    // delay transmission of first BSM by 1.0 seconds)
-    bsmApps.Start(Seconds(0));
-    bsmApps.Stop(totalTime);
-
-    // for each app, setup the app parameters
-    ApplicationContainer::Iterator aci;
-    int nodeId = 0;
-    for (aci = bsmApps.Begin(); aci != bsmApps.End(); ++aci)
-    {
-        Ptr<BsmApplication> bsmApp = DynamicCast<BsmApplication>(*aci);
-        bsmApp->Setup(i,
-                      nodeId,
-                      totalTime,
-                      wavePacketSize,
-                      waveInterval,
-                      gpsAccuracyNs,
-                      m_txSafetyRangesSq,
-                      GetWaveBsmStats(),
-                      &nodesMoving,
-                      chAccessMode,
-                      txMaxDelay);
-        nodeId++;
-    }
-}
-
-Ptr<WaveBsmStats>
-WaveBsmHelper::GetWaveBsmStats()
-{
-    return &m_waveBsmStats;
-}
-
-int64_t
-WaveBsmHelper::AssignStreams(NodeContainer c, int64_t stream)
-{
-    int64_t currentStream = stream;
-    Ptr<Node> node;
-    for (NodeContainer::Iterator i = c.Begin(); i != c.End(); ++i)
-    {
-        node = (*i);
-        for (uint32_t j = 0; j < node->GetNApplications(); j++)
-        {
-            Ptr<BsmApplication> bsmApp = DynamicCast<BsmApplication>(node->GetApplication(j));
-            if (bsmApp)
-            {
-                currentStream += bsmApp->AssignStreams(currentStream);
-            }
-        }
-    }
-    return (currentStream - stream);
-}
-
-std::vector<int>&
-WaveBsmHelper::GetNodesMoving()
-{
-    return nodesMoving;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-bsm-helper.h ns-3.39/src/wave/helper/wave-bsm-helper.h
--- ns-3.38/src/wave/helper/wave-bsm-helper.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-bsm-helper.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,148 +0,0 @@
-/*
- * Copyright (c) 2014 North Carolina State 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: Scott E. Carpenter <scarpen@ncsu.edu>
- *
- */
-
-#ifndef WAVE_BSM_HELPER_H
-#define WAVE_BSM_HELPER_H
-
-#include "ns3/application-container.h"
-#include "ns3/bsm-application.h"
-#include "ns3/internet-stack-helper.h"
-#include "ns3/mobility-model.h"
-#include "ns3/nstime.h"
-#include "ns3/object-factory.h"
-#include "ns3/wave-bsm-stats.h"
-
-#include <vector>
-
-namespace ns3
-{
-/**
- * \ingroup wave
- * \brief The WaveBsmHelper class manages
- * IEEE 1609 WAVE (Wireless Access in Vehicular Environments)
- * Basic Safety Messages (BSMs) and uses the WaveBsmStats class
- * to manage statistics about BSMs transmitted and received
- * The BSM is a ~200-byte packet that is
- * generally broadcast from every vehicle at a nominal rate of 10 Hz.
- */
-class WaveBsmHelper
-{
-  public:
-    /**
-     * \brief Constructor
-     */
-    WaveBsmHelper();
-
-    /**
-     * Helper function used to set the underlying application attributes.
-     *
-     * \param name the name of the application attribute to set
-     * \param value the value of the application attribute to set
-     */
-    void SetAttribute(std::string name, const AttributeValue& value);
-
-    /**
-     * Install an ns3::BsmApplication on each node of the input container
-     * configured with all the attributes set with SetAttribute.
-     *
-     * \param i Ipv4InterfaceContainer of the set of interfaces on which an BsmApplication
-     * will be installed on the nodes.
-     * \returns Container of Ptr to the applications installed.
-     */
-    ApplicationContainer Install(Ipv4InterfaceContainer i) const;
-
-    /**
-     * Install an ns3::BsmApplication on the node configured with all the
-     * attributes set with SetAttribute.
-     *
-     * \param node The node on which an BsmApplication will be installed.
-     * \returns Container of Ptr to the applications installed.
-     */
-    ApplicationContainer Install(Ptr<Node> node) const;
-
-    /**
-     * \brief Installs BSM generation on devices for nodes
-     * and their interfaces
-     * \param i IPv4 interface container
-     * \param totalTime total amount of time that BSM packets should be transmitted
-     * \param wavePacketSize the size, in bytes, of a WAVE BSM
-     * \param waveInterval the time, in seconds, between each WAVE BSM transmission,
-     * typically 10 Hz (0.1 second)
-     * \param gpsAccuracyNs the timing synchronization accuracy of GPS time, in nanoseconds.
-     * GPS time-sync is ~40-100 ns.  Universally synchronized time among all vehicles
-     * will result in all vehicles transmitting safety messages simultaneously, leading
-     * to excessive wireless collisions.
-     * \param ranges the expected transmission range, in m.
-     * \param chAccessMode channel access mode (0=continuous; 1=switching)
-     * \param txMaxDelay max delay prior to transmit
-     */
-    void Install(Ipv4InterfaceContainer& i,
-                 Time totalTime,             // seconds
-                 uint32_t wavePacketSize,    // bytes
-                 Time waveInterval,          // seconds
-                 double gpsAccuracyNs,       // clock drift range in number of ns
-                 std::vector<double> ranges, // m
-                 int chAccessMode,           // channel access mode (0=continuous; 1=switching)
-                 Time txMaxDelay);           // max delay prior to transmit
-
-    /**
-     * \brief Returns the WaveBsmStats instance
-     * \return the WaveBsmStats instance
-     */
-    Ptr<WaveBsmStats> GetWaveBsmStats();
-
-    /**
-     * 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.  The Install() method should have previously been
-     * called by the user.
-     *
-     * \param stream first stream index to use
-     * \param c NodeContainer of the set of nodes for which the BsmApplication
-     *          should be modified to use a fixed stream
-     * \return the number of stream indices assigned by this helper
-     */
-    int64_t AssignStreams(NodeContainer c, int64_t stream);
-
-    /**
-     * \brief Returns the list of moving nove indicators
-     * \return the list of moving node indicators
-     */
-    static std::vector<int>& GetNodesMoving();
-
-  private:
-    /**
-     * Install an ns3::BsmApplication on the node
-     *
-     * \param node The node on which an BsmApplication will be installed.
-     * \returns Ptr to the application installed.
-     */
-    Ptr<Application> InstallPriv(Ptr<Node> node) const;
-
-    ObjectFactory m_factory;     //!< Object factory.
-    WaveBsmStats m_waveBsmStats; ///< wave BSM stats
-    /// tx safety range squared, for optimization
-    std::vector<double> m_txSafetyRangesSq;
-    static std::vector<int> nodesMoving; ///< nodes moving
-};
-
-} // namespace ns3
-
-#endif /* WAVE_BSM_HELPER_H*/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-bsm-stats.cc ns-3.39/src/wave/helper/wave-bsm-stats.cc
--- ns-3.38/src/wave/helper/wave-bsm-stats.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-bsm-stats.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,203 +0,0 @@
-/*
- * Copyright (c) 2014 North Carolina State 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: Scott E. Carpenter <scarpen@ncsu.edu>
- *
- */
-
-#include "ns3/wave-bsm-stats.h"
-
-#include "ns3/integer.h"
-#include "ns3/log.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("WaveBsmStats");
-
-WaveBsmStats::WaveBsmStats()
-    : m_wavePktSendCount(0),
-      m_waveByteSendCount(0),
-      m_wavePktReceiveCount(0),
-      m_log(false)
-{
-    m_wavePktExpectedReceiveCounts.resize(10, 0);
-    m_wavePktInCoverageReceiveCounts.resize(10, 0);
-    m_waveTotalPktExpectedReceiveCounts.resize(10, 0);
-    m_waveTotalPktInCoverageReceiveCounts.resize(10, 0);
-}
-
-/* static */
-TypeId
-WaveBsmStats::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::WaveBsmStats")
-                            .SetParent<Object>()
-                            .SetGroupName("Stats")
-                            .AddConstructor<WaveBsmStats>();
-    return tid;
-}
-
-void
-WaveBsmStats::IncTxPktCount()
-{
-    m_wavePktSendCount++;
-}
-
-int
-WaveBsmStats::GetTxPktCount() const
-{
-    return m_wavePktSendCount;
-}
-
-void
-WaveBsmStats::IncExpectedRxPktCount(int index)
-{
-    m_wavePktExpectedReceiveCounts[index - 1]++;
-    m_waveTotalPktExpectedReceiveCounts[index - 1]++;
-}
-
-void
-WaveBsmStats::IncRxPktCount()
-{
-    m_wavePktReceiveCount++;
-}
-
-void
-WaveBsmStats::IncRxPktInRangeCount(int index)
-{
-    m_wavePktInCoverageReceiveCounts[index - 1]++;
-    m_waveTotalPktInCoverageReceiveCounts[index - 1]++;
-}
-
-int
-WaveBsmStats::GetRxPktCount() const
-{
-    return m_wavePktReceiveCount;
-}
-
-int
-WaveBsmStats::GetExpectedRxPktCount(int index)
-{
-    return m_wavePktExpectedReceiveCounts[index - 1];
-}
-
-int
-WaveBsmStats::GetRxPktInRangeCount(int index)
-{
-    return m_wavePktInCoverageReceiveCounts[index - 1];
-}
-
-void
-WaveBsmStats::SetTxPktCount(int count)
-{
-    m_wavePktSendCount = count;
-}
-
-void
-WaveBsmStats::SetRxPktCount(int count)
-{
-    m_wavePktReceiveCount = count;
-}
-
-void
-WaveBsmStats::IncTxByteCount(int bytes)
-{
-    m_waveByteSendCount += bytes;
-}
-
-int
-WaveBsmStats::GetTxByteCount() const
-{
-    return m_waveByteSendCount;
-}
-
-double
-WaveBsmStats::GetBsmPdr(int index)
-{
-    double pdr = 0.0;
-
-    if (m_wavePktExpectedReceiveCounts[index - 1] > 0)
-    {
-        pdr = (double)m_wavePktInCoverageReceiveCounts[index - 1] /
-              (double)m_wavePktExpectedReceiveCounts[index - 1];
-        // due to node movement, it is
-        // possible to receive a packet that is not slightly "within range" that was
-        // transmitted at the time when the nodes were slightly "out of range"
-        // thus, prevent overflow of PDR > 100%
-        if (pdr > 1.0)
-        {
-            pdr = 1.0;
-        }
-    }
-
-    return pdr;
-}
-
-double
-WaveBsmStats::GetCumulativeBsmPdr(int index)
-{
-    double pdr = 0.0;
-
-    if (m_waveTotalPktExpectedReceiveCounts[index - 1] > 0)
-    {
-        pdr = (double)m_waveTotalPktInCoverageReceiveCounts[index - 1] /
-              (double)m_waveTotalPktExpectedReceiveCounts[index - 1];
-        // due to node movement, it is
-        // possible to receive a packet that is not slightly "within range" that was
-        // transmitted at the time when the nodes were slightly "out of range"
-        // thus, prevent overflow of PDR > 100%
-        if (pdr > 1.0)
-        {
-            pdr = 1.0;
-        }
-    }
-
-    return pdr;
-}
-
-void
-WaveBsmStats::SetLogging(bool log)
-{
-    m_log = log;
-}
-
-bool
-WaveBsmStats::GetLogging() const
-{
-    return m_log;
-}
-
-void
-WaveBsmStats::SetExpectedRxPktCount(int index, int count)
-{
-    m_wavePktExpectedReceiveCounts[index - 1] = count;
-}
-
-void
-WaveBsmStats::SetRxPktInRangeCount(int index, int count)
-{
-    m_wavePktInCoverageReceiveCounts[index - 1] = count;
-}
-
-void
-WaveBsmStats::ResetTotalRxPktCounts(int index)
-{
-    m_waveTotalPktInCoverageReceiveCounts[index - 1] = 0;
-    m_waveTotalPktExpectedReceiveCounts[index - 1] = 0;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-bsm-stats.h ns-3.39/src/wave/helper/wave-bsm-stats.h
--- ns-3.38/src/wave/helper/wave-bsm-stats.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-bsm-stats.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,239 +0,0 @@
-/*
- * Copyright (c) 2014 North Carolina State 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: Scott E. Carpenter <scarpen@ncsu.edu>
- *
- */
-
-#ifndef WAVE_BSM_STATS_H
-#define WAVE_BSM_STATS_H
-
-#include "ns3/object.h"
-
-#include <vector>
-
-namespace ns3
-{
-/**
- * \ingroup wave
- * \brief The WaveBsmStats class implements a stats collector for
- * IEEE 1609 WAVE (Wireless Access in Vehicular Environments)
- * Basic Safety Messages (BSMs).  The BSM is a ~200-byte packet that is
- * generally broadcast from every vehicle at a nominal rate of 10 Hz.
- *
- * \internal
- * Note:  This class collects data elements and accessors
- * along with methods that calculate metrics from the data
- * elements.  The data and metrics calculation algorithms
- * are collected together here purely to keep them together.
- * Future work may need to add additional metric calculations,
- * and for now, we are trying to keep all related data and
- * algorithms together, although these could easily be
- * refactored in the future and moved to separate classes.
- * However, it seems that for now, moving the data elements
- * or the algorithms separately into different classes could
- * lead to confusion over usage.
- */
-class WaveBsmStats : public Object
-{
-  public:
-    /**
-     * \brief Constructor
-     */
-    WaveBsmStats();
-
-    /**
-     * Register this type.
-     * \return The TypeId.
-     */
-    static TypeId GetTypeId();
-
-    /**
-     * \brief Increments the count of transmitted packets
-     */
-    void IncTxPktCount();
-
-    /**
-     * \brief Returns the count of transmitted packets
-     * \return count of packets transmitted
-     */
-    int GetTxPktCount() const;
-
-    /*
-     * Note:
-     * The WAVE Basic Safety Message (BSM) is broadcast and
-     * unacknowledged. In order to calculate packet delivery
-     * ratio (PDR), we must count i) the packets that are
-     * actually received and ii) the transmitted packets that
-     * are expected to be received.  Both are relative to a
-     * specified (circular) coverage area.
-     *
-     * For example:  Say we have three nodes, A, B, and C, each
-     * separated by 40m, as follows:
-     *
-     *    A --<40m>-- B --<40m>-- C
-     *
-     * Let's assume that the transmission range is 50m, and only
-     * A is transmitting (i.e. broadcasting).  B can receive A's
-     * broadcasts, while C cannot.  Let's assume no dropped packets.
-     * If we set the coverage area to  100m, then the PDR is 50%,
-     * because B receives every transmission from A, while C receives
-     * none of them.  However, if we change the effective
-     * coverage area to 75m then the PDR improves to 100%, because
-     * B receives 100% of A's transmissions, and C is outside of the
-     * coverage area, and so does not factor in to the PDR.
-     */
-
-    /**
-     * \brief Increments the count of (broadcast) packets expected
-     * to be received within the coverage area1.  Broadcast packets
-     * (i.e. WAVE Basic Safety Messages) are not ACK'd.  For packet
-     * delivery ratio (PDR), we need to count transmitted packets that
-     * are expected to be received within the coverage area, even
-     * though they may not be physically received (due to collisions
-     * or receiver power thresholds).
-     * \param index index for statistics
-     */
-    void IncExpectedRxPktCount(int index);
-
-    /**
-     * \brief Increments the count of actual packets received
-     * (regardless of coverage area).
-     */
-    void IncRxPktCount();
-
-    /**
-     * \brief Increments the count of actual packets received within
-     * the coverage area(index).  Broadcast packets
-     * (i.e. WAVE Basic Safety Messages) are not ACK'd.  For packet
-     * delivery ratio (PDR), we need to count only those received packets
-     * that are actually received within the (circular) coverage area.
-     * \param index index for statistics
-     */
-    void IncRxPktInRangeCount(int index);
-
-    /**
-     * \brief Returns the count of packets received
-     * \return the count of packets received
-     */
-    int GetRxPktCount() const;
-
-    /**
-     * \brief Returns the count of expected packets received within range(index)
-     * \param index index for statistics
-     * \return the count of expected packets received within range(index)
-     */
-    int GetExpectedRxPktCount(int index);
-
-    /**
-     * \brief Increments the count of actual packets received within range(index)
-     * \param index index for statistics
-     * \return the count of actual packets received within range(index)
-     */
-    int GetRxPktInRangeCount(int index);
-
-    /**
-     * \brief Sets the count of packets expected to received
-     * \param index index for statistics
-     * \param count the count of packets
-     */
-    void SetExpectedRxPktCount(int index, int count);
-
-    /**
-     * \brief Sets the count of packets within range that are received
-     * \param index index for statistics
-     * \param count the count of packets
-     */
-    void SetRxPktInRangeCount(int index, int count);
-
-    /**
-     * \brief Resets the count of total packets
-     * expected and/or within range(index) that are received
-     * \param index index for statistics
-     */
-    void ResetTotalRxPktCounts(int index);
-
-    /**
-     * \brief Sets the count of packets transmitted
-     * \param count the count of packets transmitted
-     */
-    void SetTxPktCount(int count);
-
-    /**
-     * \brief Sets the count of packets received
-     * \param count the count of packets received
-     */
-    void SetRxPktCount(int count);
-
-    /**
-     * \brief Increments the count of (application data) bytes transmitted
-     * not including MAC/PHY overhead
-     * \param bytes the bytes of application-data transmitted
-     */
-    void IncTxByteCount(int bytes);
-
-    /**
-     * \brief Returns the count of (application data) bytes transmitted
-     * not include MAC/PHY overhead
-     * \return number of bytes of application-data transmitted
-     */
-    int GetTxByteCount() const;
-
-    /**
-     * \brief Returns the BSM Packet Delivery Ratio (PDR)
-     * which is the percent of expected packets within range(index) that
-     * are actually received
-     * \param index index for statistics
-     * \return the packet delivery ratio (PDR) of BSMs.
-     */
-    double GetBsmPdr(int index);
-
-    /**
-     * \brief Returns the cumulative BSM Packet Delivery Ratio (PDR)
-     * which is the percent of cumulative expected packets within range(index)
-     * that are actually received
-     * \param index index for statistics
-     * \return the packet delivery ratio (PDR) of BSMs.
-     */
-    double GetCumulativeBsmPdr(int index);
-
-    /**
-     * \brief Enables/disables logging
-     * \param log logging state
-     */
-    void SetLogging(bool log);
-
-    /**
-     * \brief Gets logging state
-     * \return logging state
-     */
-    bool GetLogging() const;
-
-  private:
-    int m_wavePktSendCount;                            ///< packet sent count
-    int m_waveByteSendCount;                           ///< byte sent count
-    int m_wavePktReceiveCount;                         ///< packet receive count
-    std::vector<int> m_wavePktInCoverageReceiveCounts; ///< packet in coverage receive counts
-    std::vector<int> m_wavePktExpectedReceiveCounts;   ///< packet expected receive counts
-    std::vector<int>
-        m_waveTotalPktInCoverageReceiveCounts; ///< total packet in coverage receive counts
-    std::vector<int> m_waveTotalPktExpectedReceiveCounts; ///< total packet expected receive counts
-    bool m_log;                                           ///< logging state
-};
-
-} // namespace ns3
-
-#endif /* WAVE_BSM_STATS_H*/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-helper.cc ns-3.39/src/wave/helper/wave-helper.cc
--- ns-3.38/src/wave/helper/wave-helper.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-helper.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,476 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-
-#include "wave-helper.h"
-
-#include "wave-mac-helper.h"
-
-#include "ns3/abort.h"
-#include "ns3/config.h"
-#include "ns3/log.h"
-#include "ns3/minstrel-wifi-manager.h"
-#include "ns3/names.h"
-#include "ns3/pointer.h"
-#include "ns3/qos-txop.h"
-#include "ns3/radiotap-header.h"
-#include "ns3/string.h"
-#include "ns3/wave-net-device.h"
-
-NS_LOG_COMPONENT_DEFINE("WaveHelper");
-
-namespace ns3
-{
-
-/**
- * ASCII Phy transmit sink with context
- * \param stream the output stream
- * \param context the context
- * \param p the packet
- * \param mode the mode
- * \param preamble the preamble
- * \param txLevel transmit level
- */
-static void
-AsciiPhyTransmitSinkWithContext(Ptr<OutputStreamWrapper> stream,
-                                std::string context,
-                                Ptr<const Packet> p,
-                                WifiMode mode [[maybe_unused]],
-                                WifiPreamble preamble [[maybe_unused]],
-                                uint8_t txLevel [[maybe_unused]])
-{
-    NS_LOG_FUNCTION(stream << context << p << mode << preamble << txLevel);
-    *stream->GetStream() << "t " << Simulator::Now().GetSeconds() << " " << context << " " << *p
-                         << std::endl;
-}
-
-/**
- * ASCII Phy transmit sink without context
- * \param stream the output stream
- * \param p the packet
- * \param mode the mode
- * \param preamble the preamble
- * \param txLevel transmit level
- */
-static void
-AsciiPhyTransmitSinkWithoutContext(Ptr<OutputStreamWrapper> stream,
-                                   Ptr<const Packet> p,
-                                   WifiMode mode,
-                                   WifiPreamble preamble,
-                                   uint8_t txLevel)
-{
-    NS_LOG_FUNCTION(stream << p << mode << preamble << txLevel);
-    *stream->GetStream() << "t " << Simulator::Now().GetSeconds() << " " << *p << std::endl;
-}
-
-/**
- * ASCII Phy receive sink with context
- * \param stream the output stream
- * \param context the context
- * \param p the packet
- * \param snr the SNR
- * \param mode the mode
- * \param preamble the preamble
- */
-static void
-AsciiPhyReceiveSinkWithContext(Ptr<OutputStreamWrapper> stream,
-                               std::string context,
-                               Ptr<const Packet> p,
-                               double snr,
-                               WifiMode mode,
-                               WifiPreamble preamble)
-{
-    NS_LOG_FUNCTION(stream << context << p << snr << mode << preamble);
-    *stream->GetStream() << "r " << Simulator::Now().GetSeconds() << " " << context << " " << *p
-                         << std::endl;
-}
-
-/**
- * ASCII Phy receive sink without context
- * \param stream the output stream
- * \param p the packet
- * \param snr the SNR
- * \param mode the mode
- * \param preamble the preamble
- */
-static void
-AsciiPhyReceiveSinkWithoutContext(Ptr<OutputStreamWrapper> stream,
-                                  Ptr<const Packet> p,
-                                  double snr,
-                                  WifiMode mode,
-                                  WifiPreamble preamble)
-{
-    NS_LOG_FUNCTION(stream << p << snr << mode << preamble);
-    *stream->GetStream() << "r " << Simulator::Now().GetSeconds() << " " << *p << std::endl;
-}
-
-/****************************** YansWavePhyHelper ***********************************/
-YansWavePhyHelper
-YansWavePhyHelper::Default()
-{
-    YansWavePhyHelper helper;
-    helper.SetErrorRateModel("ns3::NistErrorRateModel");
-    return helper;
-}
-
-void
-YansWavePhyHelper::EnablePcapInternal(std::string prefix,
-                                      Ptr<NetDevice> nd,
-                                      bool /* promiscuous */,
-                                      bool explicitFilename)
-{
-    //
-    // All of the Pcap enable functions vector through here including the ones
-    // that are wandering through all of devices on perhaps all of the nodes in
-    // the system.  We can only deal with devices of type WaveNetDevice.
-    //
-    Ptr<WaveNetDevice> device = nd->GetObject<WaveNetDevice>();
-    if (!device)
-    {
-        NS_LOG_INFO("YansWavePhyHelper::EnablePcapInternal(): Device "
-                    << &device << " not of type ns3::WaveNetDevice");
-        return;
-    }
-
-    std::vector<Ptr<WifiPhy>> phys = device->GetPhys();
-    NS_ABORT_MSG_IF(phys.empty(), "EnablePcapInternal(): Phy layer in WaveNetDevice must be set");
-
-    PcapHelper pcapHelper;
-
-    std::string filename;
-    if (explicitFilename)
-    {
-        filename = prefix;
-    }
-    else
-    {
-        filename = pcapHelper.GetFilenameFromDevice(prefix, device);
-    }
-
-    Ptr<PcapFileWrapper> file =
-        pcapHelper.CreateFile(filename, std::ios::out, GetPcapDataLinkType());
-
-    std::vector<Ptr<WifiPhy>>::iterator i;
-    for (i = phys.begin(); i != phys.end(); ++i)
-    {
-        Ptr<WifiPhy> phy = (*i);
-        phy->TraceConnectWithoutContext(
-            "MonitorSnifferTx",
-            MakeBoundCallback(&YansWavePhyHelper::PcapSniffTxEvent, file));
-        phy->TraceConnectWithoutContext(
-            "MonitorSnifferRx",
-            MakeBoundCallback(&YansWavePhyHelper::PcapSniffRxEvent, file));
-    }
-}
-
-void
-YansWavePhyHelper::EnableAsciiInternal(Ptr<OutputStreamWrapper> stream,
-                                       std::string prefix,
-                                       Ptr<NetDevice> nd,
-                                       bool explicitFilename)
-{
-    //
-    // All of the ascii enable functions vector through here including the ones
-    // that are wandering through all of devices on perhaps all of the nodes in
-    // the system.  We can only deal with devices of type WaveNetDevice.
-    //
-    Ptr<WaveNetDevice> device = nd->GetObject<WaveNetDevice>();
-    if (!device)
-    {
-        NS_LOG_INFO("EnableAsciiInternal(): Device " << device
-                                                     << " not of type ns3::WaveNetDevice");
-        return;
-    }
-
-    //
-    // Our trace sinks are going to use packet printing, so we have to make sure
-    // that is turned on.
-    //
-    Packet::EnablePrinting();
-
-    uint32_t nodeid = nd->GetNode()->GetId();
-    uint32_t deviceid = nd->GetIfIndex();
-    std::ostringstream oss;
-
-    //
-    // If we are not provided an OutputStreamWrapper, we are expected to create
-    // one using the usual trace filename conventions and write our traces
-    // without a context since there will be one file per context and therefore
-    // the context would be redundant.
-    //
-    if (!stream)
-    {
-        //
-        // Set up an output stream object to deal with private ofstream copy
-        // constructor and lifetime issues.  Let the helper decide the actual
-        // name of the file given the prefix.
-        //
-        AsciiTraceHelper asciiTraceHelper;
-
-        std::string filename;
-        if (explicitFilename)
-        {
-            filename = prefix;
-        }
-        else
-        {
-            filename = asciiTraceHelper.GetFilenameFromDevice(prefix, device);
-        }
-
-        Ptr<OutputStreamWrapper> theStream = asciiTraceHelper.CreateFileStream(filename);
-        //
-        // We could go poking through the phy and the state looking for the
-        // correct trace source, but we can let Config deal with that with
-        // some search cost.  Since this is presumably happening at topology
-        // creation time, it doesn't seem much of a price to pay.
-        //
-        oss.str("");
-        oss << "/NodeList/" << nodeid << "/DeviceList/" << deviceid
-            << "/$ns3::WaveNetDevice/PhyEntities/*/$ns3::WifiPhy/State/RxOk";
-        Config::ConnectWithoutContext(
-            oss.str(),
-            MakeBoundCallback(&AsciiPhyReceiveSinkWithoutContext, theStream));
-
-        oss.str("");
-        oss << "/NodeList/" << nodeid << "/DeviceList/" << deviceid
-            << "/$ns3::WaveNetDevice/PhyEntities/*/$ns3::WifiPhy/State/Tx";
-        Config::ConnectWithoutContext(
-            oss.str(),
-            MakeBoundCallback(&AsciiPhyTransmitSinkWithoutContext, theStream));
-
-        return;
-    }
-
-    //
-    // If we are provided an OutputStreamWrapper, we are expected to use it, and
-    // to provide a context.  We are free to come up with our own context if we
-    // want, and use the AsciiTraceHelper Hook*WithContext functions, but for
-    // compatibility and simplicity, we just use Config::Connect and let it deal
-    // with coming up with a context.
-    //
-    oss.str("");
-    oss << "/NodeList/" << nodeid << "/DeviceList/" << deviceid
-        << "/$ns3::WaveNetDevice/PhyEntities/*/$ns3::WifiPhy/RxOk";
-    Config::Connect(oss.str(), MakeBoundCallback(&AsciiPhyReceiveSinkWithContext, stream));
-
-    oss.str("");
-    oss << "/NodeList/" << nodeid << "/DeviceList/" << deviceid
-        << "/$ns3::WaveNetDevice/PhyEntities/*/$ns3::WifiPhy/State/Tx";
-    Config::Connect(oss.str(), MakeBoundCallback(&AsciiPhyTransmitSinkWithContext, stream));
-}
-
-/********************************** WaveHelper ******************************************/
-WaveHelper::WaveHelper()
-{
-}
-
-WaveHelper::~WaveHelper()
-{
-}
-
-WaveHelper
-WaveHelper::Default()
-{
-    WaveHelper helper;
-    // default 7 MAC entities and single PHY device.
-    helper.CreateMacForChannel(ChannelManager::GetWaveChannels());
-    helper.CreatePhys(1);
-    helper.SetChannelScheduler("ns3::DefaultChannelScheduler");
-    helper.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                   "DataMode",
-                                   StringValue("OfdmRate6MbpsBW10MHz"),
-                                   "ControlMode",
-                                   StringValue("OfdmRate6MbpsBW10MHz"),
-                                   "NonUnicastMode",
-                                   StringValue("OfdmRate6MbpsBW10MHz"));
-    return helper;
-}
-
-void
-WaveHelper::CreateMacForChannel(std::vector<uint32_t> channelNumbers)
-{
-    if (channelNumbers.empty())
-    {
-        NS_FATAL_ERROR("the WAVE MAC entities is at least one");
-    }
-    for (std::vector<uint32_t>::iterator i = channelNumbers.begin(); i != channelNumbers.end(); ++i)
-    {
-        if (!ChannelManager::IsWaveChannel(*i))
-        {
-            NS_FATAL_ERROR("the channel number " << (*i) << " is not a valid WAVE channel number");
-        }
-    }
-    m_macsForChannelNumber = channelNumbers;
-}
-
-void
-WaveHelper::CreatePhys(uint32_t phys)
-{
-    if (phys == 0)
-    {
-        NS_FATAL_ERROR("the WAVE PHY entities is at least one");
-    }
-    if (phys > ChannelManager::GetNumberOfWaveChannels())
-    {
-        NS_FATAL_ERROR("the number of assigned WAVE PHY entities is more than the number of valid "
-                       "WAVE channels");
-    }
-    m_physNumber = phys;
-}
-
-NetDeviceContainer
-WaveHelper::Install(const WifiPhyHelper& phyHelper,
-                    const WifiMacHelper& macHelper,
-                    NodeContainer c) const
-{
-    try
-    {
-        const QosWaveMacHelper& qosMac [[maybe_unused]] =
-            dynamic_cast<const QosWaveMacHelper&>(macHelper);
-    }
-    catch (const std::bad_cast&)
-    {
-        NS_FATAL_ERROR("WifiMacHelper should be the class or subclass of QosWaveMacHelper");
-    }
-
-    NetDeviceContainer devices;
-    for (NodeContainer::Iterator i = c.Begin(); i != c.End(); ++i)
-    {
-        Ptr<Node> node = *i;
-        Ptr<WaveNetDevice> device = CreateObject<WaveNetDevice>();
-
-        device->SetChannelManager(CreateObject<ChannelManager>());
-        device->SetChannelCoordinator(CreateObject<ChannelCoordinator>());
-        device->SetVsaManager(CreateObject<VsaManager>());
-        device->SetChannelScheduler(m_channelScheduler.Create<ChannelScheduler>());
-
-        for (uint32_t j = 0; j != m_physNumber; ++j)
-        {
-            std::vector<Ptr<WifiPhy>> phys = phyHelper.Create(node, device);
-            NS_ABORT_IF(phys.size() != 1);
-            phys[0]->ConfigureStandard(WIFI_STANDARD_80211p);
-            phys[0]->SetOperatingChannel(
-                WifiPhy::ChannelTuple{ChannelManager::GetCch(), 0, WIFI_PHY_BAND_5GHZ, 0});
-            device->AddPhy(phys[0]);
-        }
-
-        for (std::vector<uint32_t>::const_iterator k = m_macsForChannelNumber.begin();
-             k != m_macsForChannelNumber.end();
-             ++k)
-        {
-            Ptr<WifiMac> wifiMac = macHelper.Create(device, WIFI_STANDARD_80211p);
-            Ptr<OcbWifiMac> ocbMac = DynamicCast<OcbWifiMac>(wifiMac);
-            ocbMac->SetWifiRemoteStationManager(
-                m_stationManager.Create<WifiRemoteStationManager>());
-            ocbMac->EnableForWave(device);
-            device->AddMac(*k, ocbMac);
-        }
-
-        device->SetAddress(Mac48Address::Allocate());
-
-        node->AddDevice(device);
-        devices.Add(device);
-    }
-    return devices;
-}
-
-NetDeviceContainer
-WaveHelper::Install(const WifiPhyHelper& phy, const WifiMacHelper& mac, Ptr<Node> node) const
-{
-    return Install(phy, mac, NodeContainer(node));
-}
-
-NetDeviceContainer
-WaveHelper::Install(const WifiPhyHelper& phy, const WifiMacHelper& mac, std::string nodeName) const
-{
-    Ptr<Node> node = Names::Find<Node>(nodeName);
-    return Install(phy, mac, NodeContainer(node));
-}
-
-void
-WaveHelper::EnableLogComponents()
-{
-    WifiHelper::EnableLogComponents();
-
-    LogComponentEnable("WaveNetDevice", LOG_LEVEL_ALL);
-    LogComponentEnable("ChannelCoordinator", LOG_LEVEL_ALL);
-    LogComponentEnable("ChannelManager", LOG_LEVEL_ALL);
-    LogComponentEnable("ChannelScheduler", LOG_LEVEL_ALL);
-    LogComponentEnable("DefaultChannelScheduler", LOG_LEVEL_ALL);
-    LogComponentEnable("VsaManager", LOG_LEVEL_ALL);
-    LogComponentEnable("OcbWifiMac", LOG_LEVEL_ALL);
-    LogComponentEnable("VendorSpecificAction", LOG_LEVEL_ALL);
-    LogComponentEnable("WaveFrameExchangeManager", LOG_LEVEL_ALL);
-    LogComponentEnable("HigherLayerTxVectorTag", LOG_LEVEL_ALL);
-}
-
-int64_t
-WaveHelper::AssignStreams(NetDeviceContainer c, int64_t stream)
-{
-    int64_t currentStream = stream;
-    Ptr<NetDevice> netDevice;
-    for (NetDeviceContainer::Iterator i = c.Begin(); i != c.End(); ++i)
-    {
-        netDevice = (*i);
-        Ptr<WaveNetDevice> wave = DynamicCast<WaveNetDevice>(netDevice);
-        if (wave)
-        {
-            // Handle any random numbers in the PHY objects.
-            std::vector<Ptr<WifiPhy>> phys = wave->GetPhys();
-            for (std::vector<Ptr<WifiPhy>>::iterator j = phys.begin(); j != phys.end(); ++j)
-            {
-                currentStream += (*j)->AssignStreams(currentStream);
-            }
-
-            // Handle any random numbers in the MAC objects.
-            std::map<uint32_t, Ptr<OcbWifiMac>> macs = wave->GetMacs();
-            for (std::map<uint32_t, Ptr<OcbWifiMac>>::iterator k = macs.begin(); k != macs.end();
-                 ++k)
-            {
-                // Handle any random numbers in the station managers.
-                Ptr<WifiRemoteStationManager> manager = k->second->GetWifiRemoteStationManager();
-                Ptr<MinstrelWifiManager> minstrel = DynamicCast<MinstrelWifiManager>(manager);
-                if (minstrel)
-                {
-                    currentStream += minstrel->AssignStreams(currentStream);
-                }
-
-                PointerValue ptr;
-                k->second->GetAttribute("Txop", ptr);
-                Ptr<Txop> txop = ptr.Get<Txop>();
-                currentStream += txop->AssignStreams(currentStream);
-
-                k->second->GetAttribute("VO_Txop", ptr);
-                Ptr<QosTxop> vo_txop = ptr.Get<QosTxop>();
-                currentStream += vo_txop->AssignStreams(currentStream);
-
-                k->second->GetAttribute("VI_Txop", ptr);
-                Ptr<QosTxop> vi_txop = ptr.Get<QosTxop>();
-                currentStream += vi_txop->AssignStreams(currentStream);
-
-                k->second->GetAttribute("BE_Txop", ptr);
-                Ptr<QosTxop> be_txop = ptr.Get<QosTxop>();
-                currentStream += be_txop->AssignStreams(currentStream);
-
-                k->second->GetAttribute("BK_Txop", ptr);
-                Ptr<QosTxop> bk_txop = ptr.Get<QosTxop>();
-                currentStream += bk_txop->AssignStreams(currentStream);
-            }
-        }
-    }
-    return (currentStream - stream);
-}
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-helper.h ns-3.39/src/wave/helper/wave-helper.h
--- ns-3.38/src/wave/helper/wave-helper.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-helper.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,237 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-
-#ifndef WAVE_HELPER_H
-#define WAVE_HELPER_H
-
-#include "ns3/attribute.h"
-#include "ns3/net-device-container.h"
-#include "ns3/node-container.h"
-#include "ns3/object-factory.h"
-#include "ns3/trace-helper.h"
-#include "ns3/yans-wifi-helper.h"
-
-#include <string>
-
-namespace ns3
-{
-
-class WaveNetDevice;
-class Node;
-
-/**
- * \ingroup wave
- * To trace WaveNetDevice, we have to overwrite the trace functions of class YansWifiPhyHelper.
- * The source code is very similar with YansWifiPhy, only with some adaptation.
- */
-class YansWavePhyHelper : public YansWifiPhyHelper
-{
-  public:
-    /**
-     * Create a phy helper in a default working state.
-     * \return A phy helper
-     */
-    static YansWavePhyHelper Default();
-
-  private:
-    /**
-     * @brief Enable pcap output the indicated net device.
-     *
-     * NetDevice-specific implementation mechanism for hooking the trace and
-     * writing to the trace file.
-     *
-     * @param prefix Filename prefix to use for pcap files.
-     * @param nd Net device for which you want to enable tracing.
-     * @param promiscuous If true capture all possible packets available at the device.
-     * @param explicitFilename Treat the prefix as an explicit filename if true
-     */
-    void EnablePcapInternal(std::string prefix,
-                            Ptr<NetDevice> nd,
-                            bool promiscuous,
-                            bool explicitFilename) override;
-
-    /**
-     * \brief Enable ascii trace output on the indicated net device.
-     * \internal
-     *
-     * NetDevice-specific implementation mechanism for hooking the trace and
-     * writing to the trace file.
-     *
-     * \param stream The output stream object to use when logging ascii traces.
-     * \param prefix Filename prefix to use for ascii trace files.
-     * \param nd Net device for which you want to enable tracing.
-     * \param explicitFilename Treat the prefix as an explicit filename if true
-     */
-    void EnableAsciiInternal(Ptr<OutputStreamWrapper> stream,
-                             std::string prefix,
-                             Ptr<NetDevice> nd,
-                             bool explicitFilename) override;
-};
-
-/**
- * \ingroup wave
- * \brief helps to create WaveNetDevice objects
- *
- * This class can help to create a large set of similar
- * WaveNetDevice objects and to configure a large set of
- * their attributes during creation.
- *
- * Generally WaveHelper with default configuration will
- * create devices with 7 MAC entities,1 PHY entity and a
- * multiple-channel scheduler for to deal with these entities.
- *
- * If users can make sure on which channel this WAVE device
- * will work, they can set specific channel numbers to save
- * resources of unused channels as below:
- * WaveHelper helper = WaveHelper::Default ();
- * uint32_t channels[] = {CCH, SCH1};
- * std::vector<uint32_t> channelsVector (channels, channels + 2);
- * helper.helper.CreateMacForChannel (channelsVector);
- *
- * If users can create other channel scheduler such as "AnotherScheduler"
- * which can assign channel access in the context of  more PHY entities,
- * they can use this helper to create WAVE devices as below:
- * WaveHelper helper = WaveHelper::Default ();
- * helper.helper.CreateMacForChannel (ChannelManager::GetWaveChannels ());
- * helper.CreatePhys (2);        // or other number which should be less than 7
- * helper.SetChannelScheduler ("ns3::AnotherScheduler");
- * helper.SetRemoteStationManager ("ns3::ConstantRateWifiManager");  // or other  rate control
- * algorithms
- */
-class WaveHelper
-{
-  public:
-    WaveHelper();
-    virtual ~WaveHelper();
-
-    /**
-     * \returns a new WaveHelper in a default state
-     *
-     * The default state is defined as being seven OcbWifiMac MAC entities
-     * with an ConstantRate rate control algorithm, one WifiPhy entity and
-     * a default multiple-channel scheduler DefaultChannelScheduler for
-     * assigning channel access with these created entities.
-     */
-    static WaveHelper Default();
-
-    /**
-     * \param channelNumbers the MAC entities will be created to support these channels for multiple
-     * channel operation.
-     */
-    void CreateMacForChannel(std::vector<uint32_t> channelNumbers);
-    /**
-     * \param phys the number of PHY entity which will be created for multiple channel operation.
-     */
-    void CreatePhys(uint32_t phys);
-
-    /**
-     * \tparam Ts \deduced Argument types
-     * \param type the type of ns3::WifiRemoteStationManager to create.
-     * \param [in] args Name and AttributeValue pairs to set.
-     *
-     * All the attributes specified in this method should exist
-     * in the requested station manager.
-     */
-    template <typename... Ts>
-    void SetRemoteStationManager(std::string type, Ts&&... args);
-    /**
-     * \tparam Ts \deduced Argument types
-     * \param type the type of ns3::ChannelScheduler to create.
-     * \param [in] args Name and AttributeValue pairs to set.
-     *
-     * All the attributes specified in this method should exist
-     * in the requested channel scheduler.
-     */
-    template <typename... Ts>
-    void SetChannelScheduler(std::string type, Ts&&... args);
-
-    /**
-     * \param phy the PHY helper to create PHY objects
-     * \param mac the MAC helper to create MAC objects
-     * \param c the set of nodes on which a wifi device must be created
-     * \returns a device container which contains all the devices created by this method.
-     */
-    virtual NetDeviceContainer Install(const WifiPhyHelper& phy,
-                                       const WifiMacHelper& mac,
-                                       NodeContainer c) const;
-    /**
-     * \param phy the PHY helper to create PHY objects
-     * \param mac the MAC helper to create MAC objects
-     * \param node the node on which a wifi device must be created
-     * \returns a device container which contains all the devices created by this method.
-     */
-    virtual NetDeviceContainer Install(const WifiPhyHelper& phy,
-                                       const WifiMacHelper& mac,
-                                       Ptr<Node> node) const;
-    /**
-     * \param phy the PHY helper to create PHY objects
-     * \param mac the MAC helper to create MAC objects
-     * \param nodeName the name of node on which a wifi device must be created
-     * \returns a device container which contains all the devices created by this method.
-     */
-    virtual NetDeviceContainer Install(const WifiPhyHelper& phy,
-                                       const WifiMacHelper& mac,
-                                       std::string nodeName) const;
-
-    /**
-     * Helper to enable all WaveNetDevice log components with one statement
-     */
-    static void EnableLogComponents();
-
-    /**
-     * Assign a fixed random variable stream number to the random variables
-     * used by the Phy and Mac aspects of the WAVE models.  Each device in
-     * container c has fixed stream numbers assigned to its random variables.
-     * The Wifi channel (e.g. propagation loss model) is excluded.
-     * Return the number of streams (possibly zero) that
-     * have been assigned. The Install() method should have previously been
-     * called by the user.
-     *
-     * \param c NetDeviceContainer of the set of net devices for which the
-     *          WaveNetDevice should be modified to use fixed streams
-     * \param stream first stream index to use
-     * \return the number of stream indices assigned by this helper
-     */
-    int64_t AssignStreams(NetDeviceContainer c, int64_t stream);
-
-  protected:
-    ObjectFactory m_stationManager;               ///< station manager
-    ObjectFactory m_channelScheduler;             ///< channel scheduler
-    std::vector<uint32_t> m_macsForChannelNumber; ///< MACs for channel number
-    uint32_t m_physNumber;                        ///< Phy number
-};
-
-/***************************************************************
- *  Implementation of the templates declared above.
- ***************************************************************/
-
-template <typename... Ts>
-void
-WaveHelper::SetRemoteStationManager(std::string type, Ts&&... args)
-{
-    m_stationManager = ObjectFactory(type, std::forward<Ts>(args)...);
-}
-
-template <typename... Ts>
-void
-WaveHelper::SetChannelScheduler(std::string type, Ts&&... args)
-{
-    m_channelScheduler = ObjectFactory(type, std::forward<Ts>(args)...);
-}
-
-} // namespace ns3
-#endif /* WAVE_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-mac-helper.cc ns-3.39/src/wave/helper/wave-mac-helper.cc
--- ns-3.38/src/wave/helper/wave-mac-helper.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-mac-helper.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2009 MIRKO BANCHI
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- * Author: Mirko Banchi <mk.banchi@gmail.com>
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "wave-mac-helper.h"
-
-#include "ns3/boolean.h"
-
-namespace ns3
-{
-
-NqosWaveMacHelper::NqosWaveMacHelper()
-{
-}
-
-NqosWaveMacHelper::~NqosWaveMacHelper()
-{
-}
-
-NqosWaveMacHelper
-NqosWaveMacHelper::Default()
-{
-    NqosWaveMacHelper helper;
-    // We're making non QoS-enabled Wi-Fi MACs here, so we set the
-    // necessary attribute. I've carefully positioned this here so that
-    // someone who knows what they're doing can override with explicit
-    // attributes.
-    helper.SetType("ns3::OcbWifiMac", "QosSupported", BooleanValue(false));
-    return helper;
-}
-
-/**********  QosWifi80211pMacHelper *********/
-QosWaveMacHelper::QosWaveMacHelper()
-{
-}
-
-QosWaveMacHelper::~QosWaveMacHelper()
-{
-}
-
-QosWaveMacHelper
-QosWaveMacHelper::Default()
-{
-    QosWaveMacHelper helper;
-
-    // We're making QoS-enabled Wi-Fi MACs here, so we set the necessary
-    // attribute. I've carefully positioned this here so that someone
-    // who knows what they're doing can override with explicit
-    // attributes.
-    helper.SetType("ns3::OcbWifiMac", "QosSupported", BooleanValue(true));
-
-    return helper;
-}
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wave-mac-helper.h ns-3.39/src/wave/helper/wave-mac-helper.h
--- ns-3.38/src/wave/helper/wave-mac-helper.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wave-mac-helper.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2009 MIRKO BANCHI
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- * Author: Mirko Banchi <mk.banchi@gmail.com>
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef WAVE_MAC_HELPER_H
-#define WAVE_MAC_HELPER_H
-#include "ns3/wifi-mac-helper.h"
-
-namespace ns3
-{
-
-/**
- * \ingroup wave
- * \brief Nqos Wave Mac Helper class
- */
-class NqosWaveMacHelper : public WifiMacHelper
-{
-  public:
-    /**
-     * Create a NqosWaveMacHelper to make life easier for people who want to
-     * work with non-QOS Wave MAC layers.
-     */
-    NqosWaveMacHelper();
-
-    /**
-     * Destroy a NqosWaveMacHelper.
-     */
-    ~NqosWaveMacHelper() override;
-    /**
-     * Create a mac helper in a default working state.
-     * i.e., this is an ocb mac by default.
-     * \returns NqosWaveMacHelper
-     */
-    static NqosWaveMacHelper Default();
-    /**
-     * \tparam Ts \deduced Argument types
-     * \param type the type of ns3::WifiMac to create.
-     * \param [in] args Name and AttributeValue pairs to set.
-     *
-     * All the attributes specified in this method should exist
-     * in the requested mac.
-     *
-     * note: Here we require users set type with OcbWifiMac or its
-     * subclass, otherwise it will become error
-     */
-    template <typename... Ts>
-    void SetType(std::string type, Ts&&... args);
-};
-
-/**
- * \ingroup wave
- * \brief Qos Wave Mac Helper class
- */
-class QosWaveMacHelper : public WifiMacHelper
-{
-  public:
-    /**
-     * Create a QosWaveMacHelper that is used to make life easier when working
-     * with Wifi 802.11p devices using a QOS MAC layer.
-     */
-    QosWaveMacHelper();
-
-    /**
-     * Destroy a QosWaveMacHelper
-     */
-    ~QosWaveMacHelper() override;
-
-    /**
-     * Create a mac helper in a default working state.
-     * \return A mac helper
-     */
-    static QosWaveMacHelper Default();
-
-    /**
-     * \tparam Ts \deduced Argument types
-     * \param type the type of ns3::WifiMac to create.
-     * \param [in] args Name and AttributeValue pairs to set.
-     *
-     * All the attributes specified in this method should exist
-     * in the requested mac.
-     *
-     * note: Here we require users set type with OcbWifiMac or its
-     * subclass, otherwise it will become error
-     */
-    template <typename... Ts>
-    void SetType(std::string type, Ts&&... args);
-};
-
-/***************************************************************
- *  Implementation of the templates declared above.
- ***************************************************************/
-
-template <typename... Ts>
-void
-NqosWaveMacHelper::SetType(std::string type, Ts&&... args)
-{
-    if (type != "ns3::OcbWifiMac")
-    {
-        NS_FATAL_ERROR("QosWaveMacHelper shall set OcbWifiMac");
-    }
-    WifiMacHelper::SetType("ns3::OcbWifiMac", std::forward<Ts>(args)...);
-}
-
-template <typename... Ts>
-void
-QosWaveMacHelper::SetType(std::string type, Ts&&... args)
-{
-    if (type != "ns3::OcbWifiMac")
-    {
-        NS_FATAL_ERROR("QosWaveMacHelper shall set OcbWifiMac");
-    }
-    WifiMacHelper::SetType("ns3::OcbWifiMac", std::forward<Ts>(args)...);
-}
-
-} // namespace ns3
-
-#endif /* WAVE_MAC_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wifi-80211p-helper.cc ns-3.39/src/wave/helper/wifi-80211p-helper.cc
--- ns-3.38/src/wave/helper/wifi-80211p-helper.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/helper/wifi-80211p-helper.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2009 MIRKO BANCHI
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- * Author: Mirko Banchi <mk.banchi@gmail.com>
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "wifi-80211p-helper.h"
-
-#include "wave-mac-helper.h"
-
-#include "ns3/log.h"
-#include "ns3/string.h"
-
-#include <typeinfo>
-
-namespace ns3
-{
-
-Wifi80211pHelper::Wifi80211pHelper()
-{
-}
-
-Wifi80211pHelper::~Wifi80211pHelper()
-{
-}
-
-Wifi80211pHelper
-Wifi80211pHelper::Default()
-{
-    Wifi80211pHelper helper;
-    helper.SetStandard(WIFI_STANDARD_80211p);
-    helper.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                   "DataMode",
-                                   StringValue("OfdmRate6MbpsBW10MHz"),
-                                   "ControlMode",
-                                   StringValue("OfdmRate6MbpsBW10MHz"),
-                                   "NonUnicastMode",
-                                   StringValue("OfdmRate6MbpsBW10MHz"));
-    return helper;
-}
-
-void
-Wifi80211pHelper::SetStandard(WifiStandard standard)
-{
-    if (standard == WIFI_STANDARD_80211p)
-    {
-        WifiHelper::SetStandard(standard);
-    }
-    else
-    {
-        NS_FATAL_ERROR("wrong standard selected!");
-    }
-}
-
-void
-Wifi80211pHelper::EnableLogComponents()
-{
-    WifiHelper::EnableLogComponents();
-
-    LogComponentEnable("OcbWifiMac", LOG_LEVEL_ALL);
-    LogComponentEnable("VendorSpecificAction", LOG_LEVEL_ALL);
-}
-
-NetDeviceContainer
-Wifi80211pHelper::Install(const WifiPhyHelper& phyHelper,
-                          const WifiMacHelper& macHelper,
-                          NodeContainer c) const
-{
-    const QosWaveMacHelper* qosMac [[maybe_unused]] =
-        dynamic_cast<const QosWaveMacHelper*>(&macHelper);
-    if (qosMac == nullptr)
-    {
-        const NqosWaveMacHelper* nqosMac [[maybe_unused]] =
-            dynamic_cast<const NqosWaveMacHelper*>(&macHelper);
-        if (nqosMac == nullptr)
-        {
-            NS_FATAL_ERROR("the macHelper should be either QosWaveMacHelper or NqosWaveMacHelper"
-                           ", or should be the subclass of QosWaveMacHelper or NqosWaveMacHelper");
-        }
-    }
-
-    return WifiHelper::Install(phyHelper, macHelper, c);
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/helper/wifi-80211p-helper.h ns-3.39/src/wave/helper/wifi-80211p-helper.h
--- ns-3.38/src/wave/helper/wifi-80211p-helper.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/wave/helper/wifi-80211p-helper.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2009 MIRKO BANCHI
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- * Author: Mirko Banchi <mk.banchi@gmail.com>
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef WIFI_802_11P_HELPER_H
-#define WIFI_802_11P_HELPER_H
-#include "ns3/deprecated.h"
-#include "ns3/wifi-helper.h"
-
-namespace ns3
-{
-
-/**
- * \ingroup wave
- * \brief helps to create wifi 802.11p objects of
- * WifiNetDevice class
- *
- * This class can help to create a large set of similar
- * wifi 802.11p objects and to configure a large set of
- * their attributes during creation.
- */
-class Wifi80211pHelper : public WifiHelper
-{
-  public:
-    Wifi80211pHelper();
-    ~Wifi80211pHelper() override;
-
-    /**
-     * \returns a new Wifi80211pHelper in a default state
-     *
-     * The default state is defined as being an OcbWifiMac MAC
-     * layer with constant rate OfdmRate6MbpsBW10MHz
-     * and both objects using their default attribute values.
-     */
-    static Wifi80211pHelper Default();
-
-    /**
-     * \param standard the phy standard to configure during installation
-     *
-     * Users can only configure 802.11p with 10MHz or 5 MHz channel bandwidth.
-     * The default 802.11p standard uses 10MHz.
-     */
-    void SetStandard(WifiStandard standard) override;
-
-    /**
-     * \param phy the PHY helper to create PHY objects
-     * \param macHelper the MAC helper to create MAC objects
-     * \param c the set of nodes on which a wifi device must be created
-     * \returns a device container which contains all the devices created by this method.
-     */
-    NetDeviceContainer Install(const WifiPhyHelper& phy,
-                               const WifiMacHelper& macHelper,
-                               NodeContainer c) const override;
-
-    /**
-     * Helper to enable all WifiNetDevice log components with one statement
-     */
-    static void EnableLogComponents();
-};
-
-} // namespace ns3
-
-#endif /* WIFI_802_11P_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/bsm-application.cc ns-3.39/src/wave/model/bsm-application.cc
--- ns-3.38/src/wave/model/bsm-application.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/bsm-application.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,427 +0,0 @@
-/*
- * Copyright (c) 2014 North Carolina State 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: Scott E. Carpenter <scarpen@ncsu.edu>
- *
- */
-
-#include "ns3/bsm-application.h"
-
-#include "ns3/log.h"
-#include "ns3/mobility-helper.h"
-#include "ns3/mobility-model.h"
-#include "ns3/wave-helper.h"
-#include "ns3/wave-mac-helper.h"
-#include "ns3/wave-net-device.h"
-
-NS_LOG_COMPONENT_DEFINE("BsmApplication");
-
-namespace ns3
-{
-
-// (Arbitrary) port for establishing socket to transmit WAVE BSMs
-int BsmApplication::wavePort = 9080;
-
-NS_OBJECT_ENSURE_REGISTERED(BsmApplication);
-
-TypeId
-BsmApplication::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::BsmApplication")
-                            .SetParent<Application>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<BsmApplication>();
-    return tid;
-}
-
-BsmApplication::BsmApplication()
-    : m_waveBsmStats(nullptr),
-      m_txSafetyRangesSq(),
-      m_TotalSimTime(Seconds(10)),
-      m_wavePacketSize(200),
-      m_numWavePackets(1),
-      m_waveInterval(MilliSeconds(100)),
-      m_gpsAccuracyNs(10000),
-      m_adhocTxInterfaces(nullptr),
-      m_nodesMoving(nullptr),
-      m_unirv(nullptr),
-      m_nodeId(0),
-      m_chAccessMode(0),
-      m_txMaxDelay(MilliSeconds(10)),
-      m_prevTxDelay(MilliSeconds(0))
-{
-    NS_LOG_FUNCTION(this);
-}
-
-BsmApplication::~BsmApplication()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-BsmApplication::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-
-    // chain up
-    Application::DoDispose();
-}
-
-// Application Methods
-void
-BsmApplication::StartApplication() // Called at time specified by Start
-{
-    NS_LOG_FUNCTION(this);
-
-    // setup generation of WAVE BSM messages
-    Time waveInterPacketInterval = m_waveInterval;
-
-    // BSMs are not transmitted for the first second
-    Time startTime = Seconds(1.0);
-    // total length of time transmitting WAVE packets
-    Time totalTxTime = m_TotalSimTime - startTime;
-    // total WAVE packets needing to be sent
-    m_numWavePackets = (uint32_t)(totalTxTime.GetDouble() / m_waveInterval.GetDouble());
-
-    TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
-
-    // every node broadcasts WAVE BSM to potentially all other nodes
-    Ptr<Socket> recvSink = Socket::CreateSocket(GetNode(m_nodeId), tid);
-    recvSink->SetRecvCallback(MakeCallback(&BsmApplication::ReceiveWavePacket, this));
-    InetSocketAddress local = InetSocketAddress(Ipv4Address::GetAny(), wavePort);
-    recvSink->BindToNetDevice(GetNetDevice(m_nodeId));
-    recvSink->Bind(local);
-    recvSink->SetAllowBroadcast(true);
-
-    // dest is broadcast address
-    InetSocketAddress remote = InetSocketAddress(Ipv4Address("255.255.255.255"), wavePort);
-    recvSink->Connect(remote);
-
-    // Transmission start time for each BSM:
-    // We assume that the start transmission time
-    // for the first packet will be on a ns-3 time
-    // "Second" boundary - e.g., 1.0 s.
-    // However, the actual transmit time must reflect
-    // additional effects of 1) clock drift and
-    // 2) transmit delay requirements.
-    // 1) Clock drift - clocks are not perfectly
-    // synchronized across all nodes.  In a VANET
-    // we assume all nodes sync to GPS time, which
-    // itself is assumed  accurate to, say, 40-100 ns.
-    // Thus, the start transmission time must be adjusted
-    // by some value, t_drift.
-    // 2) Transmit delay requirements - The US
-    // minimum performance requirements for V2V
-    // BSM transmission expect a random delay of
-    // +/- 5 ms, to avoid simultaneous transmissions
-    // by all vehicles congesting the channel.  Thus,
-    // we need to adjust the start transmission time by
-    // some value, t_tx_delay.
-    // Therefore, the actual transmit time should be:
-    // t_start = t_time + t_drift + t_tx_delay
-    // t_drift is always added to t_time.
-    // t_tx_delay is supposed to be +/- 5ms, but if we
-    // allow negative numbers the time could drift to a value
-    // BEFORE the interval start time (i.e., at 100 ms
-    // boundaries, we do not want to drift into the
-    // previous interval, such as at 95 ms.  Instead,
-    // we always want to be at the 100 ms interval boundary,
-    // plus [0..10] ms tx delay.
-    // Thus, the average t_tx_delay will be
-    // within the desired range of [0..10] ms of
-    // (t_time + t_drift)
-
-    // WAVE devices sync to GPS time
-    // and all devices would like to begin broadcasting
-    // their safety messages immediately at the start of
-    // the CCH interval.  However, if all do so, then
-    // significant collisions occur.  Thus, we assume there
-    // is some GPS sync accuracy on GPS devices,
-    // typically 40-100 ns.
-    // Get a uniformly random number for GPS sync accuracy, in ns.
-    Time tDrift = NanoSeconds(m_unirv->GetInteger(0, m_gpsAccuracyNs));
-
-    // When transmitting at a default rate of 10 Hz,
-    // the subsystem shall transmit every 100 ms +/-
-    // a random value between 0 and 5 ms. [MPR-BSMTX-TXTIM-002]
-    // Source: CAMP Vehicle Safety Communications 4 Consortium
-    // On-board Minimum Performance Requirements
-    // for V2V Safety Systems Version 1.0, December 17, 2014
-    // max transmit delay (default 10ms)
-    // get value for transmit delay, as number of ns
-    uint32_t d_ns = static_cast<uint32_t>(m_txMaxDelay.GetInteger());
-    // convert random tx delay to ns-3 time
-    // see note above regarding centering tx delay
-    // offset by 5ms + a random value.
-    Time txDelay = NanoSeconds(m_unirv->GetInteger(0, d_ns));
-    m_prevTxDelay = txDelay;
-
-    Time txTime = startTime + tDrift + txDelay;
-    // schedule transmission of first packet
-    Simulator::ScheduleWithContext(recvSink->GetNode()->GetId(),
-                                   txTime,
-                                   &BsmApplication::GenerateWaveTraffic,
-                                   this,
-                                   recvSink,
-                                   m_wavePacketSize,
-                                   m_numWavePackets,
-                                   waveInterPacketInterval,
-                                   m_nodeId);
-}
-
-void
-BsmApplication::StopApplication() // Called at time specified by Stop
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-BsmApplication::Setup(Ipv4InterfaceContainer& i,
-                      int nodeId,
-                      Time totalTime,
-                      uint32_t wavePacketSize, // bytes
-                      Time waveInterval,
-                      double gpsAccuracyNs,
-                      std::vector<double> rangesSq, // m ^2
-                      Ptr<WaveBsmStats> waveBsmStats,
-                      std::vector<int>* nodesMoving,
-                      int chAccessMode,
-                      Time txMaxDelay)
-{
-    NS_LOG_FUNCTION(this);
-
-    m_unirv = CreateObject<UniformRandomVariable>();
-
-    m_TotalSimTime = totalTime;
-    m_wavePacketSize = wavePacketSize;
-    m_waveInterval = waveInterval;
-    m_gpsAccuracyNs = gpsAccuracyNs;
-    int size = rangesSq.size();
-    m_waveBsmStats = waveBsmStats;
-    m_nodesMoving = nodesMoving;
-    m_chAccessMode = chAccessMode;
-    m_txSafetyRangesSq.clear();
-    m_txSafetyRangesSq.resize(size, 0);
-
-    for (int index = 0; index < size; index++)
-    {
-        // stored as square of value, for optimization
-        m_txSafetyRangesSq[index] = rangesSq[index];
-    }
-
-    m_adhocTxInterfaces = &i;
-    m_nodeId = nodeId;
-    m_txMaxDelay = txMaxDelay;
-}
-
-void
-BsmApplication::GenerateWaveTraffic(Ptr<Socket> socket,
-                                    uint32_t pktSize,
-                                    uint32_t pktCount,
-                                    Time pktInterval,
-                                    uint32_t sendingNodeId)
-{
-    NS_LOG_FUNCTION(this);
-
-    // more packets to send?
-    if (pktCount > 0)
-    {
-        // for now, we cannot tell if each node has
-        // started mobility.  so, as an optimization
-        // only send if  this node is moving
-        // if not, then skip
-        int txNodeId = sendingNodeId;
-        Ptr<Node> txNode = GetNode(txNodeId);
-        Ptr<MobilityModel> txPosition = txNode->GetObject<MobilityModel>();
-        NS_ASSERT(txPosition);
-
-        int senderMoving = m_nodesMoving->at(txNodeId);
-        if (senderMoving != 0)
-        {
-            // send it!
-            socket->Send(Create<Packet>(pktSize));
-            // count it
-            m_waveBsmStats->IncTxPktCount();
-            m_waveBsmStats->IncTxByteCount(pktSize);
-            int wavePktsSent = m_waveBsmStats->GetTxPktCount();
-            if ((m_waveBsmStats->GetLogging() != 0) && ((wavePktsSent % 1000) == 0))
-            {
-                NS_LOG_UNCOND("Sending WAVE pkt # " << wavePktsSent);
-            }
-
-            // find other nodes within range that would be
-            // expected to receive this broadbast
-            int nRxNodes = m_adhocTxInterfaces->GetN();
-            for (int i = 0; i < nRxNodes; i++)
-            {
-                Ptr<Node> rxNode = GetNode(i);
-                int rxNodeId = rxNode->GetId();
-
-                if (rxNodeId != txNodeId)
-                {
-                    Ptr<MobilityModel> rxPosition = rxNode->GetObject<MobilityModel>();
-                    NS_ASSERT(rxPosition);
-                    // confirm that the receiving node
-                    // has also started moving in the scenario
-                    // if it has not started moving, then
-                    // it is not a candidate to receive a packet
-                    int receiverMoving = m_nodesMoving->at(rxNodeId);
-                    if (receiverMoving == 1)
-                    {
-                        double distSq = MobilityHelper::GetDistanceSquaredBetween(txNode, rxNode);
-                        if (distSq > 0.0)
-                        {
-                            // dest node within range?
-                            int rangeCount = m_txSafetyRangesSq.size();
-                            for (int index = 1; index <= rangeCount; index++)
-                            {
-                                if (distSq <= m_txSafetyRangesSq[index - 1])
-                                {
-                                    // we should expect dest node to receive broadcast pkt
-                                    m_waveBsmStats->IncExpectedRxPktCount(index);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // every BSM must be scheduled with a tx time delay
-        // of +/- (5) ms.  See comments in StartApplication().
-        // we handle this as a tx delay of [0..10] ms
-        // from the start of the pktInterval boundary
-        uint32_t d_ns = static_cast<uint32_t>(m_txMaxDelay.GetInteger());
-        Time txDelay = NanoSeconds(m_unirv->GetInteger(0, d_ns));
-
-        // do not want the tx delay to be cumulative, so
-        // deduct the previous delay value.  thus we adjust
-        // to schedule the next event at the next pktInterval,
-        // plus some new [0..10] ms tx delay
-        Time txTime = pktInterval - m_prevTxDelay + txDelay;
-        m_prevTxDelay = txDelay;
-
-        Simulator::ScheduleWithContext(socket->GetNode()->GetId(),
-                                       txTime,
-                                       &BsmApplication::GenerateWaveTraffic,
-                                       this,
-                                       socket,
-                                       pktSize,
-                                       pktCount - 1,
-                                       pktInterval,
-                                       socket->GetNode()->GetId());
-    }
-    else
-    {
-        socket->Close();
-    }
-}
-
-void
-BsmApplication::ReceiveWavePacket(Ptr<Socket> socket)
-{
-    NS_LOG_FUNCTION(this);
-
-    Ptr<Packet> packet;
-    Address senderAddr;
-    while ((packet = socket->RecvFrom(senderAddr)))
-    {
-        Ptr<Node> rxNode = socket->GetNode();
-
-        if (InetSocketAddress::IsMatchingType(senderAddr))
-        {
-            InetSocketAddress addr = InetSocketAddress::ConvertFrom(senderAddr);
-            int nodes = m_adhocTxInterfaces->GetN();
-            for (int i = 0; i < nodes; i++)
-            {
-                if (addr.GetIpv4() == m_adhocTxInterfaces->GetAddress(i))
-                {
-                    Ptr<Node> txNode = GetNode(i);
-                    HandleReceivedBsmPacket(txNode, rxNode);
-                }
-            }
-        }
-    }
-}
-
-void
-BsmApplication::HandleReceivedBsmPacket(Ptr<Node> txNode, Ptr<Node> rxNode)
-{
-    NS_LOG_FUNCTION(this);
-
-    m_waveBsmStats->IncRxPktCount();
-
-    Ptr<MobilityModel> rxPosition = rxNode->GetObject<MobilityModel>();
-    NS_ASSERT(rxPosition);
-    // confirm that the receiving node
-    // has also started moving in the scenario
-    // if it has not started moving, then
-    // it is not a candidate to receive a packet
-    int rxNodeId = rxNode->GetId();
-    int receiverMoving = m_nodesMoving->at(rxNodeId);
-    if (receiverMoving == 1)
-    {
-        double rxDistSq = MobilityHelper::GetDistanceSquaredBetween(rxNode, txNode);
-        if (rxDistSq > 0.0)
-        {
-            int rangeCount = m_txSafetyRangesSq.size();
-            for (int index = 1; index <= rangeCount; index++)
-            {
-                if (rxDistSq <= m_txSafetyRangesSq[index - 1])
-                {
-                    m_waveBsmStats->IncRxPktInRangeCount(index);
-                }
-            }
-        }
-    }
-}
-
-int64_t
-BsmApplication::AssignStreams(int64_t streamIndex)
-{
-    NS_LOG_FUNCTION(this);
-
-    NS_ASSERT(m_unirv); // should be set by Setup() prevoiusly
-    m_unirv->SetStream(streamIndex);
-
-    return 1;
-}
-
-Ptr<Node>
-BsmApplication::GetNode(int id)
-{
-    NS_LOG_FUNCTION(this);
-
-    std::pair<Ptr<Ipv4>, uint32_t> interface = m_adhocTxInterfaces->Get(id);
-    Ptr<Ipv4> pp = interface.first;
-    Ptr<Node> node = pp->GetObject<Node>();
-
-    return node;
-}
-
-Ptr<NetDevice>
-BsmApplication::GetNetDevice(int id)
-{
-    NS_LOG_FUNCTION(this);
-
-    std::pair<Ptr<Ipv4>, uint32_t> interface = m_adhocTxInterfaces->Get(id);
-    Ptr<Ipv4> pp = interface.first;
-    Ptr<NetDevice> device = pp->GetObject<NetDevice>();
-
-    return device;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/bsm-application.h ns-3.39/src/wave/model/bsm-application.h
--- ns-3.38/src/wave/model/bsm-application.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/bsm-application.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,179 +0,0 @@
-/*
- * Copyright (c) 2014 North Carolina State 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: Scott E. Carpenter <scarpen@ncsu.edu>
- *
- */
-
-#ifndef BSM_APPLICATION_H
-#define BSM_APPLICATION_H
-
-#include "ns3/application.h"
-#include "ns3/internet-stack-helper.h"
-#include "ns3/random-variable-stream.h"
-#include "ns3/wave-bsm-stats.h"
-
-namespace ns3
-{
-/**
- * \ingroup wave
- * \brief The BsmApplication class sends and receives the
- * IEEE 1609 WAVE (Wireless Access in Vehicular Environments)
- * Basic Safety Messages (BSMs) and uses the WaveBsmStats class
- * to manage statistics about BSMs transmitted and received
- * The BSM is a ~200-byte packet that is
- * generally broadcast from every vehicle at a nominal rate of 10 Hz.
- */
-class BsmApplication : public Application
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-
-    /**
-     * \brief Constructor
-     */
-    BsmApplication();
-    ~BsmApplication() override;
-
-    /**
-     * \brief Setup BSM generation parameters for a node
-     * \param i IPv4 interface container
-     * \param nodeId identifier of the node (index into container)
-     * \param totalTime total amount of time that BSM packets should be transmitted
-     * \param wavePacketSize the size, in bytes, of a WAVE BSM
-     * \param waveInterval the time, in seconds, between each WAVE BSM transmission,
-     * typically 10 Hz (0.1 second)
-     * \param gpsAccuracyNs the timing synchronization accuracy of GPS time, in nanoseconds.
-     * GPS time-sync is ~40-100 ns.  Universally synchronized time among all vehicles
-     * will result in all vehicles transmitting safety messages simultaneously, leading
-     * to excessive wireless collisions.
-     * \param rangesSq the expected transmission range, in m ^ 2.
-     * \param waveBsmStats class for WAVE BSM statistics
-     * \param nodesMoving of whether or not node(s) are moving
-     * \param mode
-     * \param txDelay
-     */
-    void Setup(Ipv4InterfaceContainer& i,
-               int nodeId,
-               Time totalTime,
-               uint32_t wavePacketSize, // bytes
-               Time waveInterval,
-               double gpsAccuracyNs,
-               std::vector<double> rangesSq, // m ^ 2
-               Ptr<WaveBsmStats> waveBsmStats,
-               std::vector<int>* nodesMoving,
-               int mode,
-               Time txDelay);
-
-    /**
-     * 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.  The Install() method should have previously been
-     * called by the user.
-     *
-     * \param streamIndex first stream index to use
-     * \return the number of stream indices assigned by this helper
-     */
-    int64_t AssignStreams(int64_t streamIndex);
-
-    /**
-     * (Arbitrary) port number that is used to create a socket for transmitting WAVE BSMs.
-     */
-    static int wavePort;
-
-  protected:
-    void DoDispose() override;
-
-  private:
-    // inherited from Application base class.
-    void StartApplication() override; ///< Called at time specified by Start
-    void StopApplication() override;  ///< Called at time specified by Stop
-
-    /**
-     * \brief Creates and transmits a WAVE BSM packet
-     * \param socket socket to use for transmission
-     * \param pktSize the size, in bytes, of the WAVE BSM packet
-     * \param pktCount the number of remaining WAVE BSM packets to be transmitted
-     * \param pktInterval the interval, in seconds, until the next packet
-     * should be transmitted
-     * \param sendingNodeId
-     */
-    void GenerateWaveTraffic(Ptr<Socket> socket,
-                             uint32_t pktSize,
-                             uint32_t pktCount,
-                             Time pktInterval,
-                             uint32_t sendingNodeId);
-
-    /**
-     * \brief Receive a WAVE BSM packet
-     * \param socket the receiving socket
-     */
-    void ReceiveWavePacket(Ptr<Socket> socket);
-
-    /**
-     * \brief Handle the receipt of a WAVE BSM packet from sender to receiver
-     * \param txNode the sending node
-     * \param rxNode the receiving node
-     */
-    void HandleReceivedBsmPacket(Ptr<Node> txNode, Ptr<Node> rxNode);
-
-    /**
-     * \brief Get the node for the desired id
-     * \param id the id of the desired node
-     * \return ptr to the desired node
-     */
-    Ptr<Node> GetNode(int id);
-
-    /**
-     * \brief Get the net device for the desired id
-     * \param id the id of the desired net device
-     * \return ptr to the desired net device
-     */
-    Ptr<NetDevice> GetNetDevice(int id);
-
-    Ptr<WaveBsmStats> m_waveBsmStats; ///< BSM stats
-    /// tx safety range squared, for optimization
-    std::vector<double> m_txSafetyRangesSq;
-    Time m_TotalSimTime;                         ///< total sim time
-    uint32_t m_wavePacketSize;                   ///< bytes
-    uint32_t m_numWavePackets;                   ///< number of wave packets
-    Time m_waveInterval;                         ///< wave interval
-    double m_gpsAccuracyNs;                      ///< GPS accuracy
-    Ipv4InterfaceContainer* m_adhocTxInterfaces; ///< transmit interfaces
-    std::vector<int>* m_nodesMoving;             ///< nodes moving
-    Ptr<UniformRandomVariable> m_unirv;          ///< random variable
-    int m_nodeId;                                ///< node ID
-    /// WAVE channel access mode.  0=continuous PHY; 1=channel-switching
-    int m_chAccessMode;
-    /**
-     * When transmitting at a default rate of 10 Hz,
-     * the subsystem shall transmit every 100 ms +/-
-     * a random value between 0 and 5 ms. [MPR-BSMTX-TXTIM-002]
-     * Source: CAMP Vehicle Safety Communications 4 Consortium
-     * On-board Minimum Performance Requirements
-     * for V2V Safety Systems Version 1.0, December 17, 2014
-     * max transmit delay (default 10ms) */
-    Time m_txMaxDelay;
-    Time m_prevTxDelay; ///< previous transmit delay
-};
-
-} // namespace ns3
-
-#endif /* BSM_APPLICATION_H*/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/channel-coordinator.cc ns-3.39/src/wave/model/channel-coordinator.cc
--- ns-3.38/src/wave/model/channel-coordinator.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/channel-coordinator.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,403 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "channel-coordinator.h"
-
-#include "ns3/log.h"
-#include "ns3/simulator.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("ChannelCoordinator");
-
-/****************************************************************
- *       This destructor is needed.
- ****************************************************************/
-
-ChannelCoordinationListener::~ChannelCoordinationListener()
-{
-}
-
-/****************************************************************/
-
-NS_OBJECT_ENSURE_REGISTERED(ChannelCoordinator);
-
-TypeId
-ChannelCoordinator::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::ChannelCoordinator")
-                            .SetParent<Object>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<ChannelCoordinator>()
-                            .AddAttribute("CchInterval",
-                                          "CCH Interval, default value is 50ms.",
-                                          TimeValue(GetDefaultCchInterval()),
-                                          MakeTimeAccessor(&ChannelCoordinator::m_cchi),
-                                          MakeTimeChecker())
-                            .AddAttribute("SchInterval",
-                                          "SCH Interval, default value is 50ms.",
-                                          TimeValue(GetDefaultSchInterval()),
-                                          MakeTimeAccessor(&ChannelCoordinator::m_schi),
-                                          MakeTimeChecker())
-                            .AddAttribute("GuardInterval",
-                                          "Guard Interval, default value is 4ms.",
-                                          TimeValue(GetDefaultGuardInterval()),
-                                          MakeTimeAccessor(&ChannelCoordinator::m_gi),
-                                          MakeTimeChecker());
-    return tid;
-}
-
-ChannelCoordinator::ChannelCoordinator()
-    : m_guardCount(0)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-ChannelCoordinator::~ChannelCoordinator()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-ChannelCoordinator::DoInitialize()
-{
-    NS_LOG_FUNCTION(this);
-    StartChannelCoordination();
-}
-
-void
-ChannelCoordinator::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    StopChannelCoordination();
-    UnregisterAllListeners();
-}
-
-Time
-ChannelCoordinator::GetDefaultCchInterval()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    // refer to Annex H of IEEE 1609.4-2010
-    const static uint8_t DEFAULT_CCH_INTERVAL = 50;
-    return MilliSeconds(DEFAULT_CCH_INTERVAL);
-}
-
-Time
-ChannelCoordinator::GetDefaultSchInterval()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    // refer to Annex H of IEEE 1609.4-2010
-    const static uint8_t DEFAULT_SCH_INTERVAL = 50;
-    return MilliSeconds(DEFAULT_SCH_INTERVAL);
-}
-
-Time
-ChannelCoordinator::GetDefaultSyncInterval()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    return GetDefaultCchInterval() + GetDefaultSchInterval();
-}
-
-Time
-ChannelCoordinator::GetDefaultGuardInterval()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    // refer to Annex H of IEEE 1609.4-2010
-    const static uint8_t SyncTolerance = 2;
-    const static uint8_t MaxChSwitchTime = 2;
-    const static uint8_t DEFAULT_GUARD_INTERVAL = SyncTolerance + MaxChSwitchTime;
-    return MilliSeconds(DEFAULT_GUARD_INTERVAL);
-}
-
-void
-ChannelCoordinator::SetCchInterval(Time cchInterval)
-{
-    NS_LOG_FUNCTION(this << cchInterval);
-    m_cchi = cchInterval;
-}
-
-Time
-ChannelCoordinator::GetCchInterval() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_cchi;
-}
-
-void
-ChannelCoordinator::SetSchInterval(Time schInterval)
-{
-    NS_LOG_FUNCTION(this << schInterval);
-    m_schi = schInterval;
-}
-
-Time
-ChannelCoordinator::GetSchInterval() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_schi;
-}
-
-Time
-ChannelCoordinator::GetSyncInterval() const
-{
-    NS_LOG_FUNCTION(this);
-    return GetCchInterval() + GetSchInterval();
-}
-
-void
-ChannelCoordinator::SetGuardInterval(Time guard)
-{
-    NS_LOG_FUNCTION(this);
-    m_gi = guard;
-}
-
-Time
-ChannelCoordinator::GetGuardInterval() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_gi;
-}
-
-Time
-ChannelCoordinator::GetSchSlot() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_schi - m_gi;
-}
-
-Time
-ChannelCoordinator::GetCchSlot() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_cchi - m_gi;
-}
-
-bool
-ChannelCoordinator::IsCchInterval(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    Time future = GetIntervalTime(duration);
-    return (future < m_cchi);
-}
-
-bool
-ChannelCoordinator::IsSchInterval(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    return !IsCchInterval(duration);
-}
-
-bool
-ChannelCoordinator::IsGuardInterval(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    Time future = GetIntervalTime(duration);
-    // the interval is either in CchInterval or SchInterval
-    Time interval = future < m_cchi ? future : future - m_cchi;
-    return interval < m_gi;
-}
-
-bool
-ChannelCoordinator::IsValidConfig() const
-{
-    NS_LOG_FUNCTION(this);
-    if (GetCchInterval().GetMilliSeconds() == 0 || GetSchInterval().GetMilliSeconds() == 0 ||
-        GetGuardInterval().GetMilliSeconds() == 0)
-    {
-        NS_LOG_WARN("the channel interval should not be zero");
-        return false;
-    }
-    // 1000 is 1000ms which is one UTC second
-    if ((1000 % GetSyncInterval().GetMilliSeconds()) != 0)
-    {
-        NS_LOG_WARN("every UTC second shall be an integer number of SyncInterval");
-        return false;
-    }
-    if (GetCchInterval() <= GetGuardInterval())
-    {
-        NS_LOG_WARN("CCH Interval should be large than GuardInterval");
-        return false;
-    }
-    if (GetSchInterval() <= GetGuardInterval())
-    {
-        NS_LOG_WARN("SCH Interval should be large than GuardInterval");
-        return false;
-    }
-    // at last, GguardInterval should be larger than real channel switch time of PHY layer.
-    // However there is no method such as GetChannelSwitchTime in the WifiPhy to support test here.
-    return true;
-}
-
-Time
-ChannelCoordinator::NeedTimeToCchInterval(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    if (IsCchInterval(duration))
-    {
-        return MilliSeconds(0);
-    }
-    return GetSyncInterval() - GetIntervalTime(duration);
-}
-
-Time
-ChannelCoordinator::NeedTimeToSchInterval(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    if (IsSchInterval(duration))
-    {
-        return MilliSeconds(0);
-    }
-    return GetCchInterval() - GetIntervalTime(duration);
-}
-
-Time
-ChannelCoordinator::NeedTimeToGuardInterval(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    if (IsGuardInterval(duration))
-    {
-        return MilliSeconds(0);
-    }
-    if (IsCchInterval(duration))
-    {
-        // the time to Guard Interval of SCH Interval
-        return (GetCchInterval() - GetIntervalTime(duration));
-    }
-    // the time to Guard Interval of next CCH Interval
-    return (GetSyncInterval() - GetIntervalTime(duration));
-}
-
-Time
-ChannelCoordinator::GetIntervalTime(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    Time future = Now() + duration;
-    Time sync = GetSyncInterval();
-    uint32_t n = future.GetMilliSeconds() / sync.GetMilliSeconds();
-    return future - MilliSeconds(n * sync.GetMilliSeconds());
-}
-
-Time
-ChannelCoordinator::GetRemainTime(Time duration) const
-{
-    NS_LOG_FUNCTION(this << duration);
-    return GetSyncInterval() - GetIntervalTime(duration);
-}
-
-void
-ChannelCoordinator::RegisterListener(Ptr<ChannelCoordinationListener> listener)
-{
-    NS_LOG_FUNCTION(this << listener);
-    NS_ASSERT(listener);
-    m_listeners.push_back(listener);
-}
-
-void
-ChannelCoordinator::UnregisterListener(Ptr<ChannelCoordinationListener> listener)
-{
-    NS_LOG_FUNCTION(this << listener);
-    NS_ASSERT(listener);
-    for (ListenersI i = m_listeners.begin(); i != m_listeners.end(); ++i)
-    {
-        if ((*i) == listener)
-        {
-            m_listeners.erase(i);
-            return;
-        }
-    }
-}
-
-void
-ChannelCoordinator::UnregisterAllListeners()
-{
-    NS_LOG_FUNCTION(this);
-    m_listeners.clear();
-}
-
-void
-ChannelCoordinator::StartChannelCoordination()
-{
-    NS_LOG_FUNCTION(this);
-    Time now = Now();
-    if ((now.GetMilliSeconds() % 1000) != 0)
-    {
-        // see chapter 5.5.2
-        NS_FATAL_ERROR("the coordination event order should start with the beginning of 1 second");
-    }
-    if (!IsValidConfig())
-    {
-        NS_FATAL_ERROR(
-            "the channel intervals configured for channel coordination events are invalid");
-    }
-    m_guardCount = 0;
-    NotifyGuardSlot();
-}
-
-void
-ChannelCoordinator::StopChannelCoordination()
-{
-    if (!m_coordination.IsExpired())
-    {
-        m_coordination.Cancel();
-    }
-    m_guardCount = 0;
-}
-
-void
-ChannelCoordinator::NotifySchSlot()
-{
-    NS_LOG_FUNCTION(this);
-    m_coordination = Simulator::Schedule(GetSchSlot(), &ChannelCoordinator::NotifyGuardSlot, this);
-    for (ListenersI i = m_listeners.begin(); i != m_listeners.end(); ++i)
-    {
-        (*i)->NotifySchSlotStart(GetSchSlot());
-    }
-}
-
-void
-ChannelCoordinator::NotifyCchSlot()
-{
-    NS_LOG_FUNCTION(this);
-    m_coordination = Simulator::Schedule(GetCchSlot(), &ChannelCoordinator::NotifyGuardSlot, this);
-    for (ListenersI i = m_listeners.begin(); i != m_listeners.end(); ++i)
-    {
-        (*i)->NotifyCchSlotStart(GetCchSlot());
-    }
-}
-
-void
-ChannelCoordinator::NotifyGuardSlot()
-{
-    NS_LOG_FUNCTION(this);
-    Time guardSlot = GetGuardInterval();
-    bool inCchi = ((m_guardCount % 2) == 0);
-    if (inCchi)
-    {
-        m_coordination = Simulator::Schedule(guardSlot, &ChannelCoordinator::NotifyCchSlot, this);
-    }
-    else
-    {
-        m_coordination = Simulator::Schedule(guardSlot, &ChannelCoordinator::NotifySchSlot, this);
-    }
-    for (ListenersI i = m_listeners.begin(); i != m_listeners.end(); ++i)
-    {
-        (*i)->NotifyGuardSlotStart(guardSlot, inCchi);
-    }
-    m_guardCount++;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/channel-coordinator.h ns-3.39/src/wave/model/channel-coordinator.h
--- ns-3.38/src/wave/model/channel-coordinator.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/channel-coordinator.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,256 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef CHANNEL_COORDINATOR_H
-#define CHANNEL_COORDINATOR_H
-#include "ns3/event-id.h"
-#include "ns3/nstime.h"
-#include "ns3/object.h"
-
-namespace ns3
-{
-
-/**
- * \ingroup wave
- * \brief receive notifications about channel coordination events.
- */
-class ChannelCoordinationListener : public SimpleRefCount<ChannelCoordinationListener>
-{
-  public:
-    virtual ~ChannelCoordinationListener();
-    /**
-     * \param duration the CCH access time (CCHI - GI) continues,
-     */
-    virtual void NotifyCchSlotStart(Time duration) = 0;
-    /**
-     * \param duration the SCH access time (SCHI - GI) continues,
-     */
-    virtual void NotifySchSlotStart(Time duration) = 0;
-    /**
-     * \param duration the time Guard access time (GI) continues
-     * \param cchi indicate whether the guard slot is in the GI of CCHI or SCHI.
-     */
-    virtual void NotifyGuardSlotStart(Time duration, bool cchi) = 0;
-};
-
-/**
- * \ingroup wave
- * \brief ChannelCoordinator deals with channel coordination in data plane (see 1609.4 chapter 5.2)
- *
- *      <------------   -----SYNCI-----------------> <-------------------SYNCI------------------>
- *                   CCHI                      SCHI                         CCHI SCHI
- *     |..************|..************|..************|..************|
- *      GI                             GI                            GI GI
- *
- *  The relation among CCHI(CCH Interval), SCHI(SCH Interval), GI(Guard Interval), SYNCI(Sync
- * Interval):
- *  1. they are all time durations, gegerally default CCHI is 50ms,
- *  default SCHI is 50ms, default GI is 4ms, default SYNCI is 100ms.
- *  2. Every UTC second shall be an integer number of sync interval, and the
- *  beginning of a sync interval shall align with beginning of UTC second;
- *  3. SYNCI is the sum of CCHI and SCHI.
- *  GI is the sum of SyncTolerance and MaxSwitchTime defined for
- *  multi-channel synchronization (see 1609.4 chapter 6.2). And since all
- *  devices the in simulation could be supposed to synchronize by "perfect GPS",
- *  here the synchronization mechanism defined in the standard will be not implemented.
- *  4. Although the real channel switch time of wifi PHY layer is very fast, and the
- * "ChannelSwitchDelay" of YansWifiPhy is 250 microseconds, here in 4ms guard interval WAVE devices
- *  cannot transmit packets while may receive packets.
- */
-class ChannelCoordinator : public Object
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    ChannelCoordinator();
-    ~ChannelCoordinator() override;
-
-    /**
-     * \return the default control channel interval for multi-channel operation (50 milliseconds).
-     */
-    static Time GetDefaultCchInterval();
-    /**
-     * \return the default service channel interval for multi-channel operation (50 milliseconds).
-     */
-    static Time GetDefaultSchInterval();
-    /**
-     * \return the default sync interval for multi-channel operation (100 milliseconds).
-     */
-    static Time GetDefaultSyncInterval();
-    /**
-     * \return the default guard channel interval for multi-channel operation (4 milliseconds).
-     */
-    static Time GetDefaultGuardInterval();
-
-    /**
-     * \param cchi the CCH interval for multi-channel operation.
-     */
-    void SetCchInterval(Time cchi);
-    /**
-     * \return the CCH interval for multi-channel operation.
-     */
-    Time GetCchInterval() const;
-    /**
-     * \param schi the SCH interval for multi-channel operation.
-     */
-    void SetSchInterval(Time schi);
-    /**
-     * \return the SCH interval for multi-channel operation.
-     */
-    Time GetSchInterval() const;
-    /**
-     * \return the Sync interval for multi-channel operation.
-     */
-    Time GetSyncInterval() const;
-    /**
-     * \param guardi the guard interval for multi-channel operation.
-     */
-    void SetGuardInterval(Time guardi);
-    /**
-     * \return the guard interval for multi-channel operation.
-     */
-    Time GetGuardInterval() const;
-    /**
-     * \return whether current channel interval configuration is valid.
-     *
-     * If users set the channel intervals different from default values here,
-     * it should be better to test whether the configuration is valid.
-     */
-    bool IsValidConfig() const;
-
-    /**
-     * \param duration the future time after duration
-     * \returns whether future time is in CCH Interval
-     */
-    bool IsCchInterval(Time duration = Seconds(0.0)) const;
-    /**
-     * \param duration the future time after duration
-     * \returns whether future time is in SCH Interval
-     */
-    bool IsSchInterval(Time duration = Seconds(0.0)) const;
-    /**
-     * \param duration the future time after duration
-     * \returns whether future time is in Guard Interval
-     */
-    bool IsGuardInterval(Time duration = Seconds(0.0)) const;
-    /**
-     * \param duration the future time after duration
-     * \returns the time to the next CCH interval.
-     * If current time is already in CCH interval, return 0;
-     */
-    Time NeedTimeToCchInterval(Time duration = Seconds(0.0)) const;
-    /**
-     * \param duration the future time after duration
-     * \returns the duration time to the next SCH interval.
-     * If current time is already in SCH interval, return 0;
-     */
-    Time NeedTimeToSchInterval(Time duration = Seconds(0.0)) const;
-    /**
-     * \param duration the future time after duration
-     * \returns the duration time to next Guard Interval;
-     * return 0 if current time is already in Guard Interval, ;
-     */
-    Time NeedTimeToGuardInterval(Time duration = Seconds(0.0)) const;
-    /**
-     * \param duration the future time after duration
-     * \return the time in a Sync Interval of future time
-     *  for example:
-     *  SyncInterval = 100ms;
-     *  Now = 5s20ms;
-     *  duration = 50ms;
-     *  then GetIntervalTime (duration) = 70ms.
-     */
-    Time GetIntervalTime(Time duration = Seconds(0.0)) const;
-    /**
-     * \param duration the future time after duration
-     * \return the remain time in a Sync Interval of future time
-     *  for example:
-     *  SyncInterval = 100ms;
-     *  Now = 5s20ms;
-     *  duration = 50ms;
-     *  then GetRemainTime (duration) = 30ms.
-     */
-    Time GetRemainTime(Time duration = Seconds(0.0)) const;
-    /**
-     * \param listener the new listener for channel coordination events.
-     *
-     * Add the input listener to the list of objects to be notified of
-     * channel coordination events.
-     */
-    void RegisterListener(Ptr<ChannelCoordinationListener> listener);
-    /**
-     * \param listener the current attached listener
-     *
-     * Remove the specified listener.
-     */
-    void UnregisterListener(Ptr<ChannelCoordinationListener> listener);
-    /**
-     * Remove all listeners.
-     */
-    void UnregisterAllListeners();
-
-  private:
-    void DoDispose() override;
-    void DoInitialize() override;
-
-    /**
-     * start to make channel coordination events
-     */
-    void StartChannelCoordination();
-    /**
-     * stop channel coordination events
-     */
-    void StopChannelCoordination();
-    /**
-     * notify listeners of a SCH slot start
-     */
-    void NotifySchSlot();
-    /**
-     * notify listeners of a CCH slot start
-     */
-    void NotifyCchSlot();
-    /**
-     * notify listeners of a guard slot start
-     */
-    void NotifyGuardSlot();
-    /**
-     * \return SCH channel access time which is SchInterval - GuardInterval, default 46ms
-     */
-    Time GetSchSlot() const;
-    /**
-     * \return CCH channel access time which is SchInterval - GuardInterval, default 46ms
-     */
-    Time GetCchSlot() const;
-
-    Time m_cchi; ///< CchInterval
-    Time m_schi; ///< SchInterval
-    Time m_gi;   ///< GuardInterval
-
-    /// Listeners typedef
-    typedef std::vector<Ptr<ChannelCoordinationListener>> Listeners;
-    /// Listeners iterator typedef
-    typedef std::vector<Ptr<ChannelCoordinationListener>>::iterator ListenersI;
-    Listeners m_listeners; ///< listeners
-
-    uint32_t m_guardCount;  ///< guard count
-    EventId m_coordination; ///< coordination event
-};
-
-} // namespace ns3
-#endif /* CHANNEL_COORDINATOR_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/channel-manager.cc ns-3.39/src/wave/model/channel-manager.cc
--- ns-3.38/src/wave/model/channel-manager.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/channel-manager.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,184 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "channel-manager.h"
-
-#include "ns3/assert.h"
-#include "ns3/log.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("ChannelManager");
-
-NS_OBJECT_ENSURE_REGISTERED(ChannelManager);
-
-TypeId
-ChannelManager::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::ChannelManager")
-                            .SetParent<Object>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<ChannelManager>();
-    return tid;
-}
-
-ChannelManager::ChannelManager()
-{
-    NS_LOG_FUNCTION(this);
-    m_channels.insert(std::make_pair(CCH, new WaveChannel(CCH)));
-    m_channels.insert(std::make_pair(SCH1, new WaveChannel(SCH1)));
-    m_channels.insert(std::make_pair(SCH2, new WaveChannel(SCH2)));
-    m_channels.insert(std::make_pair(SCH3, new WaveChannel(SCH3)));
-    m_channels.insert(std::make_pair(SCH4, new WaveChannel(SCH4)));
-    m_channels.insert(std::make_pair(SCH5, new WaveChannel(SCH5)));
-    m_channels.insert(std::make_pair(SCH6, new WaveChannel(SCH6)));
-}
-
-ChannelManager::~ChannelManager()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-uint32_t
-ChannelManager::GetCch()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    return CCH;
-}
-
-std::vector<uint32_t>
-ChannelManager::GetSchs()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    std::vector<uint32_t> schs;
-    schs.push_back(SCH1);
-    schs.push_back(SCH2);
-    schs.push_back(SCH3);
-    schs.push_back(SCH4);
-    schs.push_back(SCH5);
-    schs.push_back(SCH6);
-    return schs;
-}
-
-std::vector<uint32_t>
-ChannelManager::GetWaveChannels()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    std::vector<uint32_t> channels;
-    channels.push_back(CCH);
-    channels.push_back(SCH1);
-    channels.push_back(SCH2);
-    channels.push_back(SCH3);
-    channels.push_back(SCH4);
-    channels.push_back(SCH5);
-    channels.push_back(SCH6);
-    return channels;
-}
-
-uint32_t
-ChannelManager::GetNumberOfWaveChannels()
-{
-    NS_LOG_FUNCTION_NOARGS();
-    static uint32_t NumberOfWaveChannels = GetWaveChannels().size();
-    return NumberOfWaveChannels;
-}
-
-bool
-ChannelManager::IsCch(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION_NOARGS();
-    return channelNumber == CCH;
-}
-
-bool
-ChannelManager::IsSch(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION_NOARGS();
-    if (channelNumber < SCH1 || channelNumber > SCH6)
-    {
-        return false;
-    }
-    if (channelNumber % 2 == 1)
-    {
-        return false;
-    }
-    return (channelNumber != CCH);
-}
-
-bool
-ChannelManager::IsWaveChannel(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION_NOARGS();
-    if (channelNumber < SCH1 || channelNumber > SCH6)
-    {
-        return false;
-    }
-    if (channelNumber % 2 == 1)
-    {
-        return false;
-    }
-    return true;
-}
-
-uint32_t
-ChannelManager::GetOperatingClass(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return m_channels[channelNumber]->operatingClass;
-}
-
-bool
-ChannelManager::GetManagementAdaptable(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return m_channels[channelNumber]->adaptable;
-}
-
-WifiMode
-ChannelManager::GetManagementDataRate(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return m_channels[channelNumber]->dataRate;
-}
-
-WifiPreamble
-ChannelManager::GetManagementPreamble(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return m_channels[channelNumber]->preamble;
-}
-
-uint32_t
-ChannelManager::GetManagementPowerLevel(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return m_channels[channelNumber]->txPowerLevel;
-}
-
-void
-ChannelManager::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    std::map<uint32_t, WaveChannel*>::iterator i;
-    for (i = m_channels.begin(); i != m_channels.end(); ++i)
-    {
-        delete (i->second);
-    }
-    m_channels.clear();
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/channel-manager.h ns-3.39/src/wave/model/channel-manager.h
--- ns-3.38/src/wave/model/channel-manager.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/channel-manager.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,172 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef CHANNEL_MANAGER_H
-#define CHANNEL_MANAGER_H
-#include "ns3/object.h"
-#include "ns3/wifi-mode.h"
-#include "ns3/wifi-phy-common.h"
-
-#include <map>
-#include <vector>
-
-namespace ns3
-{
-
-/**
- * \ingroup wave
- *
- * WAVE channels
- * channel number             |   172   |   174   |   176   |   178   |   180   |   182   |   184 |
- * channel bandwidth          10MHz  10MHz  10MHz  10MHz 10MHz  10MHz  10MHz
- * channel name                      SCH1     SCH2     SCH3     CCH       SCH4     SCH5     SCH6
- * another name                     CH172   CH174   CH176   CH178   CH180  CH182   CH184
- *
- * not support
- * channel 175 : combine channel 174 and 176
- * channel 181 : combine channel 180 and 182
- */
-#define CH172 172
-#define CH174 174
-#define CH176 176
-#define CH178 178
-#define CH180 180
-#define CH182 182
-#define CH184 184
-
-#define SCH1 172
-#define SCH2 174
-#define SCH3 176
-#define CCH 178
-#define SCH4 180
-#define SCH5 182
-#define SCH6 184
-
-/**
- * \ingroup wave
- * \brief manage 7 WaveChannels and the tx information such as data rate and txPowerLevel.
- * for transmitting VSA management frames.
- */
-class ChannelManager : public Object
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    ChannelManager();
-    ~ChannelManager() override;
-
-    /**
-     * \return the channel number of WAVE CCH.
-     */
-    static uint32_t GetCch();
-    /**
-     * \return the channel number set of WAVE SCHs.
-     */
-    static std::vector<uint32_t> GetSchs();
-    /**
-     * \return the channel number set of WAVE channels.
-     *
-     * The sequence is CCH, SCH1, SCH2, SCH3, SCH4, SCH5 and SCH6.
-     */
-    static std::vector<uint32_t> GetWaveChannels();
-    /**
-     * \return the number of WAVE channels.
-     */
-    static uint32_t GetNumberOfWaveChannels();
-    /**
-     * \param channelNumber the specific channel
-     * \return whether channel is valid CCH channel
-     */
-    static bool IsCch(uint32_t channelNumber);
-    /**
-     * \param channelNumber the specific channel
-     * \return whether channel is valid SCH channel
-     */
-    static bool IsSch(uint32_t channelNumber);
-    /**
-     * \param channelNumber the specific channel
-     * \return whether channel is valid WAVE channel
-     */
-    static bool IsWaveChannel(uint32_t channelNumber);
-
-    /**
-     * \param channelNumber the specific channel
-     * \return the operating class on this channel
-     *
-     * the operating class is unused in the simulation
-     */
-    uint32_t GetOperatingClass(uint32_t channelNumber);
-    /**
-     * \param channelNumber the specific channel
-     * \return the adaptable mode for management frames
-     */
-    bool GetManagementAdaptable(uint32_t channelNumber);
-    /**
-     * \param channelNumber the specific channel
-     * \return the data rate for management frames
-     */
-    WifiMode GetManagementDataRate(uint32_t channelNumber);
-    /**
-     * \param channelNumber the specific channel
-     * \return the preamble for management frames
-     */
-    WifiPreamble GetManagementPreamble(uint32_t channelNumber);
-    /**
-     * \param channelNumber the specific channel
-     * \return the tx power level for management frames
-     */
-    uint32_t GetManagementPowerLevel(uint32_t channelNumber);
-
-  protected:
-    void DoDispose() override;
-
-  private:
-    /// 1609.4-2010 Annex H
-    static const uint32_t DEFAULT_OPERATING_CLASS = 17;
-
-    /// WaveChannel structure
-    struct WaveChannel
-    {
-        uint32_t channelNumber;  ///< channel number
-        uint32_t operatingClass; ///< operating class
-        bool adaptable;          ///< adaptable?
-        WifiMode dataRate;       ///< data rate
-        WifiPreamble preamble;   ///< preamble
-        uint32_t txPowerLevel;   ///< transmit power level
-
-        /**
-         * initializor
-         * \param channel the channel number
-         */
-        WaveChannel(uint32_t channel)
-            : channelNumber(channel),
-              operatingClass(DEFAULT_OPERATING_CLASS),
-              adaptable(true),
-              dataRate(WifiMode("OfdmRate6MbpsBW10MHz")),
-              preamble(WIFI_PREAMBLE_LONG),
-              txPowerLevel(4)
-        {
-        }
-    };
-
-    std::map<uint32_t, WaveChannel*> m_channels; ///< list of channels
-};
-
-} // namespace ns3
-#endif /* CHANNEL_MANAGER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/channel-scheduler.cc ns-3.39/src/wave/model/channel-scheduler.cc
--- ns-3.38/src/wave/model/channel-scheduler.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/channel-scheduler.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,174 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-
-#include "channel-scheduler.h"
-
-#include "ns3/log.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("ChannelScheduler");
-
-NS_OBJECT_ENSURE_REGISTERED(ChannelScheduler);
-
-/**
- * \brief Get the type ID.
- * \return the object TypeId
- */
-TypeId
-ChannelScheduler::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::ChannelScheduler").SetParent<Object>().SetGroupName("Wave");
-    return tid;
-}
-
-ChannelScheduler::ChannelScheduler()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-ChannelScheduler::~ChannelScheduler()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-ChannelScheduler::DoInitialize()
-{
-    // assign default CCH access when the device is initialized
-    AssignDefaultCchAccess();
-}
-
-void
-ChannelScheduler::SetWaveNetDevice(Ptr<WaveNetDevice> device)
-{
-    NS_LOG_FUNCTION(this << device);
-    m_device = device;
-}
-
-bool
-ChannelScheduler::IsChannelAccessAssigned(uint32_t channelNumber) const
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return (GetAssignedAccessType(channelNumber) != NoAccess);
-}
-
-bool
-ChannelScheduler::IsCchAccessAssigned() const
-{
-    NS_LOG_FUNCTION(this);
-    return (GetAssignedAccessType(CCH) != NoAccess);
-}
-
-bool
-ChannelScheduler::IsSchAccessAssigned() const
-{
-    NS_LOG_FUNCTION(this);
-    return (GetAssignedAccessType(SCH1) != NoAccess) || (GetAssignedAccessType(SCH2) != NoAccess) ||
-           (GetAssignedAccessType(SCH3) != NoAccess) || (GetAssignedAccessType(SCH4) != NoAccess) ||
-           (GetAssignedAccessType(SCH5) != NoAccess) || (GetAssignedAccessType(SCH6) != NoAccess);
-}
-
-bool
-ChannelScheduler::IsContinuousAccessAssigned(uint32_t channelNumber) const
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return (GetAssignedAccessType(channelNumber) == ContinuousAccess);
-}
-
-bool
-ChannelScheduler::IsAlternatingAccessAssigned(uint32_t channelNumber) const
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return (GetAssignedAccessType(channelNumber) == AlternatingAccess);
-}
-
-bool
-ChannelScheduler::IsExtendedAccessAssigned(uint32_t channelNumber) const
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    return (GetAssignedAccessType(channelNumber) == ExtendedAccess);
-}
-
-bool
-ChannelScheduler::IsDefaultCchAccessAssigned() const
-{
-    NS_LOG_FUNCTION(this);
-    return (GetAssignedAccessType(CCH) == DefaultCchAccess);
-}
-
-bool
-ChannelScheduler::StartSch(const SchInfo& schInfo)
-{
-    NS_LOG_FUNCTION(this << &schInfo);
-    uint32_t cn = schInfo.channelNumber;
-
-    if (ChannelManager::IsCch(schInfo.channelNumber))
-    {
-        NS_LOG_DEBUG("the channel access requirement for CCH is not allowed.");
-        return false;
-    }
-    uint32_t extends = schInfo.extendedAccess;
-    bool immediate = schInfo.immediateAccess;
-    Ptr<OcbWifiMac> mac = m_device->GetMac(cn);
-    for (EdcaParametersI i = schInfo.edcaParameters.begin(); i != schInfo.edcaParameters.end(); ++i)
-    {
-        EdcaParameter edca = i->second;
-        mac->ConfigureEdca(edca.cwmin, edca.cwmax, edca.aifsn, i->first);
-    }
-
-    if (extends == EXTENDED_CONTINUOUS)
-    {
-        return AssignContinuousAccess(cn, immediate);
-    }
-    else if (extends == EXTENDED_ALTERNATING)
-    {
-        return AssignAlternatingAccess(cn, immediate);
-    }
-    else
-    {
-        return AssignExtendedAccess(cn, extends, immediate);
-    }
-}
-
-bool
-ChannelScheduler::StopSch(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    if (ChannelManager::IsCch(channelNumber))
-    {
-        NS_LOG_DEBUG("the channel access for CCH is not allowed to be released.");
-        return false;
-    }
-    if (!IsChannelAccessAssigned(channelNumber))
-    {
-        NS_LOG_DEBUG("the channel access for channel " << channelNumber
-                                                       << " has already been released.");
-        return true;
-    }
-    return ReleaseAccess(channelNumber);
-}
-
-void
-ChannelScheduler::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    m_device = nullptr;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/channel-scheduler.h ns-3.39/src/wave/model/channel-scheduler.h
--- ns-3.38/src/wave/model/channel-scheduler.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/channel-scheduler.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,246 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef CHANNEL_SCHEDULER_H
-#define CHANNEL_SCHEDULER_H
-
-#include "wave-net-device.h"
-
-#include "ns3/qos-utils.h"
-
-#include <map>
-
-namespace ns3
-{
-class WaveNetDevice;
-
-/**
- * \ingroup wave
- * EdcaParameter structure
- */
-struct EdcaParameter
-{
-    uint32_t cwmin; ///< minimum
-    uint32_t cwmax; ///< maximum
-    uint32_t aifsn; ///< AIFSN
-};
-
-/**
- * \ingroup wave
- * EDCA parameters typedef
- */
-typedef std::map<AcIndex, EdcaParameter> EdcaParameters;
-
-/**
- * \ingroup wave
- * EDCA parameters iterator typedef
- */
-typedef std::map<AcIndex, EdcaParameter>::const_iterator EdcaParametersI;
-
-#define EXTENDED_ALTERNATING 0x00
-#define EXTENDED_CONTINUOUS 0xff
-
-/**
- * \ingroup wave
- *
- * \param channelNumber channel number that the SCH service
- * can be made available for communications.
- * \param operationalRateSet OperationalRateSet if present, as specified in IEEE Std 802.11.
- * valid range: 1-127.
- * \param immediateAccess Indicates that the MLME should provide immediate
- * access to the SCH and not wait until the next SCH interval.
- * \param extendedAccess Indicates that the MLME should provide continuous
- * access (during both SCH interval and CCH interval) to the SCH for ExtendedAccess
- * control channel intervals. A value of 255 indicates indefinite access.
- * \param edcaParameters If present, as specified in IEEE Std 802.11.
- */
-struct SchInfo
-{
-    uint32_t channelNumber; ///< channel number
-    // OperationalRateSet  operationalRateSet;  // not supported
-    bool immediateAccess;          ///< immediate access
-    uint8_t extendedAccess;        ///< extended access
-    EdcaParameters edcaParameters; ///< EDCA parameters
-
-    /// Initializer
-    SchInfo()
-        : channelNumber(SCH1),
-          immediateAccess(false),
-          extendedAccess(EXTENDED_ALTERNATING)
-    {
-    }
-
-    /**
-     * Initializer
-     * \param channel the channel number
-     * \param immediate true if immediate access
-     * \param channelAccess
-     */
-    SchInfo(uint32_t channel, bool immediate, uint32_t channelAccess)
-        : channelNumber(channel),
-          immediateAccess(immediate),
-          extendedAccess(channelAccess)
-    {
-    }
-
-    /**
-     * Initializer
-     * \param channel the channel number
-     * \param immediate true if immediate access
-     * \param channelAccess
-     * \param edca the EDCA parameters
-     */
-    SchInfo(uint32_t channel, bool immediate, uint32_t channelAccess, EdcaParameters edca)
-        : channelNumber(channel),
-          immediateAccess(immediate),
-          extendedAccess(channelAccess),
-          edcaParameters(edca)
-    {
-    }
-};
-
-/// ChannelAccess enumeration
-enum ChannelAccess
-{
-    ContinuousAccess,  // continuous access for SCHs
-    AlternatingAccess, // alternating CCH and SCH access
-    ExtendedAccess,    // extended access in SCHs
-    DefaultCchAccess,  // default continuous CCH access
-    NoAccess,          // no channel access assigned
-};
-
-/**
- * \ingroup wave
- * \brief This class will assign channel access for requests from higher layers.
- * The channel access options include (1) continuous access for SCHs, (2) alternating
- * CCH and SCH access,  (3) extended access for SCHs and (4) default continuous CCH
- * access. The options except (4) have an immediate parameter to enable immediate
- * access assignment without the need for waiting.
- * Its sub-class can use different mechanism to assign the channel access.
- */
-class ChannelScheduler : public Object
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    ChannelScheduler();
-    ~ChannelScheduler() override;
-
-    /**
-     * \param device enable channel scheduler associated with WaveNetDevice
-     */
-    virtual void SetWaveNetDevice(Ptr<WaveNetDevice> device);
-    /**
-     * \return whether CCH channel access is assigned.
-     */
-    bool IsCchAccessAssigned() const;
-    /**
-     * \return whether SCH channel access is assigned.
-     */
-    bool IsSchAccessAssigned() const;
-    /**
-     * \param channelNumber the specified channel number
-     * \return  whether channel access is assigned for the channel.
-     */
-    bool IsChannelAccessAssigned(uint32_t channelNumber) const;
-    /**
-     * \param channelNumber the specified channel number
-     * \return whether the continuous access is assigned for the specific channel.
-     */
-    bool IsContinuousAccessAssigned(uint32_t channelNumber) const;
-    /**
-     * \param channelNumber the specified channel number
-     * \return whether the continuous access is assigned for the specific channel.
-     */
-    bool IsAlternatingAccessAssigned(uint32_t channelNumber) const;
-    /**
-     * \param channelNumber the specified channel number
-     * \return whether the continuous access is assigned for the specific channel.
-     */
-    bool IsExtendedAccessAssigned(uint32_t channelNumber) const;
-    /**
-     * \return whether the continuous access is assigned for CCHl.
-     */
-    bool IsDefaultCchAccessAssigned() const;
-    /**
-     * \param channelNumber the specified channel number
-     * \return  the type of current assigned channel access for the specific channel.
-     */
-    virtual ChannelAccess GetAssignedAccessType(uint32_t channelNumber) const = 0;
-
-    /**
-     * \param schInfo the request information for assigning SCH access.
-     * \return whether the channel access is assigned successfully.
-     *
-     * This method is called to assign channel access for sending packets.
-     */
-    bool StartSch(const SchInfo& schInfo);
-    /**
-     * \param channelNumber indicating which channel should release
-     * the assigned channel access resource.
-     * \return true if successful.
-     */
-    bool StopSch(uint32_t channelNumber);
-
-  protected:
-    void DoInitialize() override;
-    void DoDispose() override;
-
-    /**
-     * \param channelNumber the specific channel
-     * \param immediate indicate whether channel switch to channel
-     * \return whether the channel access is assigned successfully
-     *
-     * This method will assign alternating access for SCHs and CCH.
-     */
-    virtual bool AssignAlternatingAccess(uint32_t channelNumber, bool immediate) = 0;
-    /**
-     * \param channelNumber the specific channel
-     * \param immediate indicate whether channel switch to channel
-     * \return whether the channel access is assigned successfully
-     *
-     * This method will assign continuous access CCH.
-     */
-    virtual bool AssignContinuousAccess(uint32_t channelNumber, bool immediate) = 0;
-    /**
-     * \param channelNumber the specific channel
-     * \param extends extension duration
-     * \param immediate indicate whether channel switch to channel
-     * \return whether the channel access is assigned successfully
-     *
-     * This method will assign extended access for SCHs.
-     */
-    virtual bool AssignExtendedAccess(uint32_t channelNumber, uint32_t extends, bool immediate) = 0;
-    /**
-     * This method will assign default CCH access for CCH.
-     * \return whether the channel access is assigned successfully
-     */
-    virtual bool AssignDefaultCchAccess() = 0;
-    /**
-     * \param channelNumber indicating for which channel should release
-     * the assigned channel access resource.
-     * \return whether the channel access is released successfully
-     */
-    virtual bool ReleaseAccess(uint32_t channelNumber) = 0;
-
-    Ptr<WaveNetDevice> m_device; ///< the device
-};
-
-} // namespace ns3
-#endif /* CHANNEL_SCHEDULER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/default-channel-scheduler.cc ns-3.39/src/wave/model/default-channel-scheduler.cc
--- ns-3.38/src/wave/model/default-channel-scheduler.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/default-channel-scheduler.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,470 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "default-channel-scheduler.h"
-
-#include "ns3/log.h"
-#include "ns3/simulator.h"
-#include "ns3/wifi-phy.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("DefaultChannelScheduler");
-
-NS_OBJECT_ENSURE_REGISTERED(DefaultChannelScheduler);
-
-/**
- * \ingroup wave
- * \brief CoordinationListener class
- */
-class CoordinationListener : public ChannelCoordinationListener
-{
-  public:
-    /**
-     * Constructor
-     *
-     * \param scheduler channel scheduler
-     */
-    CoordinationListener(DefaultChannelScheduler* scheduler)
-        : m_scheduler(scheduler)
-    {
-    }
-
-    void NotifyCchSlotStart(Time duration) override
-    {
-        m_scheduler->NotifyCchSlotStart(duration);
-    }
-
-    void NotifySchSlotStart(Time duration) override
-    {
-        m_scheduler->NotifySchSlotStart(duration);
-    }
-
-    void NotifyGuardSlotStart(Time duration, bool cchi) override
-    {
-        m_scheduler->NotifyGuardSlotStart(duration, cchi);
-    }
-
-  private:
-    DefaultChannelScheduler* m_scheduler; ///< the scheduler
-};
-
-TypeId
-DefaultChannelScheduler::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::DefaultChannelScheduler")
-                            .SetParent<ChannelScheduler>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<DefaultChannelScheduler>();
-    return tid;
-}
-
-DefaultChannelScheduler::DefaultChannelScheduler()
-    : m_channelNumber(0),
-      m_extend(EXTENDED_CONTINUOUS),
-      m_channelAccess(NoAccess),
-      m_waitChannelNumber(0),
-      m_waitExtend(0),
-      m_coordinationListener(nullptr)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-DefaultChannelScheduler::~DefaultChannelScheduler()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-DefaultChannelScheduler::DoInitialize()
-{
-    NS_LOG_FUNCTION(this);
-    ChannelScheduler::DoInitialize();
-}
-
-void
-DefaultChannelScheduler::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    m_coordinator = nullptr;
-    if (m_coordinationListener)
-    {
-        m_coordinationListener = nullptr;
-    }
-    if (!m_waitEvent.IsExpired())
-    {
-        m_waitEvent.Cancel();
-    }
-    if (!m_extendEvent.IsExpired())
-    {
-        m_waitEvent.Cancel();
-    }
-    m_phy = nullptr;
-    ChannelScheduler::DoDispose();
-}
-
-void
-DefaultChannelScheduler::SetWaveNetDevice(Ptr<WaveNetDevice> device)
-{
-    NS_LOG_FUNCTION(this << device);
-    ChannelScheduler::SetWaveNetDevice(device);
-    std::vector<Ptr<WifiPhy>> phys = device->GetPhys();
-    if (phys.size() > 1)
-    {
-        NS_LOG_WARN("The class is only in the context of single-PHY device, while there are more "
-                    "than one PHY devices");
-    }
-    // since default channel scheduler is in the context of single-PHY, we only use one phy object.
-    m_phy = device->GetPhy(0);
-    m_coordinator = device->GetChannelCoordinator();
-    m_coordinationListener = Create<CoordinationListener>(this);
-    m_coordinator->RegisterListener(m_coordinationListener);
-}
-
-enum ChannelAccess
-DefaultChannelScheduler::GetAssignedAccessType(uint32_t channelNumber) const
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    if (m_channelAccess == AlternatingAccess && channelNumber == CCH)
-    {
-        return AlternatingAccess;
-    }
-    return (m_channelNumber == channelNumber) ? m_channelAccess : NoAccess;
-}
-
-bool
-DefaultChannelScheduler::AssignAlternatingAccess(uint32_t channelNumber, bool immediate)
-{
-    NS_LOG_FUNCTION(this << channelNumber << immediate);
-    NS_ASSERT(m_channelAccess != NoAccess && m_channelNumber != 0);
-    uint32_t sch = channelNumber;
-
-    if (m_channelAccess == ContinuousAccess || m_channelAccess == ExtendedAccess)
-    {
-        return false;
-    }
-
-    if (m_channelAccess == AlternatingAccess)
-    {
-        if (m_channelNumber != sch)
-        {
-            return false;
-        }
-        else
-        {
-            return true;
-        }
-    }
-
-    // if we need immediately switch to AlternatingAccess,
-    // we switch to specific SCH.
-    if ((immediate && m_coordinator->IsSchInterval()))
-    {
-        NS_ASSERT(m_channelNumber == CCH);
-        SwitchToNextChannel(CCH, sch);
-    }
-
-    m_channelNumber = sch;
-    m_channelAccess = AlternatingAccess;
-    return true;
-}
-
-bool
-DefaultChannelScheduler::AssignContinuousAccess(uint32_t channelNumber, bool immediate)
-{
-    NS_LOG_FUNCTION(this << channelNumber << immediate);
-    NS_ASSERT(m_channelAccess != NoAccess && m_channelNumber != 0);
-    uint32_t sch = channelNumber;
-    if (m_channelAccess == AlternatingAccess || m_channelAccess == ExtendedAccess)
-    {
-        return false;
-    }
-
-    if (m_channelAccess == ContinuousAccess)
-    {
-        if (m_channelNumber != sch)
-        {
-            return false;
-        }
-        else
-        {
-            return true;
-        }
-    }
-
-    // if there is already an wait event for previous non-immediate request
-    if (!m_waitEvent.IsExpired())
-    {
-        if (m_waitChannelNumber != sch)
-        {
-            // then the coming new request will be rejected because of FCFS
-            return false;
-        }
-        else
-        {
-            if (!immediate)
-            {
-                return true;
-            }
-            // then cancel this wait event and assign access for request immediately
-            m_waitEvent.Cancel();
-        }
-    }
-
-    if (immediate || m_coordinator->IsSchInterval())
-    {
-        SwitchToNextChannel(m_channelNumber, sch);
-        m_channelNumber = sch;
-        m_channelAccess = ContinuousAccess;
-    }
-    else
-    {
-        Time wait = m_coordinator->NeedTimeToSchInterval();
-        m_waitEvent = Simulator::Schedule(wait,
-                                          &DefaultChannelScheduler::AssignContinuousAccess,
-                                          this,
-                                          sch,
-                                          false);
-        m_waitChannelNumber = sch;
-    }
-
-    return true;
-}
-
-bool
-DefaultChannelScheduler::AssignExtendedAccess(uint32_t channelNumber,
-                                              uint32_t extends,
-                                              bool immediate)
-{
-    NS_LOG_FUNCTION(this << channelNumber << extends << immediate);
-    NS_ASSERT(m_channelAccess != NoAccess && m_channelNumber != 0);
-    uint32_t sch = channelNumber;
-    if (m_channelAccess == AlternatingAccess || m_channelAccess == ContinuousAccess)
-    {
-        return false;
-    }
-
-    if (m_channelAccess == ExtendedAccess)
-    {
-        if (m_channelNumber != sch)
-        {
-            return false;
-        }
-        else
-        {
-            // if current remain extends cannot fulfill the requirement for extends
-            Time remainTime = Simulator::GetDelayLeft(m_extendEvent);
-            uint32_t remainExtends = (remainTime / m_coordinator->GetSyncInterval()).GetHigh();
-            if (remainExtends > extends)
-            {
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-    }
-
-    // if there is already an wait event for previous non-immediate request
-    if (!m_waitEvent.IsExpired())
-    {
-        NS_ASSERT(m_extendEvent.IsExpired());
-        if (m_waitChannelNumber != sch)
-        {
-            // then the coming new request will be rejected because of FCFS
-            return false;
-        }
-        else
-        {
-            if (m_waitExtend < extends)
-            {
-                return false;
-            }
-
-            if (immediate)
-            {
-                // then cancel previous wait event and
-                // go to below code to assign access for request immediately
-                m_waitEvent.Cancel();
-            }
-            else
-            {
-                return true;
-            }
-        }
-    }
-
-    if (immediate || m_coordinator->IsSchInterval())
-    {
-        SwitchToNextChannel(m_channelNumber, sch);
-        m_channelNumber = sch;
-        m_channelAccess = ExtendedAccess;
-        m_extend = extends;
-
-        Time sync = m_coordinator->GetSyncInterval();
-        // the wait time to proper interval will not be calculated as extended time.
-        Time extendedDuration =
-            m_coordinator->NeedTimeToCchInterval() + MilliSeconds(extends * sync.GetMilliSeconds());
-        // after end_duration time, DefaultChannelScheduler will release channel access
-        // automatically
-        m_extendEvent = Simulator::Schedule(extendedDuration,
-                                            &DefaultChannelScheduler::ReleaseAccess,
-                                            this,
-                                            sch);
-    }
-    else
-    {
-        Time wait = m_coordinator->NeedTimeToSchInterval();
-        m_waitEvent = Simulator::Schedule(wait,
-                                          &DefaultChannelScheduler::AssignExtendedAccess,
-                                          this,
-                                          sch,
-                                          extends,
-                                          false);
-        m_waitChannelNumber = sch;
-        m_waitExtend = extends;
-    }
-    return true;
-}
-
-bool
-DefaultChannelScheduler::AssignDefaultCchAccess()
-{
-    NS_LOG_FUNCTION(this);
-    if (m_channelAccess == DefaultCchAccess)
-    {
-        return true;
-    }
-    if (m_channelNumber != 0)
-    {
-        // This class does not support preemptive scheduling
-        NS_LOG_DEBUG("channel access is already assigned for other SCHs, thus cannot assign "
-                     "default CCH access.");
-        return false;
-    }
-    // CCH MAC is to attach single-PHY device and wake up for transmission.
-    Ptr<OcbWifiMac> cchMacEntity = m_device->GetMac(CCH);
-    if (Now().GetMilliSeconds() != 0)
-    {
-        m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CCH, 0, WIFI_PHY_BAND_5GHZ, 0});
-        Time switchTime = m_phy->GetChannelSwitchDelay();
-        cchMacEntity->MakeVirtualBusy(switchTime);
-    }
-    cchMacEntity->SetWifiPhy(m_phy);
-    cchMacEntity->Resume();
-
-    m_channelAccess = DefaultCchAccess;
-    m_channelNumber = CCH;
-    m_extend = EXTENDED_CONTINUOUS;
-    return true;
-}
-
-void
-DefaultChannelScheduler::SwitchToNextChannel(uint32_t curChannelNumber, uint32_t nextChannelNumber)
-{
-    NS_LOG_FUNCTION(this << curChannelNumber << curChannelNumber);
-    if (m_phy->GetChannelNumber() == nextChannelNumber)
-    {
-        return;
-    }
-    Ptr<OcbWifiMac> curMacEntity = m_device->GetMac(curChannelNumber);
-    Ptr<OcbWifiMac> nextMacEntity = m_device->GetMac(nextChannelNumber);
-    // Perfect channel switch operation among multiple MAC entities in the context of single PHY
-    // device. first make current MAC entity in sleep mode.
-    curMacEntity->Suspend();
-    // second unattached current MAC entity from single PHY device
-    curMacEntity->ResetWifiPhys();
-    // third switch PHY device from current channel to next channel;
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{nextChannelNumber, 0, WIFI_PHY_BAND_5GHZ, 0});
-    // four attach next MAC entity to single PHY device
-    nextMacEntity->SetWifiPhy(m_phy);
-    // Here channel switch time is required to notify next MAC entity
-    // that channel access cannot be enabled in channel switch time.
-    Time switchTime = m_phy->GetChannelSwitchDelay();
-    nextMacEntity->MakeVirtualBusy(switchTime);
-    // finally resume next MAC entity from sleep mode
-    nextMacEntity->Resume();
-}
-
-bool
-DefaultChannelScheduler::ReleaseAccess(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    NS_ASSERT(m_channelNumber != 0);
-    if (m_channelNumber != channelNumber)
-    {
-        return false;
-    }
-    // cancel  current SCH MAC activity and assigned default CCH access.
-    SwitchToNextChannel(m_channelNumber, CCH);
-    m_channelAccess = DefaultCchAccess;
-    m_channelNumber = CCH;
-    m_extend = EXTENDED_CONTINUOUS;
-    if (!m_waitEvent.IsExpired())
-    {
-        m_waitEvent.Cancel();
-    }
-    if (!m_extendEvent.IsExpired())
-    {
-        m_extendEvent.Cancel();
-    }
-    m_waitChannelNumber = 0;
-    m_waitExtend = 0;
-    return true;
-}
-
-void
-DefaultChannelScheduler::NotifyCchSlotStart(Time duration)
-{
-    NS_LOG_FUNCTION(this << duration);
-}
-
-void
-DefaultChannelScheduler::NotifySchSlotStart(Time duration)
-{
-    NS_LOG_FUNCTION(this << duration);
-}
-
-void
-DefaultChannelScheduler::NotifyGuardSlotStart(Time duration, bool cchi)
-{
-    NS_LOG_FUNCTION(this << duration << cchi);
-    // only alternating access requires channel coordination events
-    if (m_channelAccess != AlternatingAccess)
-    {
-        return;
-    }
-
-    if (cchi)
-    {
-        SwitchToNextChannel(m_channelNumber, CCH);
-        Ptr<OcbWifiMac> mac = m_device->GetMac(CCH);
-        // see chapter 6.2.5 Sync tolerance
-        // a medium busy shall be declared during the guard interval.
-        mac->MakeVirtualBusy(duration);
-    }
-    else
-    {
-        Ptr<OcbWifiMac> mac = m_device->GetMac(m_channelNumber);
-        SwitchToNextChannel(CCH, m_channelNumber);
-        // see chapter 6.2.5 Sync tolerance
-        // a medium busy shall be declared during the guard interval.
-        mac->MakeVirtualBusy(duration);
-    }
-}
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/default-channel-scheduler.h ns-3.39/src/wave/model/default-channel-scheduler.h
--- ns-3.38/src/wave/model/default-channel-scheduler.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/default-channel-scheduler.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,142 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef DEFAULT_CHANNEL_SCHEDULER_H
-#define DEFAULT_CHANNEL_SCHEDULER_H
-
-#include "wave-net-device.h"
-
-namespace ns3
-{
-class WaveNetDevice;
-
-/**
- * \ingroup wave
- * \brief This class uses a simple mechanism to assign channel access with following features:
- * (1) only in the context of single-PHY device;
- * (2) FCFS (First come First service) strategy, which seems against the description of
- * the standard (preemptive strategy).
- */
-class DefaultChannelScheduler : public ChannelScheduler
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    DefaultChannelScheduler();
-    ~DefaultChannelScheduler() override;
-
-    /**
-     * \param device enable channel scheduler associated with WaveNetDevice
-     */
-    void SetWaveNetDevice(Ptr<WaveNetDevice> device) override;
-    /**
-     * \param channelNumber the specified channel number
-     * \return  the type of current assigned channel access for the specific channel.
-     */
-    ChannelAccess GetAssignedAccessType(uint32_t channelNumber) const override;
-
-    /**
-     * Notify CCH slot start
-     * \param duration the CCH slot duration
-     */
-    void NotifyCchSlotStart(Time duration);
-    /**
-     * Notify SCH slot start
-     * \param duration the SCH slot duration
-     */
-    void NotifySchSlotStart(Time duration);
-    /**
-     * Notify guard slot start
-     * \param duration the CCH slot duration
-     * \param cchi if true, switch to next channel
-     */
-    void NotifyGuardSlotStart(Time duration, bool cchi);
-
-  private:
-    void DoInitialize() override;
-    void DoDispose() override;
-    /**
-     * \param channelNumber the specific channel
-     * \param immediate indicate whether channel switch to channel
-     * \return whether the channel access is assigned successfully
-     *
-     * This method will assign alternating access for SCHs and CCH.
-     */
-    bool AssignAlternatingAccess(uint32_t channelNumber, bool immediate) override;
-    /**
-     * \param channelNumber the specific channel
-     * \param immediate indicate whether channel switch to channel
-     * \return whether the channel access is assigned successfully
-     *
-     * This method will assign continuous SCH access CCH.
-     */
-    bool AssignContinuousAccess(uint32_t channelNumber, bool immediate) override;
-    /**
-     * \param channelNumber the specific channel
-     * \param extends extension duration
-     * \param immediate indicate whether channel switch to channel
-     * \return whether the channel access is assigned successfully
-     *
-     * This method will assign extended SCH access for SCHs.
-     */
-    bool AssignExtendedAccess(uint32_t channelNumber, uint32_t extends, bool immediate) override;
-    /**
-     * This method will assign default CCH access for CCH.
-     * \return whether the channel access is assigned successfully
-     */
-    bool AssignDefaultCchAccess() override;
-    /**
-     * \param channelNumber indicating for which channel should release
-     * the assigned channel access resource.
-     * \return whether the channel access is released successfully
-     */
-    bool ReleaseAccess(uint32_t channelNumber) override;
-    /**
-     * \param curChannelNumber switch from MAC activity for current channel
-     * \param nextChannelNumber switch to MAC activity for next channel
-     */
-    void SwitchToNextChannel(uint32_t curChannelNumber, uint32_t nextChannelNumber);
-
-    Ptr<ChannelManager> m_manager;         ///< channel manager
-    Ptr<ChannelCoordinator> m_coordinator; ///< channel coordinator
-    Ptr<WifiPhy> m_phy;                    ///< Phy
-
-    /**
-     *  when m_channelAccess is ContinuousAccess, m_channelNumber
-     *   is continuous channel number;
-     *  when m_channelAccess is AlternatingAccess, m_channelNumber
-     *   is SCH channel number, another alternating channel is CCH;
-     *  when m_channelAccess is ExtendedAccess, m_channelNumber
-     *   is extended access, extends is the number of extends access.
-     *  when m_channelAccess is DefaultCchAccess, m_channelNumber is CCH.
-     */
-    uint32_t m_channelNumber;      ///< channel number
-    uint32_t m_extend;             ///< extend
-    EventId m_extendEvent;         ///< extend event
-    ChannelAccess m_channelAccess; ///< channel access
-
-    EventId m_waitEvent;          ///< wait event
-    uint32_t m_waitChannelNumber; ///< wait channel number
-    uint32_t m_waitExtend;        ///< wait extend
-
-    Ptr<ChannelCoordinationListener> m_coordinationListener; ///< coordination listener
-};
-
-} // namespace ns3
-#endif /* DEFAULT_CHANNEL_SCHEDULER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/higher-tx-tag.cc ns-3.39/src/wave/model/higher-tx-tag.cc
--- ns-3.38/src/wave/model/higher-tx-tag.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/higher-tx-tag.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- *         Junling Bu <linlinjavaer@gmail.com>
- */
-#include "higher-tx-tag.h"
-
-#include "ns3/abort.h"
-#include "ns3/log.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("HigherLayerTxVectorTag");
-
-NS_OBJECT_ENSURE_REGISTERED(HigherLayerTxVectorTag);
-
-TypeId
-HigherLayerTxVectorTag::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::HigherLayerTxVectorTag")
-                            .SetParent<Tag>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<HigherLayerTxVectorTag>();
-    return tid;
-}
-
-TypeId
-HigherLayerTxVectorTag::GetInstanceTypeId() const
-{
-    NS_LOG_FUNCTION(this);
-    return GetTypeId();
-}
-
-HigherLayerTxVectorTag::HigherLayerTxVectorTag()
-    : m_adaptable(false)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-HigherLayerTxVectorTag::HigherLayerTxVectorTag(WifiTxVector txVector, bool adaptable)
-    : m_txVector(txVector),
-      m_mode(txVector.GetMode()),
-      m_preamble(txVector.GetPreambleType()),
-      m_channelWidth(txVector.GetChannelWidth()),
-      m_adaptable(adaptable)
-{
-    NS_LOG_FUNCTION(this);
-    NS_ABORT_MSG_IF(txVector.GetPreambleType() == WIFI_PREAMBLE_HE_MU,
-                    "HE MU is not compatible with this tag");
-}
-
-WifiTxVector
-HigherLayerTxVectorTag::GetTxVector() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_txVector;
-}
-
-bool
-HigherLayerTxVectorTag::IsAdaptable() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_adaptable;
-}
-
-uint32_t
-HigherLayerTxVectorTag::GetSerializedSize() const
-{
-    NS_LOG_FUNCTION(this);
-    return (sizeof(WifiMode) + 1 + 2 + 1);
-}
-
-void
-HigherLayerTxVectorTag::Serialize(TagBuffer i) const
-{
-    NS_LOG_FUNCTION(this << &i);
-    i.Write((uint8_t*)&m_mode, sizeof(WifiMode));
-    i.WriteU8(static_cast<uint8_t>(m_preamble));
-    i.WriteU16(m_channelWidth);
-    i.WriteU8(static_cast<uint8_t>(m_adaptable));
-}
-
-void
-HigherLayerTxVectorTag::Deserialize(TagBuffer i)
-{
-    NS_LOG_FUNCTION(this << &i);
-    i.Read((uint8_t*)&m_mode, sizeof(WifiMode));
-    m_preamble = static_cast<WifiPreamble>(i.ReadU8());
-    m_channelWidth = i.ReadU16();
-    m_adaptable = i.ReadU8();
-    m_txVector.SetMode(m_mode);
-    m_txVector.SetPreambleType(m_preamble);
-    m_txVector.SetChannelWidth(m_channelWidth);
-}
-
-void
-HigherLayerTxVectorTag::Print(std::ostream& os) const
-{
-    NS_LOG_FUNCTION(this << &os);
-    os << " TxVector=" << m_txVector << ";  Adapter=" << m_adaptable;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/higher-tx-tag.h ns-3.39/src/wave/model/higher-tx-tag.h
--- ns-3.38/src/wave/model/higher-tx-tag.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/higher-tx-tag.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- *         Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef HIGHER_LAYER_TX_VECTOR_TAG_H
-#define HIGHER_LAYER_TX_VECTOR_TAG_H
-
-#include "ns3/tag.h"
-#include "ns3/wifi-tx-vector.h"
-
-namespace ns3
-{
-
-class Tag;
-class TypeId;
-
-/**
- * \ingroup packet
- * \ingroup wave
- *
- * \brief This tag will be used to support higher layer control DataRate
- * and TxPwr_Level for transmission.
- * If the high layer enables adaptable mode,  DataRate will be the
- * minimum allowable value and TxPwr_Level will be the maximum
- * allowable value for transmission.
- * If the higher layer does not enable adaptable parameter, the
- * DataRate and TxPwr_Level will be actual values for transmission.
- * However, if this tag is not used and inserted in the packet, the actual
- * DataRate and TxPwr_Level for transmission will be determined by MAC layer.
- */
-class HigherLayerTxVectorTag : public Tag
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    TypeId GetInstanceTypeId() const override;
-
-    HigherLayerTxVectorTag();
-    /**
-     * Constructor
-     *
-     * \param txVector wifi transmit vector
-     * \param adaptable is adaptable?
-     */
-    HigherLayerTxVectorTag(WifiTxVector txVector, bool adaptable);
-    /**
-     * \returns the tx vector for transmission
-     */
-    WifiTxVector GetTxVector() const;
-    /**
-     * \returns the adaptable mode for transmission
-     */
-    bool IsAdaptable() const;
-
-    uint32_t GetSerializedSize() const override;
-    void Serialize(TagBuffer i) const override;
-    void Deserialize(TagBuffer i) override;
-    void Print(std::ostream& os) const override;
-
-  private:
-    WifiTxVector m_txVector; ///< transmit vector
-    WifiMode m_mode;         ///< wifi mode
-    WifiPreamble m_preamble; ///< wifi preamble
-    uint16_t m_channelWidth; ///< channel width in MHz
-    bool m_adaptable;        ///< adaptable
-};
-
-} // namespace ns3
-
-#endif /* HIGHER_LAYER_TX_VECTOR_TAG_H*/
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/ocb-wifi-mac.cc ns-3.39/src/wave/model/ocb-wifi-mac.cc
--- ns-3.38/src/wave/model/ocb-wifi-mac.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/ocb-wifi-mac.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,531 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- *         Junling Bu <linlinjavaer@gmail.com>
- */
-
-#include "ocb-wifi-mac.h"
-
-#include "higher-tx-tag.h"
-#include "vendor-specific-action.h"
-#include "wave-frame-exchange-manager.h"
-
-#include "ns3/channel-access-manager.h"
-#include "ns3/event-id.h"
-#include "ns3/ht-capabilities.h"
-#include "ns3/log.h"
-#include "ns3/mac-rx-middle.h"
-#include "ns3/pointer.h"
-#include "ns3/string.h"
-#include "ns3/vht-capabilities.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("OcbWifiMac");
-
-NS_OBJECT_ENSURE_REGISTERED(OcbWifiMac);
-
-/// Wildcard BSSID
-const static Mac48Address WILDCARD_BSSID = Mac48Address::GetBroadcast();
-
-TypeId
-OcbWifiMac::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::OcbWifiMac")
-                            .SetParent<WifiMac>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<OcbWifiMac>();
-    return tid;
-}
-
-OcbWifiMac::OcbWifiMac()
-{
-    NS_LOG_FUNCTION(this);
-    // Let the lower layers know that we are acting as an OCB node
-    SetTypeOfStation(OCB);
-}
-
-OcbWifiMac::~OcbWifiMac()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-OcbWifiMac::SendVsc(Ptr<Packet> vsc, Mac48Address peer, OrganizationIdentifier oi)
-{
-    NS_LOG_FUNCTION(this << vsc << peer << oi);
-    WifiMacHeader hdr;
-    hdr.SetType(WIFI_MAC_MGT_ACTION);
-    hdr.SetAddr1(peer);
-    hdr.SetAddr2(GetAddress());
-    hdr.SetAddr3(WILDCARD_BSSID);
-    hdr.SetDsNotFrom();
-    hdr.SetDsNotTo();
-    VendorSpecificActionHeader vsa;
-    vsa.SetOrganizationIdentifier(oi);
-    vsc->AddHeader(vsa);
-
-    if (GetQosSupported())
-    {
-        uint8_t tid = QosUtilsGetTidForPacket(vsc);
-        tid = tid > 7 ? 0 : tid;
-        GetQosTxop(tid)->Queue(vsc, hdr);
-    }
-    else
-    {
-        GetTxop()->Queue(vsc, hdr);
-    }
-}
-
-void
-OcbWifiMac::AddReceiveVscCallback(OrganizationIdentifier oi, VscCallback cb)
-{
-    NS_LOG_FUNCTION(this << oi << &cb);
-    m_vscManager.RegisterVscCallback(oi, cb);
-}
-
-void
-OcbWifiMac::RemoveReceiveVscCallback(OrganizationIdentifier oi)
-{
-    NS_LOG_FUNCTION(this << oi);
-    m_vscManager.DeregisterVscCallback(oi);
-}
-
-void
-OcbWifiMac::SetSsid(Ssid ssid)
-{
-    NS_LOG_WARN("in OCB mode we should not call SetSsid");
-}
-
-Ssid
-OcbWifiMac::GetSsid() const
-{
-    NS_LOG_WARN("in OCB mode we should not call GetSsid");
-    // we really do not want to return ssid, however we have to provide
-    return WifiMac::GetSsid();
-}
-
-void
-OcbWifiMac::SetBssid(Mac48Address bssid)
-{
-    NS_LOG_WARN("in OCB mode we should not call SetBsid");
-}
-
-Mac48Address
-OcbWifiMac::GetBssid(uint8_t /* linkId */) const
-{
-    NS_LOG_WARN("in OCB mode we should not call GetBssid");
-    return WILDCARD_BSSID;
-}
-
-void
-OcbWifiMac::SetLinkUpCallback(Callback<void> linkUp)
-{
-    NS_LOG_FUNCTION(this << &linkUp);
-    WifiMac::SetLinkUpCallback(linkUp);
-
-    // The approach taken here is that, from the point of view of a STA
-    // in OCB mode, the link is always up, so we immediately invoke the
-    // callback if one is set
-    linkUp();
-}
-
-void
-OcbWifiMac::SetLinkDownCallback(Callback<void> linkDown)
-{
-    NS_LOG_FUNCTION(this << &linkDown);
-    WifiMac::SetLinkDownCallback(linkDown);
-    NS_LOG_WARN("in OCB mode the like will never down, so linkDown will never be called");
-}
-
-bool
-OcbWifiMac::CanForwardPacketsTo(Mac48Address to) const
-{
-    return true;
-}
-
-void
-OcbWifiMac::Enqueue(Ptr<Packet> packet, Mac48Address to)
-{
-    NS_LOG_FUNCTION(this << packet << to);
-    if (GetWifiRemoteStationManager()->IsBrandNew(to))
-    {
-        // In ad hoc mode, we assume that every destination supports all
-        // the rates we support.
-        if (GetHtSupported() || GetVhtSupported(SINGLE_LINK_OP_ID))
-        {
-            GetWifiRemoteStationManager()->AddAllSupportedMcs(to);
-            GetWifiRemoteStationManager()->AddStationHtCapabilities(
-                to,
-                GetHtCapabilities(SINGLE_LINK_OP_ID));
-        }
-        if (GetVhtSupported(SINGLE_LINK_OP_ID))
-        {
-            GetWifiRemoteStationManager()->AddStationVhtCapabilities(
-                to,
-                GetVhtCapabilities(SINGLE_LINK_OP_ID));
-        }
-        GetWifiRemoteStationManager()->AddAllSupportedModes(to);
-        GetWifiRemoteStationManager()->RecordDisassociated(to);
-    }
-
-    WifiMacHeader hdr;
-
-    // If we are not a QoS STA then we definitely want to use AC_BE to
-    // transmit the packet. A TID of zero will map to AC_BE (through \c
-    // QosUtilsMapTidToAc()), so we use that as our default here.
-    uint8_t tid = 0;
-
-    if (GetQosSupported())
-    {
-        hdr.SetType(WIFI_MAC_QOSDATA);
-        hdr.SetQosAckPolicy(WifiMacHeader::NORMAL_ACK);
-        hdr.SetQosNoEosp();
-        hdr.SetQosNoAmsdu();
-        // About transmission of multiple frames,
-        // in Ad-hoc mode TXOP is not supported for now, so TxopLimit=0;
-        // however in OCB mode, 802.11p do not allow transmit multiple frames
-        // so TxopLimit must equal 0
-        hdr.SetQosTxopLimit(0);
-
-        // Fill in the QoS control field in the MAC header
-        tid = QosUtilsGetTidForPacket(packet);
-        // Any value greater than 7 is invalid and likely indicates that
-        // the packet had no QoS tag, so we revert to zero, which'll
-        // mean that AC_BE is used.
-        if (tid > 7)
-        {
-            tid = 0;
-        }
-        hdr.SetQosTid(tid);
-    }
-    else
-    {
-        hdr.SetType(WIFI_MAC_DATA);
-    }
-
-    if (GetHtSupported() || GetVhtSupported(SINGLE_LINK_OP_ID))
-    {
-        hdr.SetNoOrder(); // explicitly set to 0 for the time being since HT/VHT/HE control field is
-                          // not yet implemented (set it to 1 when implemented)
-    }
-    hdr.SetAddr1(to);
-    hdr.SetAddr2(GetAddress());
-    hdr.SetAddr3(WILDCARD_BSSID);
-    hdr.SetDsNotFrom();
-    hdr.SetDsNotTo();
-
-    if (GetQosSupported())
-    {
-        // Sanity check that the TID is valid
-        NS_ASSERT(tid < 8);
-        GetQosTxop(tid)->Queue(packet, hdr);
-    }
-    else
-    {
-        GetTxop()->Queue(packet, hdr);
-    }
-}
-
-/*
- * see 802.11p-2010 chapter 11.19
- * here we only care about data packet and vsa management frame
- */
-void
-OcbWifiMac::Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
-{
-    NS_LOG_FUNCTION(this << *mpdu << +linkId);
-    const WifiMacHeader* hdr = &mpdu->GetHeader();
-    // Create a copy of the MPDU payload because non-const operations like RemovePacketTag
-    // and RemoveHeader may need to be performed.
-    Ptr<Packet> packet = mpdu->GetPacket()->Copy();
-    NS_ASSERT(!hdr->IsCtl());
-    NS_ASSERT(hdr->GetAddr3() == WILDCARD_BSSID);
-
-    Mac48Address from = hdr->GetAddr2();
-    Mac48Address to = hdr->GetAddr1();
-
-    if (GetWifiRemoteStationManager()->IsBrandNew(from))
-    {
-        // In ad hoc mode, we assume that every destination supports all
-        // the rates we support.
-        if (GetHtSupported() || GetVhtSupported(SINGLE_LINK_OP_ID))
-        {
-            GetWifiRemoteStationManager()->AddAllSupportedMcs(from);
-            GetWifiRemoteStationManager()->AddStationHtCapabilities(
-                from,
-                GetHtCapabilities(SINGLE_LINK_OP_ID));
-        }
-        if (GetVhtSupported(SINGLE_LINK_OP_ID))
-        {
-            GetWifiRemoteStationManager()->AddStationVhtCapabilities(
-                from,
-                GetVhtCapabilities(SINGLE_LINK_OP_ID));
-        }
-        GetWifiRemoteStationManager()->AddAllSupportedModes(from);
-        GetWifiRemoteStationManager()->RecordDisassociated(from);
-    }
-
-    if (hdr->IsData())
-    {
-        if (hdr->IsQosData() && hdr->IsQosAmsdu())
-        {
-            NS_LOG_DEBUG("Received A-MSDU from" << from);
-            DeaggregateAmsduAndForward(mpdu);
-        }
-        else
-        {
-            ForwardUp(packet, from, to);
-        }
-        return;
-    }
-
-    // why put check here, not before "if (hdr->IsData ())" ?
-    // because WifiNetDevice::ForwardUp needs to m_promiscRx data packet
-    // and will filter data packet for itself
-    // so we need to filter management frame
-    if (to != GetAddress() && !to.IsGroup())
-    {
-        NS_LOG_LOGIC("the management frame is not for us");
-        NotifyRxDrop(packet);
-        return;
-    }
-
-    if (hdr->IsMgt() && hdr->IsAction())
-    {
-        // yes, we only care about VendorSpecificAction frame in OCB mode
-        // other management frames will be handled by WifiMac::Receive
-        VendorSpecificActionHeader vsaHdr;
-        packet->PeekHeader(vsaHdr);
-        if (vsaHdr.GetCategory() == CATEGORY_OF_VSA)
-        {
-            VendorSpecificActionHeader vsa;
-            packet->RemoveHeader(vsa);
-            OrganizationIdentifier oi = vsa.GetOrganizationIdentifier();
-            VscCallback cb = m_vscManager.FindVscCallback(oi);
-
-            if (cb.IsNull())
-            {
-                NS_LOG_DEBUG("cannot find VscCallback for OrganizationIdentifier=" << oi);
-                return;
-            }
-            bool succeed = cb(this, oi, packet, from);
-
-            if (!succeed)
-            {
-                NS_LOG_DEBUG("vsc callback could not handle the packet successfully");
-            }
-
-            return;
-        }
-    }
-    // Invoke the receive handler of our parent class to deal with any
-    // other frames. Specifically, this will handle Block Ack-related
-    // Management Action frames.
-    WifiMac::Receive(Create<WifiMpdu>(packet, *hdr), linkId);
-}
-
-void
-OcbWifiMac::ConfigureEdca(uint32_t cwmin, uint32_t cwmax, uint32_t aifsn, AcIndex ac)
-{
-    NS_LOG_FUNCTION(this << cwmin << cwmax << aifsn << ac);
-    Ptr<Txop> dcf;
-    switch (ac)
-    {
-    case AC_VO:
-        dcf = WifiMac::GetVOQueue();
-        dcf->SetWifiMac(this);
-        dcf->SetMinCw((cwmin + 1) / 4 - 1);
-        dcf->SetMaxCw((cwmin + 1) / 2 - 1);
-        dcf->SetAifsn(aifsn);
-        break;
-    case AC_VI:
-        dcf = WifiMac::GetVIQueue();
-        dcf->SetWifiMac(this);
-        dcf->SetMinCw((cwmin + 1) / 2 - 1);
-        dcf->SetMaxCw(cwmin);
-        dcf->SetAifsn(aifsn);
-        break;
-    case AC_BE:
-        dcf = WifiMac::GetBEQueue();
-        dcf->SetWifiMac(this);
-        dcf->SetMinCw(cwmin);
-        dcf->SetMaxCw(cwmax);
-        dcf->SetAifsn(aifsn);
-        break;
-    case AC_BK:
-        dcf = WifiMac::GetBKQueue();
-        dcf->SetWifiMac(this);
-        dcf->SetMinCw(cwmin);
-        dcf->SetMaxCw(cwmax);
-        dcf->SetAifsn(aifsn);
-        break;
-    case AC_BE_NQOS:
-        dcf = WifiMac::GetTxop();
-        dcf->SetWifiMac(this);
-        dcf->SetMinCw(cwmin);
-        dcf->SetMaxCw(cwmax);
-        dcf->SetAifsn(aifsn);
-        break;
-    case AC_BEACON:
-        // done by ApWifiMac
-        break;
-    case AC_UNDEF:
-        NS_FATAL_ERROR("I don't know what to do with this");
-        break;
-    }
-
-    GetLink(SINGLE_LINK_OP_ID).channelAccessManager->Add(dcf);
-}
-
-void
-OcbWifiMac::SetWifiPhy(Ptr<WifiPhy> phy)
-{
-    NS_LOG_FUNCTION(this << phy);
-    WifiMac::SetWifiPhys({phy});
-    NS_ABORT_MSG_IF(!phy->GetOperatingChannel().IsSet(),
-                    "PHY operating channel must have been set");
-    auto& link = GetLink(SINGLE_LINK_OP_ID);
-    if (link.channelAccessManager != nullptr)
-    {
-        link.channelAccessManager->SetupPhyListener(phy);
-    }
-    if (link.feManager != nullptr)
-    {
-        link.feManager->SetWifiPhy(phy);
-    }
-}
-
-void
-OcbWifiMac::ConfigureStandard(WifiStandard standard)
-{
-    NS_LOG_FUNCTION(this << standard);
-    NS_ASSERT(standard == WIFI_STANDARD_80211p);
-
-    if (GetNLinks() == 0)
-    {
-        WifiMac::SetWifiPhys({nullptr}); // for the purpose of adding a link
-    }
-
-    auto& link = GetLink(SINGLE_LINK_OP_ID);
-
-    // Setup ChannelAccessManager
-    link.channelAccessManager = CreateObject<ChannelAccessManager>();
-
-    uint32_t cwmin = 15;
-    uint32_t cwmax = 1023;
-
-    if (!GetQosSupported())
-    {
-        // The special value of AC_BE_NQOS which exists in the Access
-        // Category enumeration allows us to configure plain old DCF.
-        ConfigureEdca(cwmin, cwmax, 2, AC_BE_NQOS);
-    }
-    else
-    {
-        // Now we configure the EDCA functions
-        // see IEEE802.11p-2010 section 7.3.2.29
-        // Wave CCH and SCHs set default 802.11p EDCA
-        ConfigureEdca(cwmin, cwmax, 2, AC_VO);
-        ConfigureEdca(cwmin, cwmax, 3, AC_VI);
-        ConfigureEdca(cwmin, cwmax, 6, AC_BE);
-        ConfigureEdca(cwmin, cwmax, 9, AC_BK);
-    }
-
-    // Setup FrameExchangeManager
-    auto feManager = CreateObject<WaveFrameExchangeManager>();
-    feManager->SetWifiMac(this);
-    feManager->SetMacTxMiddle(m_txMiddle);
-    feManager->SetMacRxMiddle(m_rxMiddle);
-    feManager->SetAddress(GetAddress());
-    link.channelAccessManager->SetupFrameExchangeManager(feManager);
-    if (auto phy = GetWifiPhy(); phy != nullptr)
-    {
-        feManager->SetWifiPhy(phy);
-        link.channelAccessManager->SetupPhyListener(phy);
-    }
-    link.feManager = feManager;
-}
-
-void
-OcbWifiMac::Suspend()
-{
-    NS_LOG_FUNCTION(this);
-    GetLink(SINGLE_LINK_OP_ID).channelAccessManager->NotifySleepNow();
-    GetLink(SINGLE_LINK_OP_ID).feManager->NotifySleepNow();
-}
-
-void
-OcbWifiMac::Resume()
-{
-    NS_LOG_FUNCTION(this);
-    // wake-up operation is not required in m_low object
-    GetLink(SINGLE_LINK_OP_ID).channelAccessManager->NotifyWakeupNow();
-}
-
-void
-OcbWifiMac::MakeVirtualBusy(Time duration)
-{
-    NS_LOG_FUNCTION(this << duration);
-    GetLink(SINGLE_LINK_OP_ID)
-        .channelAccessManager->NotifyCcaBusyStartNow(duration, WIFI_CHANLIST_PRIMARY, {});
-}
-
-void
-OcbWifiMac::CancleTx(AcIndex ac)
-{
-    NS_LOG_FUNCTION(this << ac);
-    Ptr<QosTxop> queue = GetQosTxop(ac);
-    NS_ASSERT(queue);
-    // reset and flush queue
-    queue->GetWifiMacQueue()->Flush();
-}
-
-void
-OcbWifiMac::Reset()
-{
-    NS_LOG_FUNCTION(this);
-    // The switching event is used to notify MAC entity reset its operation.
-    GetLink(SINGLE_LINK_OP_ID).channelAccessManager->NotifySwitchingStartNow(Time(0));
-    GetLink(SINGLE_LINK_OP_ID).feManager->NotifySwitchingStartNow(Time(0));
-}
-
-void
-OcbWifiMac::EnableForWave(Ptr<WaveNetDevice> device)
-{
-    NS_LOG_FUNCTION(this << device);
-    // To extend current OcbWifiMac for WAVE 1609.4, we shall use WaveFrameExchangeManager
-    StaticCast<WaveFrameExchangeManager>(GetLink(SINGLE_LINK_OP_ID).feManager)
-        ->SetWaveNetDevice(device);
-}
-
-std::optional<uint8_t>
-OcbWifiMac::GetLinkIdByAddress(const Mac48Address& address) const
-{
-    return 0;
-}
-
-void
-OcbWifiMac::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    WifiMac::DoDispose();
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/ocb-wifi-mac.h ns-3.39/src/wave/model/ocb-wifi-mac.h
--- ns-3.38/src/wave/model/ocb-wifi-mac.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/ocb-wifi-mac.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,199 +0,0 @@
-/*
- * Copyright (c) 2008 INRIA
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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>
- *         Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef OCB_WIFI_MAC_H
-#define OCB_WIFI_MAC_H
-
-#include "vendor-specific-action.h"
-#include "wave-net-device.h"
-
-#include "ns3/object-factory.h"
-#include "ns3/wifi-mac-queue.h"
-#include "ns3/wifi-mac.h"
-
-namespace ns3
-{
-
-class OrganizationIdentifier;
-class WaveNetDevice;
-
-/**
- * \brief STAs communicate with each directly outside the context of a BSS
- * \ingroup wave
- *
- * In OCB mac mode,synchronization, association, dis-association
- * and authentication of normal wifi are not used for wireless access in
- * vehicular environments.
- *
- * Although Timing Advertisement frame is a specific management frame defined
- * in 802.11p. It is mainly used by IEEE Std 1609.4 for channel switch synchronization.
- * However in simulation nodes are supposed to have GPS synchronization ability,
- * so we will not implement this feature.
- */
-class OcbWifiMac : public WifiMac
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    OcbWifiMac();
-    ~OcbWifiMac() override;
-    /**
-     * \param vsc management packet to send.
-     * \param peer the address to which the packet should be sent.
-     * \param oi Organization Identifier field
-     * see 7.3.1.31 Organization Identifier field and 10.3.29 Vendor-specific action
-     *
-     * management information can be transmitted over vendor specific action frame.
-     * This will be mainly called by IEEE Std 1609.4 to send WSA from IEEE Std 1609.3
-     */
-    void SendVsc(Ptr<Packet> vsc, Mac48Address peer, OrganizationIdentifier oi);
-    /**
-     * \param oi Organization Identifier
-     * \param cb callback to invoke whenever a vendor specific action frame has been received and
-     * must be forwarded to the higher layers. every node shall register first if it wants to
-     * receive specific vendor specific content.
-     */
-    void AddReceiveVscCallback(OrganizationIdentifier oi, VscCallback cb);
-    /**
-     * \param oi Organization Identifier
-     */
-    void RemoveReceiveVscCallback(OrganizationIdentifier oi);
-
-    /**
-     * \returns the ssid which this MAC layer is going to try to stay in.
-     *
-     * This method shall not be used in WAVE environment and
-     * here it will overloaded to log warn message
-     */
-    virtual Ssid GetSsid() const;
-    /**
-     * \param ssid the current ssid of this MAC layer.
-     *
-     * This method shall not be used in WAVE environment and
-     * here it will overloaded to log warn message
-     */
-    virtual void SetSsid(Ssid ssid);
-    /**
-     * \param bssid the BSSID of the network that this device belongs to.
-     * This method shall not be used in WAVE environment and
-     * here it will overloaded to log warn message
-     */
-    virtual void SetBssid(Mac48Address bssid);
-    /**
-     * This method shall not be used in WAVE environment and
-     * here it will overloaded to log warn message
-     * \return An invalid BSSID.
-     */
-    virtual Mac48Address GetBssid(uint8_t /* linkId */) const;
-    /**
-     * SetLinkUpCallback and SetLinkDownCallback will be overloaded
-     * In OCB mode, stations can send packets directly whenever they want
-     * so the link is always up and never down even during channel switch
-     */
-    /**
-     * \param linkUp the callback to invoke when the link becomes up.
-     */
-    void SetLinkUpCallback(Callback<void> linkUp) override;
-    /**
-     * \param linkDown the callback to invoke when the link becomes down.
-     */
-    virtual void SetLinkDownCallback(Callback<void> linkDown);
-    /**
-     * \param packet the packet to send.
-     * \param to the address to which the packet should be sent.
-     *
-     * The packet should be enqueued in a tx queue, and should be
-     * dequeued as soon as the channel access function determines that
-     * access is granted to this MAC.
-     */
-    void Enqueue(Ptr<Packet> packet, Mac48Address to) override;
-    bool CanForwardPacketsTo(Mac48Address to) const override;
-    /**
-     * \param cwmin the min contention window
-     * \param cwmax the max contention window
-     * \param aifsn the arbitration inter-frame space
-     * \param ac    the access category index
-     *
-     * configure EDCA queue parameters
-     */
-    void ConfigureEdca(uint32_t cwmin, uint32_t cwmax, uint32_t aifsn, AcIndex ac);
-
-    // below six public methods are used for MAC extension defined in IEEE 1609.4
-    /**
-     * \param device make current MAC entity associated with WaveNetDevice
-     *
-     * To support MAC extension for multiple channel operation,
-     *  WaveMacLow object will be used to replace original MacLow object.
-     */
-    void EnableForWave(Ptr<WaveNetDevice> device);
-    /**
-     * To support MAC extension for multiple channel operation,
-     * Suspend the activity in current MAC entity
-     */
-    void Suspend();
-    /**
-     * To support MAC extension for multiple channel operation,
-     * Resume the activity of suspended MAC entity
-     */
-    void Resume();
-    /**
-     * \param duration the virtual busy time for MAC entity
-     *
-     * To support MAC extension for multiple channel operation,
-     * Notify MAC entity busy for some time to prevent transmission
-     */
-    void MakeVirtualBusy(Time duration);
-    /**
-     * \param ac the specified access category
-     *
-     * To support MAC extension for multiple channel operation,
-     * Cancel transmit operation for internal queue associated with a specified Access Category.
-     */
-    void CancleTx(AcIndex ac);
-    /**
-     * To support MAC extension for multiple channel operation,
-     * Reset current MAC entity and flush its internal queues.
-     */
-    void Reset();
-    /**
-     * Set the PHY.
-     *
-     * \param phy the PHY object
-     */
-    void SetWifiPhy(Ptr<WifiPhy> phy);
-
-    // Inherited from base class
-    void ConfigureStandard(WifiStandard standard) override;
-
-  protected:
-    void DoDispose() override;
-
-  private:
-    void Receive(Ptr<const WifiMpdu> mpdu, uint8_t linkId) override;
-    std::optional<uint8_t> GetLinkIdByAddress(const Mac48Address& address) const override;
-
-    VendorSpecificContentManager m_vscManager; ///< VSC manager
-};
-
-} // namespace ns3
-#endif /* OCB_WIFI_MAC_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/vendor-specific-action.cc ns-3.39/src/wave/model/vendor-specific-action.cc
--- ns-3.38/src/wave/model/vendor-specific-action.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/vendor-specific-action.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,403 +0,0 @@
-/*
- * Copyright (c) 2013 Dalian University of Technology
- * 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: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "vendor-specific-action.h"
-
-#include "ns3/assert.h"
-#include "ns3/log.h"
-
-#include <cstring>
-#include <iomanip>
-#include <iostream>
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("VendorSpecificAction");
-
-/*********** OrganizationIdentifier *******/
-
-ATTRIBUTE_HELPER_CPP(OrganizationIdentifier);
-
-OrganizationIdentifier::OrganizationIdentifier()
-    : m_type(Unknown)
-{
-    NS_LOG_FUNCTION(this);
-    m_type = Unknown;
-    std::memset(m_oi, 0, 5);
-}
-
-OrganizationIdentifier::OrganizationIdentifier(const uint8_t* str, uint32_t length)
-{
-    NS_LOG_FUNCTION(this << str << length);
-    if (length == 3)
-    {
-        m_type = OUI24;
-        std::memcpy(m_oi, str, length);
-    }
-    else if (length == 5)
-    {
-        m_type = OUI36;
-        std::memcpy(m_oi, str, length);
-    }
-    else
-    {
-        m_type = Unknown;
-        NS_FATAL_ERROR("cannot support organization identifier with length=" << length);
-    }
-}
-
-OrganizationIdentifier&
-OrganizationIdentifier::operator=(const OrganizationIdentifier& oi)
-{
-    this->m_type = oi.m_type;
-    std::memcpy(this->m_oi, oi.m_oi, 5);
-    return (*this);
-}
-
-OrganizationIdentifier::~OrganizationIdentifier()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-uint8_t
-OrganizationIdentifier::GetManagementId() const
-{
-    NS_LOG_FUNCTION(this);
-    NS_ASSERT(m_type == OUI36);
-    return (m_oi[4] & 0x0f);
-}
-
-bool
-OrganizationIdentifier::IsNull() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_type == Unknown;
-}
-
-uint32_t
-OrganizationIdentifier::GetSerializedSize() const
-{
-    NS_LOG_FUNCTION(this);
-    switch (m_type)
-    {
-    case OUI24:
-        return 3;
-    case OUI36:
-        return 5;
-    case Unknown:
-    default:
-        NS_FATAL_ERROR_NO_MSG();
-        return 0;
-    }
-}
-
-void
-OrganizationIdentifier::SetType(OrganizationIdentifierType type)
-{
-    NS_LOG_FUNCTION(this);
-    m_type = type;
-}
-
-enum OrganizationIdentifier::OrganizationIdentifierType
-OrganizationIdentifier::GetType() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_type;
-}
-
-void
-OrganizationIdentifier::Serialize(Buffer::Iterator start) const
-{
-    NS_LOG_FUNCTION(this << &start);
-    start.Write(m_oi, GetSerializedSize());
-}
-
-/*  because OrganizationIdentifier field is not standard
- *  and the length of OrganizationIdentifier is variable
- *  so data parse here is troublesome
- */
-uint32_t
-OrganizationIdentifier::Deserialize(Buffer::Iterator start)
-{
-    NS_LOG_FUNCTION(this << &start);
-    // first try to parse OUI24 with 3 bytes
-    start.Read(m_oi, 3);
-    for (std::vector<OrganizationIdentifier>::iterator i = OrganizationIdentifiers.begin();
-         i != OrganizationIdentifiers.end();
-         ++i)
-    {
-        if ((i->m_type == OUI24) && (std::memcmp(i->m_oi, m_oi, 3) == 0))
-        {
-            m_type = OUI24;
-            return 3;
-        }
-    }
-
-    // then try to parse OUI36 with 5 bytes
-    start.Read(m_oi + 3, 2);
-    for (std::vector<OrganizationIdentifier>::iterator i = OrganizationIdentifiers.begin();
-         i != OrganizationIdentifiers.end();
-         ++i)
-    {
-        if ((i->m_type == OUI36) && (std::memcmp(i->m_oi, m_oi, 4) == 0))
-        {
-            // OUI36 first check 4 bytes, then check half of the 5th byte
-            if ((i->m_oi[4] & 0xf0) == (m_oi[4] & 0xf0))
-            {
-                m_type = OUI36;
-                return 5;
-            }
-        }
-    }
-
-    // if we cannot deserialize the organization identifier field,
-    // we will fail
-    NS_FATAL_ERROR("cannot deserialize the organization identifier field successfully");
-    return 0;
-}
-
-/**
- * equality operator
- * \param a left side object
- * \param b right side object
- * \returns true if equal
- */
-bool
-operator==(const OrganizationIdentifier& a, const OrganizationIdentifier& b)
-{
-    if (a.m_type != b.m_type)
-    {
-        return false;
-    }
-
-    if (a.m_type == OrganizationIdentifier::OUI24)
-    {
-        return memcmp(a.m_oi, b.m_oi, 3) == 0;
-    }
-
-    if (a.m_type == OrganizationIdentifier::OUI36)
-    {
-        return (memcmp(a.m_oi, b.m_oi, 4) == 0) && ((a.m_oi[4] & 0xf0) == (b.m_oi[4] & 0xf0));
-    }
-
-    return false;
-}
-
-/**
- * inequality operator
- * \param a left side object
- * \param b right side object
- * \returns true if not equal
- */
-bool
-operator!=(const OrganizationIdentifier& a, const OrganizationIdentifier& b)
-{
-    return !(a == b);
-}
-
-/**
- * less than operator
- * \param a left side object
- * \param b right side object
- * \returns true if a < b
- */
-bool
-operator<(const OrganizationIdentifier& a, const OrganizationIdentifier& b)
-{
-    return memcmp(a.m_oi, b.m_oi, std::min(a.m_type, b.m_type)) < 0;
-}
-
-/**
- * output operator
- * \param os output stream
- * \param oi organization identifier
- * \returns output stream
- */
-std::ostream&
-operator<<(std::ostream& os, const OrganizationIdentifier& oi)
-{
-    for (int i = 0; i < oi.m_type; i++)
-    {
-        os << "0x" << std::hex << static_cast<int>(oi.m_oi[i]) << " ";
-    }
-    os << std::endl;
-    return os;
-}
-
-/**
- * input operator
- * \param is input stream
- * \param oi organization identifier
- * \returns input stream
- */
-std::istream&
-operator>>(std::istream& is, const OrganizationIdentifier& oi)
-{
-    return is;
-}
-
-/*********** VendorSpecificActionHeader *******/
-NS_OBJECT_ENSURE_REGISTERED(VendorSpecificActionHeader);
-
-VendorSpecificActionHeader::VendorSpecificActionHeader()
-    : m_oi(),
-      m_category(CATEGORY_OF_VSA)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-VendorSpecificActionHeader::~VendorSpecificActionHeader()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-VendorSpecificActionHeader::SetOrganizationIdentifier(OrganizationIdentifier oi)
-{
-    NS_LOG_FUNCTION(this << oi);
-    m_oi = oi;
-}
-
-OrganizationIdentifier
-VendorSpecificActionHeader::GetOrganizationIdentifier() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_oi;
-}
-
-TypeId
-VendorSpecificActionHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::VendorSpecificActionHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<VendorSpecificActionHeader>();
-
-    return tid;
-}
-
-uint8_t
-VendorSpecificActionHeader::GetCategory() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_category;
-}
-
-TypeId
-VendorSpecificActionHeader::GetInstanceTypeId() const
-{
-    NS_LOG_FUNCTION(this);
-    return GetTypeId();
-}
-
-void
-VendorSpecificActionHeader::Print(std::ostream& os) const
-{
-    NS_LOG_FUNCTION(this << &os);
-    os << "VendorSpecificActionHeader[ "
-       << "category = 0x" << std::hex << (int)m_category << "organization identifier = " << m_oi
-       << std::dec;
-}
-
-uint32_t
-VendorSpecificActionHeader::GetSerializedSize() const
-{
-    NS_LOG_FUNCTION(this);
-    return sizeof(m_category) + m_oi.GetSerializedSize();
-}
-
-void
-VendorSpecificActionHeader::Serialize(Buffer::Iterator start) const
-{
-    NS_LOG_FUNCTION(this << &start);
-    start.WriteU8(m_category);
-    m_oi.Serialize(start);
-}
-
-uint32_t
-VendorSpecificActionHeader::Deserialize(Buffer::Iterator start)
-{
-    NS_LOG_FUNCTION(this << &start);
-    m_category = start.ReadU8();
-    if (m_category != CATEGORY_OF_VSA)
-    {
-        return 0;
-    }
-    m_oi.Deserialize(start);
-
-    return GetSerializedSize();
-}
-
-/********* VendorSpecificContentManager ***********/
-VendorSpecificContentManager::VendorSpecificContentManager()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-VendorSpecificContentManager::~VendorSpecificContentManager()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-VendorSpecificContentManager::RegisterVscCallback(OrganizationIdentifier oi, VscCallback cb)
-{
-    NS_LOG_FUNCTION(this << oi << &cb);
-    if (IsVscCallbackRegistered(oi))
-    {
-        NS_LOG_WARN("there is already a VsaCallback registered for OrganizationIdentifier " << oi);
-    }
-    m_callbacks.insert(std::make_pair(oi, cb));
-}
-
-void
-VendorSpecificContentManager::DeregisterVscCallback(OrganizationIdentifier& oi)
-{
-    NS_LOG_FUNCTION(this << oi);
-    m_callbacks.erase(oi);
-}
-
-bool
-VendorSpecificContentManager::IsVscCallbackRegistered(OrganizationIdentifier& oi)
-{
-    NS_LOG_FUNCTION(this << oi);
-    if (m_callbacks.find(oi) == m_callbacks.end())
-    {
-        OrganizationIdentifiers.push_back(oi);
-        return false;
-    }
-    return true;
-}
-
-/// VSC callback function
-static VscCallback null_callback = MakeNullCallback<bool,
-                                                    Ptr<WifiMac>,
-                                                    const OrganizationIdentifier&,
-                                                    Ptr<const Packet>,
-                                                    const Address&>();
-
-VscCallback
-VendorSpecificContentManager::FindVscCallback(OrganizationIdentifier& oi)
-{
-    NS_LOG_FUNCTION(this << oi);
-    VscCallbacksI i;
-    i = m_callbacks.find(oi);
-    return (i == m_callbacks.end()) ? null_callback : i->second;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/vendor-specific-action.h ns-3.39/src/wave/model/vendor-specific-action.h
--- ns-3.38/src/wave/model/vendor-specific-action.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/vendor-specific-action.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,260 +0,0 @@
-/*
- * Copyright (c) 2013 Dalian University of Technology
- * 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: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef Vendor_Specific_Action_H
-#define Vendor_Specific_Action_H
-
-#include "ns3/address.h"
-#include "ns3/attribute-helper.h"
-#include "ns3/attribute.h"
-#include "ns3/header.h"
-#include "ns3/packet.h"
-#include "ns3/pointer.h"
-
-#include <map>
-#include <ostream>
-
-namespace ns3
-{
-
-class WifiMac;
-class VendorSpecificContentManager;
-
-/**
- * \ingroup wave
- *
- * \brief the organization identifier is a public organizationally
- * unique identifier assigned by the IEEE.
- *
- * Similar to protocol field of data packets that indicates which
- * entity of higher layer should handle received packets, Organization
- * Identifier field is used in Vendor Specific Action frames to
- * indicate which entity or higher layer should handle vendor specific
- * content.
- * Normally the value is assigned by IEEE and the length of field is
- * either 24 bits or 36 bits.
- * For more, see IEEE802.11p-2010 section 7.3.1.31 and 7.4.5
- *
- * \see attribute_OrganizationIdentifier
- */
-class OrganizationIdentifier
-{
-  public:
-    OrganizationIdentifier();
-    /**
-     * Constructor
-     *
-     * \param str identifier name
-     * \param length identifier length
-     */
-    OrganizationIdentifier(const uint8_t* str, uint32_t length);
-    /**
-     * Assignment operator
-     * \param oi object to copy from
-     * \return reference to the new object
-     */
-    OrganizationIdentifier& operator=(const OrganizationIdentifier& oi);
-    virtual ~OrganizationIdentifier();
-
-    /// OrganizationIdentifierType enumeration
-    enum OrganizationIdentifierType
-    {
-        OUI24 = 3, // 3 bytes
-        OUI36 = 5, // 5 bytes
-        Unknown = 0,
-    };
-
-    /**
-     * \returns last 4 bits when OrganizationIdentifier is OUI36
-     */
-    uint8_t GetManagementId() const;
-    /**
-     * \returns whether current OrganizationIdentifier is initial state
-     */
-    bool IsNull() const;
-    /**
-     * \param type set the type of current OrganizationIdentifier
-     */
-    void SetType(OrganizationIdentifierType type);
-    /**
-     * \returns whether this OrganizationIdentifier is OUI24 or OUI36.
-     */
-    OrganizationIdentifierType GetType() const;
-
-    // below methods will be called by VendorSpecificActionHeader
-    /**
-     * Get serialized size
-     * \returns the serialized size
-     */
-    uint32_t GetSerializedSize() const;
-    /**
-     * Serialize to buffer
-     * \param start the iterator
-     */
-    void Serialize(Buffer::Iterator start) const;
-    /**
-     * Deserialize from buffer
-     * \param start the iterator
-     * \returns the deserialize size
-     */
-    uint32_t Deserialize(Buffer::Iterator start);
-
-  private:
-    friend bool operator==(const OrganizationIdentifier& a, const OrganizationIdentifier& b);
-    friend bool operator!=(const OrganizationIdentifier& a, const OrganizationIdentifier& b);
-    friend bool operator<(const OrganizationIdentifier& a, const OrganizationIdentifier& b);
-    friend std::ostream& operator<<(std::ostream& os, const OrganizationIdentifier& oi);
-    friend std::istream& operator>>(std::istream& is, const OrganizationIdentifier& oi);
-
-    OrganizationIdentifierType m_type; ///< OI type
-    uint8_t m_oi[5];                   ///< organization identifier
-};
-
-ATTRIBUTE_HELPER_HEADER(OrganizationIdentifier);
-
-bool operator==(const OrganizationIdentifier& a, const OrganizationIdentifier& b);
-bool operator!=(const OrganizationIdentifier& a, const OrganizationIdentifier& b);
-bool operator<(const OrganizationIdentifier& a, const OrganizationIdentifier& b);
-
-std::ostream& operator<<(std::ostream& os, const OrganizationIdentifier& oi);
-std::istream& operator>>(std::istream& is, const OrganizationIdentifier& oi);
-
-/**
- * see IEEE 802.11-2007 chapter 7.3.1.11
- * Table 7-24â€”Category values
- */
-const static uint8_t CATEGORY_OF_VSA = 127;
-
-/**
- * \ingroup wave
- * See IEEE 802.11-2007 chapter 7.3.1.11 and 7.4.5
- * also IEEE 802.11p-2010 chapter 7.4.5
- * Although WifiActionHeader has been defined in wifi mgt-header.h/.cc,
- * it is not a good way to inherit from it or add vendor specific action support.
- * The reason is Action field. Other Action frames contains Action field, and
- * the VSA frame contains OUI or variable OrganizationIdentifier instead of one byte Action field.
- * Header format: | Category | Organization IdentifierType | Vendor Specific content |
- *        Octets:       1                j                        Variable
- *
- * In ns-3, VSA frame will be divided into  VendorSpecificActionHeader vsaHeader
- * and Packet vsc that indicates vendor specific content.
- *
- * This frame is used by IEEE 1609.4 to send WSA packet of IEEE 1609.3,
- * and if users want to send management information without customized management
- * frames, he can use VSA frames.
- */
-class VendorSpecificActionHeader : public Header
-{
-  public:
-    VendorSpecificActionHeader();
-    ~VendorSpecificActionHeader() override;
-
-    /**
-     * \param oi the OrganizationIdentifier of current VSA header
-     */
-    void SetOrganizationIdentifier(OrganizationIdentifier oi);
-    /**
-     * \returns current OrganizationIdentifier of the VSA header
-     */
-    OrganizationIdentifier GetOrganizationIdentifier() const;
-    /**
-     * Get the category field
-     *
-     * The category field shall be CATEGORY_OF_VSA
-     *
-     * \return The category field
-     */
-    uint8_t GetCategory() const;
-
-    /**
-     * \brief Get the type ID.
-     * \return the object 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:
-    OrganizationIdentifier m_oi; ///< OI
-    uint8_t m_category;          ///< category
-};
-
-/**
- * \param mac a pointer to the mac object which is calling this callback
- * \param oi the organization identifier of vendor specific action frame
- * \param packet the vendor specific content packet received
- * \param sender the address of the sender
- * \returns true if the callback could handle the packet successfully;
- *       false otherwise.
- */
-typedef Callback<bool,
-                 Ptr<WifiMac>,
-                 const OrganizationIdentifier&,
-                 Ptr<const Packet>,
-                 const Address&>
-    VscCallback;
-
-/**
- * \ingroup wave
- *
- * VendorSpecificContentManager class
- */
-class VendorSpecificContentManager
-{
-  public:
-    VendorSpecificContentManager();
-    virtual ~VendorSpecificContentManager();
-    /**
-     * \param oi the specific OrganizationIdentifier when receive management information
-     * by VendorSpecificAction management frame.
-     * \param cb the receive callback when oi related management packets are received
-     */
-    void RegisterVscCallback(OrganizationIdentifier oi, VscCallback cb);
-    /**
-     * \param oi the specific OrganizationIdentifier when receive management information
-     * by VendorSpecificAction management frame.
-     */
-    void DeregisterVscCallback(OrganizationIdentifier& oi);
-    /**
-     * \param oi the specific OrganizationIdentifier when receive management information
-     * by VendorSpecificAction management frame.
-     * \return true if registered
-     */
-    bool IsVscCallbackRegistered(OrganizationIdentifier& oi);
-    /**
-     * \param oi the specific OrganizationIdentifier when receive management information
-     * by VendorSpecificAction management frame.
-     * \return VscCallback
-     */
-    VscCallback FindVscCallback(OrganizationIdentifier& oi);
-
-  private:
-    /// VSC callback typedef
-    typedef std::map<OrganizationIdentifier, VscCallback> VscCallbacks;
-    /// VSC callback iterator typedef
-    typedef std::map<OrganizationIdentifier, VscCallback>::iterator VscCallbacksI;
-
-    VscCallbacks m_callbacks; ///< VSC callbacks
-};
-
-static std::vector<OrganizationIdentifier> OrganizationIdentifiers; ///< the OIs
-} // namespace ns3
-
-#endif /* Vendor_Specific_Action_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/vsa-manager.cc ns-3.39/src/wave/model/vsa-manager.cc
--- ns-3.38/src/wave/model/vsa-manager.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/vsa-manager.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,293 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "vsa-manager.h"
-
-#include "higher-tx-tag.h"
-#include "wave-net-device.h"
-
-#include "ns3/log.h"
-#include "ns3/simulator.h"
-#include "ns3/socket.h"
-#include "ns3/wifi-phy.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("VsaManager");
-
-NS_OBJECT_ENSURE_REGISTERED(VsaManager);
-
-/// OI bytes for use in organization identifier
-const static uint8_t oi_bytes_1609[5] = {0x00, 0x50, 0xC2, 0x4A, 0x40};
-/// OI for IEEE standard 1609
-const static OrganizationIdentifier oi_1609 = OrganizationIdentifier(oi_bytes_1609, 5);
-
-TypeId
-VsaManager::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::VsaManager")
-                            .SetParent<Object>()
-                            .SetGroupName("Wave")
-                            .AddConstructor<VsaManager>();
-    return tid;
-}
-
-VsaManager::VsaManager()
-    : m_device(nullptr)
-{
-    m_vsaReceived = MakeNullCallback<bool, Ptr<const Packet>, const Address&, uint32_t, uint32_t>();
-}
-
-VsaManager::~VsaManager()
-{
-}
-
-void
-VsaManager::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    RemoveAll();
-    m_device = nullptr;
-}
-
-void
-VsaManager::DoInitialize()
-{
-    std::map<uint32_t, Ptr<OcbWifiMac>> macs = m_device->GetMacs();
-    for (std::map<uint32_t, Ptr<OcbWifiMac>>::iterator i = macs.begin(); i != macs.end(); ++i)
-    {
-        i->second->AddReceiveVscCallback(oi_1609, MakeCallback(&VsaManager::ReceiveVsc, this));
-    }
-}
-
-void
-VsaManager::SetWaveNetDevice(Ptr<WaveNetDevice> device)
-{
-    NS_LOG_FUNCTION(this << device);
-    m_device = device;
-}
-
-void
-VsaManager::SendVsa(const VsaInfo& vsaInfo)
-{
-    NS_LOG_FUNCTION(this << &vsaInfo);
-    OrganizationIdentifier oi;
-    if (vsaInfo.oi.IsNull())
-    {
-        // refer to 1609.4-2010 chapter 6.4.1.1
-        uint8_t oibytes[5] = {0x00, 0x50, 0xC2, 0x4A, 0x40};
-        oibytes[4] |= (vsaInfo.managementId & 0x0f);
-        oi = OrganizationIdentifier(oibytes, 5);
-    }
-    else
-    {
-        oi = vsaInfo.oi;
-    }
-
-    // if destination MAC address is the unicast address or  repeat rate is 0,
-    // then only single one VSA frame is to be sent.
-    if (vsaInfo.peer.IsGroup() && (vsaInfo.repeatRate != 0))
-    {
-        VsaWork* vsa = new VsaWork();
-        vsa->sentInterval = vsaInfo.sendInterval;
-        vsa->channelNumber = vsaInfo.channelNumber;
-        vsa->peer = vsaInfo.peer;
-        vsa->repeatPeriod = MilliSeconds(VSA_REPEAT_PERIOD * 1000 / vsaInfo.repeatRate);
-        vsa->vsc = vsaInfo.vsc;
-        vsa->oi = oi;
-        vsa->repeat = Simulator::Schedule(vsa->repeatPeriod, &VsaManager::DoRepeat, this, vsa);
-        m_vsas.push_back(vsa);
-    }
-    DoSendVsa(vsaInfo.sendInterval, vsaInfo.channelNumber, vsaInfo.vsc->Copy(), oi, vsaInfo.peer);
-}
-
-void
-VsaManager::DoRepeat(VsaWork* vsa)
-{
-    NS_LOG_FUNCTION(this << vsa);
-    vsa->repeat = Simulator::Schedule(vsa->repeatPeriod, &VsaManager::DoRepeat, this, vsa);
-    DoSendVsa(vsa->sentInterval, vsa->channelNumber, vsa->vsc->Copy(), vsa->oi, vsa->peer);
-}
-
-void
-VsaManager::DoSendVsa(VsaTransmitInterval interval,
-                      uint32_t channel,
-                      Ptr<Packet> vsc,
-                      OrganizationIdentifier oi,
-                      Mac48Address peer)
-{
-    NS_LOG_FUNCTION(this << interval << channel << vsc << oi << peer);
-    NS_ASSERT(m_device);
-    Ptr<ChannelCoordinator> coordinator = m_device->GetChannelCoordinator();
-    Ptr<ChannelScheduler> scheduler = m_device->GetChannelScheduler();
-    Ptr<ChannelManager> manager = m_device->GetChannelManager();
-
-    // if the request is for transmitting in SCH Interval (or CCH Interval), but currently
-    // is not in SCH Interval (or CCH Interval) and , then the WAVE device  will wait
-    // some time to insert this VSA frame into MAC internal queue.
-    // if the request is for transmitting in any channel interval, then the WAVE device
-    // insert this VSA frame into MAC internal queue immediately.
-    if (interval == VSA_TRANSMIT_IN_SCHI)
-    {
-        Time wait = coordinator->NeedTimeToSchInterval();
-        if (wait != Seconds(0))
-        {
-            Simulator::Schedule(wait,
-                                &VsaManager::DoSendVsa,
-                                this,
-                                interval,
-                                channel,
-                                vsc,
-                                oi,
-                                peer);
-            return;
-        }
-    }
-    else if (interval == VSA_TRANSMIT_IN_CCHI)
-    {
-        Time wait = coordinator->NeedTimeToCchInterval();
-        if (wait != Seconds(0))
-        {
-            Simulator::Schedule(wait,
-                                &VsaManager::DoSendVsa,
-                                this,
-                                interval,
-                                channel,
-                                vsc,
-                                oi,
-                                peer);
-            return;
-        }
-    }
-    else
-    {
-        NS_ASSERT(interval == VSA_TRANSMIT_IN_BOTHI);
-        // do nothing here, since VSA_IN_BOTHI allows to sent VSA frames in any interval.
-    }
-
-    if (!scheduler->IsChannelAccessAssigned(channel))
-    {
-        NS_LOG_DEBUG("there is no channel access assigned for channel " << channel);
-        return;
-    }
-
-    // refer to 1609.4-2010 chapter 5.4.1
-    // Management frames are assigned the highest AC (AC_VO).
-    SocketPriorityTag priorityTag;
-    priorityTag.SetPriority(7);
-    vsc->AddPacketTag(priorityTag);
-
-    WifiTxVector txVector;
-    txVector.SetChannelWidth(10);
-    txVector.SetTxPowerLevel(manager->GetManagementPowerLevel(channel));
-    txVector.SetMode(manager->GetManagementDataRate(channel));
-    txVector.SetPreambleType(manager->GetManagementPreamble(channel));
-    HigherLayerTxVectorTag tag =
-        HigherLayerTxVectorTag(txVector, manager->GetManagementAdaptable(channel));
-    vsc->AddPacketTag(tag);
-
-    Ptr<OcbWifiMac> mac = m_device->GetMac(channel);
-    mac->SendVsc(vsc, peer, oi);
-}
-
-void
-VsaManager::RemoveAll()
-{
-    NS_LOG_FUNCTION(this);
-    for (std::vector<VsaWork*>::iterator i = m_vsas.begin(); i != m_vsas.end(); ++i)
-    {
-        if (!(*i)->repeat.IsExpired())
-        {
-            (*i)->repeat.Cancel();
-        }
-        (*i)->vsc = nullptr;
-        delete (*i);
-    }
-    m_vsas.clear();
-}
-
-void
-VsaManager::RemoveByChannel(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    for (std::vector<VsaWork*>::iterator i = m_vsas.begin(); i != m_vsas.end();)
-    {
-        if ((*i)->channelNumber == channelNumber)
-        {
-            if (!(*i)->repeat.IsExpired())
-            {
-                (*i)->repeat.Cancel();
-            }
-            (*i)->vsc = nullptr;
-            delete (*i);
-            i = m_vsas.erase(i);
-        }
-        else
-        {
-            ++i;
-        }
-    }
-}
-
-void
-VsaManager::RemoveByOrganizationIdentifier(const OrganizationIdentifier& oi)
-{
-    NS_LOG_FUNCTION(this << oi);
-    for (std::vector<VsaWork*>::iterator i = m_vsas.begin(); i != m_vsas.end();)
-    {
-        if ((*i)->oi == oi)
-        {
-            if (!(*i)->repeat.IsExpired())
-            {
-                (*i)->repeat.Cancel();
-            }
-            (*i)->vsc = nullptr;
-            delete (*i);
-            i = m_vsas.erase(i);
-        }
-        else
-        {
-            ++i;
-        }
-    }
-}
-
-void
-VsaManager::SetWaveVsaCallback(
-    Callback<bool, Ptr<const Packet>, const Address&, uint32_t, uint32_t> vsaCallback)
-{
-    NS_LOG_FUNCTION(this);
-    m_vsaReceived = vsaCallback;
-}
-
-bool
-VsaManager::ReceiveVsc(Ptr<WifiMac> mac,
-                       const OrganizationIdentifier& oi,
-                       Ptr<const Packet> vsc,
-                       const Address& src)
-{
-    NS_LOG_FUNCTION(this << mac << oi << vsc << src);
-    NS_ASSERT(oi == oi_1609);
-    if (m_vsaReceived.IsNull())
-    {
-        return true;
-    }
-    uint32_t channelNumber = mac->GetWifiPhy()->GetChannelNumber();
-    uint32_t managementId = oi.GetManagementId();
-    return m_vsaReceived(vsc, src, managementId, channelNumber);
-}
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/vsa-manager.h ns-3.39/src/wave/model/vsa-manager.h
--- ns-3.38/src/wave/model/vsa-manager.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/vsa-manager.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,221 +0,0 @@
-/*
- * Copyright (c) 2013 Dalian University of Technology
- * 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: Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef VSA_MANAGER_H
-#define VSA_MANAGER_H
-#include "wave-net-device.h"
-
-#include <vector>
-
-namespace ns3
-{
-
-/**
- * \ingroup wave
- *
- * \brief indicate which interval the VSA frames will be transmitted in.
- * VSA_TRANSMIT_IN_CCHI will only allow in CCH Interval;
- * VSA_TRANSMIT_IN_SCHI will only allow in SCH Interval;
- * VSA_TRANSMIT_IN_BOTHI will allow anytime.
- */
-enum VsaTransmitInterval
-{
-    VSA_TRANSMIT_IN_CCHI = 1,
-    VSA_TRANSMIT_IN_SCHI = 2,
-    VSA_TRANSMIT_IN_BOTHI = 3,
-};
-
-/**
- * \ingroup wave
- *
- * \param peer The address of the peer MAC entity to which the
- * VSA is sent.
- * \param oi Identifies the source of the data when the source
- * is not an IEEE 1609 entity. See IEEE Std 802.11p.
- * \param managementId Identifies the source of the data when the source
- * is an IEEE 1609 entity. Values are specified in IEEE P1609.0.
- * Valid range: 0-15
- * \param vsc pointer to Information that will be sent as vendor specific content.
- * \param channelNumber The channel on which the transmissions are to occur.
- * While section 7.2 of the standard specifies that channel identification
- * comprises Country String, Operating Class, and Channel Number, the channel
- * number is enough for simulation.
- * \param repeatRate The number of Vendor Specific Action frames to
- * be transmitted per 5 s. A value of 0 indicates a single message is to be sent.
- * If Destination MAC Address is an individual address, Repeat Rate is ignored.
- * \param sendInterval The channel interval in which the transmissions
- * are to occur.
- */
-struct VsaInfo
-{
-    Mac48Address peer;                ///< peer
-    OrganizationIdentifier oi;        ///< OI
-    uint8_t managementId;             ///< management ID
-    Ptr<Packet> vsc;                  ///< VSC
-    uint32_t channelNumber;           ///< channel number
-    uint8_t repeatRate;               ///< repeat rate
-    VsaTransmitInterval sendInterval; ///< send interval
-
-    /**
-     * Initializer
-     * \param peer the peer MAC address
-     * \param identifier the organization identifier
-     * \param manageId the manage ID
-     * \param vscPacket the VSC packet
-     * \param channel the channel
-     * \param repeat the repeat value
-     * \param interval the transmit interval
-     */
-    VsaInfo(Mac48Address peer,
-            OrganizationIdentifier identifier,
-            uint8_t manageId,
-            Ptr<Packet> vscPacket,
-            uint32_t channel,
-            uint8_t repeat,
-            VsaTransmitInterval interval)
-        : peer(peer),
-          oi(identifier),
-          managementId(manageId),
-          vsc(vscPacket),
-          channelNumber(channel),
-          repeatRate(repeat),
-          sendInterval(interval)
-    {
-    }
-};
-
-/**
- * \ingroup wave
- *
- * refer to 1609.4-2010 chapter 6.4
- * Vendor Specific Action (VSA) frames transmission.
- *
- * The channel interval and channel number indicating how to transmit VSA frames.
- * However making the VSA transmitted strictly in required channel interval and channel number
- * is hard to achieve. For example, if current channel is assigned â€œAlternating Accessâ€� and higher
- * layer wants VSA transmitted only in CCHI (VSA_TRANSMIT_IN_CCHI), but when packet is dequeued from
- * the internal queue of MAC layer in SCHI and can contend for channel access, how to deal with
- * this case? Reinserting into the head of the queue and dequeuing the second packet is not a good
- * choice, because it will require queue traversal. Reinserting the packet into the tail of the
- * queue is worse, because it will make queue not in order. And both solutions may affect many MAC
- * classes of Wifi module. Current approach is to guarantee packets can be inserted into the  MAC
- * internal queue strictly in channel interval and channel number required by higher layers. This
- * solution will result in high probability that packets will be sent in channel interval as higher
- * layer wants, while some packet may be sent practically in other channel interval that not follows
- * the requirement of higher layer request due to queue delay of MAC layer. Therefore, we suggest
- * users assign alternating access for sending VSAs in CCH Interval (VSA_TRANSMIT_IN_CCHI) or SCH
- * Interval (VSA_TRANSMIT_IN_SCHI), and assign continuous access or extended access for sending VSAs
- * in both interval (VSA_TRANSMIT_IN_BOTHI) to avoid the above cases.
- */
-class VsaManager : public Object
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    VsaManager();
-    ~VsaManager() override;
-
-    /**
-     * \param device WaveNetDevice associated with VsaManager
-     */
-    void SetWaveNetDevice(Ptr<WaveNetDevice> device);
-
-    /**
-     * Set wave vsa callback function
-     * \param vsaCallback the callback
-     */
-    void SetWaveVsaCallback(
-        Callback<bool, Ptr<const Packet>, const Address&, uint32_t, uint32_t> vsaCallback);
-
-    /**
-     * \param vsaInfo the tx information for VSA transmissions
-     */
-    void SendVsa(const VsaInfo& vsaInfo);
-    /**
-     * cancel all VSA transmissions
-     */
-    void RemoveAll();
-    /**
-     * \param channelNumber cancel VSA transmission specified by channel number
-     */
-    void RemoveByChannel(uint32_t channelNumber);
-    /**
-     * \param oi cancel VSA transmission specified by organization identifier
-     */
-    void RemoveByOrganizationIdentifier(const OrganizationIdentifier& oi);
-
-  private:
-    void DoDispose() override;
-    void DoInitialize() override;
-
-    /**
-     * \param mac the MAC entity which receives VSA frame
-     * \param oi the Organization Identifier of received VSA frame
-     * \param vsc the vendor specific content of received VSA frame
-     * \param src the source address of received VSA frame
-     * \return true if successful
-     */
-    bool ReceiveVsc(Ptr<WifiMac> mac,
-                    const OrganizationIdentifier& oi,
-                    Ptr<const Packet> vsc,
-                    const Address& src);
-
-    /// A number of VSA frames will be transmitted repeatedly during the period of 5s.
-    const static uint32_t VSA_REPEAT_PERIOD = 5;
-
-    /// VsaWork structure
-    struct VsaWork
-    {
-        Mac48Address peer;                ///< peer
-        OrganizationIdentifier oi;        ///< OI
-        Ptr<Packet> vsc;                  ///< VSC
-        uint32_t channelNumber;           ///< channel number
-        VsaTransmitInterval sentInterval; ///< VSA transmit interval
-        Time repeatPeriod;                ///< repeat period
-        EventId repeat;                   ///< repeat ID
-    };
-
-    /**
-     * \param vsa the specific VSA repeat work
-     *
-     * Repeat to send VSA frames
-     */
-    void DoRepeat(VsaWork* vsa);
-    /**
-     * \param interval the specific channel interval for VSA transmission
-     * \param channel the specific channel number for VSA transmission
-     * \param vsc the data field of VSA frame that contains vendor specific content
-     * \param oi the Organization Identifier for VSA frame
-     * \param peer the destination address
-     */
-    void DoSendVsa(VsaTransmitInterval interval,
-                   uint32_t channel,
-                   Ptr<Packet> vsc,
-                   OrganizationIdentifier oi,
-                   Mac48Address peer);
-
-    Callback<bool, Ptr<const Packet>, const Address&, uint32_t, uint32_t>
-        m_vsaReceived;            ///< VSA received callback
-    std::vector<VsaWork*> m_vsas; ///< VSAs
-    Ptr<WaveNetDevice> m_device;  ///< the device
-};
-
-} // namespace ns3
-#endif /* VSA_MANAGER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/wave-frame-exchange-manager.cc ns-3.39/src/wave/model/wave-frame-exchange-manager.cc
--- ns-3.38/src/wave/model/wave-frame-exchange-manager.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/wave-frame-exchange-manager.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,180 +0,0 @@
-/*
- * Copyright (c) 2020 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 "wave-frame-exchange-manager.h"
-
-#include "higher-tx-tag.h"
-
-#include "ns3/abort.h"
-#include "ns3/log.h"
-#include "ns3/qos-blocked-destinations.h"
-#include "ns3/wifi-acknowledgment.h"
-#include "ns3/wifi-protection.h"
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("WaveFrameExchangeManager");
-
-NS_OBJECT_ENSURE_REGISTERED(WaveFrameExchangeManager);
-
-TypeId
-WaveFrameExchangeManager::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::WaveFrameExchangeManager")
-                            .SetParent<QosFrameExchangeManager>()
-                            .AddConstructor<WaveFrameExchangeManager>()
-                            .SetGroupName("Wave");
-    return tid;
-}
-
-WaveFrameExchangeManager::WaveFrameExchangeManager()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-WaveFrameExchangeManager::~WaveFrameExchangeManager()
-{
-    NS_LOG_FUNCTION_NOARGS();
-}
-
-void
-WaveFrameExchangeManager::SetWaveNetDevice(Ptr<WaveNetDevice> device)
-{
-    m_scheduler = device->GetChannelScheduler();
-    m_coordinator = device->GetChannelCoordinator();
-    NS_ASSERT(m_scheduler && m_coordinator);
-}
-
-WifiTxVector
-WaveFrameExchangeManager::GetDataTxVector(Ptr<const WifiMpdu> item) const
-{
-    NS_LOG_FUNCTION(this << *item);
-    HigherLayerTxVectorTag datatag;
-    bool found;
-    found = ConstCast<Packet>(item->GetPacket())->PeekPacketTag(datatag);
-    // if high layer has not controlled transmit parameters, the real transmit parameters
-    // will be determined by MAC layer itself.
-    if (!found)
-    {
-        return m_mac->GetWifiRemoteStationManager()->GetDataTxVector(item->GetHeader(),
-                                                                     m_allowedWidth);
-    }
-
-    // if high layer has set the transmit parameters with non-adaption mode,
-    // the real transmit parameters are determined by high layer.
-    if (!datatag.IsAdaptable())
-    {
-        return datatag.GetTxVector();
-    }
-
-    // if high layer has set the transmit parameters with non-adaption mode,
-    // the real transmit parameters are determined by both high layer and MAC layer.
-    WifiTxVector txHigher = datatag.GetTxVector();
-    WifiTxVector txMac =
-        m_mac->GetWifiRemoteStationManager()->GetDataTxVector(item->GetHeader(), m_allowedWidth);
-    WifiTxVector txAdapter;
-    txAdapter.SetChannelWidth(10);
-    // the DataRate set by higher layer is the minimum data rate
-    // which is the lower bound for the actual data rate.
-    if (txHigher.GetMode().GetDataRate(txHigher.GetChannelWidth()) >
-        txMac.GetMode().GetDataRate(txMac.GetChannelWidth()))
-    {
-        txAdapter.SetMode(txHigher.GetMode());
-        txAdapter.SetPreambleType(txHigher.GetPreambleType());
-    }
-    else
-    {
-        txAdapter.SetMode(txMac.GetMode());
-        txAdapter.SetPreambleType(txMac.GetPreambleType());
-    }
-    // the TxPwr_Level set by higher layer is the maximum transmit
-    // power which is the upper bound for the actual transmit power;
-    txAdapter.SetTxPowerLevel(std::min(txHigher.GetTxPowerLevel(), txMac.GetTxPowerLevel()));
-
-    return txAdapter;
-}
-
-bool
-WaveFrameExchangeManager::StartTransmission(Ptr<Txop> dcf, uint16_t allowedWidth)
-{
-    NS_LOG_FUNCTION(this << dcf << allowedWidth);
-
-    uint32_t curChannel = m_phy->GetChannelNumber();
-    // if current channel access is not AlternatingAccess, just do as FrameExchangeManager.
-    if (!m_scheduler || !m_scheduler->IsAlternatingAccessAssigned(curChannel))
-    {
-        return FrameExchangeManager::StartTransmission(dcf, allowedWidth);
-    }
-
-    m_txTimer.Cancel();
-    m_dcf = dcf;
-    m_allowedWidth = allowedWidth;
-
-    Ptr<WifiMacQueue> queue = dcf->GetWifiMacQueue();
-
-    if (queue->IsEmpty())
-    {
-        NS_LOG_DEBUG("Queue empty");
-        m_dcf->NotifyChannelReleased(0);
-        m_dcf = nullptr;
-        return false;
-    }
-
-    m_dcf->NotifyChannelAccessed(0);
-    Ptr<WifiMpdu> mpdu = queue->PeekFirstAvailable(0);
-    NS_ASSERT(mpdu);
-
-    // assign a sequence number if this is not a fragment nor a retransmission
-    if (!mpdu->IsFragment() && !mpdu->GetHeader().IsRetry())
-    {
-        uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
-        mpdu->GetHeader().SetSequenceNumber(sequence);
-    }
-
-    WifiTxParameters txParams;
-    txParams.m_txVector = GetDataTxVector(mpdu);
-    Time remainingTime = m_coordinator->NeedTimeToGuardInterval();
-
-    if (!TryAddMpdu(mpdu, txParams, remainingTime))
-    {
-        // The attempt for this transmission will be canceled;
-        // and this packet will be pending for next transmission by QosTxop class
-        NS_LOG_DEBUG("Because the required transmission time exceeds the remainingTime = "
-                     << remainingTime.As(Time::MS)
-                     << ", currently this packet will not be transmitted.");
-    }
-    else
-    {
-        SendMpduWithProtection(mpdu, txParams);
-        return true;
-    }
-    return false;
-}
-
-void
-WaveFrameExchangeManager::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    m_scheduler = nullptr;
-    m_coordinator = nullptr;
-    FrameExchangeManager::DoDispose();
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/wave-frame-exchange-manager.h ns-3.39/src/wave/model/wave-frame-exchange-manager.h
--- ns-3.38/src/wave/model/wave-frame-exchange-manager.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/wave-frame-exchange-manager.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2020 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: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
- *         Junling Bu <linlinjavaer@gmail.com>
- *         Stefano Avallone <stavallo@unina.it>
- */
-
-#ifndef WAVE_FRAME_EXCHANGE_MANAGER_H
-#define WAVE_FRAME_EXCHANGE_MANAGER_H
-
-#include "wave-net-device.h"
-
-#include "ns3/qos-frame-exchange-manager.h"
-
-namespace ns3
-{
-
-/**
- * \ingroup wave
- *
- * This class is the subclass of QosFrameExchangeManager to provide support for MAC extension
- * (1) allows higher layer control data rate and tx power level.
- *     If higher layer does not set, they will be determined by WifiRemoteStationManager
- *          of MAC layer;
- *     If higher layer sets tx parameters in non-adaptable mode, the data rate
- *        and tx power level will be used for transmission;.
- *    If higher layer sets tx parameters in adaptable mode, the data rate
- *        will be lower bound for the actual data rate, and the power level
- *        will be upper bound for the actual transmit power.
- * (2) implements the feature described in Annex C : avoid transmission at scheduled guard intervals
- *      However, the feature is extended further here that the required transmit time is determined
- *      by MAC layer itself rather than PHY layer, which contains RTS/CTS, DATA and ACK time.
- */
-class WaveFrameExchangeManager : public QosFrameExchangeManager
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    WaveFrameExchangeManager();
-    ~WaveFrameExchangeManager() override;
-
-    // Overridden from FrameExchangeManager
-    bool StartTransmission(Ptr<Txop> dcf, uint16_t allowedWidth) override;
-
-    /**
-     * \param device WaveNetDevice associated with WaveFrameExchangeManager
-     */
-    void SetWaveNetDevice(Ptr<WaveNetDevice> device);
-
-  protected:
-    // Overridden from FrameExchangeManager
-    void DoDispose() override;
-
-  private:
-    /**
-     * Return a TXVECTOR for the DATA frame given the destination.
-     * The function consults WifiRemoteStationManager, which controls the rate
-     * to different destinations.
-     *
-     * \param item the item being asked for TXVECTOR
-     * \return TXVECTOR for the given item
-     */
-    virtual WifiTxVector GetDataTxVector(Ptr<const WifiMpdu> item) const;
-
-    Ptr<ChannelScheduler> m_scheduler;     ///< the channel scheduler
-    Ptr<ChannelCoordinator> m_coordinator; ///< the channel coordinator
-};
-
-} // namespace ns3
-
-#endif /* VHT_FRAME_EXCHANGE_MANAGER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/wave-net-device.cc ns-3.39/src/wave/model/wave-net-device.cc
--- ns-3.38/src/wave/model/wave-net-device.cc	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/wave-net-device.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,782 +0,0 @@
-/*
- * Copyright (c) 2005,2006 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>
- *         Junling Bu <linlinjavaer@gmail.com>
- */
-#include "wave-net-device.h"
-
-#include "higher-tx-tag.h"
-
-#include "ns3/channel.h"
-#include "ns3/llc-snap-header.h"
-#include "ns3/log.h"
-#include "ns3/node.h"
-#include "ns3/object-map.h"
-#include "ns3/object-vector.h"
-#include "ns3/socket.h"
-#include "ns3/wifi-phy.h"
-
-#include <algorithm>
-
-namespace ns3
-{
-
-NS_LOG_COMPONENT_DEFINE("WaveNetDevice");
-
-NS_OBJECT_ENSURE_REGISTERED(WaveNetDevice);
-
-TypeId
-WaveNetDevice::GetTypeId()
-{
-    static TypeId tid =
-        TypeId("ns3::WaveNetDevice")
-            .SetParent<NetDevice>()
-            .SetGroupName("Wave")
-            .AddConstructor<WaveNetDevice>()
-            .AddAttribute("Mtu",
-                          "The MAC-level Maximum Transmission Unit",
-                          UintegerValue(MAX_MSDU_SIZE - LLC_SNAP_HEADER_LENGTH),
-                          MakeUintegerAccessor(&WaveNetDevice::SetMtu, &WaveNetDevice::GetMtu),
-                          MakeUintegerChecker<uint16_t>(1, MAX_MSDU_SIZE - LLC_SNAP_HEADER_LENGTH))
-            .AddAttribute("Channel",
-                          "The channel attached to this device",
-                          PointerValue(),
-                          MakePointerAccessor(&WaveNetDevice::GetChannel),
-                          MakePointerChecker<Channel>())
-            .AddAttribute("PhyEntities",
-                          "The PHY entities attached to this device.",
-                          ObjectVectorValue(),
-                          MakeObjectVectorAccessor(&WaveNetDevice::m_phyEntities),
-                          MakeObjectVectorChecker<WifiPhy>())
-            .AddAttribute("MacEntities",
-                          "The MAC layer attached to this device.",
-                          ObjectMapValue(),
-                          MakeObjectMapAccessor(&WaveNetDevice::m_macEntities),
-                          MakeObjectMapChecker<OcbWifiMac>())
-            .AddAttribute("ChannelScheduler",
-                          "The channel scheduler attached to this device.",
-                          PointerValue(),
-                          MakePointerAccessor(&WaveNetDevice::SetChannelScheduler,
-                                              &WaveNetDevice::GetChannelScheduler),
-                          MakePointerChecker<ChannelScheduler>())
-            .AddAttribute("ChannelManager",
-                          "The channel manager attached to this device.",
-                          PointerValue(),
-                          MakePointerAccessor(&WaveNetDevice::SetChannelManager,
-                                              &WaveNetDevice::GetChannelManager),
-                          MakePointerChecker<ChannelManager>())
-            .AddAttribute("ChannelCoordinator",
-                          "The channel coordinator attached to this device.",
-                          PointerValue(),
-                          MakePointerAccessor(&WaveNetDevice::SetChannelCoordinator,
-                                              &WaveNetDevice::GetChannelCoordinator),
-                          MakePointerChecker<ChannelCoordinator>())
-            .AddAttribute(
-                "VsaManager",
-                "The VSA manager attached to this device.",
-                PointerValue(),
-                MakePointerAccessor(&WaveNetDevice::SetVsaManager, &WaveNetDevice::GetVsaManager),
-                MakePointerChecker<VsaManager>());
-    return tid;
-}
-
-WaveNetDevice::WaveNetDevice()
-    : m_txProfile(nullptr)
-{
-    NS_LOG_FUNCTION(this);
-}
-
-WaveNetDevice::~WaveNetDevice()
-{
-    NS_LOG_FUNCTION(this);
-}
-
-void
-WaveNetDevice::DoDispose()
-{
-    NS_LOG_FUNCTION(this);
-    if (m_txProfile != nullptr)
-    {
-        delete m_txProfile;
-        m_txProfile = nullptr;
-    }
-    for (PhyEntitiesI i = m_phyEntities.begin(); i != m_phyEntities.end(); ++i)
-    {
-        Ptr<WifiPhy> phy = (*i);
-        phy->Dispose();
-    }
-    m_phyEntities.clear();
-    for (MacEntitiesI i = m_macEntities.begin(); i != m_macEntities.end(); ++i)
-    {
-        Ptr<OcbWifiMac> mac = i->second;
-        Ptr<WifiRemoteStationManager> stationManager = mac->GetWifiRemoteStationManager();
-        stationManager->Dispose();
-        mac->Dispose();
-    }
-    m_macEntities.clear();
-    m_phyEntities.clear();
-    m_channelCoordinator->Dispose();
-    m_channelManager->Dispose();
-    m_channelScheduler->Dispose();
-    m_vsaManager->Dispose();
-    m_channelCoordinator = nullptr;
-    m_channelManager = nullptr;
-    m_channelScheduler = nullptr;
-    m_vsaManager = nullptr;
-    // chain up.
-    WifiNetDevice::DoDispose();
-}
-
-void
-WaveNetDevice::DoInitialize()
-{
-    NS_LOG_FUNCTION(this);
-    if (m_phyEntities.empty())
-    {
-        NS_FATAL_ERROR("there is no PHY entity in this WAVE device");
-    }
-    for (PhyEntitiesI i = m_phyEntities.begin(); i != m_phyEntities.end(); ++i)
-    {
-        Ptr<WifiPhy> phy = (*i);
-        phy->Initialize();
-    }
-    if (m_macEntities.empty())
-    {
-        NS_FATAL_ERROR("there is no MAC entity in this WAVE device");
-    }
-    for (MacEntitiesI i = m_macEntities.begin(); i != m_macEntities.end(); ++i)
-    {
-        Ptr<OcbWifiMac> mac = i->second;
-        mac->SetForwardUpCallback(MakeCallback(&WaveNetDevice::ForwardUp, this));
-        // Make each MAC entity in sleep mode.
-        mac->Suspend();
-        mac->Initialize();
-
-        Ptr<WifiRemoteStationManager> stationManager = mac->GetWifiRemoteStationManager();
-        // Currently PHY is not attached to MAC and will be dynamically attached and unattached to
-        // MAC latter, however WifiRemoteStationManager in the MAC shall know something  in the PHY
-        // such as supported data rates. Since these information can be treated as same when same
-        // PHY devices are added, here we force all of WifiRemoteStationManagers in multiple MAC
-        // entities only associate with single PHY device even there may be multiple PHY entities.
-        // This approach may be strange but can work fine.
-        stationManager->SetupPhy(m_phyEntities[0]);
-        stationManager->Initialize();
-    }
-    m_channelScheduler->SetWaveNetDevice(this);
-    m_vsaManager->SetWaveNetDevice(this);
-    m_channelScheduler->Initialize();
-    m_channelCoordinator->Initialize();
-    m_channelManager->Initialize();
-    m_vsaManager->Initialize();
-    NetDevice::DoInitialize();
-}
-
-void
-WaveNetDevice::AddMac(uint32_t channelNumber, Ptr<OcbWifiMac> mac)
-{
-    NS_LOG_FUNCTION(this << channelNumber << mac);
-    if (!ChannelManager::IsWaveChannel(channelNumber))
-    {
-        NS_FATAL_ERROR("The channel " << channelNumber << " is not a valid WAVE channel number");
-    }
-    if (m_macEntities.find(channelNumber) != m_macEntities.end())
-    {
-        NS_FATAL_ERROR("The MAC entity for channel " << channelNumber << " already exists.");
-    }
-    m_macEntities.insert(std::make_pair(channelNumber, mac));
-}
-
-Ptr<OcbWifiMac>
-WaveNetDevice::GetMac(uint32_t channelNumber) const
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    MacEntitiesI i = m_macEntities.find(channelNumber);
-    if (i == m_macEntities.end())
-    {
-        NS_FATAL_ERROR("there is no available MAC entity for channel " << channelNumber);
-    }
-    return i->second;
-}
-
-std::map<uint32_t, Ptr<OcbWifiMac>>
-WaveNetDevice::GetMacs() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_macEntities;
-}
-
-void
-WaveNetDevice::AddPhy(Ptr<WifiPhy> phy)
-{
-    NS_LOG_FUNCTION(this << phy);
-    if (std::find(m_phyEntities.begin(), m_phyEntities.end(), phy) != m_phyEntities.end())
-    {
-        NS_FATAL_ERROR("This PHY entity is already inserted");
-    }
-    m_phyEntities.push_back(phy);
-}
-
-Ptr<WifiPhy>
-WaveNetDevice::GetPhy(uint8_t index) const
-{
-    NS_LOG_FUNCTION(this << +index);
-    return m_phyEntities.at(index);
-}
-
-const std::vector<Ptr<WifiPhy>>&
-WaveNetDevice::GetPhys() const
-{
-    NS_LOG_FUNCTION(this);
-    return m_phyEntities;
-}
-
-bool
-WaveNetDevice::StartVsa(const VsaInfo& vsaInfo)
-{
-    NS_LOG_FUNCTION(this << &vsaInfo);
-    if (!IsAvailableChannel(vsaInfo.channelNumber))
-    {
-        return false;
-    }
-    if (!m_channelScheduler->IsChannelAccessAssigned(vsaInfo.channelNumber))
-    {
-        NS_LOG_DEBUG("there is no channel access assigned for channel " << vsaInfo.channelNumber);
-        return false;
-    }
-    if (!vsaInfo.vsc)
-    {
-        NS_LOG_DEBUG("vendor specific information shall not be null");
-        return false;
-    }
-    if (vsaInfo.oi.IsNull() && vsaInfo.managementId >= 16)
-    {
-        NS_LOG_DEBUG("when organization identifier is not set, management ID "
-                     "shall be in range from 0 to 15");
-        return false;
-    }
-
-    m_vsaManager->SendVsa(vsaInfo);
-    return true;
-}
-
-bool
-WaveNetDevice::StopVsa(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    if (!IsAvailableChannel(channelNumber))
-    {
-        return false;
-    }
-    m_vsaManager->RemoveByChannel(channelNumber);
-    return true;
-}
-
-void
-WaveNetDevice::SetWaveVsaCallback(WaveVsaCallback vsaCallback)
-{
-    NS_LOG_FUNCTION(this);
-    m_vsaManager->SetWaveVsaCallback(vsaCallback);
-}
-
-bool
-WaveNetDevice::StartSch(const SchInfo& schInfo)
-{
-    NS_LOG_FUNCTION(this << &schInfo);
-    if (!IsAvailableChannel(schInfo.channelNumber))
-    {
-        return false;
-    }
-    return m_channelScheduler->StartSch(schInfo);
-}
-
-bool
-WaveNetDevice::StopSch(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    if (!IsAvailableChannel(channelNumber))
-    {
-        return false;
-    }
-    return m_channelScheduler->StopSch(channelNumber);
-}
-
-bool
-WaveNetDevice::RegisterTxProfile(const TxProfile& txprofile)
-{
-    NS_LOG_FUNCTION(this << &txprofile);
-    if (m_txProfile != nullptr)
-    {
-        return false;
-    }
-    if (!IsAvailableChannel(txprofile.channelNumber))
-    {
-        return false;
-    }
-    if (txprofile.txPowerLevel > 8)
-    {
-        return false;
-    }
-    // IP-based packets is not allowed to send in the CCH.
-    if (txprofile.channelNumber == CCH)
-    {
-        NS_LOG_DEBUG("IP-based packets shall not be transmitted on the CCH");
-        return false;
-    }
-    if (txprofile.dataRate == WifiMode() || txprofile.txPowerLevel == 8)
-    {
-        // let MAC layer itself determine tx parameters.
-        NS_LOG_DEBUG("High layer does not want to control tx parameters.");
-    }
-    else
-    {
-        // if current PHY devices do not support data rate of the tx profile
-        for (PhyEntitiesI i = m_phyEntities.begin(); i != m_phyEntities.end(); ++i)
-        {
-            if (!((*i)->IsModeSupported(txprofile.dataRate)))
-            {
-                NS_LOG_DEBUG("This data rate " << txprofile.dataRate.GetUniqueName()
-                                               << " is not supported by current PHY device");
-                return false;
-            }
-        }
-    }
-
-    m_txProfile = new TxProfile();
-    *m_txProfile = txprofile;
-    return true;
-}
-
-bool
-WaveNetDevice::DeleteTxProfile(uint32_t channelNumber)
-{
-    NS_LOG_FUNCTION(this << channelNumber);
-    if (!IsAvailableChannel(channelNumber))
-    {
-        return false;
-    }
-    if (m_txProfile == nullptr)
-    {
-        return false;
-    }
-    if (m_txProfile->channelNumber != channelNumber)
-    {
-        return false;
-    }
-
-    delete m_txProfile;
-    m_txProfile = nullptr;
-    return true;
-}
-
-bool
-WaveNetDevice::SendX(Ptr<Packet> packet,
-                     const Address& dest,
-                     uint32_t protocol,
-                     const TxInfo& txInfo)
-{
-    NS_LOG_FUNCTION(this << packet << dest << protocol << &txInfo);
-    if (!IsAvailableChannel(txInfo.channelNumber))
-    {
-        return false;
-    }
-    if (!m_channelScheduler->IsChannelAccessAssigned(txInfo.channelNumber))
-    {
-        NS_LOG_DEBUG("there is no channel access assigned for channel " << txInfo.channelNumber);
-        return false;
-    }
-    if ((txInfo.channelNumber == CCH) &&
-        (protocol == IPv4_PROT_NUMBER || protocol == IPv6_PROT_NUMBER))
-    {
-        NS_LOG_DEBUG("IP-based packets shall not be transmitted on the CCH");
-        return false;
-    }
-    if ((txInfo.priority > 7) || txInfo.txPowerLevel > 8)
-    {
-        NS_LOG_DEBUG("invalid transmit parameters.");
-        return false;
-    }
-
-    if ((txInfo.dataRate == WifiMode()) || (txInfo.txPowerLevel == 8))
-    {
-        // let MAC layer itself determine tx parameters.
-        NS_LOG_DEBUG("High layer does not want to control tx parameters.");
-    }
-    else
-    {
-        // if current PHY devices do not support data rate of the  tx profile
-        for (PhyEntitiesI i = m_phyEntities.begin(); i != m_phyEntities.end(); ++i)
-        {
-            if (!((*i)->IsModeSupported(txInfo.dataRate)))
-            {
-                return false;
-            }
-        }
-        WifiTxVector txVector;
-        txVector.SetChannelWidth(10);
-        txVector.SetTxPowerLevel(txInfo.txPowerLevel);
-        txVector.SetMode(txInfo.dataRate);
-        txVector.SetPreambleType(txInfo.preamble);
-        HigherLayerTxVectorTag tag = HigherLayerTxVectorTag(txVector, false);
-        packet->AddPacketTag(tag);
-    }
-
-    LlcSnapHeader llc;
-    llc.SetType(protocol);
-    packet->AddHeader(llc);
-
-    // according to channel number and priority,
-    // route the packet to a proper queue.
-    SocketPriorityTag prio;
-    prio.SetPriority(txInfo.priority);
-    packet->ReplacePacketTag(prio);
-    Ptr<WifiMac> mac = GetMac(txInfo.channelNumber);
-    Mac48Address realTo = Mac48Address::ConvertFrom(dest);
-    mac->NotifyTx(packet);
-    mac->Enqueue(packet, realTo);
-    return true;
-}
-
-void
-WaveNetDevice::ChangeAddress(Address newAddress)
-{
-    NS_LOG_FUNCTION(this << newAddress);
-    Address oldAddress = GetAddress();
-    if (newAddress == oldAddress)
-    {
-        return;
-    }
-    SetAddress(newAddress);
-    // Since MAC address is changed, the MAC layer including multiple MAC entities should be reset
-    // and internal MAC queues will be flushed.
-    for (MacEntitiesI i = m_macEntities.begin(); i != m_macEntities.end(); ++i)
-    {
-        i->second->Reset();
-    }
-    m_addressChange(oldAddress, newAddress);
-}
-
-void
-WaveNetDevice::CancelTx(uint32_t channelNumber, AcIndex ac)
-{
-    if (IsAvailableChannel(channelNumber))
-    {
-        return;
-    }
-    Ptr<OcbWifiMac> mac = GetMac(channelNumber);
-    mac->CancleTx(ac);
-}
-
-void
-WaveNetDevice::SetChannelManager(Ptr<ChannelManager> channelManager)
-{
-    m_channelManager = channelManager;
-}
-
-Ptr<ChannelManager>
-WaveNetDevice::GetChannelManager() const
-{
-    return m_channelManager;
-}
-
-void
-WaveNetDevice::SetChannelScheduler(Ptr<ChannelScheduler> channelScheduler)
-{
-    m_channelScheduler = channelScheduler;
-}
-
-Ptr<ChannelScheduler>
-WaveNetDevice::GetChannelScheduler() const
-{
-    return m_channelScheduler;
-}
-
-void
-WaveNetDevice::SetChannelCoordinator(Ptr<ChannelCoordinator> channelCoordinator)
-{
-    m_channelCoordinator = channelCoordinator;
-}
-
-Ptr<ChannelCoordinator>
-WaveNetDevice::GetChannelCoordinator() const
-{
-    return m_channelCoordinator;
-}
-
-void
-WaveNetDevice::SetVsaManager(Ptr<VsaManager> vsaManager)
-{
-    m_vsaManager = vsaManager;
-}
-
-Ptr<VsaManager>
-WaveNetDevice::GetVsaManager() const
-{
-    return m_vsaManager;
-}
-
-void
-WaveNetDevice::SetIfIndex(const uint32_t index)
-{
-    m_ifIndex = index;
-}
-
-uint32_t
-WaveNetDevice::GetIfIndex() const
-{
-    return m_ifIndex;
-}
-
-Ptr<Channel>
-WaveNetDevice::GetChannel() const
-{
-    NS_ASSERT(!m_phyEntities.empty());
-    return GetPhy(0)->GetChannel();
-}
-
-void
-WaveNetDevice::SetAddress(Address address)
-{
-    for (MacEntitiesI i = m_macEntities.begin(); i != m_macEntities.end(); ++i)
-    {
-        i->second->SetAddress(Mac48Address::ConvertFrom(address));
-    }
-}
-
-Address
-WaveNetDevice::GetAddress() const
-{
-    return (GetMac(CCH))->GetAddress();
-}
-
-bool
-WaveNetDevice::SetMtu(const uint16_t mtu)
-{
-    if (mtu > MAX_MSDU_SIZE - LLC_SNAP_HEADER_LENGTH)
-    {
-        return false;
-    }
-    m_mtu = mtu;
-    return true;
-}
-
-uint16_t
-WaveNetDevice::GetMtu() const
-{
-    return m_mtu;
-}
-
-bool
-WaveNetDevice::IsLinkUp() const
-{
-    // Different from WifiNetDevice::IsLinkUp, a WaveNetDevice device
-    // is always link up so the m_linkup variable is true forever.
-    // Even the device is in channel switch state, packets can still be queued.
-    return true;
-}
-
-void
-WaveNetDevice::AddLinkChangeCallback(Callback<void> callback)
-{
-    NS_LOG_WARN("WaveNetDevice is linkup forever, so this callback will be never called");
-}
-
-bool
-WaveNetDevice::IsBroadcast() const
-{
-    return true;
-}
-
-Address
-WaveNetDevice::GetBroadcast() const
-{
-    return Mac48Address::GetBroadcast();
-}
-
-bool
-WaveNetDevice::IsMulticast() const
-{
-    return true;
-}
-
-Address
-WaveNetDevice::GetMulticast(Ipv4Address multicastGroup) const
-{
-    return Mac48Address::GetMulticast(multicastGroup);
-}
-
-Address
-WaveNetDevice::GetMulticast(Ipv6Address addr) const
-{
-    return Mac48Address::GetMulticast(addr);
-}
-
-bool
-WaveNetDevice::IsPointToPoint() const
-{
-    return false;
-}
-
-bool
-WaveNetDevice::IsBridge() const
-{
-    return false;
-}
-
-bool
-WaveNetDevice::Send(Ptr<Packet> packet, const Address& dest, uint16_t protocol)
-{
-    NS_LOG_FUNCTION(this << packet << dest << protocol);
-    if (m_txProfile == nullptr)
-    {
-        NS_LOG_DEBUG("there is no tx profile registered for transmission");
-        return false;
-    }
-    if (!m_channelScheduler->IsChannelAccessAssigned(m_txProfile->channelNumber))
-    {
-        NS_LOG_DEBUG("there is no channel access assigned for channel "
-                     << m_txProfile->channelNumber);
-        return false;
-    }
-    if (m_txProfile->dataRate == WifiMode() || m_txProfile->txPowerLevel == 8)
-    {
-        // let MAC layer itself determine tx parameters.
-        NS_LOG_DEBUG("High layer does not want to control tx parameters.");
-    }
-    else
-    {
-        WifiTxVector txVector;
-        txVector.SetTxPowerLevel(m_txProfile->txPowerLevel);
-        txVector.SetMode(m_txProfile->dataRate);
-        txVector.SetPreambleType(m_txProfile->preamble);
-        HigherLayerTxVectorTag tag = HigherLayerTxVectorTag(txVector, m_txProfile->adaptable);
-        packet->AddPacketTag(tag);
-    }
-
-    LlcSnapHeader llc;
-    llc.SetType(protocol);
-    packet->AddHeader(llc);
-
-    // qos tag is already inserted into the packet by high layer  or with default value 7 if high
-    // layer forget it.
-    Ptr<WifiMac> mac = GetMac(m_txProfile->channelNumber);
-    Mac48Address realTo = Mac48Address::ConvertFrom(dest);
-    mac->NotifyTx(packet);
-    mac->Enqueue(packet, realTo);
-    return true;
-}
-
-bool
-WaveNetDevice::NeedsArp() const
-{
-    // Whether NeedsArp or not?
-    // For IP-based packets , yes; For WSMP packets, no;
-    // so return true always.
-    return true;
-}
-
-void
-WaveNetDevice::SetReceiveCallback(NetDevice::ReceiveCallback cb)
-{
-    m_forwardUp = cb;
-}
-
-bool
-WaveNetDevice::IsAvailableChannel(uint32_t channelNumber) const
-{
-    if (!ChannelManager::IsWaveChannel(channelNumber))
-    {
-        NS_LOG_DEBUG("this is no a valid WAVE channel for channel " << channelNumber);
-        return false;
-    }
-    if (m_macEntities.find(channelNumber) == m_macEntities.end())
-    {
-        NS_LOG_DEBUG("this is no available WAVE entity  for channel " << channelNumber);
-        return false;
-    }
-    return true;
-}
-
-void
-WaveNetDevice::ForwardUp(Ptr<const Packet> packet, Mac48Address from, Mac48Address to)
-{
-    NS_LOG_FUNCTION(this << packet << from << to);
-    Ptr<Packet> copy = packet->Copy();
-    LlcSnapHeader llc;
-    copy->RemoveHeader(llc);
-    enum NetDevice::PacketType type;
-    if (to.IsBroadcast())
-    {
-        type = NetDevice::PACKET_BROADCAST;
-    }
-    else if (to.IsGroup())
-    {
-        type = NetDevice::PACKET_MULTICAST;
-    }
-    else if (to == GetAddress())
-    {
-        type = NetDevice::PACKET_HOST;
-    }
-    else
-    {
-        type = NetDevice::PACKET_OTHERHOST;
-    }
-
-    if (type != NetDevice::PACKET_OTHERHOST)
-    {
-        // currently we cannot know from which MAC entity the packet is received,
-        // so we use the MAC entity for CCH as it receives this packet.
-        Ptr<OcbWifiMac> mac = GetMac(CCH);
-        mac->NotifyRx(copy);
-        m_forwardUp(this, copy, llc.GetType(), from);
-    }
-
-    if (!m_promiscRx.IsNull())
-    {
-        // currently we cannot know from which MAC entity the packet is received,
-        // so we use the MAC entity for CCH as it receives this packet.
-        Ptr<OcbWifiMac> mac = GetMac(CCH);
-        mac->NotifyPromiscRx(copy);
-        m_promiscRx(this, copy, llc.GetType(), from, to, type);
-    }
-}
-
-bool
-WaveNetDevice::SendFrom(Ptr<Packet> packet,
-                        const Address& source,
-                        const Address& dest,
-                        uint16_t protocol)
-{
-    NS_LOG_FUNCTION(this << packet << source << dest << protocol);
-    return false;
-}
-
-void
-WaveNetDevice::SetPromiscReceiveCallback(PromiscReceiveCallback cb)
-{
-    m_promiscRx = cb;
-    for (MacEntitiesI i = m_macEntities.begin(); i != m_macEntities.end(); ++i)
-    {
-        i->second->SetPromisc();
-    }
-}
-
-bool
-WaveNetDevice::SupportsSendFrom() const
-{
-    return (GetMac(CCH))->SupportsSendFrom();
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/model/wave-net-device.h ns-3.39/src/wave/model/wave-net-device.h
--- ns-3.38/src/wave/model/wave-net-device.h	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/model/wave-net-device.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,421 +0,0 @@
-/*
- * Copyright (c) 2005,2006 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>
- *         Junling Bu <linlinjavaer@gmail.com>
- */
-#ifndef WAVE_NET_DEVICE_H
-#define WAVE_NET_DEVICE_H
-
-#include "channel-coordinator.h"
-#include "channel-manager.h"
-#include "channel-scheduler.h"
-#include "ocb-wifi-mac.h"
-#include "vendor-specific-action.h"
-#include "vsa-manager.h"
-
-#include "ns3/mac48-address.h"
-#include "ns3/packet.h"
-#include "ns3/traced-callback.h"
-#include "ns3/wifi-net-device.h"
-
-#include <map>
-#include <vector>
-
-namespace ns3
-{
-struct SchInfo;
-struct VsaInfo;
-class NetDevice;
-class ChannelScheduler;
-class VsaManager;
-class OcbWifiMac;
-
-/**
- * \defgroup wave WAVE module
- */
-
-/**
- * \ingroup wave
- *
- * \param channelNumber the specific channel
- * \param priority the priority of packet with range 0-7
- * \param dataRate the transmit data rate of packet
- * \param txPowerLevel the transmit power level with range 0-7
- * \param expireTime indicate how many milliseconds the packet
- * can stay before sent
- *
- * typically these parameters are used by higher layer to control
- * the transmission characteristics of WSMP packets.
- * When dataRate is an invalid WifiMode and txPowerLevel is 8, this
- * indicates that high layer does not set tx parameters.
- */
-struct TxInfo
-{
-    uint32_t channelNumber; ///< channel number
-    uint32_t priority;      ///< priority
-    WifiMode dataRate;      ///< data rate
-    WifiPreamble preamble;  ///< preamble
-    uint32_t txPowerLevel;  ///< transmit power level
-
-    // Time expiryTime;   // unsupported
-    /// Initializer
-    TxInfo()
-        : channelNumber(CCH),
-          priority(7),
-          txPowerLevel(8)
-    {
-    }
-
-    /**
-     * Initializer
-     * \param channel the channel
-     * \param prio the priority
-     * \param rate the wifi mode
-     * \param preamble the preamble
-     * \param powerLevel the power level
-     */
-    TxInfo(uint32_t channel,
-           uint32_t prio = 7,
-           WifiMode rate = WifiMode(),
-           WifiPreamble preamble = WIFI_PREAMBLE_LONG,
-           uint32_t powerLevel = 8)
-        : channelNumber(channel),
-          priority(prio),
-          dataRate(rate),
-          preamble(preamble),
-          txPowerLevel(powerLevel)
-    {
-    }
-};
-
-/**
- * \ingroup wave
- *
- * \param channelNumber the channel number for the SCH.
- * \param adaptable if true, the actual power level and data
- * rate for transmission are adaptable. TxPwr_Level is the maximum
- * transmit power that sets the upper bound for the actual transmit
- * power; DataRate is the minimum data rate that sets the lower
- * bound for the actual data rate. If false, the actual power level
- * and data rate for transmission are non-adaptable. TxPwr_Level and
- * DataRate are the actual values to be used for transmission.
- * \param txPowerLevel transmit power level.
- * \param dataRate transmit data rate
- */
-struct TxProfile
-{
-    uint32_t channelNumber; ///< channel number
-    bool adaptable;         ///< adaptable
-    uint32_t txPowerLevel;  ///< transmit power level
-    WifiMode dataRate;      ///< data rate
-    WifiPreamble preamble;  ///< preamble
-
-    /// Initializer
-    TxProfile()
-        : channelNumber(SCH1),
-          adaptable(false),
-          txPowerLevel(4),
-          preamble(WIFI_PREAMBLE_LONG)
-    {
-        dataRate = WifiMode("OfdmRate6MbpsBW10MHz");
-    }
-
-    /**
-     * Initializer
-     * \param channel the channel number
-     * \param adapt true to adapt
-     * \param powerLevel the power level
-     */
-    TxProfile(uint32_t channel, bool adapt = true, uint32_t powerLevel = 4)
-        : channelNumber(channel),
-          adaptable(adapt),
-          txPowerLevel(powerLevel)
-    {
-        dataRate = WifiMode("OfdmRate6MbpsBW10MHz");
-        preamble = WIFI_PREAMBLE_LONG;
-    }
-};
-
-/**
- * \ingroup wave
- *
- * This class holds together multiple, ns3::WifiPhy,
- * and ns3::OcbWifiMac (including ns3::WifiRemoteStationManager).
- * Besides that, to support multiple channel operation this
- * class also holds ns3::ChannelScheduler, ns3::ChannelManager,
- * ns3::ChannelCoordinator and ns3::VsaManager.
- *
- * these primitives specified in the standard will not be implemented
- * because of limited use in simulation:
- * void StartTimingAdvertisement ();
- * void StopTimingAdvertisement ();
- * UtcTime GetUtcTime ();
- * void SetUtcTime ();
- */
-class WaveNetDevice : public WifiNetDevice
-{
-  public:
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-    WaveNetDevice();
-    ~WaveNetDevice() override;
-
-    /**
-     * \param channelNumber the specific channel
-     * \param mac add a new available MAC entity for specific channel
-     */
-    void AddMac(uint32_t channelNumber, Ptr<OcbWifiMac> mac);
-    /**
-     * \param channelNumber the specific channel number
-     * \return corresponding MAC entity
-     */
-    Ptr<OcbWifiMac> GetMac(uint32_t channelNumber) const;
-    /**
-     * \return all inserted MAC entities.
-     */
-    std::map<uint32_t, Ptr<OcbWifiMac>> GetMacs() const;
-    /**
-     * \param phy add a new available PHY entity
-     */
-    void AddPhy(Ptr<WifiPhy> phy);
-    /**
-     * \param index the index of PHY entity
-     * \return corresponding PHY entity
-     */
-    Ptr<WifiPhy> GetPhy(uint8_t index) const override;
-    /**
-     * \return all inserted PHY entities.
-     */
-    const std::vector<Ptr<WifiPhy>>& GetPhys() const override;
-
-    /**
-     * \param channelScheduler the channel scheduler for multiple channel operation
-     */
-    void SetChannelScheduler(Ptr<ChannelScheduler> channelScheduler);
-    /**
-     * \return current channel scheduler for multiple channel operation
-     */
-    Ptr<ChannelScheduler> GetChannelScheduler() const;
-    /**
-     * \param channelManager the channel manager for multiple channel operation
-     */
-    void SetChannelManager(Ptr<ChannelManager> channelManager);
-    /**
-     * \return currentc channel manager for multiple channel operation
-     */
-    Ptr<ChannelManager> GetChannelManager() const;
-    /**
-     * \param channelCoordinator  the channel coordinator for multiple channel operation
-     */
-    void SetChannelCoordinator(Ptr<ChannelCoordinator> channelCoordinator);
-    /**
-     * \return current channel coordinator for multiple channel operation
-     */
-    Ptr<ChannelCoordinator> GetChannelCoordinator() const;
-    /**
-     * \param vsaManager the VSA manager for multiple channel operation
-     */
-    void SetVsaManager(Ptr<VsaManager> vsaManager);
-    /**
-     * \return current VSA manager for multiple channel operation
-     */
-    Ptr<VsaManager> GetVsaManager() const;
-
-    /**
-     * \param schInfo the parameters about how to start SCH service
-     * \return whether channel access is assigned successfully
-     */
-    bool StartSch(const SchInfo& schInfo);
-    /**
-     * \param channelNumber the channel which access resource will be released.
-     * \return whether channel access is released successfully
-     */
-    bool StopSch(uint32_t channelNumber);
-
-    /**
-     * \param vsaInfo the parameters about how to send VSA frame
-     * \return whether the request for VSA transmission is completed
-     */
-    bool StartVsa(const VsaInfo& vsaInfo);
-    /**
-     * \param channelNumber the channel on which VSA transmit event will be canceled.
-     * \return whether the request for stopping VSA transmission is completed
-     */
-    bool StopVsa(uint32_t channelNumber);
-    /**
-     * \param packet the packet is Vendor Specific Action frame.
-     * \param address the address of the MAC from which the management frame
-     *  was received.
-     * \param managementID identify the originator of the data.
-     *  Values are specified in IEEE P1609.0 with range 0-15.
-     * \param channelNumber the channel on which the frame was received.
-     * \returns true if the callback could handle the packet successfully, false
-     *          otherwise.
-     */
-    typedef Callback<bool, Ptr<const Packet>, const Address&, uint32_t, uint32_t> WaveVsaCallback;
-    /**
-     * \param vsaCallback the VSA receive callback for 1609 management information
-     */
-    void SetWaveVsaCallback(WaveVsaCallback vsaCallback);
-
-    /**
-     * \param txprofile transmit profile for IP-based data
-     * register a transmitter profile in the MLME before
-     * the associated IP-based data transfer starts.
-     * \return whether the tx profile is registered successfully
-     *
-     * note: This method should be called before WaveNetDevice::Send method
-     */
-    bool RegisterTxProfile(const TxProfile& txprofile);
-    /**
-     * \param channelNumber the specific channel number
-     * delete a registered transmitter profile in the MLME
-     * after the associated IP-based data transfer is complete
-     * \return whether the tx profile is unregistered successfully
-     */
-    bool DeleteTxProfile(uint32_t channelNumber);
-
-    /**
-     * \param packet packet sent from above down to Network Device
-     * \param dest mac address of the destination (already resolved)
-     * \param protocol identifies the type of payload contained in the packet.
-     *  Used to call the right L3Protocol when the packet is received.
-     * \param txInfo WSMP or other packets parameters for sending
-     * \return whether the SendX operation succeeded
-     *
-     * Normally this method is called by 1609.3 standard to
-     * send WSMP packets, however high layers can also send packets
-     * in other types except IP-based packets in CCH.
-     */
-    bool SendX(Ptr<Packet> packet, const Address& dest, uint32_t protocol, const TxInfo& txInfo);
-    /**
-     * \param newAddress  an immediate MAC-layer address change is required
-     *
-     * This method is similar with SetAddress method, but
-     * SetAddress is suggested for initializing a device, while this method
-     * is preferred for changing address and a addressChange TracedCallback
-     * will be called.
-     */
-    void ChangeAddress(Address newAddress);
-    /**
-     * \param channelNumber the specific channel number
-     * \param ac the specific access category
-     *
-     * Cancel all transmissions with the particular category and channel number.
-     */
-    void CancelTx(uint32_t channelNumber, AcIndex ac);
-
-    /**
-     * \param packet packet sent from above down to Network Device
-     * \param dest mac address of the destination (already resolved)
-     * \param protocolNumber identifies the type of payload contained in
-     *        this packet. Used to call the right L3Protocol when the packet
-     *        is received.
-     * \return whether the Send operation succeeded
-     *
-     * Normally this method is called by 1609.3 standard to
-     * send IP-based packets, however high layers can also send packets
-     * in other types except IP-based packets in CCH.
-     */
-    bool Send(Ptr<Packet> packet, const Address& dest, uint16_t protocolNumber) override;
-
-    // inherited from NetDevice base class.
-    void SetIfIndex(const uint32_t index) override;
-    uint32_t GetIfIndex() const override;
-    Ptr<Channel> GetChannel() const override;
-    void SetAddress(Address address) override;
-    Address GetAddress() const override;
-    bool SetMtu(const uint16_t mtu) override;
-    uint16_t GetMtu() const override;
-    bool IsLinkUp() const override;
-    void AddLinkChangeCallback(Callback<void> callback) override;
-    bool IsBroadcast() const override;
-    Address GetBroadcast() const override;
-    bool IsMulticast() const override;
-    Address GetMulticast(Ipv4Address multicastGroup) const override;
-    bool IsPointToPoint() const override;
-    bool IsBridge() const override;
-    bool NeedsArp() const override;
-    void SetReceiveCallback(NetDevice::ReceiveCallback cb) override;
-    Address GetMulticast(Ipv6Address addr) const override;
-    bool SendFrom(Ptr<Packet> packet,
-                  const Address& source,
-                  const Address& dest,
-                  uint16_t protocolNumber) override;
-    void SetPromiscReceiveCallback(PromiscReceiveCallback cb) override;
-    bool SupportsSendFrom() const override;
-
-  private:
-    /// This value conforms to the 802.11 specification
-    static const uint16_t MAX_MSDU_SIZE = 2304;
-
-    /// IP v4 Protocol number
-    static const uint16_t IPv4_PROT_NUMBER = 0x0800;
-    /// IP v6 Protocol number
-    static const uint16_t IPv6_PROT_NUMBER = 0x86DD;
-
-    void DoDispose() override;
-    void DoInitialize() override;
-    /**
-     * \param channelNumber the specific channel
-     * \return whether this channel is valid and available for use
-     */
-    bool IsAvailableChannel(uint32_t channelNumber) const;
-    /**
-     * Receive a packet from the lower layer and pass the
-     * packet up the stack.
-     *
-     * \param packet
-     * \param from
-     * \param to
-     */
-    void ForwardUp(Ptr<const Packet> packet, Mac48Address from, Mac48Address to);
-
-    /// MacEntities typedef
-    typedef std::map<uint32_t, Ptr<OcbWifiMac>> MacEntities;
-    /// MacEntities iterator typedef
-    typedef std::map<uint32_t, Ptr<OcbWifiMac>>::const_iterator MacEntitiesI;
-    MacEntities m_macEntities; ///< MAC entities
-    /// PhyEntities typedef
-    typedef std::vector<Ptr<WifiPhy>> PhyEntities;
-    /// PhyEntities iterator typedef
-    typedef std::vector<Ptr<WifiPhy>>::const_iterator PhyEntitiesI;
-    PhyEntities m_phyEntities; ///< Phy entities
-
-    Ptr<ChannelManager> m_channelManager;         ///< the channel manager
-    Ptr<ChannelScheduler> m_channelScheduler;     ///< the channel scheduler
-    Ptr<ChannelCoordinator> m_channelCoordinator; ///< the channel coordinator
-    Ptr<VsaManager> m_vsaManager;                 ///< the VSA manager
-    TxProfile* m_txProfile;                       ///< transmit profile
-    /**
-     * \todo The Address arguments should be passed
-     * by const reference, since they are large.
-     */
-    TracedCallback<Address, Address> m_addressChange;
-
-    // copy from WifiNetDevice
-    NetDevice::ReceiveCallback m_forwardUp;        ///< forward up receive callback
-    NetDevice::PromiscReceiveCallback m_promiscRx; ///< promiscious receive callback
-    uint32_t m_ifIndex;                            ///< IF index
-    mutable uint16_t m_mtu;                        ///< MTU
-};
-
-} // namespace ns3
-
-#endif /* WAVE_NET_DEVICE_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/test/examples-to-run.py ns-3.39/src/wave/test/examples-to-run.py
--- ns-3.38/src/wave/test/examples-to-run.py	2023-03-16 20:54:23.844869400 -0700
+++ ns-3.39/src/wave/test/examples-to-run.py	1969-12-31 16:00:00.000000000 -0800
@@ -1,24 +0,0 @@
-#! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
-
-# A list of C++ examples to run in order to ensure that they remain
-# buildable and runnable over time.  Each tuple in the list contains
-#
-#     (example_name, do_run, do_valgrind_run).
-#
-# See test.py for more information.
-cpp_examples = [
-    ("wave-simple-80211p", "True", "True"),
-    ("wave-simple-device", "True", "True"),
-    ("vanet-routing-compare --totaltime=2 --80211Mode=1", "True", "True"),
-    ("vanet-routing-compare --totaltime=2 --80211Mode=2", "True", "True"),
-    ("vanet-routing-compare --totaltime=2 --80211Mode=3", "True", "True"),
-]
-
-# A list of Python examples to run in order to ensure that they remain
-# runnable over time.  Each tuple in the list contains
-#
-#     (example_name, do_run).
-#
-# See test.py for more information.
-python_examples = []
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/test/mac-extension-test-suite.cc ns-3.39/src/wave/test/mac-extension-test-suite.cc
--- ns-3.38/src/wave/test/mac-extension-test-suite.cc	2023-03-16 20:54:23.848869300 -0700
+++ ns-3.39/src/wave/test/mac-extension-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,1558 +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
- *
- * Author: Junling Bu <linlinjavaer@gmail.com>
- */
-#include "ns3/config.h"
-#include "ns3/mobility-helper.h"
-#include "ns3/mobility-model.h"
-#include "ns3/node-list.h"
-#include "ns3/string.h"
-#include "ns3/test.h"
-#include "ns3/wave-helper.h"
-#include "ns3/wave-mac-helper.h"
-#include "ns3/wave-net-device.h"
-#include "ns3/yans-wifi-helper.h"
-
-#include <cmath>
-
-using namespace ns3;
-
-/**
- * \ingroup wave
- * \defgroup wave-test wave module tests
- */
-
-/**
- * \ingroup wave-test
- * \ingroup tests
- *
- * \brief This test case tests the channel coordination.
- * In particular, it checks the following:
- * - channel interval calculation including CCH Interval, SCH Interval,
- *   Guard Interval and Sync Interval
- * - current interval state for current time and future time
- * - channel coordination events notified at the correct time.
- */
-class ChannelCoordinationTestCase : public TestCase
-{
-  public:
-    ChannelCoordinationTestCase();
-    ~ChannelCoordinationTestCase() override;
-
-    // below three methods are used in CoordinationTestListener
-    /**
-     * Notify CCS start now function
-     * \param duration the duration
-     */
-    void NotifyCchStartNow(Time duration);
-    /**
-     * Notify SCH start now function
-     * \param duration the duration
-     */
-    void NotifySchStartNow(Time duration);
-    /**
-     * Notify guard start now function
-     * \param duration the duration
-     * \param inCchInterval the CCH interval
-     */
-    void NotifyGuardStartNow(Time duration, bool inCchInterval);
-
-  private:
-    /**
-     * Test interval after function
-     * \param cchi the CCHI
-     * \param schi the SCHI
-     * \param guardi the guard
-     */
-    void TestIntervalAfter(bool cchi, bool schi, bool guardi);
-    void DoRun() override;
-    Ptr<ChannelCoordinator> m_coordinator; ///< coordinator
-};
-
-/**
- * \ingroup wave-test
- * \ingroup tests
- *
- * \brief CoordinationTestListener is used to test channel coordination events
- */
-class CoordinationTestListener : public ChannelCoordinationListener
-{
-  public:
-    /**
-     * Constructor
-     *
-     * \param coordinatorTest channel coordination test case
-     */
-    CoordinationTestListener(ChannelCoordinationTestCase* coordinatorTest)
-        : m_coordinatorTest(coordinatorTest)
-    {
-    }
-
-    ~CoordinationTestListener() override
-    {
-    }
-
-    void NotifyCchSlotStart(Time duration) override
-    {
-        m_coordinatorTest->NotifyCchStartNow(duration);
-    }
-
-    void NotifySchSlotStart(Time duration) override
-    {
-        m_coordinatorTest->NotifySchStartNow(duration);
-    }
-
-    void NotifyGuardSlotStart(Time duration, bool cchi) override
-    {
-        m_coordinatorTest->NotifyGuardStartNow(duration, cchi);
-    }
-
-    ChannelCoordinationTestCase* m_coordinatorTest; ///< coordinator test
-};
-
-ChannelCoordinationTestCase::ChannelCoordinationTestCase()
-    : TestCase("channel-coordination")
-{
-}
-
-ChannelCoordinationTestCase::~ChannelCoordinationTestCase()
-{
-}
-
-void
-ChannelCoordinationTestCase::TestIntervalAfter(bool cchi, bool schi, bool guardi)
-{
-    uint32_t now = Now().GetMilliSeconds();
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->IsCchInterval(),
-                          cchi,
-                          "now is " << now
-                                    << "ms "
-                                       "check whether is CCH interval");
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->IsSchInterval(),
-                          schi,
-                          "now is " << now
-                                    << "ms "
-                                       "check whether is SCH interval");
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->IsGuardInterval(),
-                          guardi,
-                          "now is " << now
-                                    << "ms "
-                                       "check whether is Guard interval");
-}
-
-void
-ChannelCoordinationTestCase::NotifyCchStartNow(Time duration)
-{
-    // this method shall be called at 4ms, 104ms, ... synci * n + guardi
-    // synci is sync interval with default value 100ms
-    // guardi is guard interval with default value 4ms
-    // n is sequence number
-    int64_t now = Now().GetMilliSeconds();
-    int64_t synci = m_coordinator->GetSyncInterval().GetMilliSeconds();
-    int64_t guardi = m_coordinator->GetGuardInterval().GetMilliSeconds();
-    bool test = (((now - guardi) % synci) == 0);
-    NS_TEST_EXPECT_MSG_EQ(test, true, "the time of now shall be synci * n + guardi");
-
-    // besides that, the argument duration shall be cchi - guardi
-    Time d = m_coordinator->GetCchInterval() - m_coordinator->GetGuardInterval();
-    NS_TEST_EXPECT_MSG_EQ((duration == d), true, "the duration shall be cchi - guardi");
-}
-
-void
-ChannelCoordinationTestCase::NotifySchStartNow(Time duration)
-{
-    // this method shall be called at 54ms, 154ms, ... synci * n + cchi + guardi
-    // synci is sync interval with default value 100ms
-    // cchi is CCH interval with default value 50ms
-    // guardi is guard interval with default value 4ms
-    // n is sequence number
-    int64_t now = Now().GetMilliSeconds();
-    int64_t synci = m_coordinator->GetSyncInterval().GetMilliSeconds();
-    int64_t cchi = m_coordinator->GetCchInterval().GetMilliSeconds();
-    int64_t guardi = m_coordinator->GetGuardInterval().GetMilliSeconds();
-    bool test = ((now - guardi - cchi) % synci == 0);
-    NS_TEST_EXPECT_MSG_EQ(test, true, "the time of now shall be synci * n + cchi + guardi");
-
-    // besides that, the argument duration shall be schi - guardi
-    Time d = m_coordinator->GetSchInterval() - m_coordinator->GetGuardInterval();
-    NS_TEST_EXPECT_MSG_EQ((duration == d), true, "the duration shall be schi - guardi");
-}
-
-void
-ChannelCoordinationTestCase::NotifyGuardStartNow(Time duration, bool inCchInterval)
-{
-    int64_t now = Now().GetMilliSeconds();
-    int64_t sync = m_coordinator->GetSyncInterval().GetMilliSeconds();
-    int64_t cchi = m_coordinator->GetCchInterval().GetMilliSeconds();
-    bool test = false;
-    if (inCchInterval)
-    {
-        // if cchi, this method will be called at 0ms, 100ms, sync * n
-        test = ((now % sync) == 0);
-        NS_TEST_EXPECT_MSG_EQ(test, true, "the time of now shall be sync * n");
-    }
-    else
-    {
-        // if schi, this method will be called at 50ms, 150ms, sync * n + cchi
-        test = (((now - cchi) % sync) == 0);
-        NS_TEST_EXPECT_MSG_EQ(test, true, "the time of now shall be sync * n");
-    }
-    // the duration shall be guardi
-    test = (duration == m_coordinator->GetGuardInterval());
-    NS_TEST_EXPECT_MSG_EQ(test, true, "the duration shall be guard interval");
-}
-
-void
-ChannelCoordinationTestCase::DoRun()
-{
-    // first test configure method
-    m_coordinator = CreateObject<ChannelCoordinator>();
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->GetCchInterval(),
-                          MilliSeconds(50),
-                          "normally CCH interval is 50ms");
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->GetSchInterval(),
-                          MilliSeconds(50),
-                          "normally SCH interval is 50ms");
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->GetSyncInterval(),
-                          MilliSeconds(100),
-                          "normally Sync interval is 50ms");
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->GetGuardInterval(),
-                          MilliSeconds(4),
-                          "normally Guard interval is 50ms");
-    m_coordinator->SetCchInterval(MilliSeconds(40));
-    m_coordinator->SetSchInterval(MilliSeconds(60));
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->IsValidConfig(),
-                          true,
-                          "valid configuration of channel intervals");
-    m_coordinator->SetCchInterval(MilliSeconds(40));
-    m_coordinator->SetSchInterval(MilliSeconds(50));
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->IsValidConfig(),
-                          false,
-                          "invalid configuration of channel intervals");
-    m_coordinator->SetGuardInterval(MilliSeconds(50));
-    m_coordinator->SetCchInterval(MilliSeconds(40));
-    m_coordinator->SetSchInterval(MilliSeconds(50));
-    NS_TEST_EXPECT_MSG_EQ(m_coordinator->IsValidConfig(),
-                          false,
-                          "invalid configuration of channel intervals");
-
-    // second test member method
-    m_coordinator = CreateObject<ChannelCoordinator>();
-    Simulator::Schedule(MilliSeconds(0),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Schedule(MilliSeconds(1),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Schedule(MilliSeconds(3),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Schedule(MilliSeconds(4),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        false);
-    Simulator::Schedule(MilliSeconds(5),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        false);
-    Simulator::Schedule(MilliSeconds(50),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        true);
-    Simulator::Schedule(MilliSeconds(51),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        true);
-    Simulator::Schedule(MilliSeconds(53),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        true);
-    Simulator::Schedule(MilliSeconds(54),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        false);
-    Simulator::Schedule(MilliSeconds(55),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        false);
-    Simulator::Schedule(MilliSeconds(100),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Schedule(MilliSeconds(200),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Schedule(MilliSeconds(201),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Schedule(MilliSeconds(203),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Schedule(MilliSeconds(204),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        false);
-    Simulator::Schedule(MilliSeconds(205),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        false);
-    Simulator::Schedule(MilliSeconds(250),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        true);
-    Simulator::Schedule(MilliSeconds(251),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        true);
-    Simulator::Schedule(MilliSeconds(253),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        true);
-    Simulator::Schedule(MilliSeconds(254),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        false);
-    Simulator::Schedule(MilliSeconds(255),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        false,
-                        true,
-                        false);
-    Simulator::Schedule(MilliSeconds(300),
-                        &ChannelCoordinationTestCase::TestIntervalAfter,
-                        this,
-                        true,
-                        false,
-                        true);
-    Simulator::Stop(Seconds(1.0));
-    Simulator::Run();
-    Simulator::Destroy();
-
-    m_coordinator = CreateObject<ChannelCoordinator>();
-    // third test channel coordination events
-    Ptr<CoordinationTestListener> ptr = Create<CoordinationTestListener>(this);
-    m_coordinator->RegisterListener(ptr);
-    Simulator::Stop(Seconds(100.0));
-    Simulator::Run();
-    Simulator::Destroy();
-}
-
-/**
- * \ingroup wave-test
- * \ingroup tests
- *
- * \brief Test Case Helper
- */
-class TestCaseHelper
-{
-  public:
-    /**
-     * Create WAVE device function
-     * \param nodesNumber the number of nodes
-     * \returns the collection of nodes
-     */
-    static NetDeviceContainer CreatWaveDevice(uint32_t nodesNumber = 2);
-};
-
-#define PI 3.14159265
-
-NetDeviceContainer
-TestCaseHelper::CreatWaveDevice(uint32_t nodesNumber)
-{
-    NodeContainer nodes;
-    nodes.Create(nodesNumber);
-
-    MobilityHelper mobility;
-    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
-    mobility.Install(nodes);
-    // this is a circle with radius 10
-    Ptr<MobilityModel> model = NodeList::GetNode(0)->GetObject<MobilityModel>();
-    model->SetPosition(Vector(0, 0, 0));
-    for (uint32_t n = 1; n != nodesNumber; ++n)
-    {
-        double angle = (PI / 180) * (360 / (nodesNumber - 1) * n);
-        double x = cos(angle) * 10;
-        double y = sin(angle) * 10;
-        Ptr<MobilityModel> model = NodeList::GetNode(n)->GetObject<MobilityModel>();
-        model->SetPosition(Vector(x, y, 0));
-    }
-
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
-    YansWavePhyHelper wifiPhy = YansWavePhyHelper::Default();
-    wifiPhy.SetChannel(wifiChannel.Create());
-    QosWaveMacHelper waveMac = QosWaveMacHelper::Default();
-    WaveHelper waveHelper = WaveHelper::Default();
-    NetDeviceContainer devices = waveHelper.Install(wifiPhy, waveMac, nodes);
-    return devices;
-}
-
-/**
- *  route packets or frames in different approaches
- *  see 1609.4-2010 chapter 5.3.4
- */
-class ChannelRoutingTestCase : public TestCase
-{
-  public:
-    ChannelRoutingTestCase();
-    ~ChannelRoutingTestCase() override;
-
-    /**
-     * Send IP-based packets
-     *
-     * \param shouldSucceed is used to check whether packet sent should be successful.
-     * \param ipv6 is IPv6?
-     */
-    void SendIp(bool shouldSucceed, bool ipv6);
-    /**
-     * Send WSMP or other packets
-     * \param shouldSucceed is used to check whether packet sent should be successful.
-     * \param txInfo transmit info
-     */
-    void SendWsmp(bool shouldSucceed, const TxInfo& txInfo);
-    /**
-     * Send VSA management frames
-     * \param shouldSucceed is used to check whether packet sent should be successful.
-     * \param vsaInfo VSA info
-     */
-    void SendWsa(bool shouldSucceed, const VsaInfo& vsaInfo);
-
-  private:
-    void DoRun() override;
-    /**
-     * Receive function
-     * \param dev the device
-     * \param pkt the packet
-     * \param mode the mode
-     * \param sender the sender address
-     * \returns true if successful
-     */
-    bool Receive(Ptr<NetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender);
-    /**
-     * Receive VSA function
-     * \param pkt the packet
-     * \param address the address
-     * \returns true if successful
-     */
-    bool ReceiveVsa(Ptr<const Packet> pkt, const Address& address, uint32_t, uint32_t);
-
-    Ptr<WaveNetDevice> m_sender; ///< sender
-};
-
-ChannelRoutingTestCase::ChannelRoutingTestCase()
-    : TestCase("channel-routing")
-{
-}
-
-ChannelRoutingTestCase::~ChannelRoutingTestCase()
-{
-}
-
-void
-ChannelRoutingTestCase::SendWsmp(bool shouldSucceed, const TxInfo& txInfo)
-{
-    Ptr<Packet> packet = Create<Packet>(100);
-    const Address dest = Mac48Address::GetBroadcast();
-    uint16_t protocol = 0x80dd; // any number is OK even ipv4 and ipv6
-    bool result = m_sender->SendX(packet, dest, protocol, txInfo);
-    NS_TEST_EXPECT_MSG_EQ(result, shouldSucceed, "test SendWsmp method error");
-}
-
-void
-ChannelRoutingTestCase::SendIp(bool shouldSucceed, bool ipv6)
-{
-    Ptr<Packet> packet = Create<Packet>(100);
-    const Address dest = Mac48Address::GetBroadcast();
-    const static uint16_t IPv4_PROT_NUMBER = 0x0800;
-    const static uint16_t IPv6_PROT_NUMBER = 0x86DD;
-    uint16_t protocol = ipv6 ? IPv6_PROT_NUMBER : IPv4_PROT_NUMBER;
-    bool result = m_sender->Send(packet, dest, protocol);
-    NS_TEST_EXPECT_MSG_EQ(result, shouldSucceed, "test SendIp method error");
-}
-
-void
-ChannelRoutingTestCase::SendWsa(bool shouldSucceed, const VsaInfo& vsaInfo)
-{
-    bool result = m_sender->StartVsa(vsaInfo);
-    NS_TEST_EXPECT_MSG_EQ(result, shouldSucceed, "test SendWsa method error");
-}
-
-void
-ChannelRoutingTestCase::DoRun()
-{
-    //  check SendX method for WSMP packets
-    {
-        NetDeviceContainer devices = TestCaseHelper::CreatWaveDevice(1);
-        m_sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-
-        Simulator::Schedule(Seconds(0.1),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            true,
-                            TxInfo(CCH));
-        Simulator::Schedule(Seconds(0.1),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH1));
-        Simulator::Schedule(Seconds(0.1),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH2));
-
-        const SchInfo schInfo = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(0.2), &WaveNetDevice::StartSch, m_sender, schInfo);
-
-        Simulator::Schedule(Seconds(0.3),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            true,
-                            TxInfo(CCH));
-        Simulator::Schedule(Seconds(0.3),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            true,
-                            TxInfo(SCH1));
-        Simulator::Schedule(Seconds(0.3),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH2));
-        Simulator::Schedule(Seconds(0.3),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH3));
-        Simulator::Schedule(Seconds(0.3),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH4));
-        Simulator::Schedule(Seconds(0.3),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH5));
-        Simulator::Schedule(Seconds(0.3),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH6));
-
-        // invalid channel number
-        Simulator::Schedule(Seconds(0.4),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(0));
-        // invalid user priority
-        Simulator::Schedule(Seconds(0.4),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(CCH, 8));
-        // invalid tx parameters
-        Simulator::Schedule(Seconds(0.4),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(CCH, 7, WifiMode("OfdmRate6Mbps"), WIFI_PREAMBLE_LONG, 7));
-        Simulator::Schedule(
-            Seconds(0.4),
-            &ChannelRoutingTestCase::SendWsmp,
-            this,
-            false,
-            TxInfo(CCH, 7, WifiMode("OfdmRate3MbpsBW10MHz"), WIFI_PREAMBLE_LONG, 10));
-        // valid tx parameters
-        Simulator::Schedule(
-            Seconds(0.4),
-            &ChannelRoutingTestCase::SendWsmp,
-            this,
-            true,
-            TxInfo(CCH, 7, WifiMode("OfdmRate3MbpsBW10MHz"), WIFI_PREAMBLE_LONG, 7));
-        Simulator::Schedule(Seconds(0.4),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            true,
-                            TxInfo(CCH, 7, WifiMode(), WIFI_PREAMBLE_LONG, 8));
-
-        // release channel access at 0.6s
-        Simulator::Schedule(Seconds(0.5), &WaveNetDevice::StopSch, m_sender, SCH1);
-
-        // the packet will be dropped because channel access is not assigned again
-        Simulator::Schedule(Seconds(0.6),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            true,
-                            TxInfo(CCH));
-        Simulator::Schedule(Seconds(0.6),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH1));
-        Simulator::Schedule(Seconds(0.6),
-                            &ChannelRoutingTestCase::SendWsmp,
-                            this,
-                            false,
-                            TxInfo(SCH2));
-
-        Simulator::Stop(Seconds(1.0));
-        Simulator::Run();
-        Simulator::Destroy();
-    }
-
-    // check Send method for IP-based packets
-    {
-        NetDeviceContainer devices = TestCaseHelper::CreatWaveDevice(1);
-        m_sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-
-        bool ipv6 = true;
-        bool ipv4 = false;
-        Simulator::Schedule(Seconds(0.1), &ChannelRoutingTestCase::SendIp, this, false, ipv6);
-        Simulator::Schedule(Seconds(0.1), &ChannelRoutingTestCase::SendIp, this, false, ipv4);
-
-        const SchInfo schInfo = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(0.2), &WaveNetDevice::StartSch, m_sender, schInfo);
-
-        Simulator::Schedule(Seconds(0.3), &ChannelRoutingTestCase::SendIp, this, false, ipv6);
-        Simulator::Schedule(Seconds(0.3), &ChannelRoutingTestCase::SendIp, this, false, ipv4);
-
-        TxProfile txProfile = TxProfile(SCH1);
-        Simulator::Schedule(Seconds(0.4), &WaveNetDevice::RegisterTxProfile, m_sender, txProfile);
-
-        Simulator::Schedule(Seconds(0.5), &ChannelRoutingTestCase::SendIp, this, true, ipv6);
-        Simulator::Schedule(Seconds(0.5), &ChannelRoutingTestCase::SendIp, this, true, ipv4);
-
-        // unregister txprofile
-        Simulator::Schedule(Seconds(0.5), &WaveNetDevice::DeleteTxProfile, m_sender, SCH1);
-
-        Simulator::Schedule(Seconds(0.6), &ChannelRoutingTestCase::SendIp, this, false, ipv6);
-        Simulator::Schedule(Seconds(0.6), &ChannelRoutingTestCase::SendIp, this, false, ipv4);
-
-        // release channel access
-        // mac entities have no channel resource even phy device has ability to send
-        Simulator::Schedule(Seconds(0.7), &WaveNetDevice::StopSch, m_sender, SCH1);
-
-        Simulator::Schedule(Seconds(0.8), &ChannelRoutingTestCase::SendIp, this, false, ipv6);
-        Simulator::Schedule(Seconds(0.8), &ChannelRoutingTestCase::SendIp, this, false, ipv4);
-
-        Simulator::Stop(Seconds(1.0));
-        Simulator::Run();
-        Simulator::Destroy();
-    }
-
-    // check StartVsa method for WSA management frames
-    {
-        NetDeviceContainer devices = TestCaseHelper::CreatWaveDevice(1);
-        m_sender = DynamicCast<WaveNetDevice>(devices.Get(0));
-
-        Ptr<Packet> packet = Create<Packet>(100);
-        const Mac48Address dest = Mac48Address::GetBroadcast();
-        VsaInfo vsaInfo =
-            VsaInfo(dest, OrganizationIdentifier(), 3, packet, SCH1, 50, VSA_TRANSMIT_IN_BOTHI);
-        Simulator::Schedule(Seconds(0.1), &ChannelRoutingTestCase::SendWsa, this, false, vsaInfo);
-
-        vsaInfo.channelNumber = 0;
-        Simulator::Schedule(Seconds(0.2), &ChannelRoutingTestCase::SendWsa, this, false, vsaInfo);
-
-        vsaInfo.channelNumber = CCH;
-        Simulator::Schedule(Seconds(0.3), &ChannelRoutingTestCase::SendWsa, this, true, vsaInfo);
-        Simulator::Schedule(Seconds(0.39), &WaveNetDevice::StopVsa, m_sender, CCH);
-
-        const SchInfo schInfo = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(0.4), &WaveNetDevice::StartSch, m_sender, schInfo);
-        vsaInfo.channelNumber = CCH;
-        Simulator::Schedule(Seconds(0.4), &ChannelRoutingTestCase::SendWsa, this, true, vsaInfo);
-        vsaInfo.channelNumber = SCH1;
-        Simulator::Schedule(Seconds(0.4), &ChannelRoutingTestCase::SendWsa, this, true, vsaInfo);
-        vsaInfo.channelNumber = SCH2;
-        Simulator::Schedule(Seconds(0.4), &ChannelRoutingTestCase::SendWsa, this, false, vsaInfo);
-
-        Simulator::Schedule(Seconds(0.49), &WaveNetDevice::StopVsa, m_sender, CCH);
-        Simulator::Schedule(Seconds(0.49), &WaveNetDevice::StopVsa, m_sender, SCH1);
-        Simulator::Schedule(Seconds(0.49), &WaveNetDevice::StopSch, m_sender, SCH1);
-
-        vsaInfo.channelNumber = CCH;
-        Simulator::Schedule(Seconds(0.5), &ChannelRoutingTestCase::SendWsa, this, true, vsaInfo);
-        vsaInfo.channelNumber = SCH1;
-        Simulator::Schedule(Seconds(0.5), &ChannelRoutingTestCase::SendWsa, this, false, vsaInfo);
-        vsaInfo.channelNumber = SCH2;
-        Simulator::Schedule(Seconds(0.5), &ChannelRoutingTestCase::SendWsa, this, false, vsaInfo);
-
-        Simulator::Stop(Seconds(1.0));
-        Simulator::Run();
-        Simulator::Destroy();
-    }
-}
-
-/**
- * This test case tests channel access assignments which is done by
- * StartSch and StopSch method of WaveNetDevice.
- * channel access assignments include ContinuousAccess, ExtendedAccess,
- * and AlternatingAccess.
- * The results of this test case depend on the implementation of ChannelScheduler
- * In this case, the results depend on class "DefaultChannelScheduler".
- */
-class ChannelAccessTestCase : public TestCase
-{
-  public:
-    ChannelAccessTestCase();
-    ~ChannelAccessTestCase() override;
-
-  private:
-    /**
-     * Test continuous function
-     * \param info the schedule info
-     * \param shouldSucceed true if it should succeed
-     */
-    void TestContinuous(SchInfo& info, bool shouldSucceed);
-    /**
-     * Test continuous after function
-     * \param channelNumber the channel number
-     * \param isAccessAssigned true if access assigned
-     */
-    void TestContinuousAfter(uint32_t channelNumber, bool isAccessAssigned);
-    /**
-     * Test extended function
-     * \param info the schedule info
-     * \param shouldSucceed true if it should succeed
-     */
-    void TestExtended(SchInfo& info, bool shouldSucceed);
-    /**
-     * Test extended after function
-     * \param channelNumber the channel number
-     * \param isAccessAssigned true if access assigned
-     */
-    void TestExtendedAfter(uint32_t channelNumber, bool isAccessAssigned);
-    /**
-     * Test aternating function
-     * \param info the schedule info
-     * \param shouldSucceed true if it should succeed
-     */
-    void TestAlternating(SchInfo& info, bool shouldSucceed);
-    /**
-     * Test alternating after function
-     * \param channelNumber the channel number
-     * \param isAccessAssigned true if access assigned
-     */
-    void TestAlternatingAfter(uint32_t channelNumber, bool isAccessAssigned);
-
-    /**
-     * Send X function
-     * \param channel the channel number
-     * \param receiverId
-     */
-    void SendX(uint32_t channel, uint32_t receiverId);
-    /**
-     * Receive function
-     * \param dev the device
-     * \param pkt the packet
-     * \param mode the mode
-     * \param sender the sender address
-     * \returns true if successful
-     */
-    bool Receive(Ptr<NetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender);
-
-    void DoRun() override;
-
-    NetDeviceContainer m_devices; ///< the devices
-    Ptr<WaveNetDevice> m_sender;  ///< sender
-    uint32_t m_received;          ///< received
-};
-
-ChannelAccessTestCase::ChannelAccessTestCase()
-    : TestCase("channel-access")
-{
-}
-
-ChannelAccessTestCase::~ChannelAccessTestCase()
-{
-}
-
-void
-ChannelAccessTestCase::TestContinuous(SchInfo& info, bool shouldSucceed)
-{
-    bool result = m_sender->StartSch(info);
-    NS_TEST_EXPECT_MSG_EQ(result, shouldSucceed, "TestContinuous fail at " << Now().As(Time::S));
-}
-
-void
-ChannelAccessTestCase::TestContinuousAfter(uint32_t channelNumber, bool isAccessAssigned)
-{
-    bool result = m_sender->GetChannelScheduler()->IsContinuousAccessAssigned(channelNumber);
-    NS_TEST_EXPECT_MSG_EQ(result,
-                          isAccessAssigned,
-                          "TestContinuousAfter fail at " << Now().As(Time::S));
-}
-
-void
-ChannelAccessTestCase::TestExtended(SchInfo& info, bool shouldSucceed)
-{
-    bool result = m_sender->StartSch(info);
-    NS_TEST_EXPECT_MSG_EQ(result, shouldSucceed, "TestExtended fail at " << Now().As(Time::S));
-}
-
-void
-ChannelAccessTestCase::TestExtendedAfter(uint32_t channelNumber, bool isAccessAssigned)
-{
-    bool result = m_sender->GetChannelScheduler()->IsExtendedAccessAssigned(channelNumber);
-    NS_TEST_EXPECT_MSG_EQ(result,
-                          isAccessAssigned,
-                          "TestExtendedAfter fail at " << Now().As(Time::S));
-}
-
-void
-ChannelAccessTestCase::TestAlternating(SchInfo& info, bool shouldSucceed)
-{
-    bool result = m_sender->StartSch(info);
-    NS_TEST_EXPECT_MSG_EQ(result, shouldSucceed, "TestAlternating fail at " << Now().As(Time::S));
-}
-
-void
-ChannelAccessTestCase::TestAlternatingAfter(uint32_t channelNumber, bool isAccessAssigned)
-{
-    bool result = m_sender->GetChannelScheduler()->IsAlternatingAccessAssigned(channelNumber);
-    NS_TEST_EXPECT_MSG_EQ(result,
-                          isAccessAssigned,
-                          "TestAlternating fail at " << Now().As(Time::S));
-}
-
-void
-ChannelAccessTestCase::SendX(uint32_t channel, uint32_t receiverId)
-{
-    const static uint16_t WSMP_PROT_NUMBER = 0x88DC;
-    const Mac48Address dest = Mac48Address::GetBroadcast();
-    const TxInfo txInfo = TxInfo(channel);
-
-    uint8_t* data = new uint8_t[112];
-    data[0] = (receiverId >> 24) & 0xFF;
-    data[1] = (receiverId >> 16) & 0xFF;
-    data[2] = (receiverId >> 8) & 0xFF;
-    data[3] = (receiverId >> 0) & 0xFF;
-
-    uint64_t ts = Simulator::Now().GetTimeStep();
-    data[4] = (ts >> 56) & 0xFF;
-    data[5] = (ts >> 48) & 0xFF;
-    data[6] = (ts >> 40) & 0xFF;
-    data[7] = (ts >> 32) & 0xFF;
-    data[8] = (ts >> 24) & 0xFF;
-    data[9] = (ts >> 16) & 0xFF;
-    data[10] = (ts >> 8) & 0xFF;
-    data[11] = (ts >> 0) & 0xFF;
-
-    Ptr<Packet> p = Create<Packet>(data, 112);
-
-    m_sender->SendX(p, dest, WSMP_PROT_NUMBER, txInfo);
-
-    delete[] data;
-}
-
-bool
-ChannelAccessTestCase::Receive(Ptr<NetDevice> dev,
-                               Ptr<const Packet> pkt,
-                               uint16_t mode,
-                               const Address& sender)
-{
-    uint8_t* data = new uint8_t[pkt->GetSize()];
-    pkt->CopyData(data, pkt->GetSize());
-
-    uint32_t seq = data[0];
-    seq <<= 8;
-    seq |= data[1];
-    seq <<= 8;
-    seq |= data[2];
-    seq <<= 8;
-    seq |= data[3];
-
-    uint64_t ts = data[4];
-    ts <<= 8;
-    ts |= data[5];
-    ts <<= 8;
-    ts |= data[6];
-    ts <<= 8;
-    ts |= data[7];
-    ts <<= 8;
-    ts |= data[8];
-    ts <<= 8;
-    ts |= data[9];
-    ts <<= 8;
-    ts |= data[10];
-    ts <<= 8;
-    ts |= data[11];
-    Time sendTime = TimeStep(ts);
-
-    delete[] data;
-
-    //  SeqTsHeader seqTs;
-    //  ConstCast<Packet> (pkt)->RemoveHeader (seqTs);
-    uint32_t curNodeId = dev->GetNode()->GetId();
-    NS_TEST_EXPECT_MSG_EQ(curNodeId, seq, "fail to assign channel access");
-    m_received++;
-    return true;
-}
-
-void
-ChannelAccessTestCase::DoRun()
-{
-    // test ContinuousAccess in the sender side
-    {
-        m_devices = TestCaseHelper::CreatWaveDevice(1);
-        m_sender = DynamicCast<WaveNetDevice>(m_devices.Get(0));
-
-        // there is no need for assigning CCH continuous access.
-        SchInfo info = SchInfo(CCH, false, EXTENDED_CONTINUOUS);
-        Simulator::Schedule(Seconds(1), &ChannelAccessTestCase::TestContinuous, this, info, false);
-
-        info = SchInfo(SCH1, false, EXTENDED_CONTINUOUS);
-        Simulator::Schedule(Seconds(2), &ChannelAccessTestCase::TestContinuous, this, info, true);
-
-        // BE ATTENTION !!!
-        // because channel access is assigned in non-immediate mode, the first CCH Interval will be
-        // the wait time with DefaultCchAccess assigned, thus there is no ContinuousAccess assigned.
-        Simulator::Schedule(Seconds(2),
-                            &ChannelAccessTestCase::TestContinuousAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.01),
-                            &ChannelAccessTestCase::TestContinuousAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.049),
-                            &ChannelAccessTestCase::TestContinuousAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.05),
-                            &ChannelAccessTestCase::TestContinuousAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.051),
-                            &ChannelAccessTestCase::TestContinuousAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(2.99),
-                            &ChannelAccessTestCase::TestContinuousAfter,
-                            this,
-                            SCH1,
-                            true);
-
-        // it's OK to assign same access again,
-        Simulator::Schedule(Seconds(3), &ChannelAccessTestCase::TestContinuous, this, info, true);
-        // fail to assign continuous access for other SCH if current channel is assigned
-        info = SchInfo(SCH2, false, EXTENDED_CONTINUOUS);
-        Simulator::Schedule(Seconds(4), &ChannelAccessTestCase::TestContinuous, this, info, false);
-
-        // then we release channel access at 0.5s
-        Simulator::Schedule(Seconds(5), &WaveNetDevice::StopSch, m_sender, SCH1);
-
-        info = SchInfo(SCH2, false, EXTENDED_CONTINUOUS);
-        // succeed to assign access for other SCH is previous SCH access is released
-        Simulator::Schedule(Seconds(6), &ChannelAccessTestCase::TestContinuous, this, info, true);
-
-        Simulator::Stop(Seconds(7.0));
-        Simulator::Run();
-        Simulator::Destroy();
-    }
-
-    // test ContinuousAccess again in the receiver side
-    {
-        m_devices = TestCaseHelper::CreatWaveDevice(8);
-        m_sender = DynamicCast<WaveNetDevice>(m_devices.Get(0));
-        m_received = 0;
-
-        for (uint32_t i = 1; i != 8; ++i)
-        {
-            Ptr<WaveNetDevice> device = DynamicCast<WaveNetDevice>(m_devices.Get(i));
-            device->SetReceiveCallback(MakeCallback(&ChannelAccessTestCase::Receive, this));
-
-            // at 0s, receivers are assigned ContinuousAccess from CCH, SCH1 to SCH6
-            static std::vector<uint32_t> WaveChannels = ChannelManager::GetWaveChannels();
-            uint32_t channel = WaveChannels[i - 1];
-            const SchInfo info = SchInfo(channel, false, EXTENDED_CONTINUOUS);
-            Simulator::Schedule(Seconds(0), &WaveNetDevice::StartSch, device, info);
-        }
-
-        // at 0s, the sender is assigned DefaultCchAccess, so only node-1 can receive packets.
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, CCH, 1);
-        // if receivers assigned for SCH access can receive packets, there shall be crashed
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH1, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-
-        // at 1s, the sender is assigned ContinuousAccess for SCH1, so only node-2 can receive
-        // packets.
-        SchInfo info = SchInfo(SCH1, false, EXTENDED_CONTINUOUS);
-        Simulator::Schedule(Seconds(1), &WaveNetDevice::StartSch, m_sender, info);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH1, 2);
-        // other channel access cannot receive packets
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, CCH, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-
-        Simulator::Stop(Seconds(10.0));
-        Simulator::Run();
-        Simulator::Destroy();
-
-        NS_TEST_EXPECT_MSG_EQ(m_received, 2, "test ContinuousAccess fail in receive side");
-    }
-
-    // test ExtendedAccess in the sender side
-    {
-        m_devices = TestCaseHelper::CreatWaveDevice(1);
-        m_sender = DynamicCast<WaveNetDevice>(m_devices.Get(0));
-
-        // there is no need for assigning CCH extended access.
-        SchInfo info = SchInfo(CCH, false, 10);
-        Simulator::Schedule(Seconds(1), &ChannelAccessTestCase::TestExtended, this, info, false);
-
-        info = SchInfo(SCH1, false, 10);
-        Simulator::Schedule(Seconds(2), &ChannelAccessTestCase::TestExtended, this, info, true);
-        // succeed because request for extends 8 can be fulfilled by previous extends 10..
-        info = SchInfo(SCH1, false, 8);
-        Simulator::Schedule(Seconds(2), &ChannelAccessTestCase::TestExtended, this, info, true);
-        // fail because request for extends 12 cannot be fulfilled by previous extends 10..
-        info = SchInfo(SCH1, false, 12);
-        Simulator::Schedule(Seconds(2), &ChannelAccessTestCase::TestExtended, this, info, false);
-
-        // BE ATTENTION !!!
-        // because channel access is assigned in non-immediate mode, the first CCH Interval will be
-        // the wait time with DefaultCchAccess assigned, while there is no ExtendedAccess assigned.
-        Simulator::Schedule(Seconds(2),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.01),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.049),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.05),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(2.051),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(2.99),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            true);
-
-        // the end of extended access is (2s +  100ms + 100ms * 10) = 3.1s
-        Simulator::Schedule(Seconds(3),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(3.1),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(3.2),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            false);
-        Simulator::Schedule(Seconds(3.3),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH1,
-                            false);
-
-        // succeed to assign extended access for other SCH since previous extended access is
-        // released automatically
-        info = SchInfo(SCH2, false, 10);
-        Simulator::Schedule(Seconds(4), &ChannelAccessTestCase::TestExtended, this, info, true);
-
-        // stop it at 5s even the end of extended access is (4s + 100ms + 100ms * 10) = 5.1s
-        Simulator::Schedule(Seconds(5), &WaveNetDevice::StopSch, m_sender, SCH2);
-
-        Simulator::Schedule(Seconds(5),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH2,
-                            false);
-        Simulator::Schedule(Seconds(5.1),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH2,
-                            false);
-        Simulator::Schedule(Seconds(5.2),
-                            &ChannelAccessTestCase::TestExtendedAfter,
-                            this,
-                            SCH2,
-                            false);
-
-        Simulator::Stop(Seconds(6.0));
-        Simulator::Run();
-        Simulator::Destroy();
-    }
-
-    // test ExtendedAccess again in the receiver side
-    {
-        m_devices = TestCaseHelper::CreatWaveDevice(8);
-        m_sender = DynamicCast<WaveNetDevice>(m_devices.Get(0));
-        m_received = 0;
-
-        for (uint32_t i = 1; i != 8; ++i)
-        {
-            Ptr<WaveNetDevice> device = DynamicCast<WaveNetDevice>(m_devices.Get(i));
-            device->SetReceiveCallback(MakeCallback(&ChannelAccessTestCase::Receive, this));
-
-            // at 0s, receivers are assigned ContinuosAccess from CCH, SCH1 to SCH6
-            static std::vector<uint32_t> WaveChannels = ChannelManager::GetWaveChannels();
-            uint32_t channel = WaveChannels[i - 1];
-            const SchInfo info = SchInfo(channel, false, EXTENDED_CONTINUOUS);
-            Simulator::Schedule(Seconds(0), &WaveNetDevice::StartSch, device, info);
-        }
-
-        // at 0s, the sender is assigned DefaultCchAccess, so only node-1 can receive packets.
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, CCH, 1);
-        // if receivers assigned for SCH access can receive packets, there shall be crashed
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH1, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-
-        // at 1s, the sender is assigned ExtendedAccess for SCH1 with extends 10,
-        //, so only node-2 can receive packets from 1s - 2.1s ( 1s + 100ms + 100ms * 10)
-        SchInfo info = SchInfo(SCH1, false, 10);
-        Simulator::Schedule(Seconds(1), &WaveNetDevice::StartSch, m_sender, info);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH1, 2);
-        // other channel access cannot receive packets
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, CCH, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-        // at 2.2s the node-2 cannot receive this packet because of extended access released in
-        // node-0 but sended is assigned DefaultCchAccess again, thus node-1 can receive broadcasted
-        // packets.
-        Simulator::Schedule(Seconds(2.2), &ChannelAccessTestCase::SendX, this, CCH, 1);
-        Simulator::Schedule(Seconds(2.2), &ChannelAccessTestCase::SendX, this, SCH1, 0);
-        Simulator::Schedule(Seconds(2.2), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(2.2), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(2.2), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(2.2), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(2.2), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-
-        Simulator::Stop(Seconds(10.0));
-        Simulator::Run();
-        Simulator::Destroy();
-
-        NS_TEST_EXPECT_MSG_EQ(m_received, 3, "test ExtendedAccess fail in receive side");
-    }
-
-    // test AlternatingAccess in the sender side
-    {
-        m_devices = TestCaseHelper::CreatWaveDevice(1);
-        m_sender = DynamicCast<WaveNetDevice>(m_devices.Get(0));
-
-        // there is no need for assigning CCH alternating  access.
-        SchInfo info = SchInfo(CCH, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(1), &ChannelAccessTestCase::TestAlternating, this, info, false);
-
-        info = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(2), &ChannelAccessTestCase::TestAlternating, this, info, true);
-
-        // BE ATTENTION !!!
-        // No matter whether channel access is assigned in immediate mode or non-immediate mode,
-        // the channel access will assigned immediately which is different from the test results in
-        // ExtendedAccess assignment and ContinuousAccess assignment.
-        Simulator::Schedule(Seconds(2),
-                            &ChannelAccessTestCase::TestAlternatingAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(2.01),
-                            &ChannelAccessTestCase::TestAlternatingAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(2.049),
-                            &ChannelAccessTestCase::TestAlternatingAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(2.05),
-                            &ChannelAccessTestCase::TestAlternatingAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(2.051),
-                            &ChannelAccessTestCase::TestAlternatingAfter,
-                            this,
-                            SCH1,
-                            true);
-        Simulator::Schedule(Seconds(2.99),
-                            &ChannelAccessTestCase::TestAlternatingAfter,
-                            this,
-                            SCH1,
-                            true);
-
-        Simulator::Schedule(Seconds(3), &ChannelAccessTestCase::TestAlternating, this, info, true);
-        info = SchInfo(SCH2, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(3), &ChannelAccessTestCase::TestAlternating, this, info, false);
-        info = SchInfo(0, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(3), &ChannelAccessTestCase::TestAlternating, this, info, false);
-
-        // then we release channel access at 0.5s
-        Simulator::Schedule(Seconds(4), &WaveNetDevice::StopSch, m_sender, SCH1);
-
-        info = SchInfo(SCH2, false, EXTENDED_ALTERNATING);
-        // succeed to assign access for other SCH is previous SCH access is released
-        Simulator::Schedule(Seconds(5), &ChannelAccessTestCase::TestAlternating, this, info, true);
-
-        Simulator::Stop(Seconds(6.0));
-        Simulator::Run();
-        Simulator::Destroy();
-    }
-
-    // test AlternatingAccess again in the receiver side
-    {
-        m_devices = TestCaseHelper::CreatWaveDevice(8);
-        m_sender = DynamicCast<WaveNetDevice>(m_devices.Get(0));
-        m_received = 0;
-
-        for (uint32_t i = 1; i != 8; ++i)
-        {
-            Ptr<WaveNetDevice> device = DynamicCast<WaveNetDevice>(m_devices.Get(i));
-            device->SetReceiveCallback(MakeCallback(&ChannelAccessTestCase::Receive, this));
-
-            // at 0s, receivers are assigned ContinuosAccess from CCH, SCH1 to SCH6
-            static std::vector<uint32_t> WaveChannels = ChannelManager::GetWaveChannels();
-            uint32_t channel = WaveChannels[i - 1];
-            const SchInfo info = SchInfo(channel, false, EXTENDED_CONTINUOUS);
-            Simulator::Schedule(Seconds(0), &WaveNetDevice::StartSch, device, info);
-        }
-
-        // at 0s, the sender is assigned DefaultCchAccess, so only node-1 can receive packets.
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, CCH, 1);
-        // if receivers assigned for SCH access can receive packets, there shall be crashed
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH1, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(0.1), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-
-        // at 1s, the sender is assigned ContinuosAccess for SCH1, so only node-2 can receive
-        // packets.
-        SchInfo info = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-        Simulator::Schedule(Seconds(1), &WaveNetDevice::StartSch, m_sender, info);
-        // node-1 (assigned CCH access) and node-2 (assigned SCH1 access) can receive packets
-        // in different channel interval
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH1, 2);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, CCH, 1);
-        // other channel access cannot receive packets
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(1.1), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-
-        Simulator::Schedule(Seconds(2), &WaveNetDevice::StopSch, m_sender, SCH1);
-        // if ContinuousAccess for SCH1 is released, node-2 cannot receive packets again
-        Simulator::Schedule(Seconds(2.1), &ChannelAccessTestCase::SendX, this, CCH, 1);
-        Simulator::Schedule(Seconds(2.1), &ChannelAccessTestCase::SendX, this, SCH1, 0);
-        Simulator::Schedule(Seconds(2.1), &ChannelAccessTestCase::SendX, this, SCH2, 0);
-        Simulator::Schedule(Seconds(2.1), &ChannelAccessTestCase::SendX, this, SCH3, 0);
-        Simulator::Schedule(Seconds(2.1), &ChannelAccessTestCase::SendX, this, SCH4, 0);
-        Simulator::Schedule(Seconds(2.1), &ChannelAccessTestCase::SendX, this, SCH5, 0);
-        Simulator::Schedule(Seconds(2.1), &ChannelAccessTestCase::SendX, this, SCH6, 0);
-
-        Simulator::Stop(Seconds(10.0));
-        Simulator::Run();
-        Simulator::Destroy();
-
-        NS_TEST_EXPECT_MSG_EQ(m_received, 4, "test AlternatingAccess fail in receive side");
-    }
-}
-
-/**
- * \ingroup wave-test
- * \ingroup tests
- *
- * \brief The Annex C of  IEEE 1609.4 : "Avoiding transmission at scheduled guard intervals"
- * This feature is implemented in WaveMacLow::StartTransmission method
- */
-class AnnexC_TestCase : public TestCase
-{
-  public:
-    AnnexC_TestCase();
-    ~AnnexC_TestCase() override;
-
-  private:
-    void DoRun() override;
-
-    /**
-     * Send packet function
-     * \param packetSize the packet size
-     * \param txInfo the transmit info
-     * \param sequence the sequence
-     */
-    void SendPacket(uint32_t packetSize, const TxInfo& txInfo, uint32_t sequence);
-    /**
-     * Receive function
-     * \param dev the device
-     * \param pkt the packet
-     * \param mode the mode
-     * \param sender the sender address
-     * \returns true if successful
-     */
-    bool Receive(Ptr<NetDevice> dev, Ptr<const Packet> pkt, uint16_t mode, const Address& sender);
-
-    NetDeviceContainer m_devices;  ///< devices
-    Ptr<WaveNetDevice> m_sender;   ///< sender
-    Ptr<WaveNetDevice> m_receiver; ///< receiver
-};
-
-AnnexC_TestCase::AnnexC_TestCase()
-    : TestCase("annex-c")
-{
-}
-
-AnnexC_TestCase::~AnnexC_TestCase()
-{
-}
-
-void
-AnnexC_TestCase::SendPacket(uint32_t packetSize, const TxInfo& txInfo, uint32_t sequence)
-{
-    const static uint16_t WSMP_PROT_NUMBER = 0x88DC;
-    const Mac48Address dest = Mac48Address::ConvertFrom(m_receiver->GetAddress());
-
-    uint8_t* data = new uint8_t[packetSize];
-    data[0] = (sequence >> 24) & 0xFF;
-    data[1] = (sequence >> 16) & 0xFF;
-    data[2] = (sequence >> 8) & 0xFF;
-    data[3] = (sequence >> 0) & 0xFF;
-
-    uint64_t ts = Simulator::Now().GetTimeStep();
-    data[4] = (ts >> 56) & 0xFF;
-    data[5] = (ts >> 48) & 0xFF;
-    data[6] = (ts >> 40) & 0xFF;
-    data[7] = (ts >> 32) & 0xFF;
-    data[8] = (ts >> 24) & 0xFF;
-    data[9] = (ts >> 16) & 0xFF;
-    data[10] = (ts >> 8) & 0xFF;
-    data[11] = (ts >> 0) & 0xFF;
-
-    Ptr<Packet> p = Create<Packet>(data, packetSize);
-
-    m_sender->SendX(p, dest, WSMP_PROT_NUMBER, txInfo);
-
-    delete[] data;
-}
-
-bool
-AnnexC_TestCase::Receive(Ptr<NetDevice> dev,
-                         Ptr<const Packet> pkt,
-                         uint16_t mode,
-                         const Address& sender)
-{
-    uint8_t* data = new uint8_t[pkt->GetSize()];
-    pkt->CopyData(data, pkt->GetSize());
-
-    uint32_t seq = data[0];
-    seq <<= 8;
-    seq |= data[1];
-    seq <<= 8;
-    seq |= data[2];
-    seq <<= 8;
-    seq |= data[3];
-
-    uint64_t ts = data[4];
-    ts <<= 8;
-    ts |= data[5];
-    ts <<= 8;
-    ts |= data[6];
-    ts <<= 8;
-    ts |= data[7];
-    ts <<= 8;
-    ts |= data[8];
-    ts <<= 8;
-    ts |= data[9];
-    ts <<= 8;
-    ts |= data[10];
-    ts <<= 8;
-    ts |= data[11];
-    Time sendTime = TimeStep(ts);
-
-    delete[] data;
-
-    Time curTime = Now();
-    Time duration = curTime - sendTime;
-
-    if (seq == 1)
-    {
-        NS_TEST_EXPECT_MSG_GT(duration,
-                              ChannelCoordinator::GetDefaultSchInterval(),
-                              "fail to test Annex C when packet sequence is " << seq);
-    }
-    else if (seq == 2)
-    {
-        NS_TEST_EXPECT_MSG_LT(duration,
-                              ChannelCoordinator::GetDefaultSchInterval(),
-                              "fail to test Annex C when packet sequence is " << seq);
-    }
-    else if (seq == 3)
-    {
-        NS_TEST_EXPECT_MSG_GT(duration,
-                              ChannelCoordinator::GetDefaultCchInterval(),
-                              "fail to test Annex C when packet sequence is " << seq);
-    }
-    else if (seq == 4)
-    {
-        NS_TEST_EXPECT_MSG_LT(duration,
-                              ChannelCoordinator::GetDefaultCchInterval(),
-                              "fail to test Annex C when packet sequence is " << seq);
-    }
-    return true;
-}
-
-void
-AnnexC_TestCase::DoRun()
-{
-    m_devices = TestCaseHelper::CreatWaveDevice(2);
-    m_sender = DynamicCast<WaveNetDevice>(m_devices.Get(0));
-    m_receiver = DynamicCast<WaveNetDevice>(m_devices.Get(1));
-    m_receiver->SetReceiveCallback(MakeCallback(&AnnexC_TestCase::Receive, this));
-
-    // at 0s, the receiver is assigned AlternatingAccess  for SCH1
-    SchInfo infoReceiver = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-    Simulator::Schedule(MilliSeconds(0), &WaveNetDevice::StartSch, m_receiver, infoReceiver);
-
-    // at 0s, the sender is assigned AlternatingAccess for SCH1
-    SchInfo infoSender = SchInfo(SCH1, false, EXTENDED_ALTERNATING);
-    Simulator::Schedule(MilliSeconds(0), &WaveNetDevice::StartSch, m_sender, infoSender);
-
-    TxInfo txInfo = TxInfo(CCH, 0, WifiMode("OfdmRate3MbpsBW10MHz"), WIFI_PREAMBLE_LONG, 0);
-    // the packet size with 2312 bytes costs 6.42s, which will cancel this transmission in the CCH
-    // Interval so the receiver will receive this packet in next CCH Interval
-    Simulator::Schedule(MilliSeconds(45), &AnnexC_TestCase::SendPacket, this, 2304, txInfo, 1);
-
-    // the packet size with 312 bytes costs 1.104ms, which will not cancel transmission in the CCH
-    // Interval so the receiver can this packet is this CCH Interval
-    Simulator::Schedule(MilliSeconds(145), &AnnexC_TestCase::SendPacket, this, 312, txInfo, 2);
-
-    txInfo = TxInfo(SCH1, 0, WifiMode("OfdmRate3MbpsBW10MHz"), WIFI_PREAMBLE_LONG, 0);
-    // the packet size with 2312 bytes costs 6.42ms, which will cancel this transmission in the SCH
-    // Interval so the receiver will receive this packet in next SCH Interval
-    Simulator::Schedule(MilliSeconds(295), &AnnexC_TestCase::SendPacket, this, 2304, txInfo, 3);
-
-    // the packet size with 312 bytes costs 1.104ms, which will not cancel transmission in the SCH
-    // Interval so the receiver can this packet is this SCH Interval
-    Simulator::Schedule(MilliSeconds(395), &AnnexC_TestCase::SendPacket, this, 312, txInfo, 4);
-
-    Simulator::Stop(Seconds(1.0));
-    Simulator::Run();
-    Simulator::Destroy();
-}
-
-/**
- * \ingroup wave-test
- * \ingroup tests
- *
- * \brief Wave Mac Test Suite
- */
-class WaveMacTestSuite : public TestSuite
-{
-  public:
-    WaveMacTestSuite();
-};
-
-WaveMacTestSuite::WaveMacTestSuite()
-    : TestSuite("wave-mac-extension", UNIT)
-{
-    // TestDuration for TestCase can be QUICK, EXTENSIVE or TAKES_FOREVER
-    AddTestCase(new ChannelCoordinationTestCase, TestCase::QUICK);
-    AddTestCase(new ChannelRoutingTestCase, TestCase::QUICK);
-    AddTestCase(new ChannelAccessTestCase, TestCase::QUICK);
-    AddTestCase(new AnnexC_TestCase, TestCase::QUICK);
-}
-
-// Do not forget to allocate an instance of this TestSuite
-static WaveMacTestSuite waveMacTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wave/test/ocb-test-suite.cc ns-3.39/src/wave/test/ocb-test-suite.cc
--- ns-3.38/src/wave/test/ocb-test-suite.cc	2023-03-16 20:54:23.848869300 -0700
+++ ns-3.39/src/wave/test/ocb-test-suite.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,507 +0,0 @@
-/*
- * Copyright (c) 2013 Dalian University of Technology
- *
- * 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: Junling Bu <linlinjavaer@gmail.com>
- */
-
-#include "ns3/config.h"
-#include "ns3/data-rate.h"
-#include "ns3/mobility-helper.h"
-#include "ns3/mobility-model.h"
-#include "ns3/ocb-wifi-mac.h"
-#include "ns3/packet-socket-address.h"
-#include "ns3/packet-socket-client.h"
-#include "ns3/packet-socket-helper.h"
-#include "ns3/packet-socket-server.h"
-#include "ns3/position-allocator.h"
-#include "ns3/qos-txop.h"
-#include "ns3/rng-seed-manager.h"
-#include "ns3/sta-wifi-mac.h"
-#include "ns3/string.h"
-#include "ns3/test.h"
-#include "ns3/vector.h"
-#include "ns3/wave-mac-helper.h"
-#include "ns3/wifi-80211p-helper.h"
-#include "ns3/wifi-net-device.h"
-#include "ns3/yans-wifi-helper.h"
-
-#include <iostream>
-
-using namespace ns3;
-
-// helper function to assign streams to random variables, to control
-// randomness in the tests
-static void
-AssignWifiRandomStreams(Ptr<WifiMac> mac, int64_t stream)
-{
-    int64_t currentStream = stream;
-    PointerValue ptr;
-    if (!mac->GetQosSupported())
-    {
-        mac->GetAttribute("Txop", ptr);
-        Ptr<Txop> txop = ptr.Get<Txop>();
-        currentStream += txop->AssignStreams(currentStream);
-    }
-    else
-    {
-        mac->GetAttribute("VO_Txop", ptr);
-        Ptr<QosTxop> vo_txop = ptr.Get<QosTxop>();
-        currentStream += vo_txop->AssignStreams(currentStream);
-
-        mac->GetAttribute("VI_Txop", ptr);
-        Ptr<QosTxop> vi_txop = ptr.Get<QosTxop>();
-        currentStream += vi_txop->AssignStreams(currentStream);
-
-        mac->GetAttribute("BE_Txop", ptr);
-        Ptr<QosTxop> be_txop = ptr.Get<QosTxop>();
-        currentStream += be_txop->AssignStreams(currentStream);
-
-        mac->GetAttribute("BK_Txop", ptr);
-        Ptr<QosTxop> bk_txop = ptr.Get<QosTxop>();
-        currentStream += bk_txop->AssignStreams(currentStream);
-    }
-}
-
-/**
- * \ingroup wave-test
- * \ingroup tests
- *
- * \brief Ocb Wifi Mac Test Case
- */
-class OcbWifiMacTestCase : public TestCase
-{
-  public:
-    OcbWifiMacTestCase();
-    ~OcbWifiMacTestCase() override;
-
-  private:
-    void DoRun() override;
-
-    /**
-     * MAC associate function
-     * \param context the context
-     * \param bssid the BSSID
-     */
-    void MacAssoc(std::string context, Mac48Address bssid);
-    /**
-     * Phy receive ok trace function
-     * \param context the context
-     * \param packet the packet
-     * \param snr the SNR
-     * \param mode the mode
-     * \param preamble the preamble
-     */
-    void PhyRxOkTrace(std::string context,
-                      Ptr<const Packet> packet,
-                      double snr,
-                      WifiMode mode,
-                      WifiPreamble preamble);
-    /**
-     * Phy transmit trace function
-     * \param context the context
-     * \param packet the packet
-     * \param mode the mode
-     * \param preamble the preamble
-     * \param txPower the transmit power
-     */
-    void PhyTxTrace(std::string context,
-                    Ptr<const Packet> packet,
-                    WifiMode mode,
-                    WifiPreamble preamble,
-                    uint8_t txPower);
-    /**
-     * Get current position function
-     * \param i the current position index
-     * \returns the current position vector
-     */
-    Vector GetCurrentPosition(uint32_t i);
-    /**
-     * Advance position function
-     * \param node the node
-     */
-    void AdvancePosition(Ptr<Node> node);
-
-    /// Pre random configuration function
-    void PreRandomConfiguration();
-    /**
-     * Configure AP STA mode function
-     * \param static_node the static node
-     * \param mobile_node the mobile node
-     */
-    void ConfigureApStaMode(Ptr<Node> static_node, Ptr<Node> mobile_node);
-    /**
-     * Configure adhoc mode function
-     * \param static_node the static node
-     * \param mobile_node the mobile node
-     */
-    void ConfigureAdhocMode(Ptr<Node> static_node, Ptr<Node> mobile_node);
-    /**
-     * Configure OCB mode function
-     * \param static_node the static node
-     * \param mobile_node the mobile node
-     */
-    void ConfigureOcbMode(Ptr<Node> static_node, Ptr<Node> mobile_node);
-    /**
-     * Post device configuration function
-     * \param static_node the static node
-     * \param mobile_node the mobile node
-     */
-    void PostDeviceConfiguration(Ptr<Node> static_node, Ptr<Node> mobile_node);
-
-    Time phytx_time;  ///< Phy transmit time
-    Vector phytx_pos; ///< Phy transmit position
-
-    Time macassoc_time;  ///< MAC associate time
-    Vector macassoc_pos; ///< MAC associate position
-
-    Time phyrx_time;  ///< Phy receive time
-    Vector phyrx_pos; ///< Phy receive position
-
-    // nodes.Get (0) is static node
-    // nodes.Get (1) is mobile node
-    NodeContainer nodes; ///< the nodes
-};
-
-OcbWifiMacTestCase::OcbWifiMacTestCase()
-    : TestCase("Association time: Ap+Sta mode vs Adhoc mode vs Ocb mode")
-{
-}
-
-OcbWifiMacTestCase::~OcbWifiMacTestCase()
-{
-}
-
-// mobility is like walk on line with velocity 5 m/s
-// We prefer to update 0.5m every 0.1s rather than 5m every 1s
-void
-OcbWifiMacTestCase::AdvancePosition(Ptr<Node> node)
-{
-    Ptr<MobilityModel> mobility = node->GetObject<MobilityModel>();
-    Vector pos = mobility->GetPosition();
-    pos.x -= 0.5;
-    if (pos.x < 1.0)
-    {
-        pos.x = 1.0;
-        return;
-    }
-    mobility->SetPosition(pos);
-
-    Simulator::Schedule(Seconds(0.1), &OcbWifiMacTestCase::AdvancePosition, this, node);
-}
-
-// here are only two nodes, a stationary and a mobile one
-// the i value of the first = 0; the i value of second = 1.
-Vector
-OcbWifiMacTestCase::GetCurrentPosition(uint32_t i)
-{
-    NS_ASSERT(i < 2);
-    Ptr<Node> node = nodes.Get(i);
-    Ptr<MobilityModel> mobility = node->GetObject<MobilityModel>();
-    Vector pos = mobility->GetPosition();
-    return pos;
-}
-
-void
-OcbWifiMacTestCase::MacAssoc(std::string context, Mac48Address bssid)
-{
-    if (macassoc_time == Time(0))
-    {
-        macassoc_time = Now();
-        macassoc_pos = GetCurrentPosition(1);
-        std::cout << "MacAssoc time = " << macassoc_time.As(Time::NS)
-                  << " position = " << macassoc_pos << std::endl;
-    }
-}
-
-// We want to get the time that sta receives the first beacon frame from AP
-// it means that in this time this sta has ability to receive frame
-void
-OcbWifiMacTestCase::PhyRxOkTrace(std::string context,
-                                 Ptr<const Packet> packet,
-                                 double snr,
-                                 WifiMode mode,
-                                 WifiPreamble preamble)
-{
-    if (phyrx_time == Time(0))
-    {
-        phyrx_time = Now();
-        phyrx_pos = GetCurrentPosition(1);
-        std::cout << "PhyRxOk time = " << phyrx_time.As(Time::NS) << " position = " << phyrx_pos
-                  << std::endl;
-    }
-}
-
-// We want to get the time that STA sends the first data packet successfully
-void
-OcbWifiMacTestCase::PhyTxTrace(std::string context,
-                               Ptr<const Packet> packet,
-                               WifiMode mode,
-                               WifiPreamble preamble,
-                               uint8_t txPower)
-{
-    WifiMacHeader h;
-    packet->PeekHeader(h);
-    if ((phytx_time == Time(0)) && h.IsData())
-    {
-        phytx_time = Now();
-        phytx_pos = GetCurrentPosition(1);
-        std::cout << "PhyTx data time = " << phytx_time.As(Time::NS) << " position = " << phytx_pos
-                  << std::endl;
-    }
-}
-
-void
-OcbWifiMacTestCase::ConfigureApStaMode(Ptr<Node> static_node, Ptr<Node> mobile_node)
-{
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
-    YansWifiPhyHelper wifiPhy;
-    wifiPhy.SetChannel(wifiChannel.Create());
-
-    Ssid ssid = Ssid("wifi-default");
-    WifiMacHelper wifiStaMac;
-    wifiStaMac.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid));
-    WifiMacHelper wifiApMac;
-    wifiApMac.SetType("ns3::ApWifiMac", "Ssid", SsidValue(ssid));
-
-    WifiHelper wifi;
-    wifi.SetStandard(WIFI_STANDARD_80211p);
-    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                 "DataMode",
-                                 StringValue("OfdmRate6MbpsBW10MHz"),
-                                 "ControlMode",
-                                 StringValue("OfdmRate6MbpsBW10MHz"));
-    wifi.Install(wifiPhy, wifiStaMac, mobile_node);
-    wifi.Install(wifiPhy, wifiApMac, static_node);
-}
-
-void
-OcbWifiMacTestCase::ConfigureAdhocMode(Ptr<Node> static_node, Ptr<Node> mobile_node)
-{
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
-    YansWifiPhyHelper wifiPhy;
-    wifiPhy.SetChannel(wifiChannel.Create());
-
-    WifiMacHelper wifiMac;
-    wifiMac.SetType("ns3::AdhocWifiMac");
-
-    WifiHelper wifi;
-    wifi.SetStandard(WIFI_STANDARD_80211p);
-    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                 "DataMode",
-                                 StringValue("OfdmRate6MbpsBW10MHz"),
-                                 "ControlMode",
-                                 StringValue("OfdmRate6MbpsBW10MHz"));
-    wifi.Install(wifiPhy, wifiMac, mobile_node);
-    wifi.Install(wifiPhy, wifiMac, static_node);
-}
-
-void
-OcbWifiMacTestCase::ConfigureOcbMode(Ptr<Node> static_node, Ptr<Node> mobile_node)
-{
-    YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default();
-    YansWifiPhyHelper wifiPhy;
-    wifiPhy.SetChannel(wifiChannel.Create());
-
-    NqosWaveMacHelper wifi80211pMac = NqosWaveMacHelper::Default();
-
-    Wifi80211pHelper wifi80211p = Wifi80211pHelper::Default();
-    wifi80211p.SetRemoteStationManager("ns3::ConstantRateWifiManager",
-                                       "DataMode",
-                                       StringValue("OfdmRate6MbpsBW10MHz"),
-                                       "ControlMode",
-                                       StringValue("OfdmRate6MbpsBW10MHz"));
-    wifi80211p.Install(wifiPhy, wifi80211pMac, mobile_node);
-    wifi80211p.Install(wifiPhy, wifi80211pMac, static_node);
-}
-
-void
-OcbWifiMacTestCase::PostDeviceConfiguration(Ptr<Node> static_node, Ptr<Node> mobile_node)
-{
-    Ptr<WifiNetDevice> static_device = DynamicCast<WifiNetDevice>(static_node->GetDevice(0));
-    Ptr<WifiNetDevice> mobile_device = DynamicCast<WifiNetDevice>(mobile_node->GetDevice(0));
-
-    // Fix the stream assignment to the Dcf Txop objects (backoffs)
-    // The below stream assignment will result in the Txop object
-    // using a backoff value of zero for this test when the
-    // Txop::EndTxNoAck() calls to StartBackoffNow()
-    AssignWifiRandomStreams(static_device->GetMac(), 21);
-    AssignWifiRandomStreams(mobile_device->GetMac(), 22);
-
-    // setup mobility
-    // the initial position of static node is at 0,
-    // and the initial position of mobile node is 350.
-    MobilityHelper mobility;
-    mobility.Install(mobile_node);
-    mobility.Install(static_node);
-    Ptr<MobilityModel> mm = mobile_node->GetObject<MobilityModel>();
-    Vector possta = mm->GetPosition();
-    possta.x = 350;
-    mm->SetPosition(possta);
-    Simulator::Schedule(Seconds(1.0), &OcbWifiMacTestCase::AdvancePosition, this, mobile_node);
-
-    PacketSocketAddress socket;
-    socket.SetSingleDevice(mobile_device->GetIfIndex());
-    socket.SetPhysicalAddress(static_device->GetAddress());
-    socket.SetProtocol(1);
-
-    // give packet socket powers to nodes.
-    PacketSocketHelper packetSocket;
-    packetSocket.Install(static_node);
-    packetSocket.Install(mobile_node);
-
-    Ptr<PacketSocketClient> client = CreateObject<PacketSocketClient>();
-    client->SetRemote(socket);
-    mobile_node->AddApplication(client);
-    client->SetStartTime(Seconds(0.5));
-    client->SetStopTime(Seconds(70.0));
-
-    Ptr<PacketSocketServer> server = CreateObject<PacketSocketServer>();
-    server->SetLocal(socket);
-    static_node->AddApplication(server);
-    server->SetStartTime(Seconds(0.0));
-    server->SetStopTime(Seconds(70.5));
-
-    phytx_time = macassoc_time = phyrx_time = Time();
-    phytx_pos = macassoc_pos = phyrx_pos = Vector();
-
-    if (DynamicCast<StaWifiMac>(mobile_device->GetMac()))
-    {
-        // This trace is available only in a StaWifiMac
-        Config::Connect("/NodeList/1/DeviceList/*/Mac/Assoc",
-                        MakeCallback(&OcbWifiMacTestCase::MacAssoc, this));
-    }
-    Config::Connect("/NodeList/1/DeviceList/*/Phy/State/RxOk",
-                    MakeCallback(&OcbWifiMacTestCase::PhyRxOkTrace, this));
-    Config::Connect("/NodeList/1/DeviceList/*/Phy/State/Tx",
-                    MakeCallback(&OcbWifiMacTestCase::PhyTxTrace, this));
-}
-
-/**
- *
- *   static-node:0    <----       mobile-node:1
- *        *   ------ 350m -------    *
- *
- * the node transmit range is less than 150m
- *
- * Ap+Sta mode vs Adhoc mode vs Ocb mode
- * first test the time point when the stationary node is
- * an AP and the mobile node is a Sta
- * then test when one Ad-hoc node and another Ad-hoc node
- * last test when one OCB node and another OCB node
- */
-void
-OcbWifiMacTestCase::DoRun()
-{
-    std::cout << "test time point for Ap-Sta mode" << std::endl;
-    PreRandomConfiguration();
-    nodes = NodeContainer();
-    nodes.Create(2);
-    Ptr<Node> static_node = nodes.Get(0);
-    Ptr<Node> mobile_node = nodes.Get(1);
-    ConfigureApStaMode(static_node, mobile_node);
-    PostDeviceConfiguration(static_node, mobile_node);
-    Simulator::Stop(Seconds(71.0));
-    Simulator::Run();
-    Simulator::Destroy();
-    NS_TEST_ASSERT_MSG_LT(
-        phyrx_time,
-        macassoc_time,
-        "In Sta mode with AP, you cannot associate until receive beacon or AssocResponse frame");
-    NS_TEST_ASSERT_MSG_LT(macassoc_time,
-                          phytx_time,
-                          "In Sta mode with AP,  you cannot send data packet until associate");
-    // Are these position tests redundant with time check tests?
-    // NS_TEST_ASSERT_MSG_GT ((phyrx_pos.x - macassoc_pos.x), 0.0, "");
-    // actually macassoc_pos.x - phytx_pos.x is greater than 0
-    // however associate switch to send is so fast with less than 100ms
-    // and in our mobility model that every 0.1s update position,
-    // so turn out to be that macassoc_pos.x - phytx_pos.x is equal to 0
-    // NS_TEST_ASSERT_MSG_GT ((macassoc_pos.x - phytx_pos.x), 0.0, "");
-
-    std::cout << "test time point for Adhoc mode" << std::endl;
-    PreRandomConfiguration();
-    nodes = NodeContainer();
-    nodes.Create(2);
-    static_node = nodes.Get(0);
-    mobile_node = nodes.Get(1);
-    ConfigureAdhocMode(static_node, mobile_node);
-    PostDeviceConfiguration(static_node, mobile_node);
-    Simulator::Stop(Seconds(71.0));
-    Simulator::Run();
-    Simulator::Destroy();
-    // below test assert will fail, because AdhocWifiMac has not implement state machine.
-    // if someone takes a look at the output in adhoc mode and in Ocb mode
-    // he will find these two outputs are almost same.
-    // NS_TEST_ASSERT_MSG_LT (phyrx_time, macassoc_time, "In Adhoc mode, you cannot associate until
-    // receive beacon or AssocResponse frame" ); NS_TEST_ASSERT_MSG_LT (macassoc_time, phytx_time,
-    // "In Adhoc mode,  you cannot send data packet until associate" ); NS_TEST_ASSERT_MSG_GT
-    // ((phyrx_pos.x - macassoc_pos.x), 0.0, "");
-    // below test assert result refer to Ap-Sta mode
-    // NS_TEST_ASSERT_MSG_GT ((macassoc_pos.x - phytx_pos.x), 0.0, "");
-
-    std::cout << "test time point for Ocb mode" << std::endl;
-    PreRandomConfiguration();
-    nodes = NodeContainer();
-    nodes.Create(2);
-    static_node = nodes.Get(0);
-    mobile_node = nodes.Get(1);
-    ConfigureOcbMode(static_node, mobile_node);
-    PostDeviceConfiguration(static_node, mobile_node);
-    Simulator::Stop(Seconds(71.0));
-    Simulator::Run();
-    Simulator::Destroy();
-    NS_TEST_ASSERT_MSG_EQ(macassoc_time.GetNanoSeconds(),
-                          0,
-                          "In Ocb mode, there is no associate state machine");
-    NS_TEST_ASSERT_MSG_LT(phytx_time,
-                          phyrx_time,
-                          "before mobile node receives frames from far static node, it can send "
-                          "data packet directly");
-    NS_TEST_ASSERT_MSG_EQ(macassoc_pos.x, 0.0, "");
-    NS_TEST_ASSERT_MSG_GT((phytx_pos.x - phyrx_pos.x), 0.0, "");
-}
-
-void
-OcbWifiMacTestCase::PreRandomConfiguration()
-{
-    // Assign a seed and run number, and later fix the assignment of streams to
-    // WiFi random variables, so that the first backoff used is zero slots
-    RngSeedManager::SetSeed(1);
-    RngSeedManager::SetRun(17);
-    // the WiFi random variables is set in PostDeviceConfiguration method.
-}
-
-/**
- * \ingroup wave-test
- * \ingroup tests
- *
- * \brief Ocb Test Suite
- */
-class OcbTestSuite : public TestSuite
-{
-  public:
-    OcbTestSuite();
-};
-
-OcbTestSuite::OcbTestSuite()
-    : TestSuite("wave-80211p-ocb", UNIT)
-{
-    // TestDuration for TestCase can be QUICK, EXTENSIVE or TAKES_FOREVER
-    AddTestCase(new OcbWifiMacTestCase, TestCase::QUICK);
-}
-
-// Do not forget to allocate an instance of this TestSuite
-static OcbTestSuite ocbTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/CMakeLists.txt ns-3.39/src/wifi/CMakeLists.txt
--- ns-3.38/src/wifi/CMakeLists.txt	2023-03-16 20:54:23.848869300 -0700
+++ ns-3.39/src/wifi/CMakeLists.txt	2023-07-05 14:23:48.101428300 -0700
@@ -25,6 +25,7 @@
     model/channel-access-manager.cc
     model/ctrl-headers.cc
     model/edca-parameter-set.cc
+    model/eht/default-emlsr-manager.cc
     model/eht/eht-capabilities.cc
     model/eht/eht-configuration.cc
     model/eht/eht-frame-exchange-manager.cc
@@ -32,6 +33,7 @@
     model/eht/tid-to-link-mapping-element.cc
     model/eht/eht-phy.cc
     model/eht/eht-ppdu.cc
+    model/eht/emlsr-manager.cc
     model/eht/multi-link-element.cc
     model/error-rate-model.cc
     model/extended-capabilities.cc
@@ -73,10 +75,10 @@
     model/non-ht/erp-ofdm-ppdu.cc
     model/non-ht/ofdm-phy.cc
     model/non-ht/ofdm-ppdu.cc
+    model/non-inheritance.cc
     model/originator-block-ack-agreement.cc
     model/phy-entity.cc
     model/preamble-detection-model.cc
-    model/qos-blocked-destinations.cc
     model/qos-frame-exchange-manager.cc
     model/qos-txop.cc
     model/qos-utils.cc
@@ -116,10 +118,10 @@
     model/wifi-ack-manager.cc
     model/wifi-acknowledgment.cc
     model/wifi-assoc-manager.cc
+    model/wifi-bandwidth-filter.cc
     model/wifi-default-ack-manager.cc
     model/wifi-default-assoc-manager.cc
     model/wifi-default-protection-manager.cc
-    model/wifi-information-element-vector.cc
     model/wifi-information-element.cc
     model/wifi-mac-header.cc
     model/wifi-mac-queue-container.cc
@@ -174,6 +176,7 @@
     model/channel-access-manager.h
     model/ctrl-headers.h
     model/edca-parameter-set.h
+    model/eht/default-emlsr-manager.h
     model/eht/eht-capabilities.h
     model/eht/eht-configuration.h
     model/eht/eht-frame-exchange-manager.h
@@ -181,6 +184,7 @@
     model/eht/tid-to-link-mapping-element.h
     model/eht/eht-phy.h
     model/eht/eht-ppdu.h
+    model/eht/emlsr-manager.h
     model/eht/multi-link-element.h
     model/error-rate-model.h
     model/extended-capabilities.h
@@ -222,10 +226,10 @@
     model/non-ht/erp-ofdm-ppdu.h
     model/non-ht/ofdm-phy.h
     model/non-ht/ofdm-ppdu.h
+    model/non-inheritance.h
     model/originator-block-ack-agreement.h
     model/phy-entity.h
     model/preamble-detection-model.h
-    model/qos-blocked-destinations.h
     model/qos-frame-exchange-manager.h
     model/qos-txop.h
     model/qos-utils.h
@@ -266,10 +270,10 @@
     model/wifi-ack-manager.h
     model/wifi-acknowledgment.h
     model/wifi-assoc-manager.h
+    model/wifi-bandwidth-filter.h
     model/wifi-default-ack-manager.h
     model/wifi-default-assoc-manager.h
     model/wifi-default-protection-manager.h
-    model/wifi-information-element-vector.h
     model/wifi-information-element.h
     model/wifi-mac-header.h
     model/wifi-mac-queue-container.h
@@ -279,6 +283,7 @@
     model/wifi-mac-queue.h
     model/wifi-mac-trailer.h
     model/wifi-mac.h
+    model/wifi-mgt-header.h
     model/wifi-mode.h
     model/wifi-mpdu-type.h
     model/wifi-mpdu.h
@@ -332,6 +337,7 @@
     test/wifi-aggregation-test.cc
     test/wifi-dynamic-bw-op-test.cc
     test/wifi-eht-info-elems-test.cc
+    test/wifi-emlsr-test.cc
     test/wifi-error-rate-models-test.cc
     test/wifi-ie-fragment-test.cc
     test/wifi-mac-ofdma-test.cc
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/doc/source/figures/cca-channel-switching-multiple-interfaces.pdf ns-3.39/src/wifi/doc/source/figures/cca-channel-switching-multiple-interfaces.pdf
--- ns-3.38/src/wifi/doc/source/figures/cca-channel-switching-multiple-interfaces.pdf	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/doc/source/figures/cca-channel-switching-multiple-interfaces.pdf	2023-07-05 14:23:48.101428300 -0700
@@ -0,0 +1,293 @@
+%PDF-1.3
+%Äåòåë§ó ÐÄÆ
+3 0 obj
+<< /Filter /FlateDecode /Length 1981 >>
+stream
+xµYÛjG}Ÿ¯¨ÇÝµú~c°Ö;`l£…?I¶Vë‹’ÏÏéžéžî�Ù‹‚Œ´ÍÔTW�:uªü“Î?Ò‹tþ~õî5	zù’.^¯èý$)sA{RA1Íƒ à
“Â:Rôë–~§-izOç«GA«ËŽSüº\áQ‘>ž	Ø°Ù†f!Ú¸~H{€1Õã´¡K¼ñÍûUïL×;#kgçÌçH¸À”s’LÐÌ
+X4–)®mÈ>%®‰3§ÁvåÓx�¯Ép1Øô<Û”ƒM=Úü:ñLµž�d%F«xFÑ‚9R‚œÓŒ[¯IÂ)Á•��ºXÓg\jAëk>Enø±~ óõZ�èÖ_é3->\,öÅåå’¾Ðú7z³®C:äW×Ž—üJewóÛMó›^¾7¿R¹=ùÅIwò;8cg�«5 …´& m™äÖÖùíš(æüVY3�èJ/²M“mêÁæ�ßxÎ*¿ƒg¶õì¨•„¼®É/¢ÕäWÅkH£"¼ÑºÉ¯›ä·+ù¥>¿H,
+jq0¿®vÜk`Çp@
+„²š”–H´S%¤9Í˜ÇBêµÈ6ÓÞ&l©o=;jå„�ÐRÐ�¤•Œ{áþ—ˆ†ìwa§#�àù/‘+·,x¡Hy�ò
ŠB`Âø I	fB°bBO© ¯»‘„�Ìy!P.â´9‘™`„zŒHÔ}�‘ÕÝÕv{»Yv(ôêà%ÕïŒDÍ»8¦cJE^’8P:˜�µ‡“GV±›
+hñ’¤òñ`]MnÏ8XªÓŠë&î7ä\8å¼r ‡u«ËÃ<3ÉÆ±Ö�
µ0îÄÏ†þZ-çÈ¤
Z`YØÀœD.r”'¥:À§Š2jÓÀ4HXb;ÁÎÀ/#v>ü¸ý…Âiqõt¿ýF×=Ýˆ>6Ã�,…ívDƒ±Ü#Îè AhÒ\ƒUÐž�öé‘Ç«Žy”ÙÛDël¢P’Íù>�]‹|Ø�zæ$3M‹™Y9ƒÂF£ôªŒ«±Œ¶½¿Q—"M�ú*}ßVßo—Êvs¤n"µðÁ8H´mc®¸ì	lŽ1¼•@Q2™ƒm¬éM$xQ˜2òáq+'¼·PVr3‡ºÆôL¨'=su·7ÔQ+m&Ùv'ÃïÖ®,äÏjÀi^Ip.bÔ.|‡D•=õO’0…³
+†l¬oõß”"'õ›¹¿§ÈG\ÓâŸû§%4ú"3
+»âN:¿üqµMdõêúéï«Íúöß'ZüÉ…\æî06§}Ž²ýŽI+,ÔmÓ™´¤õ_£"�„³tœêm l%¥$Ä%Åã´2ùì^ŠŸ½ììÈ]MaÎ1(53Î=$ó54œ7Ð´ÓP¼¯›¹6>»¡»4)ÅÙHD•Œè*§â.k‚<Fí´ <�/ž„ìà‘î•zé@‚£)Ù q×ì0Dô¿tq¤*º.Nzˆg3\öçÀÙ “‡k¾œÍ¡Ë×÷uñsOOÕ³ÜÛódìy…�”¾PHM“ZÀ„£™ççéÒBb0„¾‚‘ÀŒw­´ÐûèxÄÉÇ·,É* eµZBåÐº)ê'v¿½¹¿F3üžxL]DÇ.U@‚ÖòQë´P;Ã`õ@ùZ§@
+€<°�î›»Vž= Ì,@˜ÃSª‡9-(ŽƒGÀv3†g¥1­PšÉ'ò»€x6¥1SÅBÀÙ²ueDº°x ½½/žcîé•1LÑÕû	Ùt?gTL=ˆW&�¨‹;�\#Ó8ƒF°GP /Õ]sÚLCï0y?A‘Šæ”—MûÔèç(¿AåšâE\lPÅX¼/_CäËµÓTSÐçÄ7_£›D‰ŽXÎj‚øÂ6ÚŽ3©¤µë·=e‘S¯wQfÐÕL�:¡BPÆ_ðž~3PPvð]P‚†'ÊE\0ûÅt‰Éú·Æ‘«�‹ÆÇáZ^t7}Ø­Ð7dBímûªmû@}PèvJbÇ…óŒôµ·ï;ßˆJƒí‡Sèá
+ÑòÆ„ºÿÍ£2îƒž‡Ê¨7T&A�Ö�'	‚y+Aà°âãûx;ªJÖ#–|ÌY¬N
+ ¨\«¨ÓJ4ÕxßÌµ®<{�NëbH Œt:MÆ“Ù°ReOw ÓlÂüH³¨×¦OOßU
+ÀF¬¥3Žô›ßŠÖÒÓ/Àƒö¾xî¹§«¦&¥rôw…š*�=ê^-4¤$«tÐ¹
+õ,09xAÈ{Þyì¬ì‘I³0Óè�Î CY3œ8nŽ»²�¥å¶ï¿m¯6}/§«››ÛêÅðwtò.oHŸ…ø}>Uˆ‡rQK©ç"¾(£‘4r¨t�;¶Ò}Æ•ðt$hj¬ÃJøÝ¶?pìÏ�§ÅWøiÐ‡â¹ÝsÁí[,�’èIK€Qç<k@Øço«±2 þç
ª‘WVÆU¹ÆŠ
r;U�&P®A·rañ
+ÔÖé¾¹kùÙÔâÈ?«Ï�Ê+Eüã©ÞzŸ°�>EM>@ô³€ñ¡o}Øƒéøþ²SùéîXá#B<¦‹þ$ñtX5Äkø¿�t
´«¾½/UøÌÓ}…ú"Ü…ê
+endstream
+endobj
+1 0 obj
+<< /Type /Page /Parent 2 0 R /Resources 4 0 R /Contents 3 0 R /MediaBox [0 0 1237.012 577.7907]
+>>
+endobj
+4 0 obj
+<< /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 5 0 R >> /Font << /TT1 6 0 R
+/TT2 7 0 R >> >>
+endobj
+8 0 obj
+<< /N 3 /Alternate /DeviceRGB /Length 2612 /Filter /FlateDecode >>
+stream
+x�–wTSÙ‡Ï½7½Ð" %ôz	 Ò;HQ‰I€P†„&vDF)VdTÀG‡"cEƒ‚b×	òPÆÁQDEåÝŒk	ï­5óÞšýÇYßÙç·×Ùgï}×ºPü‚ÂtX€4¡XîëÁ\ËÄ÷XÀáffGøDÔü½=™™¨HÆ³öî.€d»Û,¿P&sÖÿ‘"7C$
+EÕ6<~&å”S³Å2ÿÊô•)2†12¡	¢¬"ãÄ¯lö§æ+»É˜—&ä¡YÎ¼4žŒ»PÞš%á£Œ¡\˜%àg£|e½TIšå÷(ÓÓøœL0™_Ìç&¡l‰2Eî‰ò”Ä9¼r‹ù9hžx¦gäŠ‰Ib¦×˜iåèÈfúñ³Sùb1+”ÃMáˆxLÏô´Ž0€¯o–E%Ym™h‘í­ííYÖæhù¿Ùß~Sý=ÈzûUñ&ìÏžAŒžYßlì¬/½ö$Z›³¾•U´m@åá¬Oï ò´Þœó†l^’Äâ'‹ììlsŸk.+è7ûŸ‚oÊ¿†9÷™ËîûV;¦?�#I3eEå¦§¦KDÌÌ—Ïdý÷ÿãÀ9iÍÉÃ,œŸÀñ…èUQè”	„‰h»…<�X�.d
+„Õá6'~�khu_}…9P¸IÈo=C#$n?z}ë[1
+È¾¼h­‘¯s�2zþçú\ŠnáLA"Sæö�dr%¢,£ß„lÁ�t 
+4�.0,`
€3pÞ „€H–.Hi@²A>Ø
+A1ØvƒjpÔ�zÐN‚6p\WÀ
p€G@
+†ÁK0Þ�i‚ð¢Aª�¤™BÖZyCAP8ÅC‰�’@ùÐ&¨*ƒª¡CP=ô#tº]ƒú Ð 4ý}„˜Óa
Ø¶€Ù°;GÂËàDxœÀÛáJ¸>·Âáð,…_Â“@ÈÑFXñDB�X$!k‘"¤©Eš�¤¹�H‘qä‡¡a˜Æã‡YŒábVaÖbJ0Õ˜c˜VLæ6f3�ù‚¥bÕ±¦X'¬?v	6›�-ÄV`�`[°—±Øaì;ÇÀâp~¸\2n5®·×Œ»€ëÃ
á&ñx¼*Þï‚Ásðb|!¾
+ß�Æ¿'�	Zk‚!– $l$Tçý„Â4Q�¨Ot"†yÄ\b)±ŽØA¼I&N“I†$R$)™´�TIj"]&=&½!“É:dGrY@^O®$Ÿ _%’?P”(&OJEBÙN9J¹@y@yC¥R
¨nÔXª˜º�ZO½D}J}/G“3—ó—ãÉ­“«‘k•ë—{%O”×—w—_.Ÿ'_!Jþ¦ü¸QÁ@ÁS�£°V¡Fá´Â=…IEš¢•bˆbšb‰bƒâ5ÅQ%¼’�’·O©@é°Ò%¥!BÓ¥yÒ¸´M´:ÚeÚ0G7¤ûÓ“éÅôè½ô	e%e[å(ååå³ÊRÂ0`ø3R¥Œ“Œ»Œ�ó4æ¹ÏãÏÛ6¯i^ÿ¼)•ù*n*|•"•f••�ªLUoÕÕ�ªmªOÔ0j&jajÙjûÕ.«�Ï§ÏwžÏ�_4ÿäü‡ê°º‰z¸újõÃê=ê“š¾U—4Æ5šnšÉšåšç4Ç´hZµZåZçµ^0•™îÌTf%³‹9¡­®í§-Ñ>¤Ý«=­c¨³Xg£N³Î]’.[7A·\·SwBOK/X/_¯Qï¡>QŸ­Ÿ¤¿G¿[ÊÀÐ Ú`‹A›Á¨¡Š¡¿aža£ác#ª‘«Ñ*£Z£;Æ8c¶qŠñ>ã[&°‰�I’I�ÉMSØÔÞT`ºÏ´Ïkæh&4«5»Ç¢°ÜYY¬FÖ 9Ã<È|£y›ù+=‹X‹�Ý_,í,S-ë,Y)YXm´ê°úÃÚÄšk]c}Ç†jãc³Î¦Ýæµ­©-ßv¿í};š]°Ý»N»Ïöö"û&û1=‡x‡½÷Øtv(»„}Õëèá¸ÎñŒã'{'±ÓI§ß�YÎ)Î
Î£ðÔ-rÑqá¸r‘.d.Œ_xp¡ÔUÛ•ãZëúÌM×�çvÄmÄÝØ=Ùý¸û+K‘G‹Ç”§“çÏ^ˆ—¯W‘W¯·’÷bïjï§>:>‰>�>¾v¾«}/øaýývúÝó×ðçú×ûO8¬	è
+¤FV>2	uÃÁÁ»‚/Ò_$\ÔBüCv…<	5]ús.,4¬&ìy¸Ux~xw-bEDCÄ»H�ÈÒÈG‹�KwFÉGÅEÕGME{E—EK—X,Y³äFŒZŒ ¦={$vr©÷ÒÝK‡ãìâ
+ãî.3\–³ìÚrµå©ËÏ®�_ÁYq*ßÿ‰Â©åL®ô_¹wå×“»‡û’çÆ+ç�ñ]øeü‘—„²„ÑD—Ä]‰cI®IIãOAµàu²_ò�ä©”�”£)3©Ñ©Íi„´ø´ÓB%aŠ°+]3='½/Ã4£0CºÊiÕîU¢@Ñ‘L(sYf»˜ŽþLõHŒ$›%ƒY³j²ÞgGeŸÊQÌæôäšänËÉóÉû~5f5wug¾vþ†üÁ5îk­…Ö®\Û¹Nw]Áºáõ¾ë�m mHÙðËFË�eßnŠÞÔQ Q°¾`h³ïæÆB¹BQá½-Î[lÅllíÝf³­jÛ—"^ÑõbËâŠâO%Ü’ëßY}WùÝÌö„í½¥ö¥ûwàvwÜÝéºóX™bY^ÙÐ®à]­åÌò¢ò·»Wì¾Va[q`i�d�´2¨²½J¯jGÕ§ê¤ê��šæ½ê{·í�ÚÇÛ×¿ßmÓ�Å>¼È÷Pk­AmÅaÜá¬ÃÏë¢êº¿g_DíHñ‘ÏG…G¥ÇÂ�uÕ;Ô×7¨7”6Â�’Æ±ãqÇoýàõC{«éP3£¹ø8!9ñâÇøïž<ÙyŠ}ªé'ýŸö¶ÐZŠZ¡ÖÜÖ‰¶¤6i{L{ßé€Ó�Î-?›ÿ|ôŒö™š³ÊgKÏ‘Îœ›9Ÿw~òBÆ…ñ‹‰‡:Wt>º´äÒ�®°®ÞË�—¯^ñ¹r©Û½ûüU—«g®9];}�}½í†ý�Ö»ž–_ì~iéµïm½ép³ý–ã­Ž¾}çú]û/Þöº}åŽÿ�‹úî.¾{ÿ^Ü=é}ÞýÑ©^?Ìz8ýhýcìã¢'
+O*žª?­ýÕø×f©½ôì ×`Ï³ˆg�†¸C/ÿ•ù¯OÃÏ©Ï+F´FêG­GÏŒùŒÝz±ôÅðËŒ—Óã…¿)þ¶÷•Ñ«Ÿ~wû½gbÉÄðkÑë™?JÞ¨¾9úÖömçdèäÓwiï¦§ŠÞ«¾?ö�ý¡ûcôÇ‘éìOøO•Ÿ�?w|	üòx&mfæß÷„óû
+endstream
+endobj
+5 0 obj
+[ /ICCBased 8 0 R ]
+endobj
+10 0 obj
+<< /Type /StructTreeRoot /K 9 0 R >>
+endobj
+9 0 obj
+<< /Type /StructElem /S /Document /P 10 0 R /K [ 11 0 R 12 0 R 13 0 R 14 0 R
+15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R
+26 0 R 27 0 R 28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R
+37 0 R 38 0 R 39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R 45 0 R 46 0 R ]  >>
+endobj
+11 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 1  >>
+endobj
+12 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 2  >>
+endobj
+13 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 3  >>
+endobj
+14 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 4  >>
+endobj
+15 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 5  >>
+endobj
+16 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 6  >>
+endobj
+17 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 7  >>
+endobj
+18 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 8  >>
+endobj
+19 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 9  >>
+endobj
+20 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 10  >>
+endobj
+21 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 11  >>
+endobj
+22 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 12  >>
+endobj
+23 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 13  >>
+endobj
+24 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 14  >>
+endobj
+25 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 15  >>
+endobj
+26 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 16  >>
+endobj
+27 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 17  >>
+endobj
+28 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 18  >>
+endobj
+29 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 19  >>
+endobj
+30 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 20  >>
+endobj
+31 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 21  >>
+endobj
+32 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 22  >>
+endobj
+33 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 23  >>
+endobj
+34 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 24  >>
+endobj
+35 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 25  >>
+endobj
+36 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 26  >>
+endobj
+37 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 27  >>
+endobj
+38 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 28  >>
+endobj
+39 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 29  >>
+endobj
+40 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 30  >>
+endobj
+41 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 31  >>
+endobj
+42 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 32  >>
+endobj
+43 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 33  >>
+endobj
+44 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 34  >>
+endobj
+45 0 obj
+<< /Type /StructElem /S /P /P 9 0 R /Pg 1 0 R /K 35  >>
+endobj
+46 0 obj
+<< /Type /StructElem /S /Figure /P 9 0 R /Pg 1 0 R /K 36  >>
+endobj
+2 0 obj
+<< /Type /Pages /MediaBox [0 0 612 792] /Count 1 /Kids [ 1 0 R ] >>
+endobj
+47 0 obj
+<< /Type /Catalog /Pages 2 0 R /MarkInfo << /Marked true >> /StructTreeRoot
+10 0 R >>
+endobj
+6 0 obj
+<< /Type /Font /Subtype /TrueType /BaseFont /AAAAAB+.SFNS-Regular_wdth_opsz120000_GRAD_wght24E0000
+/FontDescriptor 48 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar
+110 /Widths [ 263 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 709 678 730 0 0 0 0 0 0 0 0 0 0 0 779 0 0 0 659 0 0 0 0 0 0 0 0 0
+0 0 0 0 574 0 0 0 588 0 0 614 0 0 0 277 0 609 ] >>
+endobj
+48 0 obj
+<< /Type /FontDescriptor /FontName /AAAAAB+.SFNS-Regular_wdth_opsz120000_GRAD_wght24E0000
+/Flags 32 /FontBBox [-396 -275 2465 957] /ItalicAngle 0 /Ascent 967 /Descent
+-211 /CapHeight 705 /StemV 88 /XHeight 533 /StemH 79 /AvgWidth 580 /MaxWidth
+2493 /FontFile2 49 0 R >>
+endobj
+49 0 obj
+<< /Length1 2728 /Length 1540 /Filter /FlateDecode >>
+stream
+x¥V	lTU=ÿÏL°¬U #.�ˆP‘  Z¤¥µ²H‰BÛÊTii¡-…B€Q£È¾•BYU � V¨1€TÄD‹P0
+!¢  ýÇóþŸ©eKH¼É¼÷îy÷ßw×÷¦plQ6êc*|h2<7+.Y¯iÚ:"gÂËo‚{ÂÙY!�ÇÍ]Â<Þê¬¹m8·p|„¬¹cNÞðÈ¾ý¤øV¹Yã#úñƒø6£³r³5‹djHÎÏ+(4œø¶z¸Ëÿ3Xú8Ýã*1ÜÕdýzÇž“Ñ¸Çyøê¹ûG+·›ÅÑŠÔWÔì�Û\/ÖÛÝv¿	V;€¸sW\9·Íh¹Šb¥)dß	6 1�ƒÊÀ!Äô@A 	¹þ…H÷oA¦ÿÊí®(÷Ã}mE´ÄÊ^s^ô¿NïU‡ÜcËnÿM$Ü‹Ï�…‚ØMý×@óm‘u2’QŒóV¦µÓfó
òÍ÷­õý¬Ý„œfÁeÁå²!(ùÆˆC Áº1!ì
+\Ü£Z»¥Ùi¦3Žëw82“9ºïC¢2¬B@5ßè:K�h5²&¢/Àv´}w±ºf”ý�ÕÈ‹¥Z¤ÔÁŠþùVÌn æ3)\8AÕQ)¦žØvè`tKÄºv0€÷«ÝŠ£“õ+–â"-Ä
Ìe¹T¤ À˜mˆÙ�ÅÎR±E˜AÇ›)l%†c&zGÐŽqqŠ9Æ¡‡i¿
+“eè0Ü‹¨‡¦j�!˜�g„�Fž7rTð5Îç
+:,ã"—KqÇ}\Æ�„Îá./Iã½ÎF�F>‡ŒK0€ÈÅÍ²vQg)Ì×ë¬V«*,ú¤öw.—Ó«9ƒ}]×%ƒ,lÆD¼€y¡™Î©‘ig™î\äP>âd†­´Úñˆæ	8„wäx™•|�;ÜÝi8«slýÎ¢Ô ÜÆR¾ÏËZÖÇ\œÀTMq6²¹,i¡9EòÆ«=òª)î�ú%9×¡ÿ¼ª‹øZc¿�5F›NIwö±v¾æÁZíîçdöæ’FµN/Õ8ÂC¸X»Æ—�­4œ”'5W™=&;›ØBöµt60ÇMÇX)™UòÌ-énÊa’É`N’­¹<â?$ºÕõà®NŒáê îÒ^ƒ�
+þ,c†YÇ^3›k9›{=}”¨HÃ$¤*¨6.�¼È4“!6Žö�*n»B¾o¢Äû’[$õ.×p¬ªþ’¾
+Èìx»aÈÃ^ÉZªÐ2¼‚6¸GÓý/¬§´u”‹ûøwS}ˆ†ê�*IŠ8�=9Eç«\ÞæOü‘ÇUÓÙÉ•üÊÈHO:×yà9•^›§óŒ?èw•'HÆmR3GÖî*#3GÞØ¡+±h­Â.è.Qï*9Ý8‹’øMºâ�ƒç��°‘�Lµ
+öCn¤y…¶Æ¨s8‘}¸ÂÅ î­à­Tß>ª¯Fêî9­[b5	næ8®âE-ýèâ=£\ÏMÎz~ªÒÒ�%ê‡$eè)iˆAK¬æIeªRö-Wá”©	u•YÈäiÿ Å ½W9µ>êû¨ë^`êpÚ³G"O/X‘®­†°y;kxž»t�|¢Mgµ9_”¨W£¯(B/—WãÓ�à^´|•w«¸²ÙŠÓÝ=èÚ:ÅÓZ?&÷så¶I×%þÂ/hnwHÏl…+„‘êþT°ŠùœË2,ÓÚtß@uå¯€Uh©êÞ'OuÑú‡N¡¹<�:Íh$·å¸dÔêïÄ(<�1Êœ!e¨vs¦òúó¬ø AíI:²ØsŽSYZ3^Ø5éVdš}.åz!zmL¬e�µH(²Àð:ÎS$ÔH¨(üiÊÆ×Ù-¿ž9®‰©È—¡†\³ùSê¨ûñKe boïÄ.|gv=ó°Î^eøeôqòøÚQ¢ìn‹ûp¿š©‹ž då ?žu%,÷Þ6ËS®	†zµï�–Ô/í¡�Ù#Šr²Æf‡
+Ãyù%�â;Š2úLè�Q<"\ß9Ñø¸ry…
+endstream
+endobj
+7 0 obj
+<< /Type /Font /Subtype /TrueType /BaseFont /AAAAAC+.SFNS-Regular_wdth_opsz120000_GRAD_wght2BC0000
+/FontDescriptor 50 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar
+119 /Widths [ 255 0 0 0 0 0 0 0 0 0 0 0 0 482 343 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 670 0 0 730 691 740 0 0 0 0 781 316 0 0 0 0 0 785 671 0 0 0 0 0 0 0
+0 706 0 0 0 0 0 0 0 588 0 585 649 599 406 643 630 285 0 0 292 0 625 618 645
+0 432 564 410 0 0 845 ] >>
+endobj
+50 0 obj
+<< /Type /FontDescriptor /FontName /AAAAAC+.SFNS-Regular_wdth_opsz120000_GRAD_wght2BC0000
+/Flags 32 /FontBBox [-396 -275 2465 957] /ItalicAngle 0 /Ascent 967 /Descent
+-211 /CapHeight 705 /StemV 88 /XHeight 537 /StemH 79 /AvgWidth 580 /MaxWidth
+2493 /FontFile2 51 0 R >>
+endobj
+51 0 obj
+<< /Length1 5084 /Length 2934 /Filter /FlateDecode >>
+stream
+x¥˜ypUE‡Ï½ï½‘EÌ8‚fj@QQ@A¨�°#‹Á°$‘@bIB”,lE�!,T
+¢Cd#ZŠh"¥S.5*‘Rgœèƒ¸€äÝ3_÷}/$ÂVy^ÞíîÓ}OŸåwN÷KÞ=ù3ä<Y,¹`zvz®XrÞ¥©Íœ5o¦?vß¹`fÖŒô,
´½³`øcçzÚ.YÙys#ãI´y³fO�Ì»¥Œûg§Ï�È—ƒŒãsÒ³gøëãÌ|Rîì9y‘q_Ú)~ÿw<Þí,7IŒ•aFÍÉpøŽ;P’Ú¶ÿ�haç?/O|Åt>/q¤áãð»qÕ±Ï0ˆk§í;±{¼±"qß7|ÜðI\µ‘ÒŒ:#)Ãm)úTC÷KEè[ÉŽ(.ý˜¡’šo‹$wÊÄà’ê))Áý’(½B]%9ÔRÊÜ$Yì&KYH¤8xœ5WHq �þ}29�$}böJ¿ÀA)w_“ÊÐTY�ND“VØ\Í8^&�¥ÛEÿ(—œœ³×Ér]l6Î‡"þô±â;­qšÎyMôñƒœ/­¤µ´‘¶r�´³óòŒ“?ÈE´í¥ƒt´Ü‹í3I’¤P~tÒœeÎç¤›à¦»GÃÕÁÁüP›Ð˜Ð¢Pqè1×ÆlŠ©‹Í�}1öpì‰[ôB·ïÂØ
±O s,»¶e�"	Î¹I2bÁÚÉZ‘ßÖzFë¾¿üÆ6 ‰  4´YBø¥ÍYšH¢3ÀI—ÛE<O;zè®ð·ÚàµÔdë
+!ÜâÄ‹œ6 <,~�›YH@dƒn¯”kŒl–8¿~†ÿmœŠ8c�s\ÖË2�× }VïÒ�tFH�QÛ�®ÖuŽ�ˆU2‹ XÒ"]æÝoƒ¤Èáê_½)z§ƒ*Þ‡>OfÈn)BÑ©²F¦’va’Ü'£àÍ–Mb­Ôx³Z×gÔÓ­ú„ÝhŸèSºn™š"!z™o={K¶-ÕH7,gÊeÛmì4çâd»ÓF¶‚Ç«Cìÿt‹è6}XSt+/º]¶ÈÝ(ö 5ˆuÎålû½NòŽéíá} “ýeò”ÓSM%[,ÿ’µ˜%ú³¾¨Ó7ìü<‚U†‘ëå�ï`ÌX®Ûõ³­e�üW–›u:Ì{^Û£I{¯B‡¡½‰h	=¿9V¤Â¸Õ}#î
DÝì¦‹�¾cÂ»X5ÁV’S‡¤€/É—à®A�ñ`l'ŽúÐ¾iœyÎN ‹üÓé+Û˜„ô6ïí®Wyµ:ÑçÈ6f?Ö:^7E8i²]òùl—TŸ£›˜5~ÛíL�z´¨§}ÉÌá‹
+í€/:Ð&
×a|åÊcò¥Ì·k–ëešÉš,½T—1ƒ¿‚§°ç"¹ØØÉ_w‹Ó�€}°S�”#²ÈHa¯{µ³fzC˜¿[–Hq„›«EÞ#šåûØ©±Üùò9ÕÕ•RÚö][Át¡.C�ejyáQ`:6ãÓÐˆ:˜ÙÜqÜþF¦ó“WííÕcÞÞËÎÏF–{Ià¢‡·\üÓ0—¸MÔcÁ+ƒÓåÚhL¬)`[‹¢6ú­ÙIþãUŠ’S¥“Pªœxø&‡¯×`ÞeT%B>]Æ�½Ñ ìÕ5t§ÍóÒ™æä°™Z¨	ú¸aASd³£í&Ã¤’Yá#¸[ÉiH+5WŸÔŸè¥§ô±¼*Ýí•ëÛäñ~3¦´À™ýe%±£TéH›J½‡ì÷tèô–­¦áƒ®g|`ØQ+MëÛe¹ùÄð}0b2û8¢¾Ãì-Z¥¥êGQ¤¯dËh\“#8Àa�ž #¯Æ5µ­.1ºAEr\ëiï�2WbT5¬ÏësvnÙ<åsÀäÃÁØJõèpú.±÷#�¯ŽŽ@ƒážê<V§èÑàÚÐQÎ¡¿œ±æ×ú7µÍ¥Ûm ÎæÄtº²F�éWìºˆPåk_5§¾¡l©•ï¨>ÅºÏgàà\Šì(ž7û}“Ù£ô¯‘	ÒIä,ZIøyx7ËmÒÃ¬Óyêê´K[(ßÃ-%”q—ï£ôÍš*�MA6áÊÑªH´r‰Vwß¾³‚ãgL¶=DØŽ›‡‡wp¨¦"ºD-¬?é;DíuÝAEÞkö‚rðås6ZæÎXjºº„Ú™Æ'N#yM.ûñ»™Þ-×ËùDðýVßÓ:ó°[
+Œ$M–ù‡œ¾J&?®+?P�E&ç“²Â,æèóôV$Œ%–X
+èÝ¡Sæ¶’`íÁ óç7ÖpG¾2Á2gµ²‚Ã;ÉÕÀNô�¾¤éŽ›*3¥F;L/`.Ç¦,Ž#CFîðd/Yb/°ºg—èzøF:ñHöŠ‚«ƒSIFŽÊXV!ÓÚ~cÇ×ÍL6²švÜì=�7 ‹°zX7RwjžÞ «Ê–wðÉ(êg:ˆ¤¸ È"]	ÑÔóÁh%¥NÖƒ�}Ë¬€ú‘-£É–f8ôŠ˜É”=roðJäŒd4€E`+�º¦ë…~m%onå/h³Q×¡ ¡%Fô´n¦tìpMýÉ‰�‚_&[=a�…ú4’žÖ¹ú"n)£.-óímôXa\ÓèÛ+³±€“.ƒ$™eÖã\n§`¬nu¦
+g‡D+ÇO2}@W†Â{�íF¤ëtŽ³Å«�éC½hA�¯„}J‰Ù±%dDšy{÷€Ï'Mß4¾27•TÔÌ•
+Òr»j“o¡ó|ébÍñ…kvÁSÁùä*÷î„("|D�QÜÄƒ‰RSÔag9`¡oâ€‡Âœ‡n�¼%ŸXÝ¬ùbê§©„õÁµìý›ïî\yl.1’°s¡¶ÖI¡µâdKKäy†8¯j¬ò7�Ô‘à4Û ÆGO©­è+0·
+-ª0”{3*:|Œ&»¹Å	8+£Ú¸|#ÍšíÜ6JYSª39š\)öJƒï‘u~5o�-Og²Ð,Ãò½ÚŽB�.÷¢áJ*ö]f‚=VQÉÉšÅÜ�ŽøÐr/!ÿÑ7ýÀ+ZÍûûÝ§h‚S;ñþ> ù�å? åÄ•J	G*ØGZaÏîI
æ
+=te°b&H8SÍ×c¡©æ;|„±0®B|œ{£øHà^;Ž›ã-�ßj
uc
€7IŽLöÈõë¶žÔ“hË¢â>McÎG&ƒ‹+ÀE_v4ß��=ß§ve“Éæà7Q– KZMYx°Ö’TbKW¡L/TN¥P™€p0¬ÒÞ¨\¨=õáÈªDyHîäB:�:lIßçPÃ¯£õzTÕû…[rh}dA5I6©ù2Ptã(‹ç ìŒ‹»JoZH_Ñõ4� êÑÈožgÔ:Ž–íºTŸÓoôW–ÎÕÏtDç[¨C~™+'88Ë‘>zÚ­�·¿j}oà-“ÀQÉ(0Òüº'*¹/ìÒW­%8B/¥Â M†³Ír+­j-8h>æ6Ã?Ed„¢=ˆlâå�â
jSøeMÕ9hÑO$æ~jQ´ŠE77mºÓSö‘ão9½�®Þ:·
ð¾Ñ�´SÍÞæº„,¾¦š5 U­¢-ÒË�77¹û«X¹»ŽõYÙ`´aE¥~í¶qì¯$3Žþ¥’ùw°Bô	ªÂÎÈÅuº~
+ƒKëˆÆÙ¿soàµÕÒ=Xö�›±"X{ú=tëÊ�îÏh{¹õÎ|ï¸W)E^¥wÊ™‡,×ì�_ösƒ9™°¤¸œX\%×¼þxu÷˜$ôŽ&#ÉÅ[Îx™Èí+h˜ÿ�ü‰7ÎWl‚bäô24¨û5ã‡Œõ¸™ù³ÒïI-ÌÈËJ��;g~Ïë®…R‡ŽKœZ˜™•wÝ-ƒCþì3
+endstream
+endobj
+52 0 obj
+<< /Producer (macOS Version 13.3.1 \(assemblage 22E261\) Quartz PDFContext)
+/CreationDate (D:20230527092209Z00'00') /ModDate (D:20230527092209Z00'00')
+>>
+endobj
+xref
+0 53
+0000000000 65535 f 
+0000002076 00000 n 
+0000008108 00000 n 
+0000000022 00000 n 
+0000002190 00000 n 
+0000005010 00000 n 
+0000008293 00000 n 
+0000010595 00000 n 
+0000002298 00000 n 
+0000005098 00000 n 
+0000005045 00000 n 
+0000005420 00000 n 
+0000005496 00000 n 
+0000005572 00000 n 
+0000005643 00000 n 
+0000005719 00000 n 
+0000005795 00000 n 
+0000005866 00000 n 
+0000005942 00000 n 
+0000006013 00000 n 
+0000006089 00000 n 
+0000006161 00000 n 
+0000006238 00000 n 
+0000006310 00000 n 
+0000006387 00000 n 
+0000006464 00000 n 
+0000006536 00000 n 
+0000006613 00000 n 
+0000006685 00000 n 
+0000006762 00000 n 
+0000006834 00000 n 
+0000006911 00000 n 
+0000006983 00000 n 
+0000007055 00000 n 
+0000007132 00000 n 
+0000007209 00000 n 
+0000007281 00000 n 
+0000007358 00000 n 
+0000007435 00000 n 
+0000007507 00000 n 
+0000007584 00000 n 
+0000007661 00000 n 
+0000007733 00000 n 
+0000007810 00000 n 
+0000007887 00000 n 
+0000007959 00000 n 
+0000008031 00000 n 
+0000008191 00000 n 
+0000008681 00000 n 
+0000008967 00000 n 
+0000011035 00000 n 
+0000011321 00000 n 
+0000014343 00000 n 
+trailer
+<< /Size 53 /Root 47 0 R /Info 52 0 R /ID [ <00c0bf558caef6e657768f19572724c0>
+<00c0bf558caef6e657768f19572724c0> ] >>
+startxref
+14513
+%%EOF
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/doc/source/figures/spectrum-wifi-phy-multiple-interfaces.pdf ns-3.39/src/wifi/doc/source/figures/spectrum-wifi-phy-multiple-interfaces.pdf
--- ns-3.38/src/wifi/doc/source/figures/spectrum-wifi-phy-multiple-interfaces.pdf	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/doc/source/figures/spectrum-wifi-phy-multiple-interfaces.pdf	2023-07-05 14:23:48.105428500 -0700
@@ -0,0 +1,320 @@
+%PDF-1.3
+%Äåòåë§ó ÐÄÆ
+3 0 obj
+<< /Filter /FlateDecode /Length 1797 >>
+stream
+x½YÛnG}ï¯¨ÇÝÆÓ÷n	¡„%D²ä•üx°–µpdÀ&Jòõ9ÕÝÓ;×µ-Ú¯==u9uêTñ‰N~§§OéätóêIzöŒž¿ØÐkúDÚñ¿¨d£BTdƒn¤��>ïéœnèds+iwKmãMlet¢^¾£ÛißøÖKy¸ˆÒ7Á[²>6ÞYÇG^’¡Sú&(z+Zz—°9£6½Î60I¦Ë'½£Ž2^yÚ}H¿-pôÜyyºxªž†ÎÂî"J[,„ñÙB1p:=Îu6I2±QÙM(Ø¤ç[Ä¢mµ¢íŽ”Iwå±ý@'Û­„OÛKø½:û¸Æõj¿[\Ý¥Ÿ>ù°&«iuž~¼º¼úýý‘Zý»¦·´ý�^néµ;§ûÎÙà’wä\£”s†d«,t$¥l´¦T¤”"ŒÎx„»ÑœÚ6àJi`ÁzN©
9§äüáL•Ïlcw¦¸œ„ÝÌZ¦ZÓx­í¢eâ¶¨½²&Â¼³mˆiß"ÕÚzÖÄ"ºœÄÊÎZ¤�n”iõ¢E	�9VÒµ>Í±J¦¯M´.8 ²o™6¦ž9±l&VnÖ2ƒš9jY/V)aŽcå“9Wf`“	Èkñó!6ù¾M€Erab†©hçZEÞ6ÚyYT›î’-p¦ä{Qs€%î•å^u¸7³BÊ~f0Ã�	êAÖË¦�:•SÆìy*™ôÂÀ'”D±Ü”ì
+±Ø…÷|: µ°
+>¸ÀiÐGë@š:§¡ã‚' ic"ÙH•ù ^eJP¢£„ó«Ì—åó,‘ÀÇDv`:À9Ì �W7L-Xä3ßéÅêrM
+?_ìö=Â “³�7‰öÞÝ}¹¸Þîÿ¹£Õ›VªuŒ#ÎˆLlh”Aè<h¥¥âL·­hMÛ?S%>±•ˆÓGz©›¨@”<<ŽŸÃ© ÓÑÓ‘òB©*?”úfUwsÁ¬Ê!Ë-_ÿ�ƒ³³îö‰ASl;¹+VÐk‡Æ˜�;úJSÐk§¾ôˆèÙFtAw¨ 7Ú×�>wz�vÜkõ®C½vúá¨‡Y·»j¬R(eL€P¦YsY+`ÕŠÜÏ¯Zn�WÜù"M�Ÿ¿I°®{þƒ„{ßÁÞ`/¾öco–`oQç}…=<yì§’�.±’À˜�³ÁB*Mytr÷å¹	÷	ï±«EŒä�ì$ƒ`\)àTB ,{%Á)ÆQ¾�Ê÷Žˆ�5Ù„ØÆ+…ö1®¢k 8:…7áõ%‰'b£B\)[ìš!öûD^%våPxF�wœ×'¼ô]¼.ÀëðÇãõâË¾¿‘ÖeUV}|^ç^1
+ß§õ�¾ª´µ*úã�gå7…|€ô,÷Ž ˜pzZf™Ö¥
M £ÊT3Á<Ÿ7Ð2­ZGë<dÃf0?œæ`Ô˜Ö;1#
+QÛ‡�þ *­g®þf1ó8¬>væ±Q_Õäê1xMøÃa_å3½l
†’„Ç4…Îc0½º€^0p(�Ö›|¡Mùµ‹[¤²Å­EÁó€ã¡xÉO|™ë­:×C3ók¨æë¡c5?{v©€�ZCÛï	œ§‹‘EÎkX��œ‘ói Kf
ä¼lYIG xš?Œö*¤®ÜíôeÕñ§_®ï:	ß›ò!Ûï’N§U§kNÿz·¿ÞäByqs³¿ÎÊTðœ?™¦Õ@ÄÕ\²2]Î¥8¬n¦¹¬Ât6Þƒ
ôvÍe
+Ùb.Ç"uîl1Ãf¤ªD*XÍ¹¬"u>—=3Äæs©#Fß{s9YÓ|W.EÝÙLsÙ)°´{«¹¨1Åê²Ö%v0ieÙÓ+ÌC;ª5„V&µÆðYŽÎ†q¼oƒU½º<žKc§±ëLã£—Äf¹¹²DÇìL,e©°È&Þ§ÅjUÊ ±áêflÞ·Íå�KdÕ=£A—Ô@qHƒn#Ï¸±‘HIlX9Ù¤ecctrœâ98,a<Ÿ‚q…÷�=EŠ…s“Äò¯üQÂ€‡•µã4“vù}�i•Kïùú§Þ5v«ò3ˆ•éýæ—týë£Qz0f`#VP0Š�@Æúø0ØâÁÒ¼tN±ñØ5q´ºØŒúRAí`•ú§åS"`l
+ow±«…Ød¯9i·�÷~„lïû“c…_ò(&¤‡­Œc"A…6ëZ¬æ±�o°MÂšþkð‚úŠ|ŠEKÃfú‘ð’½îûÞ�	£Ið±ª19,°'d¨kc›Ä„[‚èþ‹5„
6D
+¸{€rHÝ€-u“%œÔQMÐ´(Ã) Vµ)š�§º¹á„–pÂ+™¼>™EK‘yý?Mžv,
+endstream
+endobj
+1 0 obj
+<< /Type /Page /Parent 2 0 R /Resources 4 0 R /Contents 3 0 R /MediaBox [0 0 1646.883 655.1798]
+>>
+endobj
+4 0 obj
+<< /ProcSet [ /PDF /Text ] /ColorSpace << /Cs1 5 0 R >> /Font << /TT1 6 0 R
+/TT2 7 0 R /TT3 8 0 R /TT4 9 0 R >> >>
+endobj
+10 0 obj
+<< /N 3 /Alternate /DeviceRGB /Length 2612 /Filter /FlateDecode >>
+stream
+x�–wTSÙ‡Ï½7½Ð" %ôz	 Ò;HQ‰I€P†„&vDF)VdTÀG‡"cEƒ‚b×	òPÆÁQDEåÝŒk	ï­5óÞšýÇYßÙç·×Ùgï}×ºPü‚ÂtX€4¡XîëÁ\ËÄ÷XÀáffGøDÔü½=™™¨HÆ³öî.€d»Û,¿P&sÖÿ‘"7C$
+EÕ6<~&å”S³Å2ÿÊô•)2†12¡	¢¬"ãÄ¯lö§æ+»É˜—&ä¡YÎ¼4žŒ»PÞš%á£Œ¡\˜%àg£|e½TIšå÷(ÓÓøœL0™_Ìç&¡l‰2Eî‰ò”Ä9¼r‹ù9hžx¦gäŠ‰Ib¦×˜iåèÈfúñ³Sùb1+”ÃMáˆxLÏô´Ž0€¯o–E%Ym™h‘í­ííYÖæhù¿Ùß~Sý=ÈzûUñ&ìÏžAŒžYßlì¬/½ö$Z›³¾•U´m@åá¬Oï ò´Þœó†l^’Äâ'‹ììlsŸk.+è7ûŸ‚oÊ¿†9÷™ËîûV;¦?�#I3eEå¦§¦KDÌÌ—Ïdý÷ÿãÀ9iÍÉÃ,œŸÀñ…èUQè”	„‰h»…<�X�.d
+„Õá6'~�khu_}…9P¸IÈo=C#$n?z}ë[1
+È¾¼h­‘¯s�2zþçú\ŠnáLA"Sæö�dr%¢,£ß„lÁ�t 
+4�.0,`
€3pÞ „€H–.Hi@²A>Ø
+A1ØvƒjpÔ�zÐN‚6p\WÀ
p€G@
+†ÁK0Þ�i‚ð¢Aª�¤™BÖZyCAP8ÅC‰�’@ùÐ&¨*ƒª¡CP=ô#tº]ƒú Ð 4ý}„˜Óa
Ø¶€Ù°;GÂËàDxœÀÛáJ¸>·Âáð,…_Â“@ÈÑFXñDB�X$!k‘"¤©Eš�¤¹�H‘qä‡¡a˜Æã‡YŒábVaÖbJ0Õ˜c˜VLæ6f3�ù‚¥bÕ±¦X'¬?v	6›�-ÄV`�`[°—±Øaì;ÇÀâp~¸\2n5®·×Œ»€ëÃ
á&ñx¼*Þï‚Ásðb|!¾
+ß�Æ¿'�	Zk‚!– $l$Tçý„Â4Q�¨Ot"†yÄ\b)±ŽØA¼I&N“I†$R$)™´�TIj"]&=&½!“É:dGrY@^O®$Ÿ _%’?P”(&OJEBÙN9J¹@y@yC¥R
¨nÔXª˜º�ZO½D}J}/G“3—ó—ãÉ­“«‘k•ë—{%O”×—w—_.Ÿ'_!Jþ¦ü¸QÁ@ÁS�£°V¡Fá´Â=…IEš¢•bˆbšb‰bƒâ5ÅQ%¼’�’·O©@é°Ò%¥!BÓ¥yÒ¸´M´:ÚeÚ0G7¤ûÓ“éÅôè½ô	e%e[å(ååå³ÊRÂ0`ø3R¥Œ“Œ»Œ�ó4æ¹ÏãÏÛ6¯i^ÿ¼)•ù*n*|•"•f••�ªLUoÕÕ�ªmªOÔ0j&jajÙjûÕ.«�Ï§ÏwžÏ�_4ÿäü‡ê°º‰z¸újõÃê=ê“š¾U—4Æ5šnšÉšåšç4Ç´hZµZåZçµ^0•™îÌTf%³‹9¡­®í§-Ñ>¤Ý«=­c¨³Xg£N³Î]’.[7A·\·SwBOK/X/_¯Qï¡>QŸ­Ÿ¤¿G¿[ÊÀÐ Ú`‹A›Á¨¡Š¡¿aža£ác#ª‘«Ñ*£Z£;Æ8c¶qŠñ>ã[&°‰�I’I�ÉMSØÔÞT`ºÏ´Ïkæh&4«5»Ç¢°ÜYY¬FÖ 9Ã<È|£y›ù+=‹X‹�Ý_,í,S-ë,Y)YXm´ê°úÃÚÄšk]c}Ç†jãc³Î¦Ýæµ­©-ßv¿í};š]°Ý»N»Ïöö"û&û1=‡x‡½÷Øtv(»„}Õëèá¸ÎñŒã'{'±ÓI§ß�YÎ)Î
Î£ðÔ-rÑqá¸r‘.d.Œ_xp¡ÔUÛ•ãZëúÌM×�çvÄmÄÝØ=Ùý¸û+K‘G‹Ç”§“çÏ^ˆ—¯W‘W¯·’÷bïjï§>:>‰>�>¾v¾«}/øaýývúÝó×ðçú×ûO8¬	è
+¤FV>2	uÃÁÁ»‚/Ò_$\ÔBüCv…<	5]ús.,4¬&ìy¸Ux~xw-bEDCÄ»H�ÈÒÈG‹�KwFÉGÅEÕGME{E—EK—X,Y³äFŒZŒ ¦={$vr©÷ÒÝK‡ãìâ
+ãî.3\–³ìÚrµå©ËÏ®�_ÁYq*ßÿ‰Â©åL®ô_¹wå×“»‡û’çÆ+ç�ñ]øeü‘—„²„ÑD—Ä]‰cI®IIãOAµàu²_ò�ä©”�”£)3©Ñ©Íi„´ø´ÓB%aŠ°+]3='½/Ã4£0CºÊiÕîU¢@Ñ‘L(sYf»˜ŽþLõHŒ$›%ƒY³j²ÞgGeŸÊQÌæôäšänËÉóÉû~5f5wug¾vþ†üÁ5îk­…Ö®\Û¹Nw]Áºáõ¾ë�m mHÙðËFË�eßnŠÞÔQ Q°¾`h³ïæÆB¹BQá½-Î[lÅllíÝf³­jÛ—"^ÑõbËâŠâO%Ü’ëßY}WùÝÌö„í½¥ö¥ûwàvwÜÝéºóX™bY^ÙÐ®à]­åÌò¢ò·»Wì¾Va[q`i�d�´2¨²½J¯jGÕ§ê¤ê��šæ½ê{·í�ÚÇÛ×¿ßmÓ�Å>¼È÷Pk­AmÅaÜá¬ÃÏë¢êº¿g_DíHñ‘ÏG…G¥ÇÂ�uÕ;Ô×7¨7”6Â�’Æ±ãqÇoýàõC{«éP3£¹ø8!9ñâÇøïž<ÙyŠ}ªé'ýŸö¶ÐZŠZ¡ÖÜÖ‰¶¤6i{L{ßé€Ó�Î-?›ÿ|ôŒö™š³ÊgKÏ‘Îœ›9Ÿw~òBÆ…ñ‹‰‡:Wt>º´äÒ�®°®ÞË�—¯^ñ¹r©Û½ûüU—«g®9];}�}½í†ý�Ö»ž–_ì~iéµïm½ép³ý–ã­Ž¾}çú]û/Þöº}åŽÿ�‹úî.¾{ÿ^Ü=é}ÞýÑ©^?Ìz8ýhýcìã¢'
+O*žª?­ýÕø×f©½ôì ×`Ï³ˆg�†¸C/ÿ•ù¯OÃÏ©Ï+F´FêG­GÏŒùŒÝz±ôÅðËŒ—Óã…¿)þ¶÷•Ñ«Ÿ~wû½gbÉÄðkÑë™?JÞ¨¾9úÖömçdèäÓwiï¦§ŠÞ«¾?ö�ý¡ûcôÇ‘éìOøO•Ÿ�?w|	üòx&mfæß÷„óû
+endstream
+endobj
+5 0 obj
+[ /ICCBased 10 0 R ]
+endobj
+12 0 obj
+<< /Type /StructTreeRoot /K 11 0 R >>
+endobj
+11 0 obj
+<< /Type /StructElem /S /Document /P 12 0 R /K [ 13 0 R 14 0 R 15 0 R 16 0 R
+17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R 24 0 R 25 0 R 26 0 R 27 0 R
+28 0 R 29 0 R 30 0 R 31 0 R 32 0 R 33 0 R 34 0 R 35 0 R 36 0 R 37 0 R 38 0 R
+39 0 R 40 0 R 41 0 R 42 0 R 43 0 R 44 0 R ]  >>
+endobj
+13 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 1  >>
+endobj
+14 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 2  >>
+endobj
+15 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 3  >>
+endobj
+16 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 4  >>
+endobj
+17 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 5  >>
+endobj
+18 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 6  >>
+endobj
+19 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 7  >>
+endobj
+20 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 8  >>
+endobj
+21 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 9  >>
+endobj
+22 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 10  >>
+endobj
+23 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 11  >>
+endobj
+24 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 12  >>
+endobj
+25 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 13  >>
+endobj
+26 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 14  >>
+endobj
+27 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 15  >>
+endobj
+28 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 16  >>
+endobj
+29 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 17  >>
+endobj
+30 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 18  >>
+endobj
+31 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 19  >>
+endobj
+32 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 20  >>
+endobj
+33 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 21  >>
+endobj
+34 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 22  >>
+endobj
+35 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 23  >>
+endobj
+36 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 24  >>
+endobj
+37 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 25  >>
+endobj
+38 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 26  >>
+endobj
+39 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 27  >>
+endobj
+40 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 28  >>
+endobj
+41 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 29  >>
+endobj
+42 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 30  >>
+endobj
+43 0 obj
+<< /Type /StructElem /S /Figure /P 11 0 R /Pg 1 0 R /K 31  >>
+endobj
+44 0 obj
+<< /Type /StructElem /S /P /P 11 0 R /Pg 1 0 R /K 32  >>
+endobj
+2 0 obj
+<< /Type /Pages /MediaBox [0 0 612 792] /Count 1 /Kids [ 1 0 R ] >>
+endobj
+45 0 obj
+<< /Type /Catalog /Pages 2 0 R /MarkInfo << /Marked true >> /StructTreeRoot
+12 0 R >>
+endobj
+6 0 obj
+<< /Type /Font /Subtype /TrueType /BaseFont /AAAAAB+.SFNS-Regular_wdth_opsz180000_GRAD_wght
+/FontDescriptor 46 0 R /Encoding /MacRomanEncoding /FirstChar 67 /LastChar
+121 /Widths [ 691 0 0 0 0 0 0 0 0 0 839 0 0 588 0 0 601 0 0 0 932 0 0 0 0
+0 0 0 0 0 511 0 512 565 523 314 0 548 213 0 0 213 815 537 542 561 0 321 0
+313 537 0 0 0 497 ] >>
+endobj
+46 0 obj
+<< /Type /FontDescriptor /FontName /AAAAAB+.SFNS-Regular_wdth_opsz180000_GRAD_wght
+/Flags 32 /FontBBox [-396 -275 2465 957] /ItalicAngle 0 /Ascent 967 /Descent
+-211 /CapHeight 705 /StemV 88 /XHeight 511 /StemH 79 /AvgWidth 580 /MaxWidth
+2493 /FontFile2 47 0 R >>
+endobj
+47 0 obj
+<< /Length1 4400 /Length 2558 /Filter /FlateDecode >>
+stream
+x¥—	t•ÅÇï÷½÷D6¡-K©[CJ�‚�¶%‘„CØ’ k‹¬¬�”Å²Êb[TŽ¥ŠØRàPlõµ"¥BÞ»ýÍ|_ ç´çtN¾73wîÌÜåï�äçMÈ”»dº¤Þˆ±éãÄ6gÝœ¬œ)#½¹›-R§]vfz†7—JúvÙ¼¹ó(ýÙcó'ûóú”œÜþºÛžy«±é“ýóåó¦O§�Íôøë]¤l\îø|¾ŽþÞøÿøuØ#%Êžaf·6Cá‹opjIZÝNW$PÃ®Ÿ-ïþºœ-ïóiå‰ðÑû£71
ˆk—ížèäkp©òDåŸì7§ÜÒb8)#4WR£öÊ²àn¾¤_t¾´
6’Äà½’,æ{DÜÙrð)™åì‘g·ÌŠš#ÁD¾A¬I�»„oºÄ@Ë\“¦Üâø·ÕB/cïnòÓÛî7ÂxÒÌP‚öW$ä÷Õ;c�h©
+j2º[jÙÅÚRGêJ=¹‡Y}¾òKiè÷=åUùÌ‰u~ïT¸Eî'�äÀ‘`�`ÿà´àâP(Ô-ôRTLTÿ¨%Q'£³#J2"õ£WG—bÇhn©Ë‰�Dâœ;7Éˆ3W� ÚÿÔG�Ñ©Ç=å~¿þ¿ÌIÕs¡ö¡7¤R°ÑÈa:;¼1¸•æ~)ËåŠl�ïŠ«½5¬Ÿêó:\Wiž>¤Ï±É´|ùHI¦”É8iŒÞ{5¢ç4UïÒÑÚ>rZÓ<6)sZè9Æ¹rTæaÑ«ºA§ëA»¾Ø‰’H³Ó	rMè,Ý¨Ö�ùòo©½";µ‰ª6‰ìÐžð/ÃÔÍBë�ßân3-«ÉÉÚÉ�—ån¼v’dæÐ46R[¯òÅšSƒC0D³ŠbS9+r}##,z½+kF>×Ü
­¡Ügn‚æ™ÊZÑ½Ëå‚³UæÊ1™dÎåžy«“#ÜêL“U²Å§ÖÌÈ}YvÃ‘èz˜�(Ç±‹Ëw›š½Ã½_}‹æyvó$Có~z.8v¨ò'Ü¾LfdgÞ¼úB5Šº9¸÷¢Ì€‡¦[´‡ŽÁÅeZ¢o{4ÓbI“a²@dWvcþ«Z¤Í*Gë³ª–s¨ì“…²N^�ÙÞNýµfë
+]Îyëô”HD¬š8ó+„,9È½ŽŒ•Õ2ZšÊH^]d(	Ì‘’mC¢#œ°O¿fXG&È.i©+€ã<äxQGè2ý›þE?Öç´Ê^]¢‡
�üLFIOv‰^Ò9š¨¥Æ*m‰°.ø±îðrP¶8ý%Šo‹3Oã#gµ"²_{óU0Ž7g1Ôõi>:Ò™¯eî÷œž¨çåÁ–ªâà¶˜0½?¶£*˜Þ0Ø5ó³	­‹$C¾gø6H¢¥Ú—0(Ñ~XáßØy3ö¯'…2‚02ÉÐðìÂ[Ñ}¡7'PÆ8¹"‘©:W)P¶-EJõ<£V2Ëˆy÷8v_‰Wiº]'âýoÖ�îðÚR-‹¼«{8g¿™ËIäþÞ’‚„�d®~¡×t·NÕ—ðm90ÝeLÀÅ?¼i«·Î›Js Á–¨ò0_WŽpl�¾@n˜d´­/QñŠÍ"+;ò
+n¾ŒÛ»½\m¡%>×$9mUL•5ÈË0›ôWvõIÙ&SØ;‰~¸¡èÝ©×Ôrk1»¡jm}‚Ó‡êÝZ€ªètBÛPöG7µ©rª|ù­�ëJyVž’¦U®Ô$N¯¨«t-ÒºØÜHË•#²I×ëÝç¤—”nCÉ#ý<Š¾Š-Œóbe°üœ“€@>­£<.�>¬VËJŸj¥_	éIÙŽm4ßJžµš¤óÑp�Ðµ¾·¾À[-=ýPäN:ª*Gx>{�[—Jž
0WRþ¥û	Ç7Ht�¹‹–(Å@i aÞËÎSÂTÛ@3Ö_êO€Ì3ú°.°k¦’œôµˆU:›úèÿ¤¯ëIË1¿æJ<i`�`OVwi:¡}[‹špˆƒã+/ýçš:MSµŽ¢éý¾[è-¹6«�‰?O«šUqð9Íq˜Àz"�{�˜ýRß¡®ÕizÈ,é(~^4£€ qçTéï¹‡É…ï¢o¬•:“=ã¿Ž%pÆXY,©šå­$³¬$æ¹S ÈÅf§G,Ctù.UOh§Zc¸™@fòÒ4SóÂËQx.¦9@4´RÝ¥“»’ƒ½C¯ bU&ô¬a± fàXƒ¬ÇÌ=¤êq}ÓŒmj^v"{í¬I%Î, èqËe5tZ£!µÍX�½á2ÈÅTîú>òª�ëÎC»Ã3Q�o˜ÎŽîÜ;\Ú±ê›F:ÉI ‘—67©¤Ç,ÊÐ>b¯Vè?(Mõ5¢q4¥ã¦	�Ó|N¥éQRÚ¡0z¹«8qœo‡÷L9
+“2Ý�Hž+üäyL7éoÙÔ™éÅb™îd¾’Ïj8‰¦
oó¶ÅWéÔ�ÔžL´,W9»«&±é·¸z®.öbJ'ëÔ0¶t7ÊùÀl#yWXhz¬.wßCyµNªf_˜<äW¸-e‰œBÛ(=Íõi²lóYv
+á±ÛL´·®DJz*¹f>YÏÛµ©L¾*–¯e#hÛ$—ü¨,"S�¡äªBkŸTüYO!9*Õl§œ$i1<ÅôX)ˆ¬¶E/4Ý&¸O¼eÅ·j”|Ÿj0�Pž-ï�?lã‘Ðž|¹Wg‚ˆ�=8gôeä®^s†’‡KüÜ%àgJd–®�ŒoëfýÐ5G.ó„rù®~4=ÆÚaÛE*Í}éh8Ž‰ôé–£ÍJÐp}™‡7
Ü‚«%šÝÄG0—À¡�Ôáž+zJ?ÇZEú÷j.!	å™*G…VÚ'CÈÎ–ÛÇÀVÆŽÄè·M(ÖþOÂÃ™¿j�Ùy÷·1ÿ
ñ€úDÿI¼¼fÎá-.Jtñœ5usµ¥.²oõÿ<Y7li]d°›ÇˆäèÜÇ“ä3v�ÃÅÄ�©Kù¼Ž“ILN†Í¿Þlõ¥1£³(TD`5©^L9è´
à·(óÍ/�åzI_i
u4�4RUØ”¦¥Œ†—Ò:ËÅYá!¼m’´ÜÜ×4²&p
+V˜«ÉbDò¥aÛªèL0ÑÜY¡½"ïË�õ]iy_œÙzžê^ªÛô"ýexëKëÈúŽ`/+˜FÊ£t4@V¸þ¹æ¿›ß±Ž|üw•G½ÎØµ‰¼Wõ¼óæ7~©Òœw@²^j¡Éu=Ah¢°?OõTÆdŒð8�4MhVßX“Ež–8Óº¶l•œÐ/ùá¤Ì¬	9éyi“2ò³ÓrÇ�/hÝ>––Ö#).>mRVvþ*~RÙ
+endstream
+endobj
+7 0 obj
+<< /Type /Font /Subtype /TrueType /BaseFont /AAAAAC+.SFNS-Regular_wdth_opsz151EB8_GRAD_wght
+/FontDescriptor 48 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar
+121 /Widths [ 251 0 0 0 0 0 0 0 358 358 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 250 0 0 0 0 0 0 612 0 0 620 0 0 0 950 0 0 0 0
+0 0 0 0 0 532 0 536 0 548 338 0 569 230 0 0 0 843 561 0 586 0 352 0 338 561
+519 0 0 521 ] >>
+endobj
+48 0 obj
+<< /Type /FontDescriptor /FontName /AAAAAC+.SFNS-Regular_wdth_opsz151EB8_GRAD_wght
+/Flags 32 /FontBBox [-396 -275 2465 957] /ItalicAngle 0 /Ascent 967 /Descent
+-211 /CapHeight 705 /StemV 88 /XHeight 519 /StemH 79 /AvgWidth 580 /MaxWidth
+2493 /FontFile2 49 0 R >>
+endobj
+49 0 obj
+<< /Length1 4008 /Length 2312 /Filter /FlateDecode >>
+stream
+x¥WP×þvï^.U*V“63Ö&©�hc�¶ÖG�( ÁGÐúQWAÑ  `xD>‚ „ªI”FDµ¾š �è€4Ú&Mµ¶
íŒZ#ÚbÛ±3Ñ´Üýû�³‚ÉLÚéaÎÝsþýÏÿþ¿³d­\•Œ¯ ô]´ÜŸ=Œj>Î/IÍ]ììÍ£@HjJ²?ÉÙ£ƒÏ‘)$8{ãû|>š²<+ÇÝÇñ—š¾È}o®æ~èrŽ+W¸˜æ_žìð‡¦ð9>#=3ËÝ?Èçtgýü<Ûc¤e¨]Ï¡(œ‘aÞ–ú£Vx‚õû«•ëÕâjå”ëçÃê|¹õÀÔ¯õß)›ö…Ýî¸Ðq1¬NIé1úSR’I~ÓÀëœ­ðû^C´5±ÖdÄ[o¾‰!ÖK(3.!7èm”Zk8«Qj6r¾‰ÁÜZƒ0ÜŒáfš8gP‹áj¡_¿à~ â?§ÿ>ÜßÃžÿ}Óé«sÒ£–+ÆûâTŒ}œÁ¬& z»<!ø*W}8C9ûâkè§ß„éßhDc.*�qFªqËœežõ„z¦{
+<­ÖkŸÕäè]ì]ë}ÇIþ $Ùý|å¾
+æÂG
¡ÃÀã‹’|£€¶ßÐ°ÿêiÓ2£/õTºÏª/Ù3·äˆ÷VÑm÷µ€LU¾‘Êô+«˜ëûñuÅCšáü¨‡3µõ0êP„÷‘­ÎRF±“;Š<%Ø‰Ã.uŽ$ÛYâj1�¬<‡?¢˜šŠyZ÷�$j	‰²BlY)	jXàZdÀ/ÍÖ+Þ3C{øÆ5§k¥±‹æšªËLYËf>ªðÖsÉ!‡%Z–I¢ì‘ByÏ¡a&¶`> ³Y&jEäŽ¬–‡iØ³ò�ä‹hÎ8œÀO°¯bƒs’Ò–H¹”I‚ì–Oyj*5÷Â&|(ÍäXŠ3ä4°Œ�Y†oá6ù6ÁÒ&#£ÉcÈ¯e§œ’�¹îƒÃb%›2'K%–‰_^–¿H“\c¨÷‘rB¶È¯å,eY†�»…¼Se'Å‘ßæ‘U<A'¬ÛO#1æ<lK¤}MÚìz™ÌÙÆu¤’iåí…NÚiŠõ÷î“Òcå¦ç¤5Ã:sâYZéQ¸‰ržÑ%Õá)¬ÆBÔM÷ºRÇ<La|7ÓúW¤•q>Âø÷Ó\YÈQä9.¹Œt¥9{¬D¦‘	Ø¹ò¢Ì”ý.u>öÈM®‡|Ya!™—±Ì*‡Ô2£G¤�K/Â¡K_¶K�¯•�R§xØâÓ©ÿiÄ±…D±ü“uPCíU,Ð=¬†ãÊÁxÆ@ƒoß��ö»›ã=ËÒŒÀ8I³Ã˜îÙõwÉ¡»•’%5Z-hN	žaWá>j8Ê4ß–­)k$Uír<W´‹q,(?z‘ëŽ”r-#5xžgsQÍwR*Ç¤�‹ JÞÝ]t¥·Ìå¹X	–Ç+˜¾q|q“ÈwÓ¨iÝÓÌwf8æa;V0¡}`2	ù·Ô1œul„Ì.¯b°sþ
,w5bDkÏµd“tþ=&›Õžc>ÐÞ
Ã,¶ËX…ÏlªËò¶\Òïç°õV ’ÈSE©fã­—SŒ.DáQ8vã_`õrŸ%>™GOã¥—äÑ!zh¦y/2­�}¡“æÖ¬ã$�IUå
'\sÄPQ<ÆërCÞ“¿²Ûó¨�CæñG×žgW[8sÈífK€	µXiÒ Ù²C^ä*Cí
”±¯‚¼g)ZÙ¢IÝ"¯{¦L[Â;Ÿ½0‹¨“MH;f“,”ÒJ©ê²…¹’¶SÔšE“ F™•È'f-Ô´=rˆ”(µ6Y%0½�ðtá„
ýK€c(6Ž}¡7ÈûüÓŽÓ$ë;üQ0Iž{¹”²ìF»uÕéa7{.)…AÐn—ºn�G:&R÷"¨ûÔuã�F|›L‡†ß¥Êë’Ân:!R!×	"7ä¨”ÈIù)�¾IZº´�'sY±ržÝw:@)æADvÃ5õ·„ÛškÕ¼JM;y1(ÞF9$ïr1±øž¦ì•Ÿó¹›S{êù„žò;æž\ÞëÓD7•Œ=‡ã%†°ž$»Ù™¥9I1És²*°ŠöCþ ){å
>wqš(µË­ÙÔýKTW5©Ÿo]Et‹ÌY0;J{¾‰$¬Ã$–ÍYÆ[¢Î‰Noéh^s¬2š5VÕ:.V%òwºäÎ¶‰B¦j“w	²�š³Ÿàg´òÚˆkŒä}uQ{G"	~gÓ˜Èãµç×{ešl B2.{�èšûéaX�èj/{ÔK8ÑãiÀâ'Xb”®w‹ë6L#Â>©õ´Q³Ó »TkÐV%w°|l.ð>©¿×œÀ¹yTúÔ ±ß &f\n>$ÎžTRÑDÊ#œ¼\äî+5õe}¹±×¶�šÆ/ksW„
ƒˆd+È!8žg-çÒŠBÞ1·Øc
+­Èæh¦j.��ZµÒ¡5¼³ê¼O$�Ø;øM±^JÔ	‡JÙ§áu0O±©$Éiâ@‰Ô3éÇå‚âÅfl2	È�i¼[§I•ÒÇ.ñ´ÐŸScÚ–zTób’}EZì&‰V'É�ŽWÉû,7<eoõ|J�´›n•äºÅ[iç`c7e�Á(^Ã£íÓelbT›y—ÔÈGìáÛäí‡Çí­|Žæm’a-¥®ñø1ÝxÃsš_(—ä—²_ôCŒ³‡Wu@:hE“]nn&Òêúái>W¹Zw°MÎñ³ª^.«DÔ™�‘’FÝëÜÏ¬qxAU7ÑèwøÌÁZ½ñ~¯°QÖÚÍv?Ö&Ù
v3ÅÏ F¸yHkq”*ùþFôZC$ã�w¨£BÒ˜ŽÄÑAšv™‰«’sÔ˜o0"ßoÆ¹ºH†K�ÝÊÞÌ·[$HFHkXû\çç’³ïúåÃˆ|—õ:’Œ‘D»hvüvñtÆo&oçXâü\zÝ[}JrŒµ¶˜_C™=5"†�3óñÉKV¥úW&d'e¥$¤gdæ=ñƒ'&†�JxjÆ„È„ì%)YÿÉÝáÏ
+endstream
+endobj
+8 0 obj
+<< /Type /Font /Subtype /TrueType /BaseFont /AAAAAD+.SFNS-Regular_wdth_opsz151EB8_GRAD_wght24E0000
+/FontDescriptor 50 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar
+121 /Widths [ 240 0 0 0 0 0 0 0 391 391 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 280 0 0 0 0 0 0 637 0 0 644 0 0 0 977 0 0 0 0
+0 0 0 0 0 558 0 558 0 570 369 0 597 255 0 0 0 882 590 0 611 0 387 0 371 590
+545 0 0 554 ] >>
+endobj
+50 0 obj
+<< /Type /FontDescriptor /FontName /AAAAAD+.SFNS-Regular_wdth_opsz151EB8_GRAD_wght24E0000
+/Flags 32 /FontBBox [-396 -275 2465 957] /ItalicAngle 0 /Ascent 967 /Descent
+-211 /CapHeight 705 /StemV 88 /XHeight 527 /StemH 79 /AvgWidth 580 /MaxWidth
+2493 /FontFile2 51 0 R >>
+endobj
+51 0 obj
+<< /Length1 4016 /Length 2350 /Filter /FlateDecode >>
+stream
+x¥–pVÅ€Ï½ÿýó#F¤@ì8F­(ä0„`xHµBDyI € ¯Œ<„A@Ô	4@P�;>xùàÙ¦Ã N•¶ �ÚèÐRÉO¿Ýû'MÔÎ´Ó½³w÷œÝ=ïsv‹¦M'·È|	I“ÜÉ9SÅ6gÃ±	“f�`·B$~Rþ¸œ¼–Ænù ØéÂxwþä¢™18‹1kRAnlÝ�ÜarÎÌ}ù#pë)9“ÇûòÓ¦Åà$ÆÇ‚ùÿñw8ÛBzJœ¥a †Í`è)‰'Wg'ôº*¡Fvýtyÿ}frº<ý|ÍçÑc‰{#o†ÄµËöLd¿�|‰ÿ¬ù¼æ‹Ä½†JƒÖJyÎßDÜÞRN¢ÿTFGNHª7C†Ò3¼•’áVI;ï¨¬pËôH‚”x»éç¥$”$%nµ´žã�’{½ij#ŸÑÂÅ‰q‹G¯�[Ë“ßãÿ#iÞ@žÿ¨Õ58²ƒ#þrÆÆz#¢I¤±ÜÛ/·1»�ž@o"M¥™]I´ÿ4I“'¤ÜéíLr.»ÃÝÊPBè±ÐüÐUïïuïT¸ux|xAøp8×�ýq’ç7‹¬�”á‹$QZŠôu~¸I^$Y¤ú7ö_�¾‘ìýdlüýk×)ÃÂ›‘ÇJ!e‘à+ç,î7;¼Ì›Ëf8³…Ÿ‚n¥oå¼/«å”˜L¡i‰Þ¥S|üíË‹RÃŽ×éþR�gÎùêœ²ØYÈûœV1Î´g;Øÿ4�«¾>¯Ï(š“È‘ÑzÖ«”‘‡•˜8u3;	�u¸˜¨6ÌÉÅ]+;%*KÙCÓ-ÚOóuœnÕ—ôX€“þòœdðÍ‘t‚Â•kªZ­³µ¹ŽÕ<MÐbµ†’§dª¯“å2+8©ïêsPzCu;'T#p¾EVÊu=ËŽ)R)ËÀ`Üg¤•´!ºË`é®·dI'CE?ÑmzD«˜Þ.Å²Gž¶Ø—µ�ÎƒâZ¥«ôœ~¥çµLßsH7Å$ïLBõ6¬Wt‘ö×uIR‰¢¥ø‘(îkVïW)W�»åVçÆ…þ5�èUÿŒÞáŸÓ«ñ�â5¹y-ðÁÍƒÀÇF¨ÕË¡ëÞ`éXë¨sÊv;­ç³b˜›uû{‡$š-cä'î:]ô5í¥3€žº\±»\€VS™(�ËX™`v°çÖÞÓ�úZK.%‹`ñ'4iº)†}JÊõ2ó¶2TºrŠ’íþI¦J9´hºK§éfE5Ò3ÙF”à»�þ¯ôc-ÕÃf�‘Gð?Äž$y]ÿ‚WßF¾
è�¢oU3ô¢×ÜóoÔZõ”6�QÜÍ"ü*qîíâhH]Òeº…–êG,›Ö‡ÕLÉãß
+—·J_Ô.Z¤c4I›Ðz‘yŽT\�†›^¹]�¢Ó8û¬l�‘£¯"úM&qìÝ	mƒ›…Ï3�`˜z:3¦KotièRç/ö>³ãEëÁZ´û¸L’-2�h‹—‰%‚¡ÓGZAE0ÕÞ´Gd)5H
+sKq”š*0KºD[ëx� wj‰�isä¯V»2
+·%S›EoöGÕÔ1�Ò
+\ŸKX¬´÷‹è^\ºVËd=;Ÿ`Ç0Ù,U²ØlÆr!‰…FhX‹Ñ´~x+|·ÖæEÌWv°Š;rBª9{†ãw"ÄkÚ™dÒ¯aw—åën³¤OòÛbf¡™ü>¦�F¬tFÁÀQLÆôÚG}X«¯2ëc`=Ä{4ü	w¾‘Å¢êYÞ{…Ü0{)~Ï@µ€Ï4‹#j³�c™ŽÖ/MÝ êÐÜB"309MçèÂèàvÉ*J®Å•ê`ìº‹çÄy%¬ª«�5c,1Œ ;—ò2Ü®À»Gt»™Ëóó’ùý’>œwKOb—¦;ØsD+ÐŸÆ­‰ÅU­†õTl8åhmÖ”ÄÔî&ÙðîIêþÌP“¶"]0ñÃÄÒH¹ÏÂ�9¶k®¾¬ïPD6à¤*½H.¥`¬¥xŸªg 5çi¨ÒôSŠî¡è²Š¹æŽ{BËu_”ráþƒoŠŽãø,÷áJv©n6Ú[M½^hÊ;æ;¾ü®NRÖ’‹9Çuo~ùAÈ:¦�#‹¬#ûÙõ…úB”›ÖÝK€ýÎbÖé6FÃß•¿Ô[å¥ó¶hD5XNögKf¬¿‚wŒåâ)Äc	�tªÓQ™†¦k4YgbÍùdóÙÀz®G­Z¬;(�³I‹LjUßC]âsó»Ä³Ö7õS‹_,ßÊ¤¬`\b0�Õ™ì©!<‰¤_`
—Z².ˆ*"ÈøÒ§çmÖºîßÑ�WX=ëZ-­jµñò5fˆ´Á�Iðù—ê×ºŽÂM<Gwd(u%ðµÞ�3u	jSƒ„5tÛk•[N±ïµÀpØÎÚ1öƒÒ]Ôþ~–Óe.æÄš}‡›÷˜ÞKÑ³¡¶Éðå17kD<ùÐƒK‘a.5¯ŠÃehî^S¢Ûˆ…B¤˜ÃÓ…Ccä:ÖLÈ /£Ðó¼~lŸ®”òšçFðÎ9ª«§[õIÔ-¶¼vÀÔ=ÀR¿LG—ã²2jÔÌ^èÍvIÞh]¨éºÑðC#¯+[!Ó·Ö[�¦Î\¿†Ûä�ómvb5ïQ…õÖ_á¥‚kÙ@sÞ¬ÚˆtBÔ¡-ÄÑýN‰Ã¿(Ýõ}Iö/úWœE<i.¡Ä»dó—zŠ-¥‡OÝ$_‘ù5¨&“9Wxh/”_[»õë ®öÅÒ5ðùÌ/u¿ ÒÚø1‹°˜Û™S-_i%*J»Y@×T*Owm£ëy\´¸‡e�‰n|v\¾Å[‹d¡stï_ÿ¬¿Whš¿Ç?Ñ�pãÈ!ÃÏf_-ç¶ÜÄ¨94Ü4>­yfËcgp'uòTÂq®ÓÊ »e�“ê´2ïmÝ¯�y§}ƒdsý*�ÚOöØæÿ6xSP½?9‰Eî“¤Ù‘‚~iöšD•F� ÊeaÉŸó�4Ï2#r³@X®¯®xÏ´”ö2S‡dÞŸ1nÂôI9Ó²‹óŠò³¦ÎîÔµSÿ~ÉÙ2ú¦dOÈ/êÜ¥Gšü.ãú
+endstream
+endobj
+9 0 obj
+<< /Type /Font /Subtype /TrueType /BaseFont /AAAAAE+.SFNS-Regular_wdth_opsz120000_GRAD_wght
+/FontDescriptor 52 0 R /Encoding /MacRomanEncoding /FirstChar 32 /LastChar
+122 /Widths [ 278 0 0 0 0 0 0 0 0 0 0 0 0 0 293 0 0 463 602 0 642 617 636
+0 0 0 0 0 0 0 0 0 916 0 0 714 0 0 0 745 740 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 549 0 0 0 568 0 0 586 0 0 0 250 0 581 0 0 0 0 0 0 0 0 0
+0 0 536 ] >>
+endobj
+52 0 obj
+<< /Type /FontDescriptor /FontName /AAAAAE+.SFNS-Regular_wdth_opsz120000_GRAD_wght
+/Flags 32 /FontBBox [-396 -275 2465 957] /ItalicAngle 0 /Ascent 967 /Descent
+-211 /CapHeight 705 /StemV 88 /XHeight 525 /StemH 79 /AvgWidth 580 /MaxWidth
+2493 /FontFile2 53 0 R >>
+endobj
+53 0 obj
+<< /Length1 3840 /Length 2206 /Filter /FlateDecode >>
+stream
+x¥Wp”Õþþw	d:SŠ6Vy…‚©†ç* !P-�II`�C(�*0PÀ€!D^¡HÐ�¡ÑÚ
+âðÖÎ€ØX´›ÿëwï¿		 íLÏf÷Þsî¹çžsîyÜäNž’‰ÛP€šŽÊJÏ†§XÃÑ1ãóGû¸»
+hx6’™žáãˆjìÁÇ�®ï‰dåN�á©Ãã'ŽŠ­»FnBVúÔ˜|œÞfBzV¦Ïßø�ÆG³'æäÆp#'ìÏÿ�_G{›áiÔ³2VEß>ñ'–¥5y¸�úvýô†¾Ìäô†ŸE�UUÄï�Û,4×.Û=qô’€ø«ÑcÑãñû�”:ÐL’2Ü‡€Ð^ÌUêû%ö3‘\„±Á�Øê:ØÜ„âàcX(Çœà‡x)Ô9Á 0Ô3ã†b±›Šm’êÄ¤7’‡„·Áon:¯öáwX¤!i¼�Ñ¤öblÞÔŽÕö¼Ï“B±eß·q1Ì8ð¶ïÙØ FëfËœŸ9ƒœÜ>î<·Ðý«ûm y`Zàp°gð|è½ÐeqÖC†×,nM\‘ü‡†²"-�DçÖ€Œ¸�kG€ÿmôšÉ±ÆbcÉÁÌäéPïÐ;¸WZh£ÑÃvZ3©Ks£x•(F+€�Óã\Êá\Í)ìÊeÚd`*ð"2P‚Éâs°ÿÎ‘Œc„{çË¬v~ÌÓâÏÂa<'¯€U,å¾a¥,uê£\º[ãRCáæs#«4u%ÿ/È³Ô°·ƒ-¥I+�a�f¬š$«ÚùVY³jŒ¦MúÞh,�°ç±)FjÕ^±¼då¯`OÎâr&sŠ W§/ï1´—Þ{túçÁzLcïGúL®h¿b-Š"|ŠùØ!+~«ï)Îg˜/[¾d¼¬µçeS™,ý½¡qœüY©ICÑ+äECëë•òG:iˆ÷ÉB±GÙØ-T"®:Q$z¹’[7dF¢üQŽ“üª‰Ú§è¨’Wms�ÎK�Š`tº	×CÁ¸ÌzLccµ÷,Ó<†iHG[˜ð“‰:¬XÁ1�‹9�Kø5‰Rí¼ÓÅ•¯�¾Îgù7pž�c¦9ºR/—s9˜cÔ‘(á%ÍïCT~˜.¥Ï![!8Ìpp¸�ßiÀ#ègi+¹ÆÛÅ29yŸÁU^Ë½IHUâµÂB^à·ÜÅ<®—;‹˜Í]Æ°±¼¼[>¨7ÚkM´?ñ¡6ÙMAVIùþªL®¢$Ê«ÜÏÜ+Ñ“ŒhIºÉ}æC÷!•Ê�43•‹:0‹x/_0¸ '­Ñ�1cð T…ø/žá[üÄ®§(ËsÐW®Ü„¡†Â�ÊÂÙ<€YšO¡�¢ók,°k9ò)’)¬Ï©2h+l:ƒ6nÄ"óªo46Ûêß£Ž˜!?ÏÆïŒ ØÝnf*ŸçÌäq^ÑqÉfÉ��‘2ñq3g'T)hÝ½(@FXÚzêXw�™[
œTi œOÔÉÒÈd†$ªá(–ŽMuwÞ¤cµ²Å? bO…ß‹7)˜&ø
+æJÁIRgâ£’BÁt¦õú:Æk�·‚]oÊ«�ÊU7¢œú“ÆqÂW˜ý¶B5	4—«Àl}¯D/JÒñ”+×ÔMŒ�Ô'Ë´þ„W £&Ú”±Ùè}É^NIr ‚ýTÁ~z}—¡Ëp-šI­©¿°.ðš4I@IþHI4Wú(Yp!»)2òxŸÒ�JƒíVHX<Ãðk%n¯ß1®âF¯¥ÅÔCxC¹Ý™#oìS|'kg®*ËX+s	RÚ	Dª˜[ê~…E¹RêMr×T]ÖjŽlÙ)4Á]u¬ñÍ0æØY vjö¾Îí¢ðoV˜£ï<¾#ªº¦Õ0<£ßÎö-`¶*[Í­Žkç&>Âì®O‡‹^,®$¼ªÙˆµææ£Ge{!O'I#ù*ÑH°.µãXƒúuWÄàNÃruÝ©õ©ŽœÊƒŠ³
<«DèìêÞ¢'Y®³ã'êî(?ÃJ‹¥ÊbÏey1·«�•*Ÿ©ˆweøÈv‰™8£úïbN(íÌ§øŸŽê·±2zXckÜ¯7`{}ÇvõŠbù|ÇÚ†îs²ÒÖTŒJà°íê‰Á•Š¨_ÝÊf‰±>°?©¶ßÎkùG8Ü",Ãç
+ºžûå„"ŽÖ•ÿ�§DTŠRÄ#õYVcúŽç«jßã¹Eª}l¹=ŠžÔå<§’£ÝBÏ¶Ýz\Pøô”ƒ“T°¦[NHÖZ¼iÝ4gåRWíúSÌ4«*‹¯ðm…�y8â\+ŠÛbÎ)ÑÃc³•ó´ž/i¸¬j­§äJT	7>ä…\¥¦·cÖá)Kíï•°�œÛÕ»ÈaºXº˜-ê]­–ÂbIô�uÝ}×gÆ‡5ÑTJÕáUCˆ	¸aŸâù©ùô–jçäÚY<ÀvÞÛlÔŒÒ›ªRfq:™jn€ï³DŽx×8BØ3ú^ægÊ¡×åœQT}°�"7FÔç¢�j�#sóU&c³^+dìJÆ3ìý™÷Ôô£gqÊé®ÆyPuR9j¡‹4ì­\Ì÷QUõ¹µ¥^`Wõ.ü§¥—:=ð±ü¶Æ	ê­14Žñ¾âEÿ|Uéµ–«'º»jÝœ/,Ñï�\É~
+Oµ«•Y‚!$žïÔY7Y|¾ÒÓÅ	g ìÿ7ª?m½£ì¯]�zLà"7Aœ½&ÃÄ—¢¬²ÕN¹R`%Gä�müÆ)Å5[íMøTÿµG70çp·¼úž¹~Á@~c~ñ‹›V]»ï]Lõg7ü6@‚²·�ºsX²ŸTÈ�@sõ¸–z–Ü…»q‡Ýã(¿¥‡ :Ê!ú¶ë8¤ßC:$gŽ™2>}rZ^Fn$mbvÎ´Î?ï$HëŸœØ'-oL$÷?-²ç
+endstream
+endobj
+54 0 obj
+<< /Producer (macOS Version 13.3 \(assemblage 22E252\) Quartz PDFContext)
+/CreationDate (D:20230416115150Z00'00') /ModDate (D:20230416115150Z00'00')
+>>
+endobj
+xref
+0 55
+0000000000 65535 f 
+0000001892 00000 n 
+0000007641 00000 n 
+0000000022 00000 n 
+0000002006 00000 n 
+0000004849 00000 n 
+0000007826 00000 n 
+0000011104 00000 n 
+0000014206 00000 n 
+0000017360 00000 n 
+0000002136 00000 n 
+0000004939 00000 n 
+0000004885 00000 n 
+0000005234 00000 n 
+0000005311 00000 n 
+0000005383 00000 n 
+0000005460 00000 n 
+0000005537 00000 n 
+0000005614 00000 n 
+0000005691 00000 n 
+0000005768 00000 n 
+0000005840 00000 n 
+0000005912 00000 n 
+0000005990 00000 n 
+0000006063 00000 n 
+0000006136 00000 n 
+0000006214 00000 n 
+0000006287 00000 n 
+0000006360 00000 n 
+0000006438 00000 n 
+0000006511 00000 n 
+0000006584 00000 n 
+0000006662 00000 n 
+0000006735 00000 n 
+0000006813 00000 n 
+0000006886 00000 n 
+0000006964 00000 n 
+0000007037 00000 n 
+0000007115 00000 n 
+0000007188 00000 n 
+0000007266 00000 n 
+0000007339 00000 n 
+0000007417 00000 n 
+0000007490 00000 n 
+0000007568 00000 n 
+0000007724 00000 n 
+0000008179 00000 n 
+0000008458 00000 n 
+0000011527 00000 n 
+0000011806 00000 n 
+0000014636 00000 n 
+0000014922 00000 n 
+0000017777 00000 n 
+0000018056 00000 n 
+0000020350 00000 n 
+trailer
+<< /Size 55 /Root 45 0 R /Info 54 0 R /ID [ <8d323c94f076b8ccee3f5011fe73e195>
+<8d323c94f076b8ccee3f5011fe73e195> ] >>
+startxref
+20518
+%%EOF
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/doc/source/wifi-design.rst ns-3.39/src/wifi/doc/source/wifi-design.rst
--- ns-3.38/src/wifi/doc/source/wifi-design.rst	2023-03-16 20:54:23.848869300 -0700
+++ ns-3.39/src/wifi/doc/source/wifi-design.rst	2023-07-05 14:23:48.105428500 -0700
@@ -707,6 +707,18 @@
 be allocated to sub-carriers. Adjacent channels are models by the use of
 OFDM transmit spectrum masks as defined in the standards.
 
+The class ``WifiBandwidthFilter`` is used to discard signals early in the
+transmission process by ignoring any Wi-Fi PPDU whose TX band (including guard bands)
+does not overlap the current operating channel. Therefore, it bypasses the signal
+propagation/loss calculations reducing the computational load and increasing the
+simulation performance. To enable the ``WifiBandwidthFilter``, the user can use object
+aggregation as follows:
+.. sourcecode:: cpp
+
+  Ptr<WifiBandwidthFilter> wifiFilter = CreateObject<WifiBandwidthFilter> ();
+  Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel> ();
+  spectrumChannel->AddSpectrumTransmitFilter(wifiFilter);
+
 To support an easier user configuration experience, the existing
 YansWifi helper classes (in ``src/wifi/helper``) were copied and
 adapted to provide equivalent SpectrumWifi helper classes.
@@ -724,6 +736,35 @@
 ``WifiPhy::StartReceivePreamble`` to be called, and the processing continues
 as described above.
 
+Furthermore, in order to support more flexible channel switching,
+the ``SpectrumWifiPhy`` can hold multiple instances of ``WifiSpectrumPhyInterface``
+(:ref:`fig-spectrum-wifi-phy-multiple-interfaces`).
+Each of these instances handles a given frequency range of the spectrum, identified by
+a start and a stop frequency expressed in MHz, and there can be no overlap in spectrum between them.
+Only one of these ``WifiSpectrumPhyInterface`` instances corresponds to the active RF interface of the ``SpectrumWifiPhy``,
+the other ones are referred to as inactive RF interfaces and might be disconnected from the spectrum channel.
+
+.. _fig-spectrum-wifi-phy-multiple-interfaces:
+
+.. figure:: figures/spectrum-wifi-phy-multiple-interfaces.*
+   :align: center
+
+   Multiple RF interfaces concept
+
+If the ``SpectrumWifiPhy::TrackSignalsFromInactiveInterfaces`` attribute is set to true,
+inactive RF interfaces are connected to their respective spectrum channels and the ``SpectrumWifiPhy``
+forwards received signals from these inactive RF interfaces to the ``InterferenceHelper`` without processing them.
+The benefit of the latter is that more accurate PHY-CCA.indication can be generated upon channel switching
+if one or more signals started to be transmitted on the new channel before the switch occurs,
+which would be ignored otherwise. This is illustrated in Figure :ref:`fig-cca-channel-switching-multiple-interfaces`, where the parts in red are only generated when ``SpectrumWifiPhy::TrackSignalsFromInactiveInterfaces`` is set to true.
+
+.. _fig-cca-channel-switching-multiple-interfaces:
+
+.. figure:: figures/cca-channel-switching-multiple-interfaces.*
+   :align: center
+
+   Illustration of signals tracking upon channel switching
+
 The MAC model
 =============
 
@@ -1046,6 +1087,52 @@
 of a Basic Trigger Frame in order for the AP to collect information about the buffer status
 of the stations.
 
+Enhanced multi-link single radio operation (EMLSR)
+##################################################
+
+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.
+
+Non-AP MLD side
+---------------
+
+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).
+
+AP MLD side
+-----------
+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.
+
 Ack manager
 ###########
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/doc/source/wifi-user.rst ns-3.39/src/wifi/doc/source/wifi-user.rst
--- ns-3.38/src/wifi/doc/source/wifi-user.rst	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/doc/source/wifi-user.rst	2023-07-05 14:23:48.105428500 -0700
@@ -505,6 +505,17 @@
 with the exception that a channel of type ``ns3::SpectrumChannel`` instead
 of type ``ns3::YansWifiChannel`` must be used with it.
 
+Its API has been extended for 802.11be multi-link and EMLSR in order to
+attach multiple spectrum channels to a same PHY. For that purpose, a user
+may use the following command to attach a spectrum channel to the PHY objects
+that will be created upon a call to ``ns3::WifiHelper::Install``::
+
+  SpectrumWifiPhyHelper::SetPcapDataLinkType(const Ptr<SpectrumChannel> channel,
+                                             const FrequencyRange& freqRange)
+
+where FrequencyRange is a structure that contains the start and stop frequencies
+expressed in MHz which corresponds to the spectrum portion that is covered by the channel.
+
 WifiMacHelper
 =============
 
@@ -852,7 +863,7 @@
   WifiHelper wifi;
   wifi.SetStandard(WIFI_STANDARD_80211a);
 
-  YansWifiPhyHelper wifiPhy =  YansWifiPhyHelper::Default();
+  YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default();
   // ns-3 supports RadioTap and Prism tracing extensions for 802.11
   wifiPhy.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
 
@@ -893,8 +904,8 @@
 
   NodeContainer ap;
   ap.Create(1);
-  NodeContainer sta;
-  sta.Create(2);
+  NodeContainer stas;
+  stas.Create(2);
 
   WifiHelper wifi;
   wifi.SetStandard(WIFI_STANDARD_80211b);
@@ -919,18 +930,19 @@
 
   // Setup the rest of the upper mac
   Ssid ssid = Ssid("wifi-default");
-  // setup ap.
+
+  // setup AP.
   wifiMac.SetType("ns3::ApWifiMac",
                   "Ssid", SsidValue(ssid));
   NetDeviceContainer apDevice = wifi.Install(wifiPhy, wifiMac, ap);
   NetDeviceContainer devices = apDevice;
 
-  // setup sta.
+  // setup STAs.
   wifiMac.SetType("ns3::StaWifiMac",
                   "Ssid", SsidValue(ssid),
                   "ActiveProbing", BooleanValue(false));
-  NetDeviceContainer staDevice = wifi.Install(wifiPhy, wifiMac, sta);
-  devices.Add(staDevice);
+  NetDeviceContainer staDevices = wifi.Install(wifiPhy, wifiMac, stas);
+  devices.Add(staDevices);
 
   // Configure mobility
   MobilityHelper mobility;
@@ -944,3 +956,74 @@
   mobility.Install(sta);
 
   // other set up (e.g. InternetStack, Application)
+
+Multiple RF interfaces configuration
+++++++++++++++++++++++++++++++++++++
+
+  NodeContainer ap;
+  ap.Create(1);
+  NodeContainer sta;
+  sta.Create(1);
+
+  WifiHelper wifi;
+  wifi.SetStandard(WIFI_STANDARD_80211be);
+
+  // Create multiple spectrum channels
+  Ptr<MultiModelSpectrumChannel> spectrumChannel2_4Ghz =
+      CreateObject<MultiModelSpectrumChannel>();
+  Ptr<MultiModelSpectrumChannel> spectrumChannel5Ghz =
+      CreateObject<MultiModelSpectrumChannel>();
+  Ptr<MultiModelSpectrumChannel> spectrumChannel6Ghz =
+      CreateObject<MultiModelSpectrumChannel>();
+
+  // optional: set up propagation loss model separately for each spectrum channel
+
+  // 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);
+
+  // configure operating channel for each link
+  phy.Set(0, "ChannelSettings", StringValue("{42, 0, BAND_2_4GHZ, 0}"));
+  phy.Set(1, "ChannelSettings", StringValue("{42, 0, BAND_5GHZ, 0}"));
+  phy.Set(2, "ChannelSettings", StringValue("{215, 0, BAND_6GHZ, 0}"));
+
+  // configure rate manager for each link
+  wifi.SetRemoteStationManager(0,
+                               "ns3::ConstantRateWifiManager",
+                               "DataMode", StringValue("EhtMcs11"),
+                               "ControlMode", StringValue("ErpOfdmRate24Mbps"));
+  wifi.SetRemoteStationManager(1,
+                               "ns3::ConstantRateWifiManager",
+                               "DataMode", StringValue("EhtMcs9"),
+                               "ControlMode", StringValue("OfdmRate24Mbps"));
+  wifi.SetRemoteStationManager(2,
+                               "ns3::ConstantRateWifiManager",
+                               "DataMode", StringValue("EhtMcs7"),
+                               "ControlMode", StringValue("HeMcs4"));
+
+  // setup AP.
+  wifiMac.SetType("ns3::ApWifiMac",
+                  "Ssid", SsidValue(ssid));
+  NetDeviceContainer apDevice = wifi.Install(wifiPhy, wifiMac, ap);
+  NetDeviceContainer devices = apDevice;
+
+  // setup STA.
+  wifiMac.SetType("ns3::StaWifiMac",
+                  "Ssid", SsidValue(ssid),
+                  "ActiveProbing", BooleanValue(false));
+  NetDeviceContainer staDevice = wifi.Install(wifiPhy, wifiMac, sta);
+  devices.Add(staDevice);
+
+  // Configure mobility
+  MobilityHelper mobility;
+  Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
+  positionAlloc->Add(Vector(0.0, 0.0, 0.0));
+  positionAlloc->Add(Vector(5.0, 0.0, 0.0));
+  mobility.SetPositionAllocator(positionAlloc);
+  mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+  mobility.Install(c);
+
+  // other set up (e.g. InternetStack, Application)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/examples/wifi-manager-example.cc ns-3.39/src/wifi/examples/wifi-manager-example.cc
--- ns-3.38/src/wifi/examples/wifi-manager-example.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/examples/wifi-manager-example.cc	2023-07-05 14:23:48.105428500 -0700
@@ -277,7 +277,7 @@
     std::cout << "Run 'wifi-manager-example --PrintHelp' to show program options." << std::endl
               << std::endl;
 
-    if (infrastructure == false)
+    if (!infrastructure)
     {
         NS_ABORT_MSG_IF(serverNss != clientNss,
                         "In ad hoc mode, we assume sender and receiver are similarly configured");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/examples/wifi-phy-test.cc ns-3.39/src/wifi/examples/wifi-phy-test.cc
--- ns-3.38/src/wifi/examples/wifi-phy-test.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/examples/wifi-phy-test.cc	2023-07-05 14:23:48.105428500 -0700
@@ -59,7 +59,7 @@
      * \param input the PSR experiment
      * \returns the PSR experiment output
      */
-    struct PsrExperiment::Output Run(struct PsrExperiment::Input input);
+    PsrExperiment::Output Run(PsrExperiment::Input input);
 
   private:
     /// Send function
@@ -114,8 +114,8 @@
 {
 }
 
-struct PsrExperiment::Output
-PsrExperiment::Run(struct PsrExperiment::Input input)
+PsrExperiment::Output
+PsrExperiment::Run(PsrExperiment::Input input)
 {
     m_output.received = 0;
     m_input = input;
@@ -189,7 +189,7 @@
      * \param input the collision experiment data
      * \returns the experiment output
      */
-    struct CollisionExperiment::Output Run(struct CollisionExperiment::Input input);
+    CollisionExperiment::Output Run(CollisionExperiment::Input input);
 
   private:
     /// Send A function
@@ -277,8 +277,8 @@
 {
 }
 
-struct CollisionExperiment::Output
-CollisionExperiment::Run(struct CollisionExperiment::Input input)
+CollisionExperiment::Output
+CollisionExperiment::Run(CollisionExperiment::Input input)
 {
     m_output.receivedA = 0;
     m_output.receivedB = 0;
@@ -339,7 +339,7 @@
 PrintPsr(int argc, char* argv[])
 {
     PsrExperiment experiment;
-    struct PsrExperiment::Input input;
+    PsrExperiment::Input input;
 
     CommandLine cmd(__FILE__);
     cmd.AddValue("Distance", "The distance between two phys", input.distance);
@@ -351,7 +351,7 @@
                  input.txPowerLevel);
     cmd.Parse(argc, argv);
 
-    struct PsrExperiment::Output output;
+    PsrExperiment::Output output;
     output = experiment.Run(input);
 
     double psr = output.received;
@@ -361,7 +361,7 @@
 }
 
 double
-CalcPsr(struct PsrExperiment::Output output, struct PsrExperiment::Input input)
+CalcPsr(PsrExperiment::Output output, PsrExperiment::Input input)
 {
     double psr = output.received;
     psr /= input.nPackets;
@@ -371,7 +371,7 @@
 static void
 PrintPsrVsDistance(int argc, char* argv[])
 {
-    struct PsrExperiment::Input input;
+    PsrExperiment::Input input;
     CommandLine cmd(__FILE__);
     cmd.AddValue("TxPowerLevel",
                  "The power level index to use to send each packet",
@@ -385,7 +385,7 @@
     {
         std::cout << input.distance;
         PsrExperiment experiment;
-        struct PsrExperiment::Output output;
+        PsrExperiment::Output output;
 
         input.txMode = "OfdmRate6Mbps";
         output = experiment.Run(input);
@@ -427,7 +427,7 @@
 PrintSizeVsRange(int argc, char* argv[])
 {
     double targetPsr = 0.05;
-    struct PsrExperiment::Input input;
+    PsrExperiment::Input input;
     CommandLine cmd(__FILE__);
     cmd.AddValue("TxPowerLevel",
                  "The power level index to use to send each packet",
@@ -445,7 +445,7 @@
         while (high - low > precision)
         {
             double middle = low + (high - low) / 2;
-            struct PsrExperiment::Output output;
+            PsrExperiment::Output output;
             PsrExperiment experiment;
             input.distance = middle;
             output = experiment.Run(input);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/examples/wifi-test-interference-helper.cc ns-3.39/src/wifi/examples/wifi-test-interference-helper.cc
--- ns-3.38/src/wifi/examples/wifi-test-interference-helper.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/examples/wifi-test-interference-helper.cc	2023-07-05 14:23:48.105428500 -0700
@@ -108,7 +108,7 @@
      * Run function
      * \param input the interference experiment data
      */
-    void Run(struct InterferenceExperiment::Input input);
+    void Run(InterferenceExperiment::Input input);
 
   private:
     /**
@@ -213,12 +213,12 @@
 }
 
 void
-InterferenceExperiment::Run(struct InterferenceExperiment::Input input)
+InterferenceExperiment::Run(InterferenceExperiment::Input input)
 {
     m_input = input;
 
-    double range = std::max(std::abs(input.xA), input.xB);
-    Config::SetDefault("ns3::RangePropagationLossModel::MaxRange", DoubleValue(range));
+    double maxRange = std::max(std::abs(input.xA), input.xB);
+    Config::SetDefault("ns3::RangePropagationLossModel::MaxRange", DoubleValue(maxRange));
 
     Ptr<SingleModelSpectrumChannel> channel = CreateObject<SingleModelSpectrumChannel>();
     channel->SetPropagationDelayModel(CreateObject<ConstantSpeedPropagationDelayModel>());
@@ -235,7 +235,6 @@
     Ptr<Node> nodeA = CreateObject<Node>();
     Ptr<WifiNetDevice> devA = CreateObject<WifiNetDevice>();
     m_txA = CreateObject<SpectrumWifiPhy>();
-    m_txA->CreateWifiSpectrumPhyInterface(devA);
     m_txA->SetDevice(devA);
     m_txA->SetTxPowerStart(input.txPowerLevelA);
     m_txA->SetTxPowerEnd(input.txPowerLevelA);
@@ -243,7 +242,6 @@
     Ptr<Node> nodeB = CreateObject<Node>();
     Ptr<WifiNetDevice> devB = CreateObject<WifiNetDevice>();
     m_txB = CreateObject<SpectrumWifiPhy>();
-    m_txB->CreateWifiSpectrumPhyInterface(devB);
     m_txB->SetDevice(devB);
     m_txB->SetTxPowerStart(input.txPowerLevelB);
     m_txB->SetTxPowerEnd(input.txPowerLevelB);
@@ -251,7 +249,6 @@
     Ptr<Node> nodeRx = CreateObject<Node>();
     Ptr<WifiNetDevice> devRx = CreateObject<WifiNetDevice>();
     Ptr<SpectrumWifiPhy> rx = CreateObject<SpectrumWifiPhy>();
-    rx->CreateWifiSpectrumPhyInterface(devRx);
     rx->SetDevice(devRx);
 
     Ptr<InterferenceHelper> interferenceTxA = CreateObject<InterferenceHelper>();
@@ -266,9 +263,9 @@
     rx->SetInterferenceHelper(interferenceRx);
     Ptr<ErrorRateModel> errorRx = CreateObject<NistErrorRateModel>();
     rx->SetErrorRateModel(errorRx);
-    m_txA->SetChannel(channel);
-    m_txB->SetChannel(channel);
-    rx->SetChannel(channel);
+    m_txA->AddChannel(channel);
+    m_txB->AddChannel(channel);
+    rx->AddChannel(channel);
     m_txA->SetMobility(posTxA);
     m_txB->SetMobility(posTxB);
     rx->SetMobility(posRx);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/helper/spectrum-wifi-helper.cc ns-3.39/src/wifi/helper/spectrum-wifi-helper.cc
--- ns-3.38/src/wifi/helper/spectrum-wifi-helper.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/helper/spectrum-wifi-helper.cc	2023-07-05 14:23:48.105428500 -0700
@@ -27,8 +27,12 @@
 #include "ns3/mobility-model.h"
 #include "ns3/names.h"
 #include "ns3/preamble-detection-model.h"
+#include "ns3/spectrum-channel.h"
+#include "ns3/spectrum-transmit-filter.h"
 #include "ns3/spectrum-wifi-phy.h"
+#include "ns3/wifi-bandwidth-filter.h"
 #include "ns3/wifi-net-device.h"
+#include "ns3/wifi-spectrum-value-helper.h"
 
 namespace ns3
 {
@@ -43,41 +47,65 @@
     {
         phy.SetTypeId("ns3::SpectrumWifiPhy");
     }
-    m_channels.resize(m_phy.size());
     SetInterferenceHelper("ns3::InterferenceHelper");
     SetErrorRateModel("ns3::TableBasedErrorRateModel");
 }
 
 void
-SpectrumWifiPhyHelper::SetChannel(Ptr<SpectrumChannel> channel)
+SpectrumWifiPhyHelper::SetChannel(const Ptr<SpectrumChannel> channel)
 {
-    for (auto& ch : m_channels)
-    {
-        ch = channel;
-    }
+    m_channels[WHOLE_WIFI_SPECTRUM] = channel;
+    AddWifiBandwidthFilter(channel);
 }
 
 void
-SpectrumWifiPhyHelper::SetChannel(std::string channelName)
+SpectrumWifiPhyHelper::SetChannel(const std::string& channelName)
 {
     Ptr<SpectrumChannel> channel = Names::Find<SpectrumChannel>(channelName);
-    for (auto& ch : m_channels)
-    {
-        ch = channel;
-    }
+    m_channels[WHOLE_WIFI_SPECTRUM] = channel;
+    AddWifiBandwidthFilter(channel);
 }
 
 void
-SpectrumWifiPhyHelper::SetChannel(uint8_t linkId, Ptr<SpectrumChannel> channel)
+SpectrumWifiPhyHelper::AddChannel(const Ptr<SpectrumChannel> channel,
+                                  const FrequencyRange& freqRange)
 {
-    m_channels.at(linkId) = channel;
+    m_channels[freqRange] = channel;
+    AddWifiBandwidthFilter(channel);
 }
 
 void
-SpectrumWifiPhyHelper::SetChannel(uint8_t linkId, std::string channelName)
+SpectrumWifiPhyHelper::AddChannel(const std::string& channelName, const FrequencyRange& freqRange)
 {
     Ptr<SpectrumChannel> channel = Names::Find<SpectrumChannel>(channelName);
-    m_channels.at(linkId) = channel;
+    AddChannel(channel, freqRange);
+    AddWifiBandwidthFilter(channel);
+}
+
+void
+SpectrumWifiPhyHelper::AddWifiBandwidthFilter(Ptr<SpectrumChannel> channel)
+{
+    Ptr<const SpectrumTransmitFilter> p = channel->GetSpectrumTransmitFilter();
+    bool found = false;
+    while (p && !found)
+    {
+        if (DynamicCast<const WifiBandwidthFilter>(p))
+        {
+            NS_LOG_DEBUG("Found existing WifiBandwidthFilter for channel " << channel);
+            found = true;
+        }
+        else
+        {
+            NS_LOG_DEBUG("Found different SpectrumTransmitFilter for channel " << channel);
+            p = p->GetNext();
+        }
+    }
+    if (!found)
+    {
+        Ptr<WifiBandwidthFilter> pWifi = CreateObject<WifiBandwidthFilter>();
+        channel->AddSpectrumTransmitFilter(pWifi);
+        NS_LOG_DEBUG("Adding WifiBandwidthFilter to channel " << channel);
+    }
 }
 
 std::vector<Ptr<WifiPhy>>
@@ -88,7 +116,6 @@
     for (std::size_t i = 0; i < m_phy.size(); i++)
     {
         Ptr<SpectrumWifiPhy> phy = m_phy.at(i).Create<SpectrumWifiPhy>();
-        phy->CreateWifiSpectrumPhyInterface(device);
         auto interference = m_interferenceHelper.Create<InterferenceHelper>();
         phy->SetInterferenceHelper(interference);
         Ptr<ErrorRateModel> error = m_errorRateModel.at(i).Create<ErrorRateModel>();
@@ -104,7 +131,10 @@
                 m_preambleDetectionModel.at(i).Create<PreambleDetectionModel>();
             phy->SetPreambleDetectionModel(preambleDetection);
         }
-        phy->SetChannel(m_channels.at(i));
+        for (const auto& [freqRange, channel] : m_channels)
+        {
+            phy->AddChannel(channel, freqRange);
+        }
         phy->SetDevice(device);
         phy->SetMobility(node->GetObject<MobilityModel>());
         ret.emplace_back(phy);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/helper/spectrum-wifi-helper.h ns-3.39/src/wifi/helper/spectrum-wifi-helper.h
--- ns-3.38/src/wifi/helper/spectrum-wifi-helper.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/helper/spectrum-wifi-helper.h	2023-07-05 14:23:48.105428500 -0700
@@ -33,6 +33,9 @@
  * The Pcap and ASCII traces generated by the EnableAscii and EnablePcap methods defined
  * in this class correspond to PHY-level traces and come to us via WifiPhyHelper
  *
+ * This helper will install an instance of a WifiBandwidthFilter on any SpectrumChannel
+ * added to this helper (via one of the SetChannel methods), unless one has
+ * previously been installed on the channel object.
  */
 class SpectrumWifiPhyHelper : public WifiPhyHelper
 {
@@ -45,33 +48,38 @@
     SpectrumWifiPhyHelper(uint8_t nLinks = 1);
 
     /**
-     * \param channel the channel to associate to this helper
+     * \param channel the default spectrum channel to associate to this helper
      *
-     * Every PHY created by a call to Install is associated to this channel.
+     * Every PHY created by a call to Install is associated to this default spectrum channel.
      */
-    void SetChannel(Ptr<SpectrumChannel> channel);
+    void SetChannel(const Ptr<SpectrumChannel> channel);
     /**
-     * \param channelName The name of the channel to associate to this helper
+     * \param channelName The name of the default spectrum channel to associate to this helper
      *
-     * Every PHY created by a call to Install is associated to this channel.
+     * Every PHY created by a call to Install is associated to this default spectrum channel.
      */
-    void SetChannel(std::string channelName);
+    void SetChannel(const std::string& channelName);
+
     /**
-     * \param channel the channel to associate to this helper
-     * \param linkId ID of the link to configure (>0 only for 11be devices)
+     * \param channel the spectrum channel to add to this helper
+     * \param freqRange the frequency range, bounded by a minFrequency and a maxFrequency in MHz
      *
-     * The PHY associated with the given link and created by a call to Install
-     * is associated to this channel.
+     * Every PHY created by a call to Install is added to this spectrum channel.
+     * If a PHY is requested to operate or scan a channel with the specified frequency and width
+     * combination, it will activate that channel and deactivate the current channel for that PHY.
      */
-    void SetChannel(uint8_t linkId, Ptr<SpectrumChannel> channel);
+    void AddChannel(const Ptr<SpectrumChannel> channel,
+                    const FrequencyRange& freqRange = WHOLE_WIFI_SPECTRUM);
     /**
-     * \param channelName The name of the channel to associate to this helper
-     * \param linkId ID of the link to configure (>0 only for 11be devices)
+     * \param channelName The name of the spectrum channel to add to this helper
+     * \param freqRange the frequency range, bounded by a minFrequency and a maxFrequency in MHz
      *
-     * The PHY associated with the given link and created by a call to Install
-     * is associated to this channel.
+     * Every PHY created by a call to Install is added to this spectrum channel.
+     * If a PHY is requested to operate or scan a channel with the specified frequency and width
+     * combination, it will activate that channel and deactivate the current channel for that PHY.
      */
-    void SetChannel(uint8_t linkId, std::string channelName);
+    void AddChannel(const std::string& channelName,
+                    const FrequencyRange& freqRange = WHOLE_WIFI_SPECTRUM);
 
   private:
     /**
@@ -82,8 +90,15 @@
      * This method implements the pure virtual method defined in \ref ns3::WifiPhyHelper.
      */
     std::vector<Ptr<WifiPhy>> Create(Ptr<Node> node, Ptr<WifiNetDevice> device) const override;
+    /**
+     * \param channel The channel to inspect to possibly add a WifiBandwidthFilter
+     *
+     * This method will add a WifiBandwidthFilter object to the SpectrumChannel, only if
+     * one has not yet been added.
+     */
+    void AddWifiBandwidthFilter(Ptr<SpectrumChannel> channel);
 
-    std::vector<Ptr<SpectrumChannel>> m_channels; ///< the channels
+    std::map<FrequencyRange, Ptr<SpectrumChannel>> m_channels; ///< the spectrum channels
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/helper/wifi-helper.cc ns-3.39/src/wifi/helper/wifi-helper.cc
--- ns-3.38/src/wifi/helper/wifi-helper.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/helper/wifi-helper.cc	2023-07-05 14:23:48.105428500 -0700
@@ -377,7 +377,7 @@
         packet->RemoveHeader(hdr);
         extractedLength = hdr.GetLength();
         packet = packet->CreateFragment(0, static_cast<uint32_t>(extractedLength));
-        if (aMpdu.type == LAST_MPDU_IN_AGGREGATE || (hdr.GetEof() == true && hdr.GetLength() > 0))
+        if (aMpdu.type == LAST_MPDU_IN_AGGREGATE || (hdr.GetEof() && hdr.GetLength() > 0))
         {
             ampduStatusFlags |= RadiotapHeader::A_MPDU_STATUS_LAST;
         }
@@ -890,21 +890,21 @@
     LogComponentEnable("AparfWifiManager", LOG_LEVEL_ALL);
     LogComponentEnable("ArfWifiManager", LOG_LEVEL_ALL);
     LogComponentEnable("BlockAckAgreement", LOG_LEVEL_ALL);
-    LogComponentEnable("RecipientBlockAckAgreement", LOG_LEVEL_ALL);
     LogComponentEnable("BlockAckManager", LOG_LEVEL_ALL);
     LogComponentEnable("CaraWifiManager", LOG_LEVEL_ALL);
     LogComponentEnable("ChannelAccessManager", LOG_LEVEL_ALL);
     LogComponentEnable("ConstantObssPdAlgorithm", LOG_LEVEL_ALL);
     LogComponentEnable("ConstantRateWifiManager", LOG_LEVEL_ALL);
-    LogComponentEnable("ChannelAccessManager", LOG_LEVEL_ALL);
+    LogComponentEnable("DefaultEmlsrManager", LOG_LEVEL_ALL);
     LogComponentEnable("DsssErrorRateModel", LOG_LEVEL_ALL);
     LogComponentEnable("DsssPhy", LOG_LEVEL_ALL);
     LogComponentEnable("DsssPpdu", LOG_LEVEL_ALL);
     LogComponentEnable("EhtFrameExchangeManager", LOG_LEVEL_ALL);
-    LogComponentEnable("ErpOfdmPhy", LOG_LEVEL_ALL);
-    LogComponentEnable("ErpOfdmPpdu", LOG_LEVEL_ALL);
     LogComponentEnable("EhtPhy", LOG_LEVEL_ALL);
     LogComponentEnable("EhtPpdu", LOG_LEVEL_ALL);
+    LogComponentEnable("EmlsrManager", LOG_LEVEL_ALL);
+    LogComponentEnable("ErpOfdmPhy", LOG_LEVEL_ALL);
+    LogComponentEnable("ErpOfdmPpdu", LOG_LEVEL_ALL);
     LogComponentEnable("FrameExchangeManager", LOG_LEVEL_ALL);
     LogComponentEnable("HeConfiguration", LOG_LEVEL_ALL);
     LogComponentEnable("HeFrameExchangeManager", LOG_LEVEL_ALL);
@@ -926,15 +926,16 @@
     LogComponentEnable("NistErrorRateModel", LOG_LEVEL_ALL);
     LogComponentEnable("ObssPdAlgorithm", LOG_LEVEL_ALL);
     LogComponentEnable("OfdmPhy", LOG_LEVEL_ALL);
+    LogComponentEnable("OfdmPpdu", LOG_LEVEL_ALL);
     LogComponentEnable("OnoeWifiManager", LOG_LEVEL_ALL);
     LogComponentEnable("OriginatorBlockAckAgreement", LOG_LEVEL_ALL);
-    LogComponentEnable("OfdmPpdu", LOG_LEVEL_ALL);
     LogComponentEnable("ParfWifiManager", LOG_LEVEL_ALL);
     LogComponentEnable("PhyEntity", LOG_LEVEL_ALL);
     LogComponentEnable("QosFrameExchangeManager", LOG_LEVEL_ALL);
     LogComponentEnable("QosTxop", LOG_LEVEL_ALL);
-    LogComponentEnable("RraaWifiManager", LOG_LEVEL_ALL);
+    LogComponentEnable("RecipientBlockAckAgreement", LOG_LEVEL_ALL);
     LogComponentEnable("RrMultiUserScheduler", LOG_LEVEL_ALL);
+    LogComponentEnable("RraaWifiManager", LOG_LEVEL_ALL);
     LogComponentEnable("RrpaaWifiManager", LOG_LEVEL_ALL);
     LogComponentEnable("SimpleFrameCaptureModel", LOG_LEVEL_ALL);
     LogComponentEnable("SpectrumWifiPhy", LOG_LEVEL_ALL);
@@ -958,8 +959,8 @@
     LogComponentEnable("WifiMpdu", LOG_LEVEL_ALL);
     LogComponentEnable("WifiNetDevice", LOG_LEVEL_ALL);
     LogComponentEnable("WifiPhyStateHelper", LOG_LEVEL_ALL);
-    LogComponentEnable("WifiPhy", LOG_LEVEL_ALL);
     LogComponentEnable("WifiPhyOperatingChannel", LOG_LEVEL_ALL);
+    LogComponentEnable("WifiPhy", LOG_LEVEL_ALL);
     LogComponentEnable("WifiPpdu", LOG_LEVEL_ALL);
     LogComponentEnable("WifiProtectionManager", LOG_LEVEL_ALL);
     LogComponentEnable("WifiPsdu", LOG_LEVEL_ALL);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/helper/wifi-helper.h ns-3.39/src/wifi/helper/wifi-helper.h
--- ns-3.38/src/wifi/helper/wifi-helper.h	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/src/wifi/helper/wifi-helper.h	2023-07-05 14:23:48.105428500 -0700
@@ -24,7 +24,6 @@
 
 #include "wifi-mac-helper.h"
 
-#include "ns3/deprecated.h"
 #include "ns3/qos-utils.h"
 #include "ns3/trace-helper.h"
 #include "ns3/wifi-phy.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/helper/wifi-mac-helper.cc ns-3.39/src/wifi/helper/wifi-mac-helper.cc
--- ns-3.38/src/wifi/helper/wifi-mac-helper.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/helper/wifi-mac-helper.cc	2023-07-05 14:23:48.105428500 -0700
@@ -20,6 +20,8 @@
 #include "wifi-mac-helper.h"
 
 #include "ns3/boolean.h"
+#include "ns3/eht-configuration.h"
+#include "ns3/emlsr-manager.h"
 #include "ns3/frame-exchange-manager.h"
 #include "ns3/multi-user-scheduler.h"
 #include "ns3/wifi-ack-manager.h"
@@ -40,6 +42,7 @@
     m_queueScheduler.SetTypeId("ns3::FcfsWifiQueueScheduler");
     m_protectionManager.SetTypeId("ns3::WifiDefaultProtectionManager");
     m_ackManager.SetTypeId("ns3::WifiDefaultAckManager");
+    m_emlsrManager.SetTypeId("ns3::DefaultEmlsrManager");
 }
 
 WifiMacHelper::~WifiMacHelper()
@@ -67,9 +70,9 @@
     Ptr<WifiMacQueueScheduler> queueScheduler = m_queueScheduler.Create<WifiMacQueueScheduler>();
     mac->SetMacQueueScheduler(queueScheduler);
 
-    // WaveNetDevice stores PHY entities in a different member than WifiNetDevice, hence
-    // GetNPhys() would return 0. We have to attach a protection manager and an ack manager
-    // to the unique instance of frame exchange manager anyway
+    // WaveNetDevice (through ns-3.38) stores PHY entities in a different member than WifiNetDevice,
+    // hence GetNPhys() would return 0. We have to attach a protection manager and an ack manager to
+    // the unique instance of frame exchange manager anyway
     for (uint8_t linkId = 0; linkId < std::max<uint8_t>(device->GetNPhys(), 1); ++linkId)
     {
         auto fem = mac->GetFrameExchangeManager(linkId);
@@ -107,12 +110,23 @@
     }
 
     // create and install the Association Manager if this is a STA
-    if (auto staMac = DynamicCast<StaWifiMac>(mac); staMac != nullptr)
+    auto staMac = DynamicCast<StaWifiMac>(mac);
+    if (staMac)
     {
         Ptr<WifiAssocManager> assocManager = m_assocManager.Create<WifiAssocManager>();
         staMac->SetAssocManager(assocManager);
     }
 
+    // create and install the EMLSR Manager if this is an EHT non-AP MLD with EMLSR activated
+    if (BooleanValue emlsrActivated;
+        standard >= WIFI_STANDARD_80211be && staMac && staMac->GetNLinks() > 1 &&
+        device->GetEhtConfiguration()->GetAttributeFailSafe("EmlsrActivated", emlsrActivated) &&
+        emlsrActivated.Get())
+    {
+        auto emlsrManager = m_emlsrManager.Create<EmlsrManager>();
+        staMac->SetEmlsrManager(emlsrManager);
+    }
+
     return mac;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/helper/wifi-mac-helper.h ns-3.39/src/wifi/helper/wifi-mac-helper.h
--- ns-3.38/src/wifi/helper/wifi-mac-helper.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/helper/wifi-mac-helper.h	2023-07-05 14:23:48.105428500 -0700
@@ -120,6 +120,16 @@
     void SetMultiUserScheduler(std::string type, Args&&... args);
 
     /**
+     * Helper function used to set the EMLSR Manager that can be installed on an EHT non-AP MLD.
+     *
+     * \tparam Args \deduced Template type parameter pack for the sequence of name-value pairs.
+     * \param type the type of EMLSR Manager
+     * \param args A sequence of name-value pairs of the attributes to set.
+     */
+    template <typename... Args>
+    void SetEmlsrManager(std::string type, Args&&... args);
+
+    /**
      * \param device the device within which the MAC object will reside
      * \param standard the standard to configure during installation
      * \returns a new MAC object.
@@ -135,6 +145,7 @@
     ObjectFactory m_protectionManager; ///< Factory to create a protection manager
     ObjectFactory m_ackManager;        ///< Factory to create an acknowledgment manager
     ObjectFactory m_muScheduler;       ///< Multi-user Scheduler object factory
+    ObjectFactory m_emlsrManager;      ///< EMLSR Manager object factory
 };
 
 } // namespace ns3
@@ -194,6 +205,14 @@
     m_muScheduler.Set(args...);
 }
 
+template <typename... Args>
+void
+WifiMacHelper::SetEmlsrManager(std::string type, Args&&... args)
+{
+    m_emlsrManager.SetTypeId(type);
+    m_emlsrManager.Set(args...);
+}
+
 } // namespace ns3
 
 #endif /* WIFI_MAC_HELPER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ap-wifi-mac.cc ns-3.39/src/wifi/model/ap-wifi-mac.cc
--- ns-3.38/src/wifi/model/ap-wifi-mac.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/ap-wifi-mac.cc	2023-07-05 14:23:48.109428400 -0700
@@ -23,7 +23,6 @@
 
 #include "amsdu-subframe-header.h"
 #include "channel-access-manager.h"
-#include "frame-exchange-manager.h"
 #include "mac-rx-middle.h"
 #include "mac-tx-middle.h"
 #include "mgt-headers.h"
@@ -35,6 +34,8 @@
 #include "wifi-net-device.h"
 #include "wifi-phy.h"
 
+#include "ns3/eht-configuration.h"
+#include "ns3/eht-frame-exchange-manager.h"
 #include "ns3/he-configuration.h"
 #include "ns3/ht-configuration.h"
 #include "ns3/log.h"
@@ -423,11 +424,11 @@
     return true;
 }
 
-SupportedRates
+AllSupportedRates
 ApWifiMac::GetSupportedRates(uint8_t linkId) const
 {
     NS_LOG_FUNCTION(this << +linkId);
-    SupportedRates rates;
+    AllSupportedRates rates;
     // Send the set of supported rates and make sure that we indicate
     // the Basic Rate set in this set of supported rates.
     for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
@@ -662,11 +663,29 @@
     NS_LOG_FUNCTION(this << +linkId << frameType << to);
     NS_ABORT_IF(GetNLinks() == 1);
 
-    MultiLinkElement mle(MultiLinkElement::BASIC_VARIANT, frameType);
+    MultiLinkElement mle(MultiLinkElement::BASIC_VARIANT);
     mle.SetMldMacAddress(GetAddress());
     mle.SetLinkIdInfo(linkId);
     mle.SetBssParamsChangeCount(0);
 
+    auto ehtConfiguration = GetEhtConfiguration();
+    NS_ASSERT(ehtConfiguration);
+
+    if (BooleanValue emlsrActivated;
+        ehtConfiguration->GetAttributeFailSafe("EmlsrActivated", emlsrActivated) &&
+        emlsrActivated.Get())
+    {
+        mle.SetEmlsrSupported(true);
+        // When the EMLSR Padding Delay subfield is included in a frame sent by an AP affiliated
+        // with an AP MLD, the EMLSR Padding Delay subfield is reserved.
+        // When the EMLSR Transition Delay subfield is included in a frame sent by an AP affiliated
+        // with an AP MLD, the EMLSR Transition Delay subfield is reserved. (Sec. 9.4.2.312.2.3
+        // of 802.11be D2.3)
+        TimeValue time;
+        ehtConfiguration->GetAttribute("TransitionTimeout", time);
+        mle.SetTransitionTimeout(time.Get());
+    }
+
     // if the Multi-Link Element is being inserted in a (Re)Association Response frame
     // and the remote station is affiliated with an MLD, try multi-link setup
     if (auto staMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(to);
@@ -919,50 +938,52 @@
     hdr.SetDsNotTo();
     Ptr<Packet> packet = Create<Packet>();
     MgtProbeResponseHeader probe;
-    probe.SetSsid(GetSsid());
-    probe.SetSupportedRates(GetSupportedRates(linkId));
+    probe.Get<Ssid>() = GetSsid();
+    auto supportedRates = GetSupportedRates(linkId);
+    probe.Get<SupportedRates>() = supportedRates.rates;
+    probe.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
     probe.SetBeaconIntervalUs(GetBeaconInterval().GetMicroSeconds());
-    probe.SetCapabilities(GetCapabilities(linkId));
+    probe.Capabilities() = GetCapabilities(linkId);
     GetWifiRemoteStationManager(linkId)->SetShortPreambleEnabled(
         GetLink(linkId).shortPreambleEnabled);
     GetWifiRemoteStationManager(linkId)->SetShortSlotTimeEnabled(
         GetLink(linkId).shortSlotTimeEnabled);
     if (GetDsssSupported(linkId))
     {
-        probe.SetDsssParameterSet(GetDsssParameterSet(linkId));
+        probe.Get<DsssParameterSet>() = GetDsssParameterSet(linkId);
     }
     if (GetErpSupported(linkId))
     {
-        probe.SetErpInformation(GetErpInformation(linkId));
+        probe.Get<ErpInformation>() = GetErpInformation(linkId);
     }
     if (GetQosSupported())
     {
-        probe.SetEdcaParameterSet(GetEdcaParameterSet(linkId));
+        probe.Get<EdcaParameterSet>() = GetEdcaParameterSet(linkId);
     }
     if (GetHtSupported())
     {
-        probe.SetExtendedCapabilities(GetExtendedCapabilities());
-        probe.SetHtCapabilities(GetHtCapabilities(linkId));
-        probe.SetHtOperation(GetHtOperation(linkId));
+        probe.Get<ExtendedCapabilities>() = GetExtendedCapabilities();
+        probe.Get<HtCapabilities>() = GetHtCapabilities(linkId);
+        probe.Get<HtOperation>() = GetHtOperation(linkId);
     }
-    if (GetVhtSupported(SINGLE_LINK_OP_ID))
+    if (GetVhtSupported(linkId))
     {
-        probe.SetVhtCapabilities(GetVhtCapabilities(linkId));
-        probe.SetVhtOperation(GetVhtOperation(linkId));
+        probe.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
+        probe.Get<VhtOperation>() = GetVhtOperation(linkId);
     }
     if (GetHeSupported())
     {
-        probe.SetHeCapabilities(GetHeCapabilities(linkId));
-        probe.SetHeOperation(GetHeOperation(linkId));
+        probe.Get<HeCapabilities>() = GetHeCapabilities(linkId);
+        probe.Get<HeOperation>() = GetHeOperation(linkId);
         if (auto muEdcaParameterSet = GetMuEdcaParameterSet(); muEdcaParameterSet.has_value())
         {
-            probe.SetMuEdcaParameterSet(std::move(*muEdcaParameterSet));
+            probe.Get<MuEdcaParameterSet>() = std::move(*muEdcaParameterSet);
         }
     }
     if (GetEhtSupported())
     {
-        probe.SetEhtCapabilities(GetEhtCapabilities(linkId));
-        probe.SetEhtOperation(GetEhtOperation(linkId));
+        probe.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
+        probe.Get<EhtOperation>() = GetEhtOperation(linkId);
 
         if (GetNLinks() > 1)
         {
@@ -975,7 +996,7 @@
              */
             if (auto rnr = GetReducedNeighborReport(linkId); rnr.has_value())
             {
-                probe.SetReducedNeighborReport(std::move(*rnr));
+                probe.Get<ReducedNeighborReport>() = std::move(*rnr);
             }
             /*
              * If an AP affiliated with an AP MLD is not in a multiple BSSID set [..], the AP
@@ -985,7 +1006,8 @@
              * channel switching, extended channel switching, and channel quieting) are
              * satisfied. (Sec. 35.3.4.4 of 802.11be D2.1.1)
              */
-            probe.SetMultiLinkElement(GetMultiLinkElement(linkId, WIFI_MAC_MGT_PROBE_RESPONSE));
+            probe.Get<MultiLinkElement>() =
+                GetMultiLinkElement(linkId, WIFI_MAC_MGT_PROBE_RESPONSE);
         }
     }
     packet->AddHeader(probe);
@@ -1028,41 +1050,39 @@
         remoteStationManager->RecordDisassociated(to);
         code.SetFailure();
     }
-    assoc.SetSupportedRates(GetSupportedRates(linkId));
+    auto supportedRates = GetSupportedRates(linkId);
+    assoc.Get<SupportedRates>() = supportedRates.rates;
+    assoc.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
     assoc.SetStatusCode(code);
-    assoc.SetCapabilities(GetCapabilities(linkId));
-    if (GetErpSupported(linkId))
-    {
-        assoc.SetErpInformation(GetErpInformation(linkId));
-    }
+    assoc.Capabilities() = GetCapabilities(linkId);
     if (GetQosSupported())
     {
-        assoc.SetEdcaParameterSet(GetEdcaParameterSet(linkId));
+        assoc.Get<EdcaParameterSet>() = GetEdcaParameterSet(linkId);
     }
     if (GetHtSupported())
     {
-        assoc.SetExtendedCapabilities(GetExtendedCapabilities());
-        assoc.SetHtCapabilities(GetHtCapabilities(linkId));
-        assoc.SetHtOperation(GetHtOperation(linkId));
+        assoc.Get<ExtendedCapabilities>() = GetExtendedCapabilities();
+        assoc.Get<HtCapabilities>() = GetHtCapabilities(linkId);
+        assoc.Get<HtOperation>() = GetHtOperation(linkId);
     }
     if (GetVhtSupported(linkId))
     {
-        assoc.SetVhtCapabilities(GetVhtCapabilities(linkId));
-        assoc.SetVhtOperation(GetVhtOperation(linkId));
+        assoc.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
+        assoc.Get<VhtOperation>() = GetVhtOperation(linkId);
     }
     if (GetHeSupported())
     {
-        assoc.SetHeCapabilities(GetHeCapabilities(linkId));
-        assoc.SetHeOperation(GetHeOperation(linkId));
+        assoc.Get<HeCapabilities>() = GetHeCapabilities(linkId);
+        assoc.Get<HeOperation>() = GetHeOperation(linkId);
         if (auto muEdcaParameterSet = GetMuEdcaParameterSet(); muEdcaParameterSet.has_value())
         {
-            assoc.SetMuEdcaParameterSet(std::move(*muEdcaParameterSet));
+            assoc.Get<MuEdcaParameterSet>() = std::move(*muEdcaParameterSet);
         }
     }
     if (GetEhtSupported())
     {
-        assoc.SetEhtCapabilities(GetEhtCapabilities(linkId));
-        assoc.SetEhtOperation(GetEhtOperation(linkId));
+        assoc.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
+        assoc.Get<EhtOperation>() = GetEhtOperation(linkId);
     }
     return assoc;
 }
@@ -1080,7 +1100,7 @@
         linkIdStaAddrMap[linkId] = to;
     }
 
-    if (const auto& mle = assoc.GetMultiLinkElement())
+    if (const auto& mle = assoc.Get<MultiLinkElement>())
     {
         const auto staMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(to);
         NS_ABORT_MSG_IF(!staMldAddress.has_value(),
@@ -1226,7 +1246,7 @@
     {
         assoc.SetAssociationId(aid);
     }
-    if (const auto& mle = assoc.GetMultiLinkElement())
+    if (const auto& mle = assoc.Get<MultiLinkElement>())
     {
         for (std::size_t idx = 0; idx < mle->GetNPerStaProfileSubelements(); idx++)
         {
@@ -1263,17 +1283,12 @@
     // stored its MLD address in the remote station manager
     if (GetNLinks() > 1 && GetWifiRemoteStationManager(linkId)->GetMldAddress(to).has_value())
     {
-        assoc.SetMultiLinkElement(GetMultiLinkElement(linkId, hdr.GetType(), to));
+        assoc.Get<MultiLinkElement>() = GetMultiLinkElement(linkId, hdr.GetType(), to);
     }
 
     auto linkIdStaAddrMap = GetLinkIdStaAddrMap(assoc, to, linkId);
     SetAid(assoc, linkIdStaAddrMap);
 
-    if (GetNLinks() > 1)
-    {
-        ConfigQueueScheduler(linkIdStaAddrMap, to, linkId);
-    }
-
     Ptr<Packet> packet = Create<Packet>();
     packet->AddHeader(assoc);
 
@@ -1299,39 +1314,6 @@
 }
 
 void
-ApWifiMac::ConfigQueueScheduler(const LinkIdStaAddrMap& linkIdStaAddrMap,
-                                const Mac48Address& to,
-                                uint8_t linkId)
-{
-    NS_LOG_FUNCTION(this << to << +linkId);
-
-    // get a list of the IDs of the links to setup
-    std::list<uint8_t> linkIds;
-    std::transform(linkIdStaAddrMap.cbegin(),
-                   linkIdStaAddrMap.cend(),
-                   std::back_inserter(linkIds),
-                   [](auto&& linkIdStaAddrPair) { return linkIdStaAddrPair.first; });
-
-    // get the MLD address of the STA, if affiliated with a non-AP MLD, or the STA address
-    auto staAddr = to;
-    if (auto mldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(to))
-    {
-        staAddr = *mldAddr;
-    }
-
-    // configure the queue scheduler to only use the links that have been setup for
-    // transmissions to this station
-    for (const auto& [acIndex, wifiAc] : wifiAcList)
-    {
-        for (auto tid : {wifiAc.GetLowTid(), wifiAc.GetHighTid()})
-        {
-            WifiContainerQueueId queueId(WIFI_QOSDATA_UNICAST_QUEUE, staAddr, tid);
-            m_scheduler->SetLinkIds(acIndex, queueId, linkIds);
-        }
-    }
-}
-
-void
 ApWifiMac::SendOneBeacon(uint8_t linkId)
 {
     NS_LOG_FUNCTION(this << +linkId);
@@ -1345,48 +1327,50 @@
     hdr.SetDsNotTo();
     Ptr<Packet> packet = Create<Packet>();
     MgtBeaconHeader beacon;
-    beacon.SetSsid(GetSsid());
-    beacon.SetSupportedRates(GetSupportedRates(linkId));
+    beacon.Get<Ssid>() = GetSsid();
+    auto supportedRates = GetSupportedRates(linkId);
+    beacon.Get<SupportedRates>() = supportedRates.rates;
+    beacon.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
     beacon.SetBeaconIntervalUs(GetBeaconInterval().GetMicroSeconds());
-    beacon.SetCapabilities(GetCapabilities(linkId));
+    beacon.Capabilities() = GetCapabilities(linkId);
     GetWifiRemoteStationManager(linkId)->SetShortPreambleEnabled(link.shortPreambleEnabled);
     GetWifiRemoteStationManager(linkId)->SetShortSlotTimeEnabled(link.shortSlotTimeEnabled);
     if (GetDsssSupported(linkId))
     {
-        beacon.SetDsssParameterSet(GetDsssParameterSet(linkId));
+        beacon.Get<DsssParameterSet>() = GetDsssParameterSet(linkId);
     }
     if (GetErpSupported(linkId))
     {
-        beacon.SetErpInformation(GetErpInformation(linkId));
+        beacon.Get<ErpInformation>() = GetErpInformation(linkId);
     }
     if (GetQosSupported())
     {
-        beacon.SetEdcaParameterSet(GetEdcaParameterSet(linkId));
+        beacon.Get<EdcaParameterSet>() = GetEdcaParameterSet(linkId);
     }
     if (GetHtSupported())
     {
-        beacon.SetExtendedCapabilities(GetExtendedCapabilities());
-        beacon.SetHtCapabilities(GetHtCapabilities(linkId));
-        beacon.SetHtOperation(GetHtOperation(linkId));
+        beacon.Get<ExtendedCapabilities>() = GetExtendedCapabilities();
+        beacon.Get<HtCapabilities>() = GetHtCapabilities(linkId);
+        beacon.Get<HtOperation>() = GetHtOperation(linkId);
     }
     if (GetVhtSupported(linkId))
     {
-        beacon.SetVhtCapabilities(GetVhtCapabilities(linkId));
-        beacon.SetVhtOperation(GetVhtOperation(linkId));
+        beacon.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
+        beacon.Get<VhtOperation>() = GetVhtOperation(linkId);
     }
     if (GetHeSupported())
     {
-        beacon.SetHeCapabilities(GetHeCapabilities(linkId));
-        beacon.SetHeOperation(GetHeOperation(linkId));
+        beacon.Get<HeCapabilities>() = GetHeCapabilities(linkId);
+        beacon.Get<HeOperation>() = GetHeOperation(linkId);
         if (auto muEdcaParameterSet = GetMuEdcaParameterSet(); muEdcaParameterSet.has_value())
         {
-            beacon.SetMuEdcaParameterSet(std::move(*muEdcaParameterSet));
+            beacon.Get<MuEdcaParameterSet>() = std::move(*muEdcaParameterSet);
         }
     }
     if (GetEhtSupported())
     {
-        beacon.SetEhtCapabilities(GetEhtCapabilities(linkId));
-        beacon.SetEhtOperation(GetEhtOperation(linkId));
+        beacon.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
+        beacon.Get<EhtOperation>() = GetEhtOperation(linkId);
 
         if (GetNLinks() > 1)
         {
@@ -1399,7 +1383,7 @@
              */
             if (auto rnr = GetReducedNeighborReport(linkId); rnr.has_value())
             {
-                beacon.SetReducedNeighborReport(std::move(*rnr));
+                beacon.Get<ReducedNeighborReport>() = std::move(*rnr);
             }
             /*
              * If an AP affiliated with an AP MLD is not in a multiple BSSID set [..], the AP
@@ -1409,7 +1393,7 @@
              * channel switching, extended channel switching, and channel quieting) are
              * satisfied. (Sec. 35.3.4.4 of 802.11be D2.1.1)
              */
-            beacon.SetMultiLinkElement(GetMultiLinkElement(linkId, WIFI_MAC_MGT_BEACON));
+            beacon.Get<MultiLinkElement>() = GetMultiLinkElement(linkId, WIFI_MAC_MGT_BEACON);
         }
     }
     packet->AddHeader(beacon);
@@ -1458,7 +1442,17 @@
                 GetWifiRemoteStationManager(*linkId)->GetMldAddress(hdr.GetAddr1());
             staMldAddress.has_value())
         {
-            // the STA is affiliated with an MLD
+            /**
+             * The STA is affiliated with an MLD. From Sec. 35.3.7.1.4 of 802.11be D3.0:
+             * When a link becomes enabled for a non-AP STA that is affiliated with a non-AP MLD
+             * after successful association with an AP MLD with (Re)Association Request/Response
+             * frames transmitted on another link [...], the power management mode of the non-AP
+             * STA, immediately after the acknowledgement of the (Re)Association Response frame
+             * [...], is power save mode, and its power state is doze.
+             *
+             * Thus, STAs operating on all the links but the link used to establish association
+             * transition to power save mode.
+             */
             for (uint8_t i = 0; i < GetNLinks(); i++)
             {
                 auto stationManager = GetWifiRemoteStationManager(i);
@@ -1469,10 +1463,30 @@
                     NS_LOG_DEBUG("AP=" << GetFrameExchangeManager(i)->GetAddress()
                                        << " associated with STA=" << *staAddress);
                     stationManager->RecordGotAssocTxOk(*staAddress);
+                    StaSwitchingToPsMode(*staAddress, i);
                 }
             }
         }
     }
+    else if (hdr.IsAction())
+    {
+        if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
+            category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            // the EMLSR client acknowledged the EML Operating Mode Notification frame;
+            // we can stop the timer and enforce the configuration deriving from the
+            // EML Notification frame sent by the EMLSR client
+            if (auto eventIt = m_transitionTimeoutEvents.find(hdr.GetAddr1());
+                eventIt != m_transitionTimeoutEvents.cend() && eventIt->second.IsRunning())
+            {
+                // no need to wait until the expiration of the transition timeout
+                eventIt->second.PeekEventImpl()->Invoke();
+                eventIt->second.Cancel();
+            }
+        }
+    }
 }
 
 void
@@ -1514,6 +1528,56 @@
     }
 }
 
+void
+ApWifiMac::ProcessPowerManagementFlag(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << *mpdu << linkId);
+
+    Mac48Address staAddr = mpdu->GetHeader().GetAddr2();
+    bool staInPsMode = GetWifiRemoteStationManager(linkId)->IsInPsMode(staAddr);
+
+    if (!staInPsMode && mpdu->GetHeader().IsPowerManagement())
+    {
+        // the sending STA is switching to Power Save mode
+        StaSwitchingToPsMode(staAddr, linkId);
+    }
+    else if (staInPsMode && !mpdu->GetHeader().IsPowerManagement())
+    {
+        // the sending STA is switching back to Active mode
+        StaSwitchingToActiveModeOrDeassociated(staAddr, linkId);
+    }
+}
+
+void
+ApWifiMac::StaSwitchingToPsMode(const Mac48Address& staAddr, uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << staAddr << linkId);
+
+    GetWifiRemoteStationManager(linkId)->SetPsMode(staAddr, true);
+
+    // Block frames addressed to the STA in PS mode
+    NS_LOG_DEBUG("Block destination " << staAddr << " on link " << +linkId);
+    auto staMldAddr = GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr);
+    BlockUnicastTxOnLinks(WifiQueueBlockedReason::POWER_SAVE_MODE, staMldAddr, {linkId});
+}
+
+void
+ApWifiMac::StaSwitchingToActiveModeOrDeassociated(const Mac48Address& staAddr, uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << staAddr << linkId);
+
+    GetWifiRemoteStationManager(linkId)->SetPsMode(staAddr, false);
+
+    if (GetWifiRemoteStationManager(linkId)->IsAssociated(staAddr))
+    {
+        // the station is still associated, unblock its frames
+        NS_LOG_DEBUG("Unblock destination " << staAddr << " on link " << +linkId);
+        auto staMldAddr =
+            GetWifiRemoteStationManager(linkId)->GetMldAddress(staAddr).value_or(staAddr);
+        UnblockUnicastTxOnLinks(WifiQueueBlockedReason::POWER_SAVE_MODE, staMldAddr, {linkId});
+    }
+}
+
 std::optional<uint8_t>
 ApWifiMac::IsAssociated(const Mac48Address& address) const
 {
@@ -1562,6 +1626,10 @@
             (apLinkId = IsAssociated(mpdu->GetHeader().GetAddr2())) &&
             mpdu->GetHeader().GetAddr1() == GetFrameExchangeManager(*apLinkId)->GetAddress())
         {
+            // this MPDU is being acknowledged by the AP, so we can process
+            // the Power Management flag
+            ProcessPowerManagementFlag(mpdu, *apLinkId);
+
             Mac48Address to = hdr->GetAddr3();
             // Address3 can be our MLD address (e.g., this is an MPDU containing a single MSDU
             // addressed to us) or a BSSID (e.g., this is an MPDU containing an A-MSDU)
@@ -1578,7 +1646,7 @@
                         DeaggregateAmsduAndForward(mpdu);
                         packet = nullptr;
                     }
-                    else
+                    else if (hdr->HasData())
                     {
                         ForwardUp(packet, from, GetAddress());
                     }
@@ -1606,7 +1674,7 @@
                 }
                 ForwardUp(packet, from, to);
             }
-            else
+            else if (hdr->HasData())
             {
                 ForwardUp(packet, from, to);
             }
@@ -1627,6 +1695,13 @@
     }
     else if (hdr->IsMgt())
     {
+        if (hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress() &&
+            GetWifiRemoteStationManager(linkId)->IsAssociated(from))
+        {
+            // this MPDU is being acknowledged by the AP, so we can process
+            // the Power Management flag
+            ProcessPowerManagementFlag(mpdu, linkId);
+        }
         if (hdr->IsProbeReq() && (hdr->GetAddr1().IsGroup() ||
                                   hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress()))
         {
@@ -1642,8 +1717,8 @@
             }
             MgtProbeRequestHeader probeRequestHeader;
             packet->PeekHeader(probeRequestHeader);
-            const Ssid& ssid = probeRequestHeader.GetSsid();
-            if (ssid == GetSsid() || ssid.IsBroadcast())
+            const auto& ssid = probeRequestHeader.Get<Ssid>();
+            if (ssid == GetSsid() || ssid->IsBroadcast())
             {
                 NS_LOG_DEBUG("Probe request received from " << from << ": send probe response");
                 SendProbeResp(from, linkId);
@@ -1652,8 +1727,10 @@
         }
         else if (hdr->GetAddr1() == GetFrameExchangeManager(linkId)->GetAddress())
         {
-            if (hdr->IsAssocReq() || hdr->IsReassocReq())
+            switch (hdr->GetType())
             {
+            case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
+            case WIFI_MAC_MGT_REASSOCIATION_REQUEST: {
                 NS_LOG_DEBUG(((hdr->IsAssocReq()) ? "Association" : "Reassociation")
                              << " request received from " << from
                              << ((GetNLinks() > 1) ? " on link ID " + std::to_string(linkId) : ""));
@@ -1678,8 +1755,7 @@
                 SendAssocResp(hdr->GetAddr2(), hdr->IsReassocReq(), linkId);
                 return;
             }
-            else if (hdr->IsDisassociation())
-            {
+            case WIFI_MAC_MGT_DISASSOCIATION: {
                 NS_LOG_DEBUG("Disassociation received from " << from);
                 GetWifiRemoteStationManager(linkId)->RecordDisassociated(from);
                 auto& staList = GetLink(linkId).staList;
@@ -1700,11 +1776,31 @@
                         }
                         UpdateShortSlotTimeEnabled(linkId);
                         UpdateShortPreambleEnabled(linkId);
+                        StaSwitchingToActiveModeOrDeassociated(from, linkId);
                         break;
                     }
                 }
                 return;
             }
+            case WIFI_MAC_MGT_ACTION: {
+                auto pkt = mpdu->GetPacket()->Copy();
+                auto [category, action] = WifiActionHeader::Remove(pkt);
+                if (category == WifiActionHeader::PROTECTED_EHT &&
+                    action.protectedEhtAction ==
+                        WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION &&
+                    IsAssociated(hdr->GetAddr2()))
+                {
+                    // received an EML Operating Mode Notification frame from an associated station
+                    MgtEmlOperatingModeNotification frame;
+                    pkt->RemoveHeader(frame);
+                    ReceiveEmlNotification(frame, hdr->GetAddr2(), linkId);
+                    return;
+                }
+                break;
+            }
+            default:;
+                // do nothing
+            }
         }
     }
 
@@ -1735,9 +1831,11 @@
 
         // first, verify that the the station's supported
         // rate set is compatible with our Basic Rate set
-        const CapabilityInformation& capabilities = frame.GetCapabilities();
+        const CapabilityInformation& capabilities = frame.Capabilities();
         remoteStationManager->AddSupportedPhyPreamble(from, capabilities.IsShortPreamble());
-        const SupportedRates& rates = frame.GetSupportedRates();
+        NS_ASSERT(frame.template Get<SupportedRates>());
+        const auto rates = AllSupportedRates{*frame.template Get<SupportedRates>(),
+                                             frame.template Get<ExtendedSupportedRatesIE>()};
 
         if (rates.GetNRates() == 0)
         {
@@ -1747,7 +1845,7 @@
         if (GetHtSupported())
         {
             // check whether the HT STA supports all MCSs in Basic MCS Set
-            const auto& htCapabilities = frame.GetHtCapabilities();
+            const auto& htCapabilities = frame.template Get<HtCapabilities>();
             if (htCapabilities.has_value() && htCapabilities->IsSupportedMcs(0))
             {
                 for (uint8_t i = 0; i < remoteStationManager->GetNBasicMcs(); i++)
@@ -1763,7 +1861,7 @@
         if (GetVhtSupported(linkId))
         {
             // check whether the VHT STA supports all MCSs in Basic MCS Set
-            const auto& vhtCapabilities = frame.GetVhtCapabilities();
+            const auto& vhtCapabilities = frame.template Get<VhtCapabilities>();
             if (vhtCapabilities.has_value() && vhtCapabilities->GetVhtCapabilitiesInfo() != 0)
             {
                 for (uint8_t i = 0; i < remoteStationManager->GetNBasicMcs(); i++)
@@ -1779,7 +1877,7 @@
         if (GetHeSupported())
         {
             // check whether the HE STA supports all MCSs in Basic MCS Set
-            const auto& heCapabilities = frame.GetHeCapabilities();
+            const auto& heCapabilities = frame.template Get<HeCapabilities>();
             if (heCapabilities.has_value() && heCapabilities->GetSupportedMcsAndNss() != 0)
             {
                 for (uint8_t i = 0; i < remoteStationManager->GetNBasicMcs(); i++)
@@ -1817,17 +1915,17 @@
         }
         if (GetHtSupported())
         {
-            const auto& htCapabilities = frame.GetHtCapabilities();
+            const auto& htCapabilities = frame.template Get<HtCapabilities>();
             if (htCapabilities.has_value() && htCapabilities->IsSupportedMcs(0))
             {
                 remoteStationManager->AddStationHtCapabilities(from, *htCapabilities);
             }
-            // const ExtendedCapabilities& extendedCapabilities = frame.GetExtendedCapabilities ();
+            // const ExtendedCapabilities& extendedCapabilities = frame.GetExtendedCapabilities();
             // TODO: to be completed
         }
         if (GetVhtSupported(linkId))
         {
-            const auto& vhtCapabilities = frame.GetVhtCapabilities();
+            const auto& vhtCapabilities = frame.template Get<VhtCapabilities>();
             // we will always fill in RxHighestSupportedLgiDataRate field at TX, so this can be used
             // to check whether it supports VHT
             if (vhtCapabilities.has_value() &&
@@ -1846,7 +1944,7 @@
         }
         if (GetHeSupported())
         {
-            const auto& heCapabilities = frame.GetHeCapabilities();
+            const auto& heCapabilities = frame.template Get<HeCapabilities>();
             if (heCapabilities.has_value() && heCapabilities->GetSupportedMcsAndNss() != 0)
             {
                 remoteStationManager->AddStationHeCapabilities(from, *heCapabilities);
@@ -1862,7 +1960,7 @@
         }
         if (GetEhtSupported())
         {
-            if (const auto& ehtCapabilities = frame.GetEhtCapabilities())
+            if (const auto& ehtCapabilities = frame.template Get<EhtCapabilities>())
             {
                 remoteStationManager->AddStationEhtCapabilities(from, *ehtCapabilities);
             }
@@ -1890,14 +1988,25 @@
 
     // lambda to process received Multi-Link Element
     auto recvMle = [&](auto&& frame) {
-        const auto& mle = frame.get().GetMultiLinkElement();
+        const auto& mle = frame.get().template Get<MultiLinkElement>();
 
         if (!mle.has_value())
         {
             return;
         }
 
+        auto emlCapabilities = std::make_shared<CommonInfoBasicMle::EmlCapabilities>();
+        if (mle->HasEmlCapabilities())
+        {
+            emlCapabilities->emlsrSupport = mle->IsEmlsrSupported() ? 1 : 0;
+            emlCapabilities->emlsrPaddingDelay =
+                CommonInfoBasicMle::EncodeEmlsrPaddingDelay(mle->GetEmlsrPaddingDelay());
+            emlCapabilities->emlsrTransitionDelay =
+                CommonInfoBasicMle::EncodeEmlsrTransitionDelay(mle->GetEmlsrTransitionDelay());
+        }
+
         GetWifiRemoteStationManager(linkId)->SetMldAddress(from, mle->GetMldMacAddress());
+        GetWifiRemoteStationManager(linkId)->AddStationEmlCapabilities(from, emlCapabilities);
 
         for (std::size_t i = 0; i < mle->GetNPerStaProfileSubelements(); i++)
         {
@@ -1925,6 +2034,9 @@
                                 newLinkId);
             GetWifiRemoteStationManager(newLinkId)->SetMldAddress(perStaProfile.GetStaMacAddress(),
                                                                   mle->GetMldMacAddress());
+            GetWifiRemoteStationManager(newLinkId)->AddStationEmlCapabilities(
+                perStaProfile.GetStaMacAddress(),
+                emlCapabilities);
         }
     };
 
@@ -1932,6 +2044,146 @@
 }
 
 void
+ApWifiMac::ReceiveEmlNotification(MgtEmlOperatingModeNotification& frame,
+                                  const Mac48Address& sender,
+                                  uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << frame << sender << linkId);
+
+    auto ehtConfiguration = GetEhtConfiguration();
+
+    if (BooleanValue emlsrActivated;
+        !ehtConfiguration ||
+        !ehtConfiguration->GetAttributeFailSafe("EmlsrActivated", emlsrActivated) ||
+        !emlsrActivated.Get())
+    {
+        NS_LOG_DEBUG(
+            "Received an EML Operating Mode Notification frame but EMLSR is not activated");
+        return;
+    }
+
+    if (frame.m_emlControl.emlsrParamUpdateCtrl)
+    {
+        NS_ASSERT(frame.m_emlsrParamUpdate);
+        auto emlCapabilities =
+            GetWifiRemoteStationManager(linkId)->GetStationEmlCapabilities(sender);
+        NS_ASSERT_MSG(emlCapabilities, "EML Capabilities not stored for STA " << sender);
+
+        // update values stored in remote station manager
+        emlCapabilities->emlsrPaddingDelay = frame.m_emlsrParamUpdate->paddingDelay;
+        emlCapabilities->emlsrTransitionDelay = frame.m_emlsrParamUpdate->transitionDelay;
+    }
+
+    auto mldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(sender);
+    NS_ASSERT_MSG(mldAddress, "No MLD address stored for STA " << sender);
+    auto emlsrLinks =
+        frame.m_emlControl.emlsrMode == 1 ? frame.GetLinkBitmap() : std::list<uint8_t>{};
+
+    // The AP MLD has to consider the changes carried by the received EML Notification frame
+    // as effective at the same time as the non-AP MLD. Therefore, we need to start a time
+    // when the transmission of the Ack following the received EML Notification frame is
+    // 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 */) {
+            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");
+
+            auto ackDuration =
+                WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand());
+
+            TimeValue transitionTimeout;
+            ehtConfiguration->GetAttribute("TransitionTimeout", transitionTimeout);
+
+            m_transitionTimeoutEvents[sender] =
+                Simulator::Schedule(ackDuration + transitionTimeout.Get(), [=]() {
+                    for (uint8_t id = 0; id < GetNLinks(); id++)
+                    {
+                        auto linkAddress =
+                            GetWifiRemoteStationManager(id)->GetAffiliatedStaAddress(*mldAddress);
+                        if (!linkAddress)
+                        {
+                            // this link has not been setup by the non-AP MLD
+                            continue;
+                        }
+
+                        if (!emlsrLinks.empty())
+                        {
+                            // the non-AP MLD is enabling EMLSR mode
+                            /**
+                             * After the successful transmission of the EML Operating Mode
+                             * Notification frame by the non-AP STA affiliated with the non-AP MLD,
+                             * the non-AP MLD shall operate in the EMLSR mode and the other non-AP
+                             * STAs operating on the corresponding EMLSR links shall transition to
+                             * active mode after the transition delay indicated in the Transition
+                             * Timeout subfield in the EML Capabilities subfield of the Basic
+                             * Multi-Link element or immediately after receiving an EML Operating
+                             * Mode Notification frame from one of the APs operating on the EMLSR
+                             * links and affiliated with the AP MLD (Sec. 35.3.17 of 802.11be D3.0)
+                             */
+                            auto enabled = std::find(emlsrLinks.cbegin(), emlsrLinks.cend(), id) !=
+                                           emlsrLinks.cend();
+                            if (enabled)
+                            {
+                                StaSwitchingToActiveModeOrDeassociated(*linkAddress, id);
+                            }
+                            GetWifiRemoteStationManager(id)->SetEmlsrEnabled(*linkAddress, enabled);
+                        }
+                        else
+                        {
+                            // the non-AP MLD is disabling EMLSR mode
+                            /**
+                             * After the successful transmission of the EML Operating Mode
+                             * Notification frame by the non-AP STA affiliated with the non-AP MLD,
+                             * the non-AP MLD shall disable the EMLSR mode and the other non-AP
+                             * STAs operating on the corresponding EMLSR links shall transition to
+                             * power save mode after the transition delay indicated in the
+                             * Transition Timeout subfield in the EML Capabilities subfield of the
+                             * Basic Multi-Link element or immediately after receiving an EML
+                             * Operating Mode Notification frame from one of the APs operating on
+                             * the EMLSR links and affiliated with the AP MLD. (Sec. 35.3.17 of
+                             * 802.11be D3.0)
+                             */
+                            if (id != linkId &&
+                                GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*linkAddress))
+                            {
+                                StaSwitchingToPsMode(*linkAddress, id);
+                            }
+                            GetWifiRemoteStationManager(id)->SetEmlsrEnabled(*linkAddress, false);
+                        }
+                    }
+                });
+        });
+
+    // connect the callback to the PHY TX begin trace to catch the Ack and disconnect
+    // after its transmission begins
+    auto phy = GetLink(linkId).phy;
+    phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb);
+    Simulator::Schedule(phy->GetSifs() + NanoSeconds(1),
+                        [=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); });
+
+    // An AP MLD with dot11EHTEMLSROptionActivated equal to true sets the EMLSR Mode subfield
+    // to the value obtained from the EMLSR Mode subfield of the received EML Operating Mode
+    // Notification frame. (Sec. 9.6.35.8 of 802.11be D3.0)
+
+    // When included in a frame sent by an AP affiliated with an AP MLD, the EMLSR Parameter
+    // Update Control subfield is set to 0. (Sec. 9.6.35.8 of 802.11be D3.0)
+    frame.m_emlControl.emlsrParamUpdateCtrl = 0;
+
+    // An AP MLD with dot11EHTEMLSROptionImplemented equal to true sets the EMLSR Link Bitmap
+    // subfield to the value obtained from the EMLSR Link Bitmap subfield of the received
+    // EML Operating Mode Notification frame. (Sec. 9.6.35.8 of 802.11be D3.0)
+
+    // The EMLSR Parameter Update field [..] is present if [..] the Action frame is sent by
+    // a non-AP STA affiliated with a non-AP MLD (Sec. 9.6.35.8 of 802.11be D3.0)
+    frame.m_emlsrParamUpdate.reset();
+
+    auto ehtFem = StaticCast<EhtFrameExchangeManager>(GetFrameExchangeManager(linkId));
+    ehtFem->SendEmlOperatingModeNotification(sender, frame);
+}
+
+void
 ApWifiMac::DeaggregateAmsduAndForward(Ptr<const WifiMpdu> mpdu)
 {
     NS_LOG_FUNCTION(this << *mpdu);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ap-wifi-mac.h ns-3.39/src/wifi/model/ap-wifi-mac.h
--- ns-3.38/src/wifi/model/ap-wifi-mac.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/ap-wifi-mac.h	2023-07-05 14:23:48.109428400 -0700
@@ -31,7 +31,7 @@
 namespace ns3
 {
 
-class SupportedRates;
+struct AllSupportedRates;
 class CapabilityInformation;
 class DsssParameterSet;
 class ErpInformation;
@@ -48,6 +48,7 @@
 class MgtAssocRequestHeader;
 class MgtReassocRequestHeader;
 class MgtAssocResponseHeader;
+class MgtEmlOperatingModeNotification;
 
 /// variant holding a  reference to a (Re)Association Request
 using AssocReqRefVariant = std::variant<std::reference_wrapper<MgtAssocRequestHeader>,
@@ -238,6 +239,18 @@
     void ParseReportedStaInfo(const AssocReqRefVariant& assoc, Mac48Address from, uint8_t linkId);
 
     /**
+     * Take necessary actions upon receiving the given EML Operating Mode Notification frame
+     * from the given station on the given link.
+     *
+     * \param frame the received EML Operating Mode Notification frame
+     * \param sender the MAC address of the sender of the frame
+     * \param linkId the ID of the link over which the frame was received
+     */
+    void ReceiveEmlNotification(MgtEmlOperatingModeNotification& frame,
+                                const Mac48Address& sender,
+                                uint8_t linkId);
+
+    /**
      * The packet we sent was successfully received by the receiver
      * (i.e. we received an Ack from the receiver).  If the packet
      * was an association response to the receiver, we record that
@@ -329,18 +342,6 @@
                                          const Mac48Address& to,
                                          uint8_t linkId);
     /**
-     * Configure the queue scheduler so that frames stored in the container queues associated
-     * with the station which we are sending an Association Response frame to are only transmitted
-     * on the setup links.
-     *
-     * \param linkIdStaAddrMap a map of (link ID, remote STA address) of the links to setup
-     * \param to the Receiver Address (RA) of the Association Response frame
-     * \param linkId the ID of the link on which the Association Response frame is being sent
-     */
-    void ConfigQueueScheduler(const LinkIdStaAddrMap& linkIdStaAddrMap,
-                              const Mac48Address& to,
-                              uint8_t linkId);
-    /**
      * Forward an association or a reassociation response packet to the DCF/EDCA.
      *
      * \param to the address of the STA we are sending an association response to
@@ -357,6 +358,31 @@
     void SendOneBeacon(uint8_t linkId);
 
     /**
+     * Process the Power Management bit in the Frame Control field of an MPDU
+     * successfully received on the given link.
+     *
+     * \param mpdu the successfully received MPDU
+     * \param linkId the ID of the given link
+     */
+    void ProcessPowerManagementFlag(Ptr<const WifiMpdu> mpdu, uint8_t linkId);
+    /**
+     * Perform the necessary actions when a given station switches from active mode
+     * to powersave mode.
+     *
+     * \param staAddr the MAC address of the given station
+     * \param linkId the ID of the link on which the given station is operating
+     */
+    void StaSwitchingToPsMode(const Mac48Address& staAddr, uint8_t linkId);
+    /**
+     * Perform the necessary actions when a given station deassociates or switches
+     * from powersave mode to active mode.
+     *
+     * \param staAddr the MAC address of the given station
+     * \param linkId the ID of the link on which the given station is operating
+     */
+    void StaSwitchingToActiveModeOrDeassociated(const Mac48Address& staAddr, uint8_t linkId);
+
+    /**
      * Return the Capability information of the current AP for the given link.
      *
      * \param linkId the ID of the given link
@@ -438,7 +464,7 @@
      * \param linkId the ID of the given link
      * \return all rates that we support
      */
-    SupportedRates GetSupportedRates(uint8_t linkId) const;
+    AllSupportedRates GetSupportedRates(uint8_t linkId) const;
     /**
      * Return the DSSS Parameter Set that we support on the given link
      *
@@ -501,6 +527,8 @@
     bool m_enableNonErpProtection; //!< Flag whether protection mechanism is used or not when
                                    //!< non-ERP STAs are present within the BSS
     Time m_bsrLifetime;            //!< Lifetime of Buffer Status Reports
+    /// transition timeout events running for EMLSR clients
+    std::map<Mac48Address, EventId> m_transitionTimeoutEvents;
 
     /// store value and timestamp for each Buffer Status Report
     struct BsrType
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/block-ack-manager.cc ns-3.39/src/wifi/model/block-ack-manager.cc
--- ns-3.38/src/wifi/model/block-ack-manager.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/block-ack-manager.cc	2023-07-05 14:23:48.109428400 -0700
@@ -600,8 +600,6 @@
     // schedule a BlockAckRequest
     NS_LOG_DEBUG("Schedule a Block Ack Request for agreement (" << recipient << ", " << +tid
                                                                 << ")");
-    Ptr<Packet> bar = Create<Packet>();
-    bar->AddHeader(GetBlockAckReqHeader(recipient, tid));
 
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_CTL_BACKREQ);
@@ -612,7 +610,7 @@
     hdr.SetNoRetry();
     hdr.SetNoMoreFragments();
 
-    ScheduleBar(Create<WifiMpdu>(bar, hdr));
+    ScheduleBar(GetBlockAckReqHeader(recipient, tid), hdr);
 }
 
 void
@@ -660,28 +658,27 @@
 }
 
 void
-BlockAckManager::ScheduleBar(Ptr<WifiMpdu> bar)
+BlockAckManager::ScheduleBar(const CtrlBAckRequestHeader& reqHdr, const WifiMacHeader& hdr)
 {
-    NS_LOG_FUNCTION(this << *bar);
-    NS_ASSERT(bar->GetHeader().IsBlockAckReq());
+    NS_LOG_FUNCTION(this << reqHdr << hdr);
 
-    CtrlBAckRequestHeader reqHdr;
-    bar->GetPacket()->PeekHeader(reqHdr);
     uint8_t tid = reqHdr.GetTidInfo();
 
-    WifiContainerQueueId queueId(WIFI_CTL_QUEUE, bar->GetHeader().GetAddr2(), std::nullopt);
+    WifiContainerQueueId queueId(WIFI_CTL_QUEUE, WIFI_UNICAST, hdr.GetAddr1(), std::nullopt);
+    auto pkt = Create<Packet>();
+    pkt->AddHeader(reqHdr);
     Ptr<WifiMpdu> item = nullptr;
 
     // if a BAR for the given agreement is present, replace it with the new one
     while ((item = m_queue->PeekByQueueId(queueId, item)))
     {
-        if (item->GetHeader().IsBlockAckReq() &&
-            item->GetHeader().GetAddr1() == bar->GetHeader().GetAddr1())
+        if (item->GetHeader().IsBlockAckReq() && item->GetHeader().GetAddr1() == hdr.GetAddr1())
         {
             CtrlBAckRequestHeader otherHdr;
             item->GetPacket()->PeekHeader(otherHdr);
             if (otherHdr.GetTidInfo() == tid)
             {
+                auto bar = Create<WifiMpdu>(pkt, hdr, item->GetTimestamp());
                 // replace item with bar
                 m_queue->Replace(item, bar);
                 return;
@@ -689,7 +686,7 @@
         }
     }
 
-    m_queue->Enqueue(bar);
+    m_queue->Enqueue(Create<WifiMpdu>(pkt, hdr));
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/block-ack-manager.h ns-3.39/src/wifi/model/block-ack-manager.h
--- ns-3.38/src/wifi/model/block-ack-manager.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/block-ack-manager.h	2023-07-05 14:23:48.109428400 -0700
@@ -32,6 +32,7 @@
 #include "ns3/traced-callback.h"
 
 #include <map>
+#include <optional>
 
 namespace ns3
 {
@@ -408,18 +409,19 @@
     CtrlBAckRequestHeader GetBlockAckReqHeader(const Mac48Address& recipient, uint8_t tid) const;
 
     /**
-     * \param bar the BlockAckRequest to enqueue
+     * \param reqHdr the BlockAckRequest header
+     * \param hdr the 802.11 MAC header
      *
-     * Enqueue the given BlockAckRequest into the queue storing the next (MU-)BAR
+     * Enqueue the given BlockAckRequest into the queue storing the next BAR
      * frames to transmit. If a BAR for the same recipient and TID is already present
      * in the queue, it is replaced by the new one. If the given BAR is retransmitted,
      * it is placed at the head of the queue, otherwise at the tail.
      */
-    void ScheduleBar(Ptr<WifiMpdu> bar);
+    void ScheduleBar(const CtrlBAckRequestHeader& reqHdr, const WifiMacHeader& hdr);
     /**
      * \param muBar the MU-BAR Trigger Frame to enqueue
      *
-     * Enqueue the given MU-BAR Trigger Frame into the queue storing the next (MU-)BAR
+     * Enqueue the given MU-BAR Trigger Frame into the queue storing the next MU-BAR
      * frames to transmit. If the given MU-BAR Trigger Frame is retransmitted,
      * it is placed at the head of the queue, otherwise at the tail.
      */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/block-ack-type.h ns-3.39/src/wifi/model/block-ack-type.h
--- ns-3.38/src/wifi/model/block-ack-type.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/block-ack-type.h	2023-07-05 14:23:48.109428400 -0700
@@ -20,6 +20,7 @@
 #ifndef BLOCK_ACK_TYPE_H
 #define BLOCK_ACK_TYPE_H
 
+#include <cstdint>
 #include <ostream>
 #include <vector>
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/channel-access-manager.cc ns-3.39/src/wifi/model/channel-access-manager.cc
--- ns-3.38/src/wifi/model/channel-access-manager.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/channel-access-manager.cc	2023-07-05 14:23:48.109428400 -0700
@@ -140,6 +140,13 @@
 }
 
 void
+ChannelAccessManager::DoInitialize()
+{
+    NS_LOG_FUNCTION(this);
+    InitLastBusyStructs();
+}
+
+void
 ChannelAccessManager::DoDispose()
 {
     NS_LOG_FUNCTION(this);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/channel-access-manager.h ns-3.39/src/wifi/model/channel-access-manager.h
--- ns-3.38/src/wifi/model/channel-access-manager.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/channel-access-manager.h	2023-07-05 14:23:48.109428400 -0700
@@ -261,6 +261,7 @@
     bool IsBusy() const;
 
   protected:
+    void DoInitialize() override;
     void DoDispose() override;
 
   private:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ctrl-headers.cc ns-3.39/src/wifi/model/ctrl-headers.cc
--- ns-3.38/src/wifi/model/ctrl-headers.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/ctrl-headers.cc	2023-07-05 14:23:48.109428400 -0700
@@ -458,10 +458,12 @@
 
     for (auto& bitmapLen : m_baType.m_bitmapLen)
     {
-        m_baInfo.push_back({.m_aidTidInfo = 0,
-                            .m_startingSeq = 0,
-                            .m_bitmap = std::vector<uint8_t>(bitmapLen, 0),
-                            .m_ra = Mac48Address()});
+        BaInfoInstance baInfoInstance{.m_aidTidInfo = 0,
+                                      .m_startingSeq = 0,
+                                      .m_bitmap = std::vector<uint8_t>(bitmapLen, 0),
+                                      .m_ra = Mac48Address()};
+
+        m_baInfo.emplace_back(baInfoInstance);
     }
 }
 
@@ -1630,7 +1632,8 @@
       m_ulBandwidth(0),
       m_giAndLtfType(0),
       m_apTxPower(0),
-      m_ulSpatialReuse(0)
+      m_ulSpatialReuse(0),
+      m_padding(0)
 {
 }
 
@@ -1697,6 +1700,7 @@
     m_giAndLtfType = trigger.m_giAndLtfType;
     m_apTxPower = trigger.m_apTxPower;
     m_ulSpatialReuse = trigger.m_ulSpatialReuse;
+    m_padding = trigger.m_padding;
     m_userInfoFields.clear();
     m_userInfoFields = trigger.m_userInfoFields;
     return *this;
@@ -1761,7 +1765,7 @@
         size += ui.GetSerializedSize();
     }
 
-    size += 2; // Padding field
+    size += m_padding;
 
     return size;
 }
@@ -1800,7 +1804,10 @@
         i = ui.Serialize(i);
     }
 
-    i.WriteHtolsbU16(0xffff); // Padding field, used as delimiter
+    for (std::size_t count = 0; count < m_padding; count++)
+    {
+        i.WriteU8(0xff); // Padding field
+    }
 }
 
 uint32_t
@@ -1821,6 +1828,7 @@
     uint8_t bit54and55 = (commonInfo >> 54) & 0x03;
     m_variant = bit54and55 == 3 ? TriggerFrameVariant::HE : TriggerFrameVariant::EHT;
     m_userInfoFields.clear();
+    m_padding = 0;
 
     NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::BFRP_TRIGGER,
                     "BFRP Trigger frame is not supported");
@@ -1829,15 +1837,12 @@
     NS_ABORT_MSG_IF(m_triggerType == TriggerFrameType::NFRP_TRIGGER,
                     "NFRP Trigger frame is not supported");
 
-    bool isPadding = false;
-
-    // We always add a Padding field (of two octets of all 1s) as delimiter
-    while (!isPadding)
+    while (i.GetRemainingSize() >= 2)
     {
         // read the first 2 bytes to check if we encountered the Padding field
         if (i.ReadU16() == 0xffff)
         {
-            isPadding = true;
+            m_padding = i.GetRemainingSize() + 2;
         }
         else
         {
@@ -1874,8 +1879,7 @@
 {
 #define FOO(x)                                                                                     \
     case TriggerFrameType::x:                                                                      \
-        return #x;                                                                                 \
-        break;
+        return #x;
 
     switch (type)
     {
@@ -2112,6 +2116,19 @@
     return m_ulSpatialReuse;
 }
 
+void
+CtrlTriggerHeader::SetPaddingSize(std::size_t size)
+{
+    NS_ABORT_MSG_IF(size == 1, "The Padding field, if present, shall be at least two octets");
+    m_padding = size;
+}
+
+std::size_t
+CtrlTriggerHeader::GetPaddingSize() const
+{
+    return m_padding;
+}
+
 CtrlTriggerHeader
 CtrlTriggerHeader::GetCommonInfoField() const
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ctrl-headers.h ns-3.39/src/wifi/model/ctrl-headers.h
--- ns-3.38/src/wifi/model/ctrl-headers.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/ctrl-headers.h	2023-07-05 14:23:48.109428400 -0700
@@ -936,10 +936,7 @@
  *
  * The Padding field is optional, given that other techniques (post-EOF A-MPDU
  * padding, aggregating other MPDUs in the A-MPDU) are available to satisfy the
- * minimum time requirement. Currently, however, a Padding field of the minimum
- * length (2 octets) is appended to every Trigger frame that is transmitted. In
- * such a way, deserialization stops when a User Info field with the AID12 subfield
- * set to 4095 (which indicates the start of a Padding field) is encountered.
+ * minimum time requirement. The size in bytes of the Padding field is configurable.
  */
 class CtrlTriggerHeader : public Header
 {
@@ -1187,6 +1184,17 @@
      */
     uint16_t GetUlSpatialReuse() const;
     /**
+     * Set the size in bytes of the Padding field. The Padding field, if present,
+     * shall be at least two octets in length.
+     *
+     * \param size the size in bytes of the Padding field
+     */
+    void SetPaddingSize(std::size_t size);
+    /**
+     * \return the size in bytes of the Padding field
+     */
+    std::size_t GetPaddingSize() const;
+    /**
      * Get a copy of the Common Info field of this Trigger frame.
      * Note that the User Info fields are excluded.
      *
@@ -1329,6 +1337,7 @@
     uint8_t m_giAndLtfType;         //!< GI And LTF Type subfield
     uint8_t m_apTxPower;            //!< Tx Power used by AP to transmit the Trigger Frame
     uint16_t m_ulSpatialReuse;      //!< Value for the Spatial Reuse field in HE-SIG-A
+    std::size_t m_padding;          //!< the size in bytes of the Padding field
 
     /**
      * List of User Info fields
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/default-emlsr-manager.cc ns-3.39/src/wifi/model/eht/default-emlsr-manager.cc
--- ns-3.38/src/wifi/model/eht/default-emlsr-manager.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/eht/default-emlsr-manager.cc	2023-07-05 14:23:48.109428400 -0700
@@ -0,0 +1,85 @@
+/*
+ * 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 "default-emlsr-manager.h"
+
+#include "ns3/log.h"
+#include "ns3/wifi-mpdu.h"
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("DefaultEmlsrManager");
+
+NS_OBJECT_ENSURE_REGISTERED(DefaultEmlsrManager);
+
+TypeId
+DefaultEmlsrManager::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::DefaultEmlsrManager")
+                            .SetParent<EmlsrManager>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<DefaultEmlsrManager>();
+    return tid;
+}
+
+DefaultEmlsrManager::DefaultEmlsrManager()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+DefaultEmlsrManager::~DefaultEmlsrManager()
+{
+    NS_LOG_FUNCTION_NOARGS();
+}
+
+void
+DefaultEmlsrManager::DoNotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << *mpdu << linkId);
+
+    if (mpdu->GetHeader().IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout())
+    {
+        m_assocLinkId = linkId;
+    }
+}
+
+uint8_t
+DefaultEmlsrManager::GetLinkToSendEmlNotification()
+{
+    NS_LOG_FUNCTION(this);
+    NS_ASSERT_MSG(m_assocLinkId, "No recorded link on which Assoc Response was received");
+    return *m_assocLinkId;
+}
+
+std::optional<uint8_t>
+DefaultEmlsrManager::ResendNotification(Ptr<const WifiMpdu> mpdu)
+{
+    NS_LOG_FUNCTION(this);
+    NS_ASSERT_MSG(m_assocLinkId, "No recorded link on which Assoc Response was received");
+    return *m_assocLinkId;
+}
+
+void
+DefaultEmlsrManager::NotifyEmlsrModeChanged()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/default-emlsr-manager.h ns-3.39/src/wifi/model/eht/default-emlsr-manager.h
--- ns-3.38/src/wifi/model/eht/default-emlsr-manager.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/eht/default-emlsr-manager.h	2023-07-05 14:23:48.109428400 -0700
@@ -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>
+ */
+
+#ifndef DEFAULT_EMLSR_MANAGER_H
+#define DEFAULT_EMLSR_MANAGER_H
+
+#include "emlsr-manager.h"
+
+#include <optional>
+
+namespace ns3
+{
+
+/**
+ * \ingroup wifi
+ *
+ * DefaultEmlsrManager is the default EMLSR manager.
+ */
+class DefaultEmlsrManager : public EmlsrManager
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    DefaultEmlsrManager();
+    ~DefaultEmlsrManager() override;
+
+  protected:
+    uint8_t GetLinkToSendEmlNotification() override;
+    std::optional<uint8_t> ResendNotification(Ptr<const WifiMpdu> mpdu) override;
+
+  private:
+    void DoNotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId) override;
+    void NotifyEmlsrModeChanged() override;
+
+    std::optional<uint8_t> m_assocLinkId; /**< ID of the link on which Association Response
+                                               was received */
+};
+
+} // namespace ns3
+
+#endif /* DEFAULT_EMLSR_MANAGER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-capabilities.cc ns-3.39/src/wifi/model/eht/eht-capabilities.cc
--- ns-3.38/src/wifi/model/eht/eht-capabilities.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/eht/eht-capabilities.cc	2023-07-05 14:23:48.109428400 -0700
@@ -423,6 +423,12 @@
     return IE_EXT_EHT_CAPABILITIES;
 }
 
+void
+EhtCapabilities::Print(std::ostream& os) const
+{
+    os << "EHT Capabilities="; // TODO
+}
+
 uint16_t
 EhtCapabilities::GetInformationFieldSize() const
 {
@@ -726,11 +732,4 @@
     return count;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const EhtCapabilities& ehtCapabilities)
-{
-    // TODO
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-capabilities.h ns-3.39/src/wifi/model/eht/eht-capabilities.h
--- ns-3.38/src/wifi/model/eht/eht-capabilities.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/eht/eht-capabilities.h	2023-07-05 14:23:48.109428400 -0700
@@ -261,6 +261,7 @@
     // Implementations of pure virtual methods, or overridden from base class.
     WifiInformationElementId ElementId() const override;
     WifiInformationElementId ElementIdExt() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Set the maximum MPDU length.
@@ -347,22 +348,12 @@
     void SerializeInformationField(Buffer::Iterator start) const override;
     uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
 
-    friend std::ostream& operator<<(std::ostream& os, const EhtCapabilities& ehtCapabilities);
-
     bool m_is2_4Ghz; //!< flag indicating whether PHY is operating in 2.4 GHz based on other IEs
                      //!< contained in the same management frame
     std::optional<HeCapabilities>
         m_heCapabilities; //!< HE capabilities contained in the same management frame if present
 };
 
-/**
- * output stream output operator
- * \param os the output stream
- * \param ehtCapabilities the EHT capabilities
- * \returns the output stream
- */
-std::ostream& operator<<(std::ostream& os, const EhtCapabilities& ehtCapabilities);
-
 } // namespace ns3
 
 #endif /* EHT_CAPABILITIES_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-configuration.cc ns-3.39/src/wifi/model/eht/eht-configuration.cc
--- ns-3.38/src/wifi/model/eht/eht-configuration.cc	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/eht/eht-configuration.cc	2023-07-05 14:23:48.109428400 -0700
@@ -19,6 +19,7 @@
 
 #include "eht-configuration.h"
 
+#include "ns3/boolean.h"
 #include "ns3/log.h"
 
 namespace ns3
@@ -41,10 +42,25 @@
 TypeId
 EhtConfiguration::GetTypeId()
 {
-    static ns3::TypeId tid = ns3::TypeId("ns3::EhtConfiguration")
-                                 .SetParent<Object>()
-                                 .SetGroupName("Wifi")
-                                 .AddConstructor<EhtConfiguration>();
+    static ns3::TypeId tid =
+        ns3::TypeId("ns3::EhtConfiguration")
+            .SetParent<Object>()
+            .SetGroupName("Wifi")
+            .AddConstructor<EhtConfiguration>()
+            .AddAttribute("EmlsrActivated",
+                          "Whether EMLSR option is activated. If activated, EMLSR mode can be "
+                          "enabled on the EMLSR links by an installed EMLSR Manager.",
+                          TypeId::ATTR_GET |
+                              TypeId::ATTR_CONSTRUCT, // prevent setting after construction
+                          BooleanValue(false),
+                          MakeBooleanAccessor(&EhtConfiguration::m_emlsrActivated),
+                          MakeBooleanChecker())
+            .AddAttribute("TransitionTimeout",
+                          "The Transition Timeout (not used by non-AP MLDs). "
+                          "Possible values are 0us or 2^n us, with n=7..16.",
+                          TimeValue(MicroSeconds(0)),
+                          MakeTimeAccessor(&EhtConfiguration::m_transitionTimeout),
+                          MakeTimeChecker(MicroSeconds(0), MicroSeconds(65536)));
     return tid;
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-configuration.h ns-3.39/src/wifi/model/eht/eht-configuration.h
--- ns-3.38/src/wifi/model/eht/eht-configuration.h	2023-03-16 20:54:23.852869300 -0700
+++ ns-3.39/src/wifi/model/eht/eht-configuration.h	2023-07-05 14:23:48.109428400 -0700
@@ -20,6 +20,7 @@
 #ifndef EHT_CONFIGURATION_H
 #define EHT_CONFIGURATION_H
 
+#include "ns3/nstime.h"
 #include "ns3/object.h"
 
 namespace ns3
@@ -44,6 +45,10 @@
      * \return the object TypeId
      */
     static TypeId GetTypeId();
+
+  private:
+    bool m_emlsrActivated;    //!< whether EMLSR option is activated
+    Time m_transitionTimeout; //!< Transition timeout
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.cc ns-3.39/src/wifi/model/eht/eht-frame-exchange-manager.cc
--- ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-frame-exchange-manager.cc	2023-07-05 14:23:48.109428400 -0700
@@ -22,7 +22,9 @@
 #include "eht-phy.h"
 
 #include "ns3/abort.h"
+#include "ns3/ap-wifi-mac.h"
 #include "ns3/log.h"
+#include "ns3/wifi-mac-queue.h"
 
 #undef NS_LOG_APPEND_CONTEXT
 #define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "][mac=" << m_self << "] "
@@ -67,8 +69,8 @@
 {
     NS_LOG_FUNCTION(this << *mpdu);
 
-    // alias needs only be created for non-broadcast data frames exchanged between two MLDs
-    if (!mpdu->GetHeader().IsData() || m_mac->GetNLinks() == 1 ||
+    // alias needs only be created for non-broadcast QoS data frames exchanged between two MLDs
+    if (!mpdu->GetHeader().IsQosData() || m_mac->GetNLinks() == 1 ||
         mpdu->GetHeader().GetAddr1().IsGroup() ||
         !GetWifiRemoteStationManager()->GetMldAddress(mpdu->GetHeader().GetAddr1()))
     {
@@ -118,9 +120,147 @@
         txVector.SetSigBMode(sigBMode);
     }
 
+    if (!m_apMac)
+    {
+        HeFrameExchangeManager::ForwardPsduDown(psdu, txVector);
+        return;
+    }
+
+    auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
+
+    // check if the EMLSR clients shall switch back to listening operation at the end of this PPDU
+    for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
+    {
+        auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
+
+        if (GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
+            GetEmlsrSwitchToListening(psdu, aid, *clientIt))
+        {
+            EmlsrSwitchToListening(*clientIt, txDuration);
+            // this client is no longer involved in the current TXOP
+            clientIt = m_protectedStas.erase(clientIt);
+        }
+        else
+        {
+            clientIt++;
+        }
+    }
+
     HeFrameExchangeManager::ForwardPsduDown(psdu, txVector);
 }
 
+void
+EhtFrameExchangeManager::ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << psduMap << txVector);
+
+    if (!m_apMac)
+    {
+        HeFrameExchangeManager::ForwardPsduMapDown(psduMap, txVector);
+        return;
+    }
+
+    auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, m_phy->GetPhyBand());
+
+    // check if the EMLSR clients shall switch back to listening operation at the end of this PPDU
+    for (auto clientIt = m_protectedStas.begin(); clientIt != m_protectedStas.end();)
+    {
+        auto aid = GetWifiRemoteStationManager()->GetAssociationId(*clientIt);
+
+        if (auto psduMapIt = psduMap.find(aid);
+            GetWifiRemoteStationManager()->GetEmlsrEnabled(*clientIt) &&
+            (psduMapIt == psduMap.cend() ||
+             GetEmlsrSwitchToListening(psduMapIt->second, aid, *clientIt)))
+        {
+            EmlsrSwitchToListening(*clientIt, txDuration);
+            // this client is no longer involved in the current TXOP
+            clientIt = m_protectedStas.erase(clientIt);
+        }
+        else
+        {
+            clientIt++;
+        }
+    }
+
+    HeFrameExchangeManager::ForwardPsduMapDown(psduMap, txVector);
+}
+
+void
+EhtFrameExchangeManager::EmlsrSwitchToListening(const Mac48Address& address, const Time& delay)
+{
+    NS_LOG_FUNCTION(this << address << delay.As(Time::US));
+
+    // this EMLSR client switches back to listening operation a transition delay
+    // after the given delay
+    auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
+    NS_ASSERT(mldAddress);
+    auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
+    NS_ASSERT(emlCapabilities);
+
+    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->emlsrTransitionDelay),
+                                [=]() {
+                                    m_mac->UnblockUnicastTxOnLinks(
+                                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY,
+                                        *mldAddress,
+                                        {linkId});
+                                });
+        }
+    }
+}
+
+void
+EhtFrameExchangeManager::SendEmlOperatingModeNotification(
+    const Mac48Address& dest,
+    const MgtEmlOperatingModeNotification& frame)
+{
+    NS_LOG_FUNCTION(this << dest << frame);
+
+    WifiMacHeader hdr;
+    hdr.SetType(WIFI_MAC_MGT_ACTION);
+    hdr.SetAddr1(dest);
+    hdr.SetAddr2(m_self);
+    hdr.SetAddr3(m_bssid);
+    hdr.SetDsNotTo();
+    hdr.SetDsNotFrom();
+
+    // get the sequence number for the TWT Setup management frame
+    const auto sequence = m_txMiddle->GetNextSequenceNumberFor(&hdr);
+    hdr.SetSequenceNumber(sequence);
+
+    WifiActionHeader actionHdr;
+    WifiActionHeader::ActionValue action;
+    action.protectedEhtAction = WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION;
+    actionHdr.SetAction(WifiActionHeader::PROTECTED_EHT, action);
+
+    auto packet = Create<Packet>();
+    packet->AddHeader(frame);
+    packet->AddHeader(actionHdr);
+
+    // Use AC_VO to send management frame addressed to a QoS STA (Sec. 10.2.3.2 of 802.11-2020)
+    m_mac->GetQosTxop(AC_VO)->Queue(Create<WifiMpdu>(packet, hdr));
+}
+
 std::optional<double>
 EhtFrameExchangeManager::GetMostRecentRssi(const Mac48Address& address) const
 {
@@ -154,4 +294,141 @@
     return std::nullopt;
 }
 
+void
+EhtFrameExchangeManager::SendMuRts(const WifiTxParameters& txParams)
+{
+    NS_LOG_FUNCTION(this << &txParams);
+
+    uint8_t maxPaddingDelay = 0;
+
+    // block transmissions on the other EMLSR links of the EMLSR clients
+    for (const auto& address : m_sentRtsTo)
+    {
+        if (!GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
+        {
+            continue;
+        }
+
+        auto emlCapabilities = GetWifiRemoteStationManager()->GetStationEmlCapabilities(address);
+        NS_ASSERT(emlCapabilities);
+        maxPaddingDelay = std::max(maxPaddingDelay, emlCapabilities->emlsrPaddingDelay);
+
+        auto mldAddress = GetWifiRemoteStationManager()->GetMldAddress(address);
+        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});
+            }
+        }
+    }
+
+    // add padding (if needed)
+    if (maxPaddingDelay > 0)
+    {
+        NS_ASSERT(txParams.m_protection &&
+                  txParams.m_protection->method == WifiProtection::MU_RTS_CTS);
+        WifiMuRtsCtsProtection* protection =
+            static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
+        NS_ASSERT(protection->muRts.IsMuRts());
+
+        // see formula (35-1) in Sec. 35.5.2.2.3 of 802.11be D3.0
+        auto rate = protection->muRtsTxVector.GetMode().GetDataRate(protection->muRtsTxVector);
+        std::size_t nDbps = rate / 1e6 * 4; // see Table 17-4 of 802.11-2020
+        protection->muRts.SetPaddingSize((1 << (maxPaddingDelay + 2)) * nDbps / 8);
+    }
+
+    HeFrameExchangeManager::SendMuRts(txParams);
+}
+
+bool
+EhtFrameExchangeManager::GetEmlsrSwitchToListening(Ptr<const WifiPsdu> psdu,
+                                                   uint16_t aid,
+                                                   const Mac48Address& address) const
+{
+    NS_LOG_FUNCTION(this << psdu << aid << address);
+
+    // Sec. 35.3.17 of 802.11be D3.0:
+    // The non-AP MLD shall be switched back to the listening operation on the EMLSR links after
+    // the EMLSR transition delay time if [...] the non-AP STA affiliated with the non-AP MLD
+    // does not detect [...] any of the following frames:
+    // - an individually addressed frame with the RA equal to the MAC address of the non-AP STA
+    // affiliated with the non-AP MLD
+    if (psdu->GetAddr1() == address)
+    {
+        return false;
+    }
+
+    // - a Trigger frame that has one of the User Info fields addressed to the non-AP STA
+    // affiliated with the non-AP MLD
+    for (const auto& mpdu : *PeekPointer(psdu))
+    {
+        if (mpdu->GetHeader().IsTrigger())
+        {
+            CtrlTriggerHeader trigger;
+            mpdu->GetPacket()->PeekHeader(trigger);
+            if (trigger.FindUserInfoWithAid(aid) != trigger.end())
+            {
+                return false;
+            }
+        }
+    }
+
+    // - a CTS-to-self frame with the RA equal to the MAC address of the AP affiliated with
+    // the AP MLD
+    if (psdu->GetHeader(0).IsCts())
+    {
+        if (m_apMac && psdu->GetAddr1() == m_self)
+        {
+            return false;
+        }
+        if (m_staMac && psdu->GetAddr1() == m_bssid)
+        {
+            return false;
+        }
+    }
+
+    // - a Multi-STA BlockAck frame that has one of the Per AID TID Info fields addressed to
+    // the non-AP STA affiliated with the non-AP MLD
+    if (psdu->GetHeader(0).IsBlockAck())
+    {
+        CtrlBAckResponseHeader blockAck;
+        psdu->GetPayload(0)->PeekHeader(blockAck);
+        if (blockAck.IsMultiSta() && !blockAck.FindPerAidTidInfoWithAid(aid).empty())
+        {
+            return false;
+        }
+    }
+
+    // - a NDP Announcement frame that has one of the STA Info fields addressed to the non-AP
+    // STA affiliated with the non-AP MLD and a sounding NDP
+    // TODO NDP Announcement frame not supported yet
+
+    return true;
+}
+
+void
+EhtFrameExchangeManager::NotifyChannelReleased(Ptr<Txop> txop)
+{
+    NS_LOG_FUNCTION(this << txop);
+
+    // 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(20);
+    for (const auto& address : m_protectedStas)
+    {
+        if (GetWifiRemoteStationManager()->GetEmlsrEnabled(address))
+        {
+            EmlsrSwitchToListening(address, delay);
+        }
+    }
+
+    HeFrameExchangeManager::NotifyChannelReleased(txop);
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.h ns-3.39/src/wifi/model/eht/eht-frame-exchange-manager.h
--- ns-3.38/src/wifi/model/eht/eht-frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-frame-exchange-manager.h	2023-07-05 14:23:48.109428400 -0700
@@ -21,6 +21,7 @@
 #define EHT_FRAME_EXCHANGE_MANAGER_H
 
 #include "ns3/he-frame-exchange-manager.h"
+#include "ns3/mgt-headers.h"
 
 namespace ns3
 {
@@ -46,6 +47,15 @@
     Ptr<WifiMpdu> CreateAliasIfNeeded(Ptr<WifiMpdu> mpdu) const override;
 
     /**
+     * Send an EML Operating Mode Notification frame to the given station.
+     *
+     * \param dest the MAC address of the receiver
+     * \param frame the EML Operating Mode Notification frame to send
+     */
+    void SendEmlOperatingModeNotification(const Mac48Address& dest,
+                                          const MgtEmlOperatingModeNotification& frame);
+
+    /**
      * Get the RSSI (in dBm) of the most recent packet received from the station having
      * the given address. If there is no such information for the given station and the
      * station is affiliated with an MLD, return the RSSI (in dBm) of the most recent
@@ -56,8 +66,34 @@
      */
     std::optional<double> GetMostRecentRssi(const Mac48Address& address) const override;
 
+    /**
+     * \param psdu the given PSDU
+     * \param aid the AID of an EMLSR client
+     * \param address the link MAC address of an EMLSR client
+     * \return whether the EMLSR client having the given AID and MAC address shall switch back to
+     *         the listening operation when receiving the given PSDU
+     */
+    bool GetEmlsrSwitchToListening(Ptr<const WifiPsdu> psdu,
+                                   uint16_t aid,
+                                   const Mac48Address& address) const;
+
   protected:
     void ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector& txVector) override;
+    void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector) override;
+    void SendMuRts(const WifiTxParameters& txParams) override;
+    void NotifyChannelReleased(Ptr<Txop> txop) override;
+
+    /**
+     * This method is intended to be called when an AP MLD detects that an EMLSR client previously
+     * involved in the current TXOP will start waiting for the transition delay interval (to switch
+     * back to listening operation) after the given delay.
+     * This method blocks the transmissions on all the EMLSR links of the given EMLSR client until
+     * the transition delay advertised by the EMLSR client expires.
+     *
+     * \param address the link MAC address of the given EMLSR client
+     * \param delay the given delay
+     */
+    void EmlsrSwitchToListening(const Mac48Address& address, const Time& delay);
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-operation.cc ns-3.39/src/wifi/model/eht/eht-operation.cc
--- ns-3.38/src/wifi/model/eht/eht-operation.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-operation.cc	2023-07-05 14:23:48.109428400 -0700
@@ -1,4 +1,3 @@
-/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
 /*
  * Copyright (c) 2022
  *
@@ -28,6 +27,33 @@
 {
 
 void
+EhtOperation::Print(std::ostream& os) const
+{
+    os << "EHT Operation=" << +m_params.opInfoPresent << "|" << +m_params.disabledSubchBmPresent
+       << "|" << +m_params.defaultPeDur << "|" << +m_params.grpBuIndLimit << "|"
+       << +m_params.grpBuExp << "|[";
+    for (const auto& maxRxNss : m_mcsNssSet.maxRxNss)
+    {
+        os << +maxRxNss << "|";
+    }
+    os << "]|[";
+    for (const auto& maxTxNss : m_mcsNssSet.maxTxNss)
+    {
+        os << +maxTxNss << "|";
+    }
+    os << "]";
+    if (m_opInfo.has_value())
+    {
+        os << "|" << +m_opInfo->control.channelWidth << "|" << +m_opInfo->ccfs0 << "|"
+           << +m_opInfo->ccfs1;
+        if (m_opInfo->disabledSubchBm.has_value())
+        {
+            os << "|" << m_opInfo->disabledSubchBm.value();
+        }
+    }
+}
+
+void
 EhtOperation::EhtOpParams::Serialize(Buffer::Iterator& start) const
 {
     uint8_t val = opInfoPresent | (disabledSubchBmPresent << 1) | (defaultPeDur << 2) |
@@ -260,34 +286,4 @@
                                              << +count << ")");
     return length;
 }
-
-std::ostream&
-operator<<(std::ostream& os, const EhtOperation& ehtOperation)
-{
-    os << +ehtOperation.m_params.opInfoPresent << "|"
-       << +ehtOperation.m_params.disabledSubchBmPresent << "|"
-       << +ehtOperation.m_params.defaultPeDur << "|" << +ehtOperation.m_params.grpBuIndLimit << "|"
-       << +ehtOperation.m_params.grpBuExp << "|[";
-    for (const auto& maxRxNss : ehtOperation.m_mcsNssSet.maxRxNss)
-    {
-        os << +maxRxNss << "|";
-    }
-    os << "]|[";
-    for (const auto& maxTxNss : ehtOperation.m_mcsNssSet.maxTxNss)
-    {
-        os << +maxTxNss << "|";
-    }
-    os << "]";
-    if (ehtOperation.m_opInfo.has_value())
-    {
-        os << "|" << +ehtOperation.m_opInfo->control.channelWidth << "|"
-           << +ehtOperation.m_opInfo->ccfs0 << "|" << +ehtOperation.m_opInfo->ccfs1;
-        if (ehtOperation.m_opInfo->disabledSubchBm.has_value())
-        {
-            os << "|" << ehtOperation.m_opInfo->disabledSubchBm.value();
-        }
-    }
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-operation.h ns-3.39/src/wifi/model/eht/eht-operation.h
--- ns-3.38/src/wifi/model/eht/eht-operation.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-operation.h	2023-07-05 14:23:48.109428400 -0700
@@ -1,4 +1,3 @@
-/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
 /*
  * Copyright (c) 2022
  *
@@ -161,6 +160,7 @@
     EhtOperation();
     WifiInformationElementId ElementId() const override;
     WifiInformationElementId ElementIdExt() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Set the max Rx NSS for input MCS index range
@@ -187,16 +187,6 @@
     uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
 };
 
-/**
- * output stream output operator
- *
- * \param os output stream
- * \param ehtOperation the EHT operation
- *
- * \returns output stream
- */
-std::ostream& operator<<(std::ostream& os, const EhtOperation& ehtOperation);
-
 } // namespace ns3
 
 #endif /* EHT_OPERATION_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-phy.cc ns-3.39/src/wifi/model/eht/eht-phy.cc
--- ns-3.38/src/wifi/model/eht/eht-phy.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-phy.cc	2023-07-05 14:23:48.109428400 -0700
@@ -129,6 +129,20 @@
     }
 }
 
+uint32_t
+EhtPhy::GetSigBSize(const WifiTxVector& txVector) const
+{
+    if (ns3::IsDlMu(txVector.GetPreambleType()) && ns3::IsEht(txVector.GetPreambleType()))
+    {
+        return EhtPpdu::GetEhtSigFieldSize(
+            txVector.GetChannelWidth(),
+            txVector.GetRuAllocation(
+                m_wifiPhy ? m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20) : 0),
+            txVector.GetEhtPpduType());
+    }
+    return HePhy::GetSigBSize(txVector);
+}
+
 Time
 EhtPhy::CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const
 {
@@ -160,10 +174,8 @@
     NS_LOG_FUNCTION(this << psdus << txVector << ppduDuration);
     return Create<EhtPpdu>(psdus,
                            txVector,
-                           m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                               txVector.GetChannelWidth()),
+                           m_wifiPhy->GetOperatingChannel(),
                            ppduDuration,
-                           m_wifiPhy->GetPhyBand(),
                            ObtainNextUid(txVector),
                            HePpdu::PSD_NON_HE_PORTION);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-phy.h ns-3.39/src/wifi/model/eht/eht-phy.h
--- ns-3.38/src/wifi/model/eht/eht-phy.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-phy.h	2023-07-05 14:23:48.109428400 -0700
@@ -262,6 +262,7 @@
     WifiPhyRxfailureReason GetFailureReason(WifiPpduField field) const override;
     Time CalculateNonOfdmaDurationForHeTb(const WifiTxVector& txVector) const override;
     Time CalculateNonOfdmaDurationForHeMu(const WifiTxVector& txVector) const override;
+    uint32_t GetSigBSize(const WifiTxVector& txVector) const override;
 
     /**
      * Create and return the EHT MCS corresponding to
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-ppdu.cc ns-3.39/src/wifi/model/eht/eht-ppdu.cc
--- ns-3.38/src/wifi/model/eht/eht-ppdu.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-ppdu.cc	2023-07-05 14:23:48.109428400 -0700
@@ -22,6 +22,7 @@
 #include "eht-phy.h"
 
 #include "ns3/log.h"
+#include "ns3/wifi-phy-operating-channel.h"
 #include "ns3/wifi-psdu.h"
 
 namespace ns3
@@ -31,34 +32,62 @@
 
 EhtPpdu::EhtPpdu(const WifiConstPsduMap& psdus,
                  const WifiTxVector& txVector,
-                 uint16_t txCenterFreq,
+                 const WifiPhyOperatingChannel& channel,
                  Time ppduDuration,
-                 WifiPhyBand band,
                  uint64_t uid,
                  TxPsdFlag flag)
-    : HePpdu(psdus, txVector, txCenterFreq, ppduDuration, band, uid, flag)
+    : HePpdu(psdus, txVector, channel, ppduDuration, uid, flag)
 {
-    NS_LOG_FUNCTION(this << psdus << txVector << txCenterFreq << ppduDuration << band << uid
-                         << flag);
+    NS_LOG_FUNCTION(this << psdus << txVector << channel << ppduDuration << uid << flag);
+    SetPhyHeaders(txVector, ppduDuration);
+}
+
+void
+EhtPpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
+{
+    NS_LOG_FUNCTION(this << txVector << ppduDuration);
+    SetEhtPhyHeader(txVector);
+}
 
-    // For EHT SU transmissions (carried in EHT MU PPDUs), we have to:
-    // - store the EHT-SIG content channels
-    // - store the MCS and the number of streams for the data field
-    // because this is not done by the parent class.
-    // This is a workaround needed until we properly implement 11be PHY headers.
-    if (ns3::IsDlMu(m_preamble) && !txVector.IsDlMu())
-    {
-        m_contentChannelAlloc = txVector.GetContentChannelAllocation();
-        m_ruAllocation = txVector.GetRuAllocation();
-        m_ehtSuMcs = txVector.GetMode().GetMcsValue();
-        m_ehtSuNStreams = txVector.GetNss();
+void
+EhtPpdu::SetEhtPhyHeader(const WifiTxVector& txVector)
+{
+    const auto bssColor = txVector.GetBssColor();
+    NS_ASSERT(bssColor < 64);
+    if (ns3::IsDlMu(m_preamble))
+    {
+        const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(20);
+        m_ehtPhyHeader.emplace<EhtMuPhyHeader>(EhtMuPhyHeader{
+            .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth()),
+            .m_bssColor = bssColor,
+            .m_ppduType = txVector.GetEhtPpduType(),
+            .m_ehtSigMcs = txVector.GetSigBMode().GetMcsValue(),
+            .m_giLtfSize = GetGuardIntervalAndNltfEncoding(txVector.GetGuardInterval(),
+                                                           2 /*NLTF currently unused*/),
+            /* See section 36.3.12.8.2 of IEEE 802.11be D3.0 (EHT-SIG content channels):
+             * In non-OFDMA transmission, the Common field of the EHT-SIG content channel does not
+             * contain the RU Allocation subfield. For non-OFDMA transmission except for EHT
+             * sounding NDP, the Common field of the EHT-SIG content channel is encoded together
+             * with the first User field and this encoding block contains a CRC and Tail, referred
+             * to as a common encoding block. */
+            .m_ruAllocationA =
+                txVector.IsMu() ? std::optional{txVector.GetRuAllocation(p20Index)} : std::nullopt,
+            // TODO: RU Allocation-B not supported yet
+            .m_contentChannels = GetEhtSigContentChannels(txVector, p20Index)});
+    }
+    else if (ns3::IsUlMu(m_preamble))
+    {
+        m_ehtPhyHeader.emplace<EhtTbPhyHeader>(EhtTbPhyHeader{
+            .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth()),
+            .m_bssColor = bssColor,
+            .m_ppduType = txVector.GetEhtPpduType()});
     }
 }
 
 WifiPpduType
 EhtPpdu::GetType() const
 {
-    if (m_muUserInfos.empty())
+    if (m_psdus.count(SU_STA_ID) > 0)
     {
         return WIFI_PPDU_TYPE_SU;
     }
@@ -77,40 +106,111 @@
 bool
 EhtPpdu::IsDlMu() const
 {
-    return (m_preamble == WIFI_PREAMBLE_EHT_MU) && !m_muUserInfos.empty();
+    return (m_preamble == WIFI_PREAMBLE_EHT_MU) && (m_psdus.count(SU_STA_ID) == 0);
 }
 
 bool
 EhtPpdu::IsUlMu() const
 {
-    return (m_preamble == WIFI_PREAMBLE_EHT_TB) && !m_muUserInfos.empty();
+    return (m_preamble == WIFI_PREAMBLE_EHT_TB) && (m_psdus.count(SU_STA_ID) == 0);
 }
 
 void
-EhtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
-                                   const LSigHeader& lSig,
-                                   const HeSigHeader& heSig) const
-{
-    txVector.SetMode(EhtPhy::GetEhtMcs(m_ehtSuMcs));
-    txVector.SetChannelWidth(heSig.GetChannelWidth());
-    txVector.SetNss(m_ehtSuNStreams);
-    txVector.SetGuardInterval(heSig.GetGuardInterval());
-    txVector.SetBssColor(heSig.GetBssColor());
-    txVector.SetLength(lSig.GetLength());
+EhtPpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector) const
+{
+    txVector.SetLength(m_lSig.GetLength());
     txVector.SetAggregation(m_psdus.size() > 1 || m_psdus.begin()->second->IsAggregate());
-    if (!m_muUserInfos.empty())
+    if (ns3::IsDlMu(m_preamble))
     {
-        txVector.SetEhtPpduType(0); // FIXME set to 2 for DL MU-MIMO (non-OFDMA) transmission
+        auto ehtPhyHeader = std::get_if<EhtMuPhyHeader>(&m_ehtPhyHeader);
+        NS_ASSERT(ehtPhyHeader);
+        txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(ehtPhyHeader->m_bandwidth));
+        txVector.SetBssColor(ehtPhyHeader->m_bssColor);
+        txVector.SetEhtPpduType(ehtPhyHeader->m_ppduType);
+        txVector.SetSigBMode(HePhy::GetVhtMcs(ehtPhyHeader->m_ehtSigMcs));
+        txVector.SetGuardInterval(GetGuardIntervalFromEncoding(ehtPhyHeader->m_giLtfSize));
+        const auto ruAllocation = ehtPhyHeader->m_ruAllocationA; // RU Allocation-B not supported
+                                                                 // yet
+        if (const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(20);
+            ruAllocation.has_value())
+        {
+            txVector.SetRuAllocation(ruAllocation.value(), p20Index);
+            SetHeMuUserInfos(txVector, ruAllocation.value(), ehtPhyHeader->m_contentChannels);
+        }
+        if (ehtPhyHeader->m_ppduType == 1) // EHT SU
+        {
+            NS_ASSERT(ehtPhyHeader->m_contentChannels.size() == 1 &&
+                      ehtPhyHeader->m_contentChannels.front().size() == 1);
+            txVector.SetMode(
+                EhtPhy::GetEhtMcs(ehtPhyHeader->m_contentChannels.front().front().mcs));
+            txVector.SetNss(ehtPhyHeader->m_contentChannels.front().front().nss);
+        }
+    }
+    else if (ns3::IsUlMu(m_preamble))
+    {
+        auto ehtPhyHeader = std::get_if<EhtTbPhyHeader>(&m_ehtPhyHeader);
+        NS_ASSERT(ehtPhyHeader);
+        txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(ehtPhyHeader->m_bandwidth));
+        txVector.SetBssColor(ehtPhyHeader->m_bssColor);
+        txVector.SetEhtPpduType(ehtPhyHeader->m_ppduType);
     }
-    for (const auto& muUserInfo : m_muUserInfos)
+}
+
+std::pair<std::size_t, std::size_t>
+EhtPpdu::GetNumRusPerEhtSigBContentChannel(uint16_t channelWidth,
+                                           uint8_t ehtPpduType,
+                                           const RuAllocation& ruAllocation)
+{
+    if (ehtPpduType == 1)
     {
-        txVector.SetHeMuUserInfo(muUserInfo.first, muUserInfo.second);
+        return {1, 0};
     }
-    if (ns3::IsDlMu(m_preamble))
+    return HePpdu::GetNumRusPerHeSigBContentChannel(channelWidth, ruAllocation);
+}
+
+HePpdu::HeSigBContentChannels
+EhtPpdu::GetEhtSigContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
+{
+    if (txVector.GetEhtPpduType() == 1)
+    {
+        // according to spec the TXVECTOR shall have a correct STA-ID even for SU transmission,
+        // but this is not set by the MAC for simplification, so set to 0 for now.
+        return HeSigBContentChannels{{{0, txVector.GetNss(), txVector.GetMode().GetMcsValue()}}};
+    }
+    return HePpdu::GetHeSigBContentChannels(txVector, p20Index);
+}
+
+uint32_t
+EhtPpdu::GetEhtSigFieldSize(uint16_t channelWidth,
+                            const RuAllocation& ruAllocation,
+                            uint8_t ehtPpduType)
+{
+    // FIXME: EHT-SIG is not implemented yet, hence this is a copy of HE-SIG-B
+    auto commonFieldSize = 4 /* CRC */ + 6 /* tail */;
+    if (channelWidth <= 40)
+    {
+        commonFieldSize += 8; // only one allocation subfield
+    }
+    else
     {
-        txVector.SetSigBMode(HePhy::GetVhtMcs(heSig.GetMcs()));
-        txVector.SetRuAllocation(m_ruAllocation);
+        commonFieldSize +=
+            8 * (channelWidth / 40) /* one allocation field per 40 MHz */ + 1 /* center RU */;
     }
+
+    auto numRusPerContentChannel =
+        GetNumRusPerEhtSigBContentChannel(channelWidth, ehtPpduType, ruAllocation);
+    auto maxNumRusPerContentChannel =
+        std::max(numRusPerContentChannel.first, numRusPerContentChannel.second);
+    auto maxNumUserBlockFields = maxNumRusPerContentChannel /
+                                 2; // handle last user block with single user, if any, further down
+    std::size_t userSpecificFieldSize =
+        maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */);
+    if (maxNumRusPerContentChannel % 2 != 0)
+    {
+        userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */;
+    }
+
+    return commonFieldSize + userSpecificFieldSize;
 }
 
 Ptr<WifiPpdu>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/eht-ppdu.h ns-3.39/src/wifi/model/eht/eht-ppdu.h
--- ns-3.38/src/wifi/model/eht/eht-ppdu.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/eht-ppdu.h	2023-07-05 14:23:48.109428400 -0700
@@ -22,6 +22,8 @@
 
 #include "ns3/he-ppdu.h"
 
+#include <optional>
+
 /**
  * \file
  * \ingroup wifi
@@ -41,39 +43,125 @@
 {
   public:
     /**
+     * PHY header for EHT TB PPDUs
+     */
+    struct EhtTbPhyHeader
+    {
+        // U-SIG fields
+        uint8_t m_phyVersionId{0}; ///< PHY Version Identifier field
+        uint8_t m_bandwidth{0};    ///< Bandwidth field
+        uint8_t m_bssColor{0};     ///< BSS color field
+        uint8_t m_ppduType{0};     ///< PPDU Type And Compressed Mode field
+
+    }; // struct EhtTbPhyHeader
+
+    /**
+     * PHY header for EHT MU PPDUs
+     */
+    struct EhtMuPhyHeader
+    {
+        // U-SIG fields
+        uint8_t m_phyVersionId{0}; ///< PHY Version Identifier field
+        uint8_t m_bandwidth{0};    ///< Bandwidth field
+        uint8_t m_bssColor{0};     ///< BSS color field
+        uint8_t m_ppduType{0};     ///< PPDU Type And Compressed Mode field
+        uint8_t m_ehtSigMcs{0};    ///< EHT-SIG-B MCS
+
+        // EHT-SIG fields
+        uint8_t m_giLtfSize{0}; ///< GI+LTF Size field
+
+        std::optional<RuAllocation> m_ruAllocationA; //!< RU Allocation-A that are going to be
+                                                     //!< carried in EHT-SIG common subfields
+        std::optional<RuAllocation> m_ruAllocationB; //!< RU Allocation-B that are going to be
+                                                     //!< carried in EHT-SIG common subfields
+
+        HeSigBContentChannels m_contentChannels; //!< EHT-SIG Content Channels
+    };                                           // struct EhtMuPhyHeader
+
+    /// type of the EHT PHY header
+    using EhtPhyHeader = std::variant<std::monostate, EhtTbPhyHeader, EhtMuPhyHeader>;
+
+    /**
      * Create an EHT PPDU, storing a map of PSDUs.
      *
      * This PPDU can either be UL or DL.
      *
      * \param psdus the PHY payloads (PSDUs)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param ppduDuration the transmission duration of this PPDU
-     * \param band the WifiPhyBand used for the transmission of this PPDU
      * \param uid the unique ID of this PPDU or of the triggering PPDU if this is an EHT TB PPDU
      * \param flag the flag indicating the type of Tx PSD to build
      */
     EhtPpdu(const WifiConstPsduMap& psdus,
             const WifiTxVector& txVector,
-            uint16_t txCenterFreq,
+            const WifiPhyOperatingChannel& channel,
             Time ppduDuration,
-            WifiPhyBand band,
             uint64_t uid,
             TxPsdFlag flag);
 
     WifiPpduType GetType() const override;
     Ptr<WifiPpdu> Copy() const override;
 
+    /**
+     * Get the number of RUs per EHT-SIG-B content channel.
+     * This function will be used once EHT PHY headers are implemented.
+     *
+     * \param channelWidth the channel width occupied by the PPDU (in MHz)
+     * \param ehtPpduType the EHT_PPDU_TYPE used by the PPDU
+     * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz
+
+     * \return a pair containing the number of RUs in each EHT-SIG-B content channel (resp. 1 and 2)
+     */
+    static std::pair<std::size_t, std::size_t> GetNumRusPerEhtSigBContentChannel(
+        uint16_t channelWidth,
+        uint8_t ehtPpduType,
+        const std::vector<uint8_t>& ruAllocation);
+
+    /**
+     * Get the EHT-SIG content channels for a given PPDU
+     * IEEE 802.11be-D3.1 36.3.12.8.2 EHT-SIG content channels
+     *
+     * \param txVector the TXVECTOR used for the PPDU
+     * \param p20Index the index of the primary20 channel
+     * \return EHT-SIG content channels
+     */
+    static HeSigBContentChannels GetEhtSigContentChannels(const WifiTxVector& txVector,
+                                                          uint8_t p20Index);
+
+    /**
+     * Get variable length EHT-SIG field size
+     * \param channelWidth the channel width occupied by the PPDU (in MHz)
+     * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz
+     * \param ehtPpduType the EHT_PPDU_TYPE used by the PPDU
+     * \return field size in bytes
+     */
+    static uint32_t GetEhtSigFieldSize(uint16_t channelWidth,
+                                       const std::vector<uint8_t>& ruAllocation,
+                                       uint8_t ehtPpduType);
+
   private:
     bool IsDlMu() const override;
     bool IsUlMu() const override;
-    void SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
-                                   const LSigHeader& lSig,
-                                   const HeSigHeader& heSig) const override;
-
-    uint8_t m_ehtSuMcs{0};      //!< EHT-MCS for EHT SU transmissions
-    uint8_t m_ehtSuNStreams{1}; //!< Number of streams for EHT SU transmissions
-};                              // class EhtPpdu
+    void SetTxVectorFromPhyHeaders(WifiTxVector& txVector) const override;
+
+    /**
+     * Fill in the PHY headers.
+     *
+     * \param txVector the TXVECTOR that was used for this PPDU
+     * \param ppduDuration the transmission duration of this PPDU
+     */
+    void SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration);
+
+    /**
+     * Fill in the EHT PHY header.
+     *
+     * \param txVector the TXVECTOR that was used for this PPDU
+     */
+    void SetEhtPhyHeader(const WifiTxVector& txVector);
+
+    EhtPhyHeader m_ehtPhyHeader; //!< the EHT PHY header
+};                               // class EhtPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/emlsr-manager.cc ns-3.39/src/wifi/model/eht/emlsr-manager.cc
--- ns-3.38/src/wifi/model/eht/emlsr-manager.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/eht/emlsr-manager.cc	2023-07-05 14:23:48.109428400 -0700
@@ -0,0 +1,341 @@
+/*
+ * 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 "emlsr-manager.h"
+
+#include "eht-configuration.h"
+#include "eht-frame-exchange-manager.h"
+
+#include "ns3/abort.h"
+#include "ns3/assert.h"
+#include "ns3/attribute-container.h"
+#include "ns3/log.h"
+#include "ns3/wifi-mpdu.h"
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("EmlsrManager");
+
+NS_OBJECT_ENSURE_REGISTERED(EmlsrManager);
+
+TypeId
+EmlsrManager::GetTypeId()
+{
+    static TypeId tid =
+        TypeId("ns3::EmlsrManager")
+            .SetParent<Object>()
+            .SetGroupName("Wifi")
+            .AddAttribute("EmlsrPaddingDelay",
+                          "The EMLSR Paddind Delay (not used by AP MLDs). "
+                          "Possible values are 0 us, 32 us, 64 us, 128 us or 256 us.",
+                          TimeValue(MicroSeconds(0)),
+                          MakeTimeAccessor(&EmlsrManager::m_emlsrPaddingDelay),
+                          MakeTimeChecker(MicroSeconds(0), MicroSeconds(256)))
+            .AddAttribute("EmlsrTransitionDelay",
+                          "The EMLSR Transition Delay (not used by AP MLDs). "
+                          "Possible values are 0 us, 16 us, 32 us, 64 us, 128 us or 256 us.",
+                          TimeValue(MicroSeconds(0)),
+                          MakeTimeAccessor(&EmlsrManager::m_emlsrTransitionDelay),
+                          MakeTimeChecker(MicroSeconds(0), MicroSeconds(256)))
+            .AddAttribute(
+                "EmlsrLinkSet",
+                "IDs of the links on which EMLSR mode will be enabled. An empty set "
+                "indicates to disable EMLSR.",
+                AttributeContainerValue<UintegerValue>(),
+                MakeAttributeContainerAccessor<UintegerValue>(&EmlsrManager::SetEmlsrLinks),
+                MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint8_t>()));
+    return tid;
+}
+
+EmlsrManager::EmlsrManager()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+EmlsrManager::~EmlsrManager()
+{
+    NS_LOG_FUNCTION_NOARGS();
+}
+
+void
+EmlsrManager::DoDispose()
+{
+    NS_LOG_FUNCTION(this);
+    m_staMac->TraceDisconnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
+    m_staMac->TraceDisconnectWithoutContext("DroppedMpdu",
+                                            MakeCallback(&EmlsrManager::TxDropped, this));
+    m_staMac = nullptr;
+    m_transitionTimeoutEvent.Cancel();
+    Object::DoDispose();
+}
+
+void
+EmlsrManager::SetWifiMac(Ptr<StaWifiMac> mac)
+{
+    NS_LOG_FUNCTION(this << mac);
+    NS_ASSERT(mac);
+    m_staMac = mac;
+
+    NS_ABORT_MSG_IF(!m_staMac->GetEhtConfiguration(), "EmlsrManager requires EHT support");
+    NS_ABORT_MSG_IF(m_staMac->GetNLinks() <= 1, "EmlsrManager can only be installed on MLDs");
+    NS_ABORT_MSG_IF(m_staMac->GetTypeOfStation() != STA,
+                    "EmlsrManager can only be installed on non-AP MLDs");
+
+    m_staMac->TraceConnectWithoutContext("AckedMpdu", MakeCallback(&EmlsrManager::TxOk, this));
+    m_staMac->TraceConnectWithoutContext("DroppedMpdu",
+                                         MakeCallback(&EmlsrManager::TxDropped, this));
+}
+
+const std::set<uint8_t>&
+EmlsrManager::GetEmlsrLinks() const
+{
+    return m_emlsrLinks;
+}
+
+Ptr<StaWifiMac>
+EmlsrManager::GetStaMac() const
+{
+    return m_staMac;
+}
+
+Ptr<EhtFrameExchangeManager>
+EmlsrManager::GetEhtFem(uint8_t linkId) const
+{
+    return StaticCast<EhtFrameExchangeManager>(m_staMac->GetFrameExchangeManager(linkId));
+}
+
+void
+EmlsrManager::SetTransitionTimeout(Time timeout)
+{
+    NS_LOG_FUNCTION(this << timeout.As(Time::US));
+    m_emlsrTransitionTimeout = timeout;
+}
+
+std::optional<Time>
+EmlsrManager::GetTransitionTimeout() const
+{
+    return m_emlsrTransitionTimeout;
+}
+
+void
+EmlsrManager::SetEmlsrLinks(const std::set<uint8_t>& linkIds)
+{
+    NS_LOG_FUNCTION(this);
+    NS_ABORT_MSG_IF(linkIds.size() == 1, "Cannot enable EMLSR mode on a single link");
+
+    if (linkIds != m_emlsrLinks)
+    {
+        m_nextEmlsrLinks = linkIds;
+    }
+
+    if (GetStaMac() && GetStaMac()->IsAssociated() && GetTransitionTimeout() && m_nextEmlsrLinks)
+    {
+        // Request to enable EMLSR mode on the given links, provided that they have been setup
+        SendEmlOperatingModeNotification();
+    }
+}
+
+void
+EmlsrManager::NotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << *mpdu << linkId);
+
+    const auto& hdr = mpdu->GetHeader();
+
+    DoNotifyMgtFrameReceived(mpdu, linkId);
+
+    if (hdr.IsAssocResp() && GetStaMac()->IsAssociated() && GetTransitionTimeout() &&
+        m_nextEmlsrLinks && !m_nextEmlsrLinks->empty())
+    {
+        // we just completed ML setup with an AP MLD that supports EMLSR and a non-empty
+        // set of EMLSR links have been configured, hence enable EMLSR mode on those links
+        SendEmlOperatingModeNotification();
+    }
+
+    if (hdr.IsAction() && hdr.GetAddr2() == m_staMac->GetBssid(linkId))
+    {
+        // this is an action frame sent by an AP of the AP MLD we are associated with
+        auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
+        if (category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            if (m_transitionTimeoutEvent.IsRunning())
+            {
+                // no need to wait until the expiration of the transition timeout
+                m_transitionTimeoutEvent.PeekEventImpl()->Invoke();
+                m_transitionTimeoutEvent.Cancel();
+            }
+        }
+    }
+}
+
+void
+EmlsrManager::SendEmlOperatingModeNotification()
+{
+    NS_LOG_FUNCTION(this);
+
+    NS_ABORT_MSG_IF(!m_emlsrTransitionTimeout,
+                    "AP did not advertise a Transition Timeout, cannot send EML notification");
+    NS_ASSERT_MSG(m_nextEmlsrLinks, "Need to set EMLSR links before calling this method");
+
+    MgtEmlOperatingModeNotification frame;
+
+    // Add the EMLSR Parameter Update field if needed
+    if (m_lastAdvPaddingDelay != m_emlsrPaddingDelay ||
+        m_lastAdvTransitionDelay != m_emlsrTransitionDelay)
+    {
+        m_lastAdvPaddingDelay = m_emlsrPaddingDelay;
+        m_lastAdvTransitionDelay = m_emlsrTransitionDelay;
+        frame.m_emlControl.emlsrParamUpdateCtrl = 1;
+        frame.m_emlsrParamUpdate = MgtEmlOperatingModeNotification::EmlsrParamUpdate{};
+        frame.m_emlsrParamUpdate->paddingDelay =
+            CommonInfoBasicMle::EncodeEmlsrPaddingDelay(m_lastAdvPaddingDelay);
+        frame.m_emlsrParamUpdate->transitionDelay =
+            CommonInfoBasicMle::EncodeEmlsrTransitionDelay(m_lastAdvTransitionDelay);
+    }
+
+    // We must verify that the links included in the given EMLSR link set (if any) have been setup.
+    auto setupLinkIds = m_staMac->GetSetupLinkIds();
+
+    for (auto emlsrLinkIt = m_nextEmlsrLinks->begin(); emlsrLinkIt != m_nextEmlsrLinks->end();)
+    {
+        if (auto setupLinkIt = setupLinkIds.find(*emlsrLinkIt); setupLinkIt != setupLinkIds.cend())
+        {
+            setupLinkIds.erase(setupLinkIt);
+            auto apLinkId = m_staMac->GetApLinkId(*emlsrLinkIt);
+            NS_ASSERT(apLinkId);
+            frame.SetLinkIdInBitmap(*apLinkId);
+            emlsrLinkIt++;
+        }
+        else
+        {
+            NS_LOG_DEBUG("Link ID " << +(*emlsrLinkIt) << " has not been setup");
+            emlsrLinkIt = m_nextEmlsrLinks->erase(emlsrLinkIt);
+        }
+    }
+
+    // EMLSR Mode is enabled if and only if the set of EMLSR links is not empty
+    frame.m_emlControl.emlsrMode = m_nextEmlsrLinks->empty() ? 0 : 1;
+
+    // TODO if this is a single radio non-AP MLD and not all setup links are in the EMLSR link
+    // set, we have to put setup links that are not included in the given EMLSR link set (i.e.,
+    // those remaining in setupLinkIds, if m_nextEmlsrLinks is not empty) in the sleep mode:
+    // For the EMLSR mode enabled in a single radio non-AP MLD, the STA(s) affiliated with
+    // the non-AP MLD that operates on the enabled link(s) that corresponds to the bit
+    // position(s) of the EMLSR Link Bitmap subfield set to 0 shall be in doze state if a
+    // non-AP STA affiliated with the non-AP MLD that operates on one of the EMLSR links is
+    // in awake state. (Sec. 35.3.17 of 802.11be D3.0)
+
+    auto linkId = GetLinkToSendEmlNotification();
+    GetEhtFem(linkId)->SendEmlOperatingModeNotification(m_staMac->GetBssid(linkId), frame);
+}
+
+void
+EmlsrManager::TxOk(Ptr<const WifiMpdu> mpdu)
+{
+    NS_LOG_FUNCTION(this << *mpdu);
+
+    const auto& hdr = mpdu->GetHeader();
+
+    if (hdr.IsAssocReq())
+    {
+        // store padding delay and transition delay advertised in AssocReq
+        MgtAssocRequestHeader assocReq;
+        mpdu->GetPacket()->PeekHeader(assocReq);
+        auto& mle = assocReq.Get<MultiLinkElement>();
+        NS_ASSERT_MSG(mle, "AssocReq should contain a Multi-Link Element");
+        m_lastAdvPaddingDelay = mle->GetEmlsrPaddingDelay();
+        m_lastAdvTransitionDelay = mle->GetEmlsrTransitionDelay();
+    }
+
+    if (hdr.IsMgt() && hdr.IsAction())
+    {
+        if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
+            category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            // the EML Operating Mode Notification frame that we sent has been acknowledged.
+            // Start the transition timeout to wait until the request can be made effective
+            NS_ASSERT_MSG(m_emlsrTransitionTimeout, "No transition timeout received from AP");
+            m_transitionTimeoutEvent = Simulator::Schedule(*m_emlsrTransitionTimeout,
+                                                           &EmlsrManager::ChangeEmlsrMode,
+                                                           this);
+        }
+    }
+}
+
+void
+EmlsrManager::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
+{
+    NS_LOG_FUNCTION(this << reason << *mpdu);
+
+    const auto& hdr = mpdu->GetHeader();
+
+    if (hdr.IsMgt() && hdr.IsAction())
+    {
+        auto pkt = mpdu->GetPacket()->Copy();
+        if (auto [category, action] = WifiActionHeader::Remove(pkt);
+            category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            // the EML Operating Mode Notification frame has been dropped. Ask the subclass
+            // whether the frame needs to be resent
+            auto linkId = ResendNotification(mpdu);
+            if (linkId)
+            {
+                MgtEmlOperatingModeNotification frame;
+                pkt->RemoveHeader(frame);
+                GetEhtFem(*linkId)->SendEmlOperatingModeNotification(m_staMac->GetBssid(*linkId),
+                                                                     frame);
+            }
+            else
+            {
+                m_nextEmlsrLinks.reset();
+            }
+        }
+    }
+}
+
+void
+EmlsrManager::ChangeEmlsrMode()
+{
+    NS_LOG_FUNCTION(this);
+
+    // After the successful transmission of the EML Operating Mode Notification frame by the
+    // non-AP STA affiliated with the non-AP MLD, the non-AP MLD shall operate in the EMLSR mode
+    // and the other non-AP STAs operating on the corresponding EMLSR links shall transition to
+    // active mode after the transition delay indicated in the Transition Timeout subfield in the
+    // EML Capabilities subfield of the Basic Multi-Link element or immediately after receiving an
+    // EML Operating Mode Notification frame from one of the APs operating on the EMLSR links and
+    // affiliated with the AP MLD. (Sec. 35.3.17 of 802.11be D3.0)
+    NS_ASSERT_MSG(m_nextEmlsrLinks, "No set of EMLSR links stored");
+    m_emlsrLinks.swap(*m_nextEmlsrLinks);
+    m_nextEmlsrLinks.reset();
+
+    // TODO Make other non-AP STAs operating on the corresponding EMLSR links transition to
+    // active mode or passive mode (depending on whether EMLSR mode has been enabled or disabled)
+
+    NotifyEmlsrModeChanged();
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/emlsr-manager.h ns-3.39/src/wifi/model/eht/emlsr-manager.h
--- ns-3.38/src/wifi/model/eht/emlsr-manager.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/eht/emlsr-manager.h	2023-07-05 14:23:48.109428400 -0700
@@ -0,0 +1,181 @@
+/*
+ * 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 EMLSR_MANAGER_H
+#define EMLSR_MANAGER_H
+
+#include "ns3/mac48-address.h"
+#include "ns3/mgt-headers.h"
+#include "ns3/object.h"
+#include "ns3/sta-wifi-mac.h"
+
+#include <optional>
+#include <set>
+
+namespace ns3
+{
+
+class EhtFrameExchangeManager;
+class WifiMpdu;
+
+/**
+ * \ingroup wifi
+ *
+ * EmlsrManager is an abstract base class defining the API that EHT non-AP MLDs
+ * with EMLSR activated can use to handle the operations on the EMLSR links
+ */
+class EmlsrManager : public Object
+{
+  public:
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+    EmlsrManager();
+    ~EmlsrManager() override;
+
+    /**
+     * Set the wifi MAC. Note that it must be the MAC of an EHT non-AP MLD.
+     *
+     * \param mac the wifi MAC
+     */
+    void SetWifiMac(Ptr<StaWifiMac> mac);
+
+    /**
+     * Set the Transition Timeout advertised by the associated AP with EMLSR activated.
+     *
+     * \param timeout the advertised Transition Timeout
+     */
+    void SetTransitionTimeout(Time timeout);
+
+    /**
+     * \return the Transition Timeout, if advertised by the associated AP
+     */
+    std::optional<Time> GetTransitionTimeout() const;
+
+    /**
+     * Take actions to enable EMLSR mode on the given set of links, if non-empty, or
+     * disable EMLSR mode, otherwise.
+     *
+     * \param linkIds the IDs of the links on which EMLSR mode should be enabled
+     *                (empty to disable EMLSR mode)
+     */
+    void SetEmlsrLinks(const std::set<uint8_t>& linkIds);
+
+    /**
+     * \return the set of links on which EMLSR mode is enabled
+     */
+    const std::set<uint8_t>& GetEmlsrLinks() const;
+
+    /**
+     * Notify the reception of a management frame addressed to us.
+     *
+     * \param mpdu the received MPDU
+     * \param linkId the ID of the link over which the MPDU was received
+     */
+    void NotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId);
+
+  protected:
+    void DoDispose() override;
+
+    /**
+     * \return the MAC of the non-AP MLD managed by this EMLSR Manager.
+     */
+    Ptr<StaWifiMac> GetStaMac() const;
+
+    /**
+     * \param linkId the ID of the given link
+     * \return the EHT FrameExchangeManager attached to the non-AP STA operating on the given link
+     */
+    Ptr<EhtFrameExchangeManager> GetEhtFem(uint8_t linkId) const;
+
+    /**
+     * \return the ID of the link on which the EML Operating Mode Notification frame has to be sent
+     */
+    virtual uint8_t GetLinkToSendEmlNotification() = 0;
+
+    /**
+     * A previous EML Operating Mode Notification frame was dropped. Ask the subclass whether
+     * the frame needs to be re-sent on the given link (if any).
+     *
+     * \param mpdu the dropped MPDU that includes the EML Operating Mode Notification frame
+     * \return the ID of the link over which to re-send the frame, if needed
+     */
+    virtual std::optional<uint8_t> ResendNotification(Ptr<const WifiMpdu> mpdu) = 0;
+
+    Time m_emlsrPaddingDelay;    //!< EMLSR Padding delay
+    Time m_emlsrTransitionDelay; //!< EMLSR Transition delay
+
+  private:
+    /**
+     * Send an EML Operating Mode Notification frame.
+     */
+    void SendEmlOperatingModeNotification();
+
+    /**
+     * Notify the subclass of the reception of a management frame addressed to us.
+     *
+     * \param mpdu the received MPDU
+     * \param linkId the ID of the link over which the MPDU was received
+     */
+    virtual void DoNotifyMgtFrameReceived(Ptr<const WifiMpdu> mpdu, uint8_t linkId) = 0;
+
+    /**
+     * Notify the acknowledgment of the given MPDU.
+     *
+     * \param mpdu the acknowledged MPDU
+     */
+    void TxOk(Ptr<const WifiMpdu> mpdu);
+
+    /**
+     * Notify that the given MPDU has been discarded 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);
+
+    /**
+     * This method is called to make an EMLSR mode change effective after the transition
+     * delay has elapsed or a notification response has been received from the AP.
+     */
+    void ChangeEmlsrMode();
+
+    /**
+     * Notify subclass that EMLSR mode changed.
+     */
+    virtual void NotifyEmlsrModeChanged() = 0;
+
+    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 */
+    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
+                                                            notification frame is acknowledged */
+    Time m_lastAdvPaddingDelay;                        //!< last advertised padding delay
+    Time m_lastAdvTransitionDelay;                     //!< last advertised transition delay
+    EventId m_transitionTimeoutEvent; /**< Timer started after the successful transmission of an
+                                           EML Operating Mode Notification frame */
+};
+
+} // namespace ns3
+
+#endif /* EMLSR_MANAGER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/multi-link-element.cc ns-3.39/src/wifi/model/eht/multi-link-element.cc
--- ns-3.38/src/wifi/model/eht/multi-link-element.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/multi-link-element.cc	2023-07-05 14:23:48.109428400 -0700
@@ -49,8 +49,6 @@
     ret += (m_linkIdInfo.has_value() ? 1 : 0);
     ret += (m_bssParamsChangeCount.has_value() ? 1 : 0);
     ret += (m_mediumSyncDelayInfo.has_value() ? 2 : 0);
-    // NOTE Fig. 9-1002h of 802.11be D1.5 reports that the size of the EML Capabilities
-    // subfield is 3 octets, but this is likely a typo (the correct size is 2 octets)
     ret += (m_emlCapabilities.has_value() ? 2 : 0);
     ret += (m_mldCapabilities.has_value() ? 2 : 0);
     return ret;
@@ -155,17 +153,83 @@
     return count;
 }
 
+uint8_t
+CommonInfoBasicMle::EncodeEmlsrPaddingDelay(Time delay)
+{
+    auto delayUs = delay.GetMicroSeconds();
+
+    if (delayUs == 0)
+    {
+        return 0;
+    }
+
+    for (uint8_t i = 1; i <= 4; i++)
+    {
+        if (1 << (i + 4) == delayUs)
+        {
+            return i;
+        }
+    }
+
+    NS_ABORT_MSG("Value not allowed (" << delay.As(Time::US) << ")");
+    return 0;
+}
+
+Time
+CommonInfoBasicMle::DecodeEmlsrPaddingDelay(uint8_t value)
+{
+    NS_ABORT_MSG_IF(value > 4, "Value not allowed (" << +value << ")");
+    if (value == 0)
+    {
+        return MicroSeconds(0);
+    }
+    return MicroSeconds(1 << (4 + value));
+}
+
+uint8_t
+CommonInfoBasicMle::EncodeEmlsrTransitionDelay(Time delay)
+{
+    auto delayUs = delay.GetMicroSeconds();
+
+    if (delayUs == 0)
+    {
+        return 0;
+    }
+
+    for (uint8_t i = 1; i <= 5; i++)
+    {
+        if (1 << (i + 3) == delayUs)
+        {
+            return i;
+        }
+    }
+
+    NS_ABORT_MSG("Value not allowed (" << delay.As(Time::US) << ")");
+    return 0;
+}
+
+Time
+CommonInfoBasicMle::DecodeEmlsrTransitionDelay(uint8_t value)
+{
+    NS_ABORT_MSG_IF(value > 5, "Value not allowed (" << +value << ")");
+    if (value == 0)
+    {
+        return MicroSeconds(0);
+    }
+    return MicroSeconds(1 << (3 + value));
+}
+
 /**
  * MultiLinkElement
  */
-MultiLinkElement::MultiLinkElement(WifiMacType frameType)
-    : m_frameType(frameType),
+MultiLinkElement::MultiLinkElement(ContainingFrame frame)
+    : m_containingFrame(frame),
       m_commonInfo(std::in_place_type<std::monostate>) // initialize as UNSET
 {
 }
 
-MultiLinkElement::MultiLinkElement(Variant variant, WifiMacType frameType)
-    : MultiLinkElement(frameType)
+MultiLinkElement::MultiLinkElement(Variant variant, ContainingFrame frame)
+    : MultiLinkElement(frame)
 {
     NS_ASSERT(variant != UNSET);
     SetVariant(variant);
@@ -328,10 +392,110 @@
     return std::get<BASIC_VARIANT>(m_commonInfo).m_mediumSyncDelayInfo.has_value();
 }
 
-MultiLinkElement::PerStaProfileSubelement::PerStaProfileSubelement(Variant variant,
-                                                                   WifiMacType frameType)
+void
+MultiLinkElement::SetEmlsrSupported(bool supported)
+{
+    auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
+    if (!emlCapabilities.has_value())
+    {
+        emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
+    }
+    emlCapabilities->emlsrSupport = supported ? 1 : 0;
+}
+
+void
+MultiLinkElement::SetEmlsrPaddingDelay(Time delay)
+{
+    auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
+    if (!emlCapabilities.has_value())
+    {
+        emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
+    }
+    emlCapabilities->emlsrPaddingDelay = CommonInfoBasicMle::EncodeEmlsrPaddingDelay(delay);
+}
+
+void
+MultiLinkElement::SetEmlsrTransitionDelay(Time delay)
+{
+    auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
+    if (!emlCapabilities.has_value())
+    {
+        emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
+    }
+    emlCapabilities->emlsrTransitionDelay = CommonInfoBasicMle::EncodeEmlsrTransitionDelay(delay);
+}
+
+void
+MultiLinkElement::SetTransitionTimeout(Time timeout)
+{
+    auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
+    if (!emlCapabilities.has_value())
+    {
+        emlCapabilities = CommonInfoBasicMle::EmlCapabilities{};
+    }
+    auto timeoutUs = timeout.GetMicroSeconds();
+
+    if (timeoutUs == 0)
+    {
+        emlCapabilities->transitionTimeout = 0;
+    }
+    else
+    {
+        uint8_t i;
+        for (i = 1; i <= 10; i++)
+        {
+            if (1 << (i + 6) == timeoutUs)
+            {
+                emlCapabilities->transitionTimeout = i;
+                break;
+            }
+        }
+        NS_ABORT_MSG_IF(i > 10, "Value not allowed (" << timeout.As(Time::US) << ")");
+    }
+}
+
+bool
+MultiLinkElement::HasEmlCapabilities() const
+{
+    return std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities.has_value();
+}
+
+bool
+MultiLinkElement::IsEmlsrSupported() const
+{
+    return std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities->emlsrSupport;
+}
+
+Time
+MultiLinkElement::GetEmlsrPaddingDelay() const
+{
+    auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
+    NS_ASSERT(emlCapabilities);
+    return CommonInfoBasicMle::DecodeEmlsrPaddingDelay(emlCapabilities->emlsrPaddingDelay);
+}
+
+Time
+MultiLinkElement::GetEmlsrTransitionDelay() const
+{
+    auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
+    NS_ASSERT(emlCapabilities);
+    return CommonInfoBasicMle::DecodeEmlsrTransitionDelay(emlCapabilities->emlsrTransitionDelay);
+}
+
+Time
+MultiLinkElement::GetTransitionTimeout() const
+{
+    auto& emlCapabilities = std::get<BASIC_VARIANT>(m_commonInfo).m_emlCapabilities;
+    NS_ASSERT(emlCapabilities);
+    if (emlCapabilities->transitionTimeout == 0)
+    {
+        return MicroSeconds(0);
+    }
+    return MicroSeconds(1 << (6 + emlCapabilities->transitionTimeout));
+}
+
+MultiLinkElement::PerStaProfileSubelement::PerStaProfileSubelement(Variant variant)
     : m_variant(variant),
-      m_frameType(frameType),
       m_staControl(0)
 {
 }
@@ -339,26 +503,23 @@
 MultiLinkElement::PerStaProfileSubelement::PerStaProfileSubelement(
     const PerStaProfileSubelement& perStaProfile)
     : m_variant(perStaProfile.m_variant),
-      m_frameType(perStaProfile.m_frameType),
       m_staControl(perStaProfile.m_staControl),
       m_staMacAddress(perStaProfile.m_staMacAddress)
 {
     // deep copy of the STA Profile field
-    if (perStaProfile.HasAssocRequest())
-    {
-        m_staProfile = std::make_unique<MgtAssocRequestHeader>(
-            *static_cast<MgtAssocRequestHeader*>(perStaProfile.m_staProfile.get()));
-    }
-    else if (perStaProfile.HasReassocRequest())
-    {
-        m_staProfile = std::make_unique<MgtReassocRequestHeader>(
-            *static_cast<MgtReassocRequestHeader*>(perStaProfile.m_staProfile.get()));
-    }
-    else if (perStaProfile.HasAssocResponse())
-    {
-        m_staProfile = std::make_unique<MgtAssocResponseHeader>(
-            *static_cast<MgtAssocResponseHeader*>(perStaProfile.m_staProfile.get()));
-    }
+    auto staProfileCopy = [&](auto&& frame) {
+        using Ptr = std::decay_t<decltype(frame)>;
+        if constexpr (std::is_same_v<Ptr, std::monostate>)
+        {
+            return;
+        }
+        else
+        {
+            using T = std::decay_t<decltype(*frame.get())>;
+            m_staProfile = std::make_unique<T>(*frame.get());
+        }
+    };
+    std::visit(staProfileCopy, perStaProfile.m_staProfile);
 }
 
 MultiLinkElement::PerStaProfileSubelement&
@@ -371,26 +532,23 @@
     }
 
     m_variant = perStaProfile.m_variant;
-    m_frameType = perStaProfile.m_frameType;
     m_staControl = perStaProfile.m_staControl;
     m_staMacAddress = perStaProfile.m_staMacAddress;
 
     // deep copy of the STA Profile field
-    if (perStaProfile.HasAssocRequest())
-    {
-        m_staProfile = std::make_unique<MgtAssocRequestHeader>(
-            *static_cast<MgtAssocRequestHeader*>(perStaProfile.m_staProfile.get()));
-    }
-    else if (perStaProfile.HasReassocRequest())
-    {
-        m_staProfile = std::make_unique<MgtReassocRequestHeader>(
-            *static_cast<MgtReassocRequestHeader*>(perStaProfile.m_staProfile.get()));
-    }
-    else if (perStaProfile.HasAssocResponse())
-    {
-        m_staProfile = std::make_unique<MgtAssocResponseHeader>(
-            *static_cast<MgtAssocResponseHeader*>(perStaProfile.m_staProfile.get()));
-    }
+    auto staProfileCopy = [&](auto&& frame) {
+        using Ptr = std::decay_t<decltype(frame)>;
+        if constexpr (std::is_same_v<Ptr, std::monostate>)
+        {
+            return;
+        }
+        else
+        {
+            using T = std::decay_t<decltype(*frame.get())>;
+            m_staProfile = std::make_unique<T>(*frame.get());
+        }
+    };
+    std::visit(staProfileCopy, perStaProfile.m_staProfile);
 
     return *this;
 }
@@ -445,50 +603,35 @@
 MultiLinkElement::PerStaProfileSubelement::SetAssocRequest(
     const std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader>& assoc)
 {
-    switch (m_frameType)
-    {
-    case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
-        m_staProfile =
-            std::make_unique<MgtAssocRequestHeader>(std::get<MgtAssocRequestHeader>(assoc));
-        break;
-    case WIFI_MAC_MGT_REASSOCIATION_REQUEST:
-        m_staProfile =
-            std::make_unique<MgtReassocRequestHeader>(std::get<MgtReassocRequestHeader>(assoc));
-        break;
-    default:
-        NS_ABORT_MSG("Invalid frame type: " << m_frameType);
-    }
+    std::visit(
+        [&](auto&& frame) {
+            m_staProfile = std::make_unique<std::decay_t<decltype(frame)>>(frame);
+        },
+        assoc);
 }
 
 void
 MultiLinkElement::PerStaProfileSubelement::SetAssocRequest(
     std::variant<MgtAssocRequestHeader, MgtReassocRequestHeader>&& assoc)
 {
-    switch (m_frameType)
-    {
-    case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
-        m_staProfile = std::make_unique<MgtAssocRequestHeader>(
-            std::move(std::get<MgtAssocRequestHeader>(assoc)));
-        break;
-    case WIFI_MAC_MGT_REASSOCIATION_REQUEST:
-        m_staProfile = std::make_unique<MgtReassocRequestHeader>(
-            std::move(std::get<MgtReassocRequestHeader>(assoc)));
-        break;
-    default:
-        NS_ABORT_MSG("Invalid frame type: " << m_frameType);
-    }
+    std::visit(
+        [&](auto&& frame) {
+            using T = std::decay_t<decltype(frame)>;
+            m_staProfile = std::make_unique<T>(std::forward<T>(frame));
+        },
+        assoc);
 }
 
 bool
 MultiLinkElement::PerStaProfileSubelement::HasAssocRequest() const
 {
-    return m_staProfile && m_frameType == WIFI_MAC_MGT_ASSOCIATION_REQUEST;
+    return std::holds_alternative<std::unique_ptr<MgtAssocRequestHeader>>(m_staProfile);
 }
 
 bool
 MultiLinkElement::PerStaProfileSubelement::HasReassocRequest() const
 {
-    return m_staProfile && m_frameType == WIFI_MAC_MGT_REASSOCIATION_REQUEST;
+    return std::holds_alternative<std::unique_ptr<MgtReassocRequestHeader>>(m_staProfile);
 }
 
 AssocReqRefVariant
@@ -496,40 +639,35 @@
 {
     if (HasAssocRequest())
     {
-        return *static_cast<MgtAssocRequestHeader*>(m_staProfile.get());
+        return *std::get<std::unique_ptr<MgtAssocRequestHeader>>(m_staProfile);
     }
     NS_ABORT_UNLESS(HasReassocRequest());
-    return *static_cast<MgtReassocRequestHeader*>(m_staProfile.get());
+    return *std::get<std::unique_ptr<MgtReassocRequestHeader>>(m_staProfile);
 }
 
 void
 MultiLinkElement::PerStaProfileSubelement::SetAssocResponse(const MgtAssocResponseHeader& assoc)
 {
-    NS_ABORT_IF(m_frameType != WIFI_MAC_MGT_ASSOCIATION_RESPONSE &&
-                m_frameType != WIFI_MAC_MGT_REASSOCIATION_RESPONSE);
     m_staProfile = std::make_unique<MgtAssocResponseHeader>(assoc);
 }
 
 void
 MultiLinkElement::PerStaProfileSubelement::SetAssocResponse(MgtAssocResponseHeader&& assoc)
 {
-    NS_ABORT_IF(m_frameType != WIFI_MAC_MGT_ASSOCIATION_RESPONSE &&
-                m_frameType != WIFI_MAC_MGT_REASSOCIATION_RESPONSE);
     m_staProfile = std::make_unique<MgtAssocResponseHeader>(std::move(assoc));
 }
 
 bool
 MultiLinkElement::PerStaProfileSubelement::HasAssocResponse() const
 {
-    return m_staProfile && (m_frameType == WIFI_MAC_MGT_ASSOCIATION_RESPONSE ||
-                            m_frameType == WIFI_MAC_MGT_REASSOCIATION_RESPONSE);
+    return std::holds_alternative<std::unique_ptr<MgtAssocResponseHeader>>(m_staProfile);
 }
 
 MgtAssocResponseHeader&
 MultiLinkElement::PerStaProfileSubelement::GetAssocResponse() const
 {
     NS_ABORT_IF(!HasAssocResponse());
-    return *static_cast<MgtAssocResponseHeader*>(m_staProfile.get());
+    return *std::get<std::unique_ptr<MgtAssocResponseHeader>>(m_staProfile);
 }
 
 uint8_t
@@ -558,10 +696,25 @@
 
     ret += GetStaInfoLength();
 
-    if (HasAssocRequest() || HasReassocRequest() || HasAssocResponse())
-    {
-        ret += m_staProfile->GetSerializedSize();
-    }
+    auto staProfileSize = [&](auto&& frame) {
+        using T = std::decay_t<decltype(frame)>;
+        if constexpr (std::is_same_v<T, std::monostate>)
+        {
+            NS_ASSERT_MSG(std::holds_alternative<std::monostate>(m_containingFrame),
+                          "Missing management frame for Per-STA Profile subelement");
+            return static_cast<uint32_t>(0);
+        }
+        else
+        {
+            using U = std::decay_t<decltype(*frame)>;
+            NS_ASSERT_MSG(
+                std::holds_alternative<std::reference_wrapper<const U>>(m_containingFrame),
+                "Containing frame type and frame type in Per-STA Profile do not match");
+            const auto& containing = std::get<std::reference_wrapper<const U>>(m_containingFrame);
+            return frame->GetSerializedSizeInPerStaProfile(containing);
+        }
+    };
+    ret += std::visit(staProfileSize, m_staProfile);
 
     return ret;
 }
@@ -577,10 +730,25 @@
         WriteTo(start, m_staMacAddress);
     }
     // TODO add other subfields of the STA Info field
-    if (HasAssocRequest() || HasReassocRequest() || HasAssocResponse())
-    {
-        m_staProfile->Serialize(start);
-    }
+    auto staProfileSerialize = [&](auto&& frame) {
+        using T = std::decay_t<decltype(frame)>;
+        if constexpr (std::is_same_v<T, std::monostate>)
+        {
+            NS_ASSERT_MSG(std::holds_alternative<std::monostate>(m_containingFrame),
+                          "Missing management frame for Per-STA Profile subelement");
+            return;
+        }
+        else
+        {
+            using U = std::decay_t<decltype(*frame)>;
+            NS_ASSERT_MSG(
+                std::holds_alternative<std::reference_wrapper<const U>>(m_containingFrame),
+                "Containing frame type and frame type in Per-STA Profile do not match");
+            const auto& containing = std::get<std::reference_wrapper<const U>>(m_containingFrame);
+            frame->SerializeInPerStaProfile(start, containing);
+        }
+    };
+    std::visit(staProfileSerialize, m_staProfile);
 }
 
 uint16_t
@@ -588,46 +756,37 @@
                                                                        uint16_t length)
 {
     Buffer::Iterator i = start;
-    uint16_t count = 0;
 
     m_staControl = i.ReadLsbtohU16();
-    count += 2;
-
     i.ReadU8(); // STA Info Length
-    count++;
 
     if (HasStaMacAddress())
     {
         ReadFrom(i, m_staMacAddress);
-        count += 6;
     }
 
     // TODO add other subfields of the STA Info field
+    uint16_t count = i.GetDistanceFrom(start);
 
-    if (count >= length)
+    NS_ASSERT_MSG(count <= length,
+                  "Bytes read (" << count << ") exceed expected number (" << length << ")");
+
+    if (count == length)
     {
         return count;
     }
 
-    if (m_frameType == WIFI_MAC_MGT_ASSOCIATION_REQUEST)
-    {
-        MgtAssocRequestHeader assoc;
-        count += assoc.Deserialize(i);
-        SetAssocRequest(std::move(assoc));
-    }
-    else if (m_frameType == WIFI_MAC_MGT_REASSOCIATION_REQUEST)
-    {
-        MgtReassocRequestHeader reassoc;
-        count += reassoc.Deserialize(i);
-        SetAssocRequest(std::move(reassoc));
-    }
-    else if (m_frameType == WIFI_MAC_MGT_ASSOCIATION_RESPONSE ||
-             m_frameType == WIFI_MAC_MGT_REASSOCIATION_RESPONSE)
-    {
-        MgtAssocResponseHeader assoc;
-        count += assoc.Deserialize(i);
-        SetAssocResponse(assoc);
-    }
+    auto staProfileDeserialize = [&](auto&& frame) {
+        using T = std::decay_t<decltype(frame)>;
+        if constexpr (!std::is_same_v<T, std::monostate>)
+        {
+            using U = std::decay_t<decltype(frame.get())>;
+            U assoc;
+            count += assoc.DeserializeFromPerStaProfile(i, length - count, frame.get());
+            m_staProfile = std::make_unique<U>(std::move(assoc));
+        }
+    };
+    std::visit(staProfileDeserialize, m_containingFrame);
 
     return count;
 }
@@ -637,8 +796,7 @@
 {
     auto variant = GetVariant();
     NS_ABORT_IF(variant == UNSET);
-    NS_ABORT_IF(m_frameType == WIFI_MAC_DATA);
-    m_perStaProfileSubelements.emplace_back(variant, m_frameType);
+    m_perStaProfileSubelements.emplace_back(variant);
 }
 
 std::size_t
@@ -682,6 +840,7 @@
 
     for (const auto& subelement : m_perStaProfileSubelements)
     {
+        subelement.m_containingFrame = m_containingFrame;
         ret += subelement.GetSerializedSize();
     }
 
@@ -748,11 +907,14 @@
     {
         switch (static_cast<SubElementId>(i.PeekU8()))
         {
-        case PER_STA_PROFILE_SUBELEMENT_ID:
+        case PER_STA_PROFILE_SUBELEMENT_ID: {
             AddPerStaProfileSubelement();
-            i = GetPerStaProfile(GetNPerStaProfileSubelements() - 1).Deserialize(i);
+            auto& perStaProfile = GetPerStaProfile(GetNPerStaProfileSubelements() - 1);
+            perStaProfile.m_containingFrame = m_containingFrame;
+            i = perStaProfile.Deserialize(i);
             count = i.GetDistanceFrom(start);
-            break;
+        }
+        break;
         default:
             NS_ABORT_MSG("Unsupported Subelement ID: " << +i.PeekU8());
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/eht/multi-link-element.h ns-3.39/src/wifi/model/eht/multi-link-element.h
--- ns-3.38/src/wifi/model/eht/multi-link-element.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/eht/multi-link-element.h	2023-07-05 14:23:48.109428400 -0700
@@ -118,6 +118,28 @@
      * \return the number of bytes read
      */
     uint8_t Deserialize(Buffer::Iterator start, uint16_t presence);
+
+    /**
+     * \param delay the EMLSR Padding delay
+     * \return the encoded value for the EMLSR Padding Delay subfield
+     */
+    static uint8_t EncodeEmlsrPaddingDelay(Time delay);
+    /**
+     * \param value the value for the EMLSR Padding Delay subfield
+     * \return the corresponding EMLSR Padding delay
+     */
+    static Time DecodeEmlsrPaddingDelay(uint8_t value);
+
+    /**
+     * \param delay the EMLSR Transition delay
+     * \return the encoded value for the EMLSR Transition Delay subfield
+     */
+    static uint8_t EncodeEmlsrTransitionDelay(Time delay);
+    /**
+     * \param value the value for the EMLSR Transition Delay subfield
+     * \return the corresponding EMLSR Transition delay
+     */
+    static Time DecodeEmlsrTransitionDelay(uint8_t value);
 };
 
 /**
@@ -162,19 +184,25 @@
         PER_STA_PROFILE_SUBELEMENT_ID = 0
     };
 
+    /// Typedef for structure holding a reference to the containing frame
+    using ContainingFrame = std::variant<std::monostate,
+                                         std::reference_wrapper<const MgtAssocRequestHeader>,
+                                         std::reference_wrapper<const MgtReassocRequestHeader>,
+                                         std::reference_wrapper<const MgtAssocResponseHeader>>;
+
     /**
      * Construct a Multi-Link Element with no variant set.
      *
-     * \param frameType the type of the frame containing the Multi-Link Element
+     * \param frame the management frame containing this Multi-Link Element
      */
-    MultiLinkElement(WifiMacType frameType);
+    MultiLinkElement(ContainingFrame frame = {});
     /**
      * Constructor
      *
      * \param variant the Multi-Link element variant (cannot be UNSET)
-     * \param frameType the type of the frame containing the Multi-Link Element
+     * \param frame the management frame containing this Multi-Link Element
      */
-    MultiLinkElement(Variant variant, WifiMacType frameType);
+    MultiLinkElement(Variant variant, ContainingFrame frame = {});
 
     WifiInformationElementId ElementId() const override;
     WifiInformationElementId ElementIdExt() const override;
@@ -310,6 +338,77 @@
     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.
+     *
+     * \param supported whether EMLSR mode is supported
+     */
+    void SetEmlsrSupported(bool supported);
+    /**
+     * Set the EMLSR Padding Delay subfield of the EML Capabilities subfield in the
+     * Common Info field. Make sure that this is a Basic Multi-Link Element.
+     *
+     * \param delay the EMLSR Padding delay (0us, 32us, 64us, 128us or 256us)
+     */
+    void SetEmlsrPaddingDelay(Time delay);
+    /**
+     * Set the EMLSR Transition Delay subfield of the EML Capabilities subfield in the
+     * Common Info field. Make sure that this is a Basic Multi-Link Element.
+     *
+     * \param delay the EMLSR Transition delay (0us, 16us, 32us, 64us, 128us or 256us)
+     */
+    void SetEmlsrTransitionDelay(Time delay);
+    /**
+     * Set the Transition Timeout subfield of the EML Capabilities subfield in the
+     * Common Info field. Make sure that this is a Basic Multi-Link Element.
+     *
+     * \param timeout the Transition Timeout (0us or 2^n us, with n=7..16)
+     */
+    void SetTransitionTimeout(Time timeout);
+    /**
+     * Return true if the EML Capabilities subfield in the Common Info field is present
+     * and false otherwise. Make sure that this is a Basic Multi-Link Element.
+     *
+     * \return whether the EML Capabilities subfield in the Common Info field is present
+     */
+    bool HasEmlCapabilities() const;
+    /**
+     * Return true if the EMLSR Support subfield of the EML Capabilities subfield in the
+     * Common Info field is set to 1 and false otherwise. Make sure that this is a Basic
+     * Multi-Link Element and the EML Capabilities subfield is present.
+     *
+     * \return whether the EMLSR Support subfield is set to 1
+     */
+    bool IsEmlsrSupported() const;
+    /**
+     * Get the EMLSR Padding Delay subfield of the EML Capabilities subfield in the
+     * Common Info field. Make sure that this is a Basic Multi-Link Element and the
+     * EML Capabilities subfield is present.
+     *
+     * \return the EMLSR Padding Delay
+     */
+    Time GetEmlsrPaddingDelay() const;
+    /**
+     * Get the EMLSR Transition Delay subfield of the EML Capabilities subfield in the
+     * Common Info field. Make sure that this is a Basic Multi-Link Element and the
+     * EML Capabilities subfield is present.
+     *
+     * \return the EMLSR Transition Delay
+     */
+    Time GetEmlsrTransitionDelay() const;
+    /**
+     * Get the Transition Timeout subfield of the EML Capabilities subfield in the
+     * Common Info field. Make sure that this is a Basic Multi-Link Element and the
+     * EML Capabilities subfield is present.
+     *
+     * \return the Transition Timeout
+     */
+    Time GetTransitionTimeout() const;
+
+    mutable ContainingFrame m_containingFrame; //!< reference to the mgt frame containing this MLE
+
+    /**
      * \ingroup wifi
      * Per-STA Profile Subelement of Multi-Link element.
      * See Sec. 9.4.2.312.2.3 of 802.11be D1.5
@@ -328,9 +427,8 @@
          * Constructor
          *
          * \param variant the Multi-Link element variant
-         * \param frameType the type of the frame containing the Multi-Link Element
          */
-        PerStaProfileSubelement(Variant variant, WifiMacType frameType);
+        PerStaProfileSubelement(Variant variant);
 
         /**
          * Copy constructor performing a deep copy of the object
@@ -460,18 +558,23 @@
          */
         uint8_t GetStaInfoLength() const;
 
+        mutable ContainingFrame
+            m_containingFrame; //!< the mgt frame containing this Per-STA Profile
+
       private:
         uint16_t GetInformationFieldSize() const override;
         void SerializeInformationField(Buffer::Iterator start) const override;
         uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
 
         Variant m_variant;            //!< Multi-Link element variant
-        WifiMacType m_frameType;      //!< type of the frame containing the Multi-Link Element
         uint16_t m_staControl;        //!< STA Control field
         Mac48Address m_staMacAddress; //!< STA MAC address
-        std::unique_ptr<Header> m_staProfile; /**< STA Profile field, containing the frame body of a
-                                                   frame of the same type as the frame containing
-                                                 the Multi-Link Element */
+        std::variant<std::monostate,
+                     std::unique_ptr<MgtAssocRequestHeader>,
+                     std::unique_ptr<MgtReassocRequestHeader>,
+                     std::unique_ptr<MgtAssocResponseHeader>>
+            m_staProfile; /**< STA Profile field, containing the frame body of a frame of the
+                               same type as the frame containing the Multi-Link Element */
     };
 
     /**
@@ -507,8 +610,6 @@
      */
     void SetVariant(Variant variant);
 
-    WifiMacType m_frameType; //!< type of the frame containing the Multi-Link Element
-
     /// Typedef for structure holding a Common Info field
     using CommonInfo = std::variant<CommonInfoBasicMle,
                                     // TODO Add other variants
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/extended-capabilities.cc ns-3.39/src/wifi/model/extended-capabilities.cc
--- ns-3.38/src/wifi/model/extended-capabilities.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/extended-capabilities.cc	2023-07-05 14:23:48.109428400 -0700
@@ -92,6 +92,12 @@
 }
 
 void
+ExtendedCapabilities::Print(std::ostream& os) const
+{
+    os << "Extended Capabilities=" << +GetExtendedCapabilitiesByte1();
+}
+
+void
 ExtendedCapabilities::SetHtSupported(uint8_t htSupported)
 {
     m_htSupported = htSupported;
@@ -373,11 +379,4 @@
     return length;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const ExtendedCapabilities& extendedCapabilities)
-{
-    os << +extendedCapabilities.GetExtendedCapabilitiesByte1();
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/extended-capabilities.h ns-3.39/src/wifi/model/extended-capabilities.h
--- ns-3.38/src/wifi/model/extended-capabilities.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/extended-capabilities.h	2023-07-05 14:23:48.109428400 -0700
@@ -41,6 +41,7 @@
     uint16_t GetInformationFieldSize() const override;
     void SerializeInformationField(Buffer::Iterator start) const override;
     uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
+    void Print(std::ostream& os) const override;
     /**
      * Set the HT Supported flag.
      *
@@ -226,16 +227,6 @@
                             ///< element should be added to the frame or not
 };
 
-/**
- * output stream output operator
- *
- * \param os output stream
- * \param extendedCapabilities the extended capabilities
- *
- * \returns output stream
- */
-std::ostream& operator<<(std::ostream& os, const ExtendedCapabilities& extendedCapabilities);
-
 } // namespace ns3
 
 #endif /* EXTENDED_CAPABILITIES_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/fcfs-wifi-queue-scheduler.cc ns-3.39/src/wifi/model/fcfs-wifi-queue-scheduler.cc
--- ns-3.38/src/wifi/model/fcfs-wifi-queue-scheduler.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/fcfs-wifi-queue-scheduler.cc	2023-07-05 14:23:48.109428400 -0700
@@ -132,15 +132,11 @@
 
     const auto queueId = WifiMacQueueContainer::GetQueueId(mpdu);
 
-    if (GetWifiMacQueue(ac)->GetNPackets(queueId) > 1)
-    {
-        // Enqueue takes place at the tail, while the priority is determined by the
-        // head of the queue. Therefore, if the queue was not empty before inserting
-        // this MPDU, priority does not change
-        return;
-    }
+    // priority is determined by the head of the queue
+    auto item = GetWifiMacQueue(ac)->PeekByQueueId(queueId);
+    NS_ASSERT(item);
 
-    SetPriority(ac, queueId, {mpdu->GetExpiryTime(), std::get<WifiContainerQueueType>(queueId)});
+    SetPriority(ac, queueId, {item->GetTimestamp(), std::get<WifiContainerQueueType>(queueId)});
 }
 
 void
@@ -161,7 +157,7 @@
         {
             SetPriority(ac,
                         queueId,
-                        {item->GetExpiryTime(), std::get<WifiContainerQueueType>(queueId)});
+                        {item->GetTimestamp(), std::get<WifiContainerQueueType>(queueId)});
         }
     }
 }
@@ -184,7 +180,7 @@
         {
             SetPriority(ac,
                         queueId,
-                        {item->GetExpiryTime(), std::get<WifiContainerQueueType>(queueId)});
+                        {item->GetTimestamp(), std::get<WifiContainerQueueType>(queueId)});
         }
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/frame-exchange-manager.cc ns-3.39/src/wifi/model/frame-exchange-manager.cc
--- ns-3.38/src/wifi/model/frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/frame-exchange-manager.cc	2023-07-05 14:23:48.109428400 -0700
@@ -19,8 +19,8 @@
 
 #include "frame-exchange-manager.h"
 
-#include "qos-blocked-destinations.h"
 #include "snr-tag.h"
+#include "sta-wifi-mac.h"
 #include "wifi-mac-queue.h"
 #include "wifi-mac-trailer.h"
 #include "wifi-utils.h"
@@ -319,7 +319,7 @@
     if (!mpdu)
     {
         NS_LOG_DEBUG("Queue empty");
-        m_dcf->NotifyChannelReleased(m_linkId);
+        NotifyChannelReleased(m_dcf);
         m_dcf = nullptr;
         return false;
     }
@@ -414,26 +414,50 @@
         m_mpdu->SetInFlight(m_linkId);
     }
 
-    switch (m_txParams.m_protection->method)
+    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
+FrameExchangeManager::StartProtection(const WifiTxParameters& txParams)
+{
+    NS_LOG_FUNCTION(this << &txParams);
+
+    switch (txParams.m_protection->method)
     {
     case WifiProtection::RTS_CTS:
-        SendRts(m_txParams);
+        SendRts(txParams);
         break;
     case WifiProtection::CTS_TO_SELF:
-        SendCtsToSelf(m_txParams);
+        SendCtsToSelf(txParams);
         break;
     case WifiProtection::NONE:
-        SendMpdu();
+        ProtectionCompleted();
         break;
     default:
-        NS_ABORT_MSG("Unknown protection type");
+        NS_ABORT_MSG("Unknown protection type: " << txParams.m_protection.get());
     }
+}
 
-    if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
-    {
-        // we are done with frames that do not require acknowledgment
-        m_mpdu = nullptr;
-    }
+void
+FrameExchangeManager::ProtectionCompleted()
+{
+    NS_LOG_FUNCTION(this);
+    m_protectedStas.merge(m_sentRtsTo);
+    m_sentRtsTo.clear();
+    NS_ASSERT(m_mpdu);
+    SendMpdu();
+}
+
+const std::set<Mac48Address>&
+FrameExchangeManager::GetProtectedStas() const
+{
+    return m_protectedStas;
 }
 
 void
@@ -499,7 +523,39 @@
 {
     NS_LOG_FUNCTION(this << *mpdu << txVector);
 
-    m_phy->Send(Create<WifiPsdu>(mpdu, false), txVector);
+    auto psdu = Create<WifiPsdu>(mpdu, false);
+    FinalizeMacHeader(psdu);
+    m_phy->Send(psdu, txVector);
+}
+
+void
+FrameExchangeManager::FinalizeMacHeader(Ptr<const WifiPsdu> psdu)
+{
+    NS_LOG_FUNCTION(this << psdu);
+
+    if (m_mac->GetTypeOfStation() != STA)
+    {
+        return;
+    }
+
+    auto pmMode = StaticCast<StaWifiMac>(m_mac)->GetPmMode(m_linkId);
+
+    for (const auto& mpdu : *PeekPointer(psdu))
+    {
+        switch (pmMode)
+        {
+        case WIFI_PM_ACTIVE:
+        case WIFI_PM_SWITCHING_TO_ACTIVE:
+            mpdu->GetHeader().SetNoPowerManagement();
+            break;
+        case WIFI_PM_POWERSAVE:
+        case WIFI_PM_SWITCHING_TO_PS:
+            mpdu->GetHeader().SetPowerManagement();
+            break;
+        default:
+            NS_ABORT_MSG("Unknown PM mode: " << +pmMode);
+        }
+    }
 }
 
 void
@@ -675,6 +731,8 @@
                   mpdu,
                   rtsCtsProtection->rtsTxVector);
     m_channelAccessManager->NotifyCtsTimeoutStartNow(timeout);
+    NS_ASSERT(m_sentRtsTo.empty());
+    m_sentRtsTo = {receiver};
 
     ForwardMpduDown(mpdu, rtsCtsProtection->rtsTxVector);
 }
@@ -762,7 +820,9 @@
     Time ctsDuration = m_phy->CalculateTxDuration(GetCtsSize(),
                                                   ctsToSelfProtection->ctsTxVector,
                                                   m_phy->GetPhyBand());
-    Simulator::Schedule(ctsDuration + m_phy->GetSifs(), &FrameExchangeManager::SendMpdu, this);
+    Simulator::Schedule(ctsDuration + m_phy->GetSifs(),
+                        &FrameExchangeManager::ProtectionCompleted,
+                        this);
 }
 
 void
@@ -846,7 +906,7 @@
     }
     else
     {
-        m_dcf->NotifyChannelReleased(m_linkId);
+        NotifyChannelReleased(m_dcf);
         m_dcf = nullptr;
     }
 }
@@ -856,11 +916,19 @@
 {
     NS_LOG_FUNCTION(this);
     // A non-QoS station always releases the channel upon a transmission failure
-    m_dcf->NotifyChannelReleased(m_linkId);
+    NotifyChannelReleased(m_dcf);
     m_dcf = nullptr;
 }
 
 void
+FrameExchangeManager::NotifyChannelReleased(Ptr<Txop> txop)
+{
+    NS_LOG_FUNCTION(this << txop);
+    txop->NotifyChannelReleased(m_linkId);
+    m_protectedStas.clear();
+}
+
+void
 FrameExchangeManager::NormalAckTimeout(Ptr<WifiMpdu> mpdu, const WifiTxVector& txVector)
 {
     NS_LOG_FUNCTION(this << *mpdu << txVector);
@@ -913,6 +981,7 @@
 {
     NS_LOG_FUNCTION(this << *psdu);
 
+    m_sentRtsTo.clear();
     for (const auto& mpdu : *PeekPointer(psdu))
     {
         if (mpdu->IsQueued())
@@ -1237,7 +1306,7 @@
 
             m_txTimer.Cancel();
             m_channelAccessManager->NotifyCtsTimeoutResetNow();
-            Simulator::Schedule(m_phy->GetSifs(), &FrameExchangeManager::SendMpdu, this);
+            Simulator::Schedule(m_phy->GetSifs(), &FrameExchangeManager::ProtectionCompleted, this);
         }
         else if (hdr.IsAck() && m_mpdu && m_txTimer.IsRunning() &&
                  m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/frame-exchange-manager.h ns-3.39/src/wifi/model/frame-exchange-manager.h
--- ns-3.38/src/wifi/model/frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/frame-exchange-manager.h	2023-07-05 14:23:48.109428400 -0700
@@ -241,6 +241,11 @@
     virtual bool VirtualCsMediumIdle() const;
 
     /**
+     * \return the set of stations that have successfully received an RTS in this TXOP.
+     */
+    const std::set<Mac48Address>& GetProtectedStas() const;
+
+    /**
      * Notify that an internal collision has occurred for the given Txop
      *
      * \param txop the Txop for which an internal collision has occurred
@@ -300,6 +305,18 @@
     void SendMpduWithProtection(Ptr<WifiMpdu> mpdu, WifiTxParameters& txParams);
 
     /**
+     * Start the protection mechanism indicated by the given TX parameters
+     *
+     * \param txParams the TX parameters
+     */
+    virtual void StartProtection(const WifiTxParameters& txParams);
+
+    /**
+     * Transmit prepared frame upon successful protection mechanism.
+     */
+    virtual void ProtectionCompleted();
+
+    /**
      * Update the NAV, if needed, based on the Duration/ID of the given <i>psdu</i>.
      *
      * \param psdu the received PSDU
@@ -438,6 +455,13 @@
      */
     virtual uint32_t GetPsduSize(Ptr<const WifiMpdu> mpdu, const WifiTxVector& txVector) const;
 
+    /**
+     * Notify the given Txop that channel has been released.
+     *
+     * \param txop the given Txop
+     */
+    virtual void NotifyChannelReleased(Ptr<Txop> txop);
+
     Ptr<Txop> m_dcf;                                  //!< the DCF/EDCAF that gained channel access
     WifiTxTimer m_txTimer;                            //!< the timer set upon frame transmission
     EventId m_navResetEvent;                          //!< the event to reset the NAV after an RTS
@@ -449,13 +473,23 @@
     Mac48Address m_self;                              //!< the MAC address of this device
     Mac48Address m_bssid;                             //!< BSSID address (Mac48Address)
     Time m_navEnd;                                    //!< NAV expiration time
-    uint8_t m_linkId;                  //!< the ID of the link this object is associated with
+    std::set<Mac48Address> m_sentRtsTo; //!< the STA(s) which we sent an RTS to (waiting for CTS)
+    std::set<Mac48Address> m_protectedStas; //!< STAs that have replied to an RTS in this TXOP
+    uint8_t m_linkId;                       //!< the ID of the link this object is associated with
     uint16_t m_allowedWidth;           //!< the allowed width in MHz for the current transmission
     bool m_promisc;                    //!< Flag if the device is operating in promiscuous mode
     DroppedMpdu m_droppedMpduCallback; //!< the dropped MPDU callback
     AckedMpdu m_ackedMpduCallback;     //!< the acknowledged MPDU callback
 
     /**
+     * Finalize the MAC header of the MPDUs in the given PSDU before transmission. Tasks
+     * performed by this method include setting the Power Management flag in the MAC header.
+     *
+     * \param psdu the given PSDU
+     */
+    virtual void FinalizeMacHeader(Ptr<const WifiPsdu> psdu);
+
+    /**
      * Forward an MPDU down to the PHY layer.
      *
      * \param mpdu the MPDU to forward down
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/constant-obss-pd-algorithm.cc ns-3.39/src/wifi/model/he/constant-obss-pd-algorithm.cc
--- ns-3.38/src/wifi/model/he/constant-obss-pd-algorithm.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/constant-obss-pd-algorithm.cc	2023-07-05 14:23:48.109428400 -0700
@@ -20,10 +20,10 @@
 #include "constant-obss-pd-algorithm.h"
 
 #include "he-configuration.h"
-#include "he-phy.h"
 
 #include "ns3/config.h"
 #include "ns3/double.h"
+#include "ns3/eht-phy.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/sta-wifi-mac.h"
@@ -56,7 +56,13 @@
 void
 ConstantObssPdAlgorithm::ConnectWifiNetDevice(const Ptr<WifiNetDevice> device)
 {
-    Ptr<WifiPhy> phy = device->GetPhy();
+    auto phy = device->GetPhy();
+    if (phy->GetStandard() >= WIFI_STANDARD_80211be)
+    {
+        auto ehtPhy = DynamicCast<EhtPhy>(device->GetPhy()->GetPhyEntity(WIFI_MOD_CLASS_EHT));
+        NS_ASSERT(ehtPhy);
+        ehtPhy->SetEndOfHeSigACallback(MakeCallback(&ConstantObssPdAlgorithm::ReceiveHeSigA, this));
+    }
     auto hePhy = DynamicCast<HePhy>(phy->GetPhyEntity(WIFI_MOD_CLASS_HE));
     NS_ASSERT(hePhy);
     hePhy->SetEndOfHeSigACallback(MakeCallback(&ConstantObssPdAlgorithm::ReceiveHeSigA, this));
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-capabilities.cc ns-3.39/src/wifi/model/he/he-capabilities.cc
--- ns-3.38/src/wifi/model/he/he-capabilities.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-capabilities.cc	2023-07-05 14:23:48.109428400 -0700
@@ -136,6 +136,14 @@
     return IE_EXT_HE_CAPABILITIES;
 }
 
+void
+HeCapabilities::Print(std::ostream& os) const
+{
+    os << "HE Capabilities=" << GetHeMacCapabilitiesInfo1() << "|" << +GetHeMacCapabilitiesInfo2()
+       << "|" << GetHePhyCapabilitiesInfo1() << "|" << GetHePhyCapabilitiesInfo2() << "|"
+       << +GetHePhyCapabilitiesInfo3() << "|" << GetSupportedMcsAndNss();
+}
+
 uint16_t
 HeCapabilities::GetInformationFieldSize() const
 {
@@ -609,16 +617,4 @@
     return std::min<uint32_t>((1UL << (20 + m_maxAmpduLengthExponent)) - 1, 6500631);
 }
 
-std::ostream&
-operator<<(std::ostream& os, const HeCapabilities& heCapabilities)
-{
-    os << heCapabilities.GetHeMacCapabilitiesInfo1() << "|"
-       << +heCapabilities.GetHeMacCapabilitiesInfo2() << "|"
-       << heCapabilities.GetHePhyCapabilitiesInfo1() << "|"
-       << heCapabilities.GetHePhyCapabilitiesInfo2() << "|"
-       << +heCapabilities.GetHePhyCapabilitiesInfo3() << "|"
-       << heCapabilities.GetSupportedMcsAndNss();
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-capabilities.h ns-3.39/src/wifi/model/he/he-capabilities.h
--- ns-3.38/src/wifi/model/he/he-capabilities.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-capabilities.h	2023-07-05 14:23:48.109428400 -0700
@@ -38,6 +38,7 @@
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
     WifiInformationElementId ElementIdExt() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Set the HE MAC Capabilities Info field in the HE Capabilities information element.
@@ -333,14 +334,6 @@
     std::vector<uint8_t> m_rxBwMap;  //!< receive BW map
 };
 
-/**
- * output stream output operator
- * \param os the output stream
- * \param heCapabilities the HE capabilities
- * \returns the output stream
- */
-std::ostream& operator<<(std::ostream& os, const HeCapabilities& heCapabilities);
-
 } // namespace ns3
 
 #endif /* HE_CAPABILITY_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-frame-exchange-manager.cc ns-3.39/src/wifi/model/he/he-frame-exchange-manager.cc
--- ns-3.38/src/wifi/model/he/he-frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-frame-exchange-manager.cc	2023-07-05 14:23:48.113428400 -0700
@@ -54,6 +54,14 @@
            psduMap.cbegin()->second->GetHeader(0).IsTrigger();
 }
 
+bool
+IsTrigger(const WifiConstPsduMap& psduMap)
+{
+    return psduMap.size() == 1 && psduMap.cbegin()->first == SU_STA_ID &&
+           psduMap.cbegin()->second->GetNMpdus() == 1 &&
+           psduMap.cbegin()->second->GetHeader(0).IsTrigger();
+}
+
 TypeId
 HeFrameExchangeManager::GetTypeId()
 {
@@ -282,23 +290,62 @@
         }
     }
 
-    if (m_txParams.m_protection->method == WifiProtection::RTS_CTS)
+    StartProtection(m_txParams);
+}
+
+void
+HeFrameExchangeManager::StartProtection(const WifiTxParameters& txParams)
+{
+    NS_LOG_FUNCTION(this << &txParams);
+
+    NS_ABORT_MSG_IF(m_psduMap.size() > 1 &&
+                        txParams.m_protection->method == WifiProtection::RTS_CTS,
+                    "Cannot use RTS/CTS with MU PPDUs");
+    if (txParams.m_protection->method == WifiProtection::MU_RTS_CTS)
     {
-        NS_ABORT_MSG_IF(m_psduMap.size() > 1, "Cannot use RTS/CTS with MU PPDUs");
-        SendRts(m_txParams);
+        RecordSentMuRtsTo(txParams);
+        SendMuRts(txParams);
     }
-    else if (m_txParams.m_protection->method == WifiProtection::MU_RTS_CTS)
+    else
     {
-        SendMuRts(m_txParams);
+        VhtFrameExchangeManager::StartProtection(txParams);
     }
-    else if (m_txParams.m_protection->method == WifiProtection::NONE)
+}
+
+void
+HeFrameExchangeManager::RecordSentMuRtsTo(const WifiTxParameters& txParams)
+{
+    NS_LOG_FUNCTION(this << &txParams);
+
+    NS_ASSERT(txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS);
+    WifiMuRtsCtsProtection* protection =
+        static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
+
+    NS_ASSERT(protection->muRts.IsMuRts());
+    NS_ASSERT_MSG(m_apMac, "APs only can send MU-RTS TF");
+    const auto& aidAddrMap = m_apMac->GetStaList(m_linkId);
+    NS_ASSERT(m_sentRtsTo.empty());
+
+    for (const auto& userInfo : protection->muRts)
     {
-        SendPsduMap();
+        const auto addressIt = aidAddrMap.find(userInfo.GetAid12());
+        NS_ASSERT_MSG(addressIt != aidAddrMap.end(), "AID not found");
+        m_sentRtsTo.insert(addressIt->second);
     }
-    else
+}
+
+void
+HeFrameExchangeManager::ProtectionCompleted()
+{
+    NS_LOG_FUNCTION(this);
+    if (!m_psduMap.empty())
     {
-        NS_ABORT_MSG("Unknown or prohibited protection type: " << m_txParams.m_protection.get());
+        m_protectedStas.merge(m_sentRtsTo);
+        m_sentRtsTo.clear();
+        SendPsduMap();
+        return;
     }
+    VhtFrameExchangeManager::ProtectionCompleted();
 }
 
 Time
@@ -387,8 +434,15 @@
 {
     NS_LOG_FUNCTION(this << *muRts << txVector);
 
-    NS_ASSERT(!m_psduMap.empty());
+    if (m_psduMap.empty())
+    {
+        // A CTS Timeout occurred when protecting a single PSDU that is not included
+        // in a DL MU PPDU is handled by the parent classes
+        VhtFrameExchangeManager::CtsTimeout(muRts, txVector);
+        return;
+    }
 
+    m_sentRtsTo.clear();
     for (const auto& psdu : m_psduMap)
     {
         for (const auto& mpdu : *PeekPointer(psdu.second))
@@ -509,8 +563,9 @@
                 uint8_t tid = *tids.begin();
 
                 NS_ASSERT(m_edca);
-                m_edca->GetBaManager()->ScheduleBar(
-                    m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid));
+                auto [reqHdr, hdr] =
+                    m_mac->GetQosTxop(tid)->PrepareBlockAckRequest(psdu.second->GetAddr1(), tid);
+                m_edca->GetBaManager()->ScheduleBar(reqHdr, hdr);
             }
         }
 
@@ -872,9 +927,10 @@
         NS_LOG_DEBUG("Transmitting: [STAID=" << psdu.first << ", " << *psdu.second << "]");
     }
     NS_LOG_DEBUG("TXVECTOR: " << txVector);
-    for (const auto& psdu : psduMap)
+    for (const auto& [staId, psdu] : psduMap)
     {
-        NotifyTxToEdca(psdu.second);
+        FinalizeMacHeader(psdu);
+        NotifyTxToEdca(psdu);
     }
     if (psduMap.size() > 1 || psduMap.begin()->second->IsAggregate() ||
         psduMap.begin()->second->IsSingle())
@@ -1565,6 +1621,12 @@
 {
     NS_LOG_FUNCTION(this << muRtsHdr << trigger << muRtsSnr);
 
+    if (!UlMuCsMediumIdle(trigger))
+    {
+        NS_LOG_DEBUG("UL MU CS indicated medium busy, cannot send CTS");
+        return;
+    }
+
     NS_ASSERT(m_staMac != nullptr && m_staMac->IsAssociated());
     WifiTxVector ctsTxVector = GetCtsTxVectorAfterMuRts(trigger, m_staMac->GetAssociationId());
     ctsTxVector.SetTriggerResponding(true);
@@ -1942,13 +2004,9 @@
     // is disabled (see 26.17.3.3), then the RXVECTOR parameter BSS_COLOR of a PPDU shall not be
     // used to classify the PPDU")
     const auto bssColor = m_mac->GetHeConfiguration()->GetBssColor();
-    if (bssColor != 0 && bssColor == txVector.GetBssColor())
-    {
-        return true;
-    }
 
     // the other two conditions using the RXVECTOR parameter PARTIAL_AID are not implemented
-    return false;
+    return bssColor != 0 && bssColor == txVector.GetBssColor();
 }
 
 void
@@ -1982,6 +2040,17 @@
     Time duration = psdu->GetDuration();
     NS_LOG_DEBUG("Duration/ID=" << duration);
 
+    if (psdu->GetHeader(0).IsCfEnd())
+    {
+        // An HE STA that maintains two NAVs (see 26.2.4) and receives a CF-End frame should reset
+        // the basic NAV if the received CF-End frame is carried in an inter-BSS PPDU and reset the
+        // intra-BSS NAV if the received CF-End frame is carried in an intra-BSS PPDU. (Sec. 26.2.5
+        // of 802.11ax-2021)
+        NS_LOG_DEBUG("Received CF-End, resetting the intra-BSS NAV");
+        IntraBssNavResetTimeout();
+        return;
+    }
+
     // For all other received frames the STA shall update its NAV when the received
     // Duration is greater than the STAâ€™s current NAV value (IEEE 802.11-2020 sec. 10.3.2.4)
     auto intraBssNavEnd = Simulator::Now() + duration;
@@ -2280,9 +2349,11 @@
 
             m_txTimer.Cancel();
             m_channelAccessManager->NotifyCtsTimeoutResetNow();
-            Simulator::Schedule(m_phy->GetSifs(), &HeFrameExchangeManager::SendPsduMap, this);
+            Simulator::Schedule(m_phy->GetSifs(),
+                                &HeFrameExchangeManager::ProtectionCompleted,
+                                this);
         }
-        else if (hdr.IsCts() && !m_psduMap.empty() && m_txTimer.IsRunning() &&
+        else if (hdr.IsCts() && m_txTimer.IsRunning() &&
                  m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS_AFTER_MU_RTS)
         {
             NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
@@ -2292,7 +2363,9 @@
 
             m_txTimer.Cancel();
             m_channelAccessManager->NotifyCtsTimeoutResetNow();
-            Simulator::Schedule(m_phy->GetSifs(), &HeFrameExchangeManager::SendPsduMap, this);
+            Simulator::Schedule(m_phy->GetSifs(),
+                                &HeFrameExchangeManager::ProtectionCompleted,
+                                this);
         }
         else if (hdr.IsAck() && m_txTimer.IsRunning() &&
                  m_txTimer.GetReason() == WifiTxTimer::WAIT_NORMAL_ACK_AFTER_DL_MU_PPDU)
@@ -2524,20 +2597,13 @@
                 //   the non-AP STA (this is guaranteed if we get here)
                 // - The UL MU CS condition indicates that the medium is idle
                 // (Sec. 26.2.6.3 of 802.11ax-2021)
-                if (UlMuCsMediumIdle(trigger))
-                {
-                    NS_LOG_DEBUG("Schedule CTS");
-                    Simulator::Schedule(m_phy->GetSifs(),
-                                        &HeFrameExchangeManager::SendCtsAfterMuRts,
-                                        this,
-                                        hdr,
-                                        trigger,
-                                        rxSignalInfo.snr);
-                }
-                else
-                {
-                    NS_LOG_DEBUG("Cannot schedule CTS");
-                }
+                NS_LOG_DEBUG("Schedule CTS");
+                Simulator::Schedule(m_phy->GetSifs(),
+                                    &HeFrameExchangeManager::SendCtsAfterMuRts,
+                                    this,
+                                    hdr,
+                                    trigger,
+                                    rxSignalInfo.snr);
             }
             else if (trigger.IsMuBar())
             {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-frame-exchange-manager.h ns-3.39/src/wifi/model/he/he-frame-exchange-manager.h
--- ns-3.38/src/wifi/model/he/he-frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-frame-exchange-manager.h	2023-07-05 14:23:48.113428400 -0700
@@ -52,6 +52,13 @@
 bool IsTrigger(const WifiPsduMap& psduMap);
 
 /**
+ * \param psduMap a PSDU map
+ * \return true if the given PSDU map contains a single PSDU including a single MPDU
+ *         that carries a Trigger Frame
+ */
+bool IsTrigger(const WifiConstPsduMap& psduMap);
+
+/**
  * \ingroup wifi
  *
  * HeFrameExchangeManager handles the frame exchange sequences
@@ -156,6 +163,8 @@
     void CtsTimeout(Ptr<WifiMpdu> rts, const WifiTxVector& txVector) override;
     void UpdateNav(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
     void NavResetTimeout() override;
+    void StartProtection(const WifiTxParameters& txParams) override;
+    void ProtectionCompleted() override;
 
     /**
      * Clear the TXOP holder if the intra-BSS NAV counted down to zero (includes the case
@@ -184,11 +193,18 @@
                                     Time response) const;
 
     /**
+     * Record the stations being solicited by an MU-RTS TF.
+     *
+     * \param txParams the TX parameters for the data frame protected by the MU-RTS TF.
+     */
+    void RecordSentMuRtsTo(const WifiTxParameters& txParams);
+
+    /**
      * Send an MU-RTS to begin an MU-RTS/CTS frame exchange protecting an MU PPDU.
      *
      * \param txParams the TX parameters for the data frame
      */
-    void SendMuRts(const WifiTxParameters& txParams);
+    virtual void SendMuRts(const WifiTxParameters& txParams);
 
     /**
      * Called when no CTS frame is received after an MU-RTS.
@@ -240,7 +256,7 @@
      * \param psduMap the map of PSDUs to transmit
      * \param txVector the TXVECTOR used to transmit the MU PPDU
      */
-    void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector);
+    virtual void ForwardPsduMapDown(WifiConstPsduMap psduMap, WifiTxVector& txVector);
 
     /**
      * Take the necessary actions after that some BlockAck frames are missing
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-operation.cc ns-3.39/src/wifi/model/he/he-operation.cc
--- ns-3.38/src/wifi/model/he/he-operation.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-operation.cc	2023-07-05 14:23:48.113428400 -0700
@@ -48,6 +48,12 @@
     return IE_EXT_HE_OPERATION;
 }
 
+void
+HeOperation::Print(std::ostream& os) const
+{
+    os << "HE Operation=" << GetHeOperationParameters() << "|" << GetBasicHeMcsAndNssSet();
+}
+
 uint16_t
 HeOperation::GetInformationFieldSize() const
 {
@@ -152,11 +158,4 @@
     return length;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const HeOperation& HeOperation)
-{
-    os << HeOperation.GetHeOperationParameters() << "|" << HeOperation.GetBasicHeMcsAndNssSet();
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-operation.h ns-3.39/src/wifi/model/he/he-operation.h
--- ns-3.38/src/wifi/model/he/he-operation.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-operation.h	2023-07-05 14:23:48.113428400 -0700
@@ -40,6 +40,7 @@
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
     WifiInformationElementId ElementIdExt() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Set the HE Operation Parameters field in the HE Operation information element.
@@ -101,16 +102,6 @@
     // TODO: VHT Operation Information subfields not defined in the standard yet.
 };
 
-/**
- * output stream output operator
- *
- * \param os output stream
- * \param HeOperation the HE operation
- *
- * \returns output stream
- */
-std::ostream& operator<<(std::ostream& os, const HeOperation& HeOperation);
-
 } // namespace ns3
 
 #endif /* HE_OPERATION_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-phy.cc ns-3.39/src/wifi/model/he/he-phy.cc
--- ns-3.38/src/wifi/model/he/he-phy.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-phy.cc	2023-07-05 14:23:48.113428400 -0700
@@ -216,14 +216,25 @@
                : MicroSeconds(8); // HE-SIG-A (first and second symbol)
 }
 
-Time
-HePhy::GetSigBDuration(const WifiTxVector& txVector) const
+uint32_t
+HePhy::GetSigBSize(const WifiTxVector& txVector) const
 {
-    if (ns3::IsDlMu(txVector.GetPreambleType())) // See section 27.3.11.8 of IEEE 802.11ax-2021
+    if (ns3::IsDlMu(txVector.GetPreambleType()))
     {
         NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
+        return HePpdu::GetSigBFieldSize(
+            txVector.GetChannelWidth(),
+            txVector.GetRuAllocation(
+                m_wifiPhy ? m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20) : 0));
+    }
+    return 0;
+}
 
-        auto sigBSize = GetSigBFieldSize(txVector);
+Time
+HePhy::GetSigBDuration(const WifiTxVector& txVector) const
+{
+    if (auto sigBSize = GetSigBSize(txVector); sigBSize > 0)
+    {
         auto symbolDuration = MicroSeconds(4);
         // Number of data bits per symbol
         auto ndbps =
@@ -339,10 +350,8 @@
     NS_LOG_FUNCTION(this << psdus << txVector << ppduDuration);
     return Create<HePpdu>(psdus,
                           txVector,
-                          m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                              txVector.GetChannelWidth()),
+                          m_wifiPhy->GetOperatingChannel(),
                           ppduDuration,
-                          m_wifiPhy->GetPhyBand(),
                           ObtainNextUid(txVector),
                           HePpdu::PSD_NON_HE_PORTION);
 }
@@ -369,8 +378,7 @@
                         << (ofdmaStarted ? "Y" : "N") << ") "
                         << "and schedule OFDMA payload reception in "
                         << GetDuration(WIFI_PPDU_FIELD_TRAINING, txVector).As(Time::NS));
-            Ptr<Event> event =
-                CreateInterferenceEvent(ppdu, txVector, rxDuration, rxPowersW, !ofdmaStarted);
+            auto event = CreateInterferenceEvent(ppdu, rxDuration, rxPowersW, !ofdmaStarted);
             uint16_t staId = GetStaId(ppdu);
             NS_ASSERT(m_beginOfdmaPayloadRxEvents.find(staId) == m_beginOfdmaPayloadRxEvents.end());
             m_beginOfdmaPayloadRxEvents[staId] =
@@ -384,7 +392,7 @@
             // PHY receives the OFDMA payload while having dropped the preamble
             NS_LOG_INFO("Consider OFDMA part of the PPDU as interference since device dropped the "
                         "preamble");
-            CreateInterferenceEvent(ppdu, txVector, rxDuration, rxPowersW);
+            CreateInterferenceEvent(ppdu, rxDuration, rxPowersW);
             // the OFDMA part of the PPDU will be noise _after_ the completion of the current event
             ErasePreambleEvent(ppdu, rxDuration);
         }
@@ -451,24 +459,19 @@
     // 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.
-    auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
+    const auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
     const auto& currentPreambleEvents = GetCurrentPreambleEvents();
-    auto it = currentPreambleEvents.find(uidPreamblePair);
-    bool isResponseToTrigger = (m_previouslyTxPpduUid == ppdu->GetUid());
+    const auto it = currentPreambleEvents.find(uidPreamblePair);
+    const auto isResponseToTrigger = (m_previouslyTxPpduUid == ppdu->GetUid());
     if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU || isResponseToTrigger)
     {
         const auto& txVector = ppdu->GetTxVector();
-        Time rxDuration;
-        if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
-        {
-            rxDuration = CalculateNonOfdmaDurationForHeTb(
-                txVector); // the OFDMA part of the transmission will be added later on
-        }
-        else
-        {
-            rxDuration = ppdu->GetTxDuration();
-        }
-        if (it != currentPreambleEvents.end())
+        const auto rxDuration =
+            (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
+                ? CalculateNonOfdmaDurationForHeTb(
+                      txVector) // the OFDMA part of the transmission will be added later on
+                : ppdu->GetTxDuration();
+        if (it != currentPreambleEvents.cend())
         {
             if (ppdu->GetType() == WIFI_PPDU_TYPE_UL_MU)
             {
@@ -482,27 +485,13 @@
             }
             event = it->second;
 
-            auto heConfiguration = m_wifiPhy->GetDevice()->GetHeConfiguration();
-            NS_ASSERT(heConfiguration);
-            // DoStartReceivePayload(), which is called when we start receiving the Data field,
-            // computes the max offset among TB PPDUs based on the begin OFDMA payload RX events,
-            // which are scheduled by StartReceivePreamble() when starting the reception of the
-            // OFDMA portion. Therefore, the maximum delay cannot exceed the duration of the
-            // training fields that are between the start of the OFDMA portion and the start
-            // of the Data field.
-            Time maxDelay = GetDuration(WIFI_PPDU_FIELD_TRAINING, txVector);
-            if (heConfiguration->GetMaxTbPpduDelay().IsStrictlyPositive())
-            {
-                maxDelay = Min(maxDelay, heConfiguration->GetMaxTbPpduDelay());
-            }
-
-            if (Simulator::Now() - event->GetStartTime() > maxDelay)
+            if (Simulator::Now() - event->GetStartTime() > GetMaxDelayPpduSameUid(txVector))
             {
                 // This HE TB PPDU arrived too late to be decoded properly. The HE TB PPDU
                 // is dropped and added as interference
-                event = CreateInterferenceEvent(ppdu, txVector, rxDuration, rxPowersW);
+                event = CreateInterferenceEvent(ppdu, rxDuration, rxPowersW);
                 NS_LOG_DEBUG("Drop HE TB PPDU that arrived too late");
-                m_wifiPhy->NotifyRxDrop(GetAddressedPsduInPpdu(ppdu), HE_TB_PPDU_TOO_LATE);
+                m_wifiPhy->NotifyRxDrop(GetAddressedPsduInPpdu(ppdu), PPDU_TOO_LATE);
             }
             else
             {
@@ -537,7 +526,7 @@
             {
                 NS_LOG_DEBUG("Received response to a trigger frame for UID " << ppdu->GetUid());
             }
-            event = CreateInterferenceEvent(ppdu, txVector, rxDuration, rxPowersW);
+            event = CreateInterferenceEvent(ppdu, rxDuration, rxPowersW);
             AddPreambleEvent(event);
         }
     }
@@ -546,24 +535,12 @@
         const auto& txVector = ppdu->GetTxVector();
         Time rxDuration = CalculateNonOfdmaDurationForHeMu(
             txVector); // the OFDMA part of the transmission will be added later on
-        event = CreateInterferenceEvent(ppdu, ppdu->GetTxVector(), rxDuration, rxPowersW);
+        event = CreateInterferenceEvent(ppdu, rxDuration, rxPowersW);
         AddPreambleEvent(event);
     }
     else
     {
-        if (it == currentPreambleEvents.end())
-        {
-            event = PhyEntity::DoGetEvent(ppdu, rxPowersW);
-        }
-        else
-        {
-            NS_LOG_DEBUG(
-                "Update received power of the event associated to these UL transmissions with UID "
-                << ppdu->GetUid());
-            event = it->second;
-            UpdateInterferenceEvent(event, rxPowersW);
-            return nullptr;
-        }
+        event = PhyEntity::DoGetEvent(ppdu, rxPowersW);
     }
     return event;
 }
@@ -968,11 +945,9 @@
     auto it = rxPowersW.end();
     if (g_log.IsEnabled(ns3::LOG_FUNCTION))
     {
-        it = std::max_element(
-            rxPowersW.begin(),
-            rxPowersW.end(),
-            [](const std::pair<WifiSpectrumBand, double>& p1,
-               const std::pair<WifiSpectrumBand, double>& p2) { return p1.second < p2.second; });
+        it = std::max_element(rxPowersW.cbegin(),
+                              rxPowersW.cend(),
+                              [](const auto& p1, const auto& p2) { return p1.second < p2.second; });
     }
     NS_LOG_FUNCTION(this << *event << it->second);
     NS_ASSERT(GetCurrentEvent());
@@ -1000,13 +975,13 @@
     NotifyPayloadBegin(ppdu->GetTxVector(), payloadDuration);
 }
 
-std::pair<uint16_t, WifiSpectrumBand>
+std::pair<uint16_t, WifiSpectrumBandInfo>
 HePhy::GetChannelWidthAndBand(const WifiTxVector& txVector, uint16_t staId) const
 {
     if (txVector.IsMu())
     {
-        return std::make_pair(HeRu::GetBandwidth(txVector.GetRu(staId).GetRuType()),
-                              GetRuBandForRx(txVector, staId));
+        return {HeRu::GetBandwidth(txVector.GetRu(staId).GetRuType()),
+                GetRuBandForRx(txVector, staId)};
     }
     else
     {
@@ -1014,11 +989,10 @@
     }
 }
 
-WifiSpectrumBand
+WifiSpectrumBandInfo
 HePhy::GetRuBandForTx(const WifiTxVector& txVector, uint16_t staId) const
 {
     NS_ASSERT(txVector.IsMu());
-    WifiSpectrumBand band;
     HeRu::RuSpec ru = txVector.GetRu(staId);
     uint16_t channelWidth = txVector.GetChannelWidth();
     NS_ASSERT(channelWidth <= m_wifiPhy->GetChannelWidth());
@@ -1026,19 +1000,23 @@
         channelWidth,
         ru.GetRuType(),
         ru.GetPhyIndex(channelWidth, m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
-    HeRu::SubcarrierRange range = std::make_pair(group.front().first, group.back().second);
+    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)
-    band =
-        m_wifiPhy->ConvertHeRuSubcarriers(channelWidth, GetGuardBandwidth(channelWidth), range, 0);
-    return band;
+    auto indices = ConvertHeRuSubcarriers(channelWidth,
+                                          GetGuardBandwidth(channelWidth),
+                                          m_wifiPhy->GetSubcarrierSpacing(),
+                                          subcarrierRange,
+                                          0);
+    auto frequencies = m_wifiPhy->ConvertIndicesToFrequencies(indices);
+    return {indices, frequencies};
 }
 
-WifiSpectrumBand
+WifiSpectrumBandInfo
 HePhy::GetRuBandForRx(const WifiTxVector& txVector, uint16_t staId) const
 {
     NS_ASSERT(txVector.IsMu());
-    WifiSpectrumBand band;
     HeRu::RuSpec ru = txVector.GetRu(staId);
     uint16_t channelWidth = txVector.GetChannelWidth();
     NS_ASSERT(channelWidth <= m_wifiPhy->GetChannelWidth());
@@ -1046,18 +1024,21 @@
         channelWidth,
         ru.GetRuType(),
         ru.GetPhyIndex(channelWidth, m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
-    HeRu::SubcarrierRange range = std::make_pair(group.front().first, group.back().second);
+    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
-    band = m_wifiPhy->ConvertHeRuSubcarriers(
+    auto indices = ConvertHeRuSubcarriers(
         channelWidth,
         GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
-        range,
+        m_wifiPhy->GetSubcarrierSpacing(),
+        subcarrierRange,
         m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth));
-    return band;
+    auto frequencies = m_wifiPhy->ConvertIndicesToFrequencies(indices);
+    return {indices, frequencies};
 }
 
-WifiSpectrumBand
+WifiSpectrumBandInfo
 HePhy::GetNonOfdmaBand(const WifiTxVector& txVector, uint16_t staId) const
 {
     NS_ASSERT(txVector.IsUlMu() && (txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE));
@@ -1076,13 +1057,16 @@
         nonOfdmaRu.GetRuType(),
         nonOfdmaRu.GetPhyIndex(channelWidth,
                                m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(20)));
-    HeRu::SubcarrierRange range =
+    HeRu::SubcarrierRange subcarrierRange =
         std::make_pair(groupPreamble.front().first, groupPreamble.back().second);
-    return m_wifiPhy->ConvertHeRuSubcarriers(
+    auto indices = ConvertHeRuSubcarriers(
         channelWidth,
         GetGuardBandwidth(m_wifiPhy->GetChannelWidth()),
-        range,
+        m_wifiPhy->GetSubcarrierSpacing(),
+        subcarrierRange,
         m_wifiPhy->GetOperatingChannel().GetPrimaryChannelIndex(channelWidth));
+    auto frequencies = m_wifiPhy->ConvertIndicesToFrequencies(indices);
+    return {indices, frequencies};
 }
 
 uint16_t
@@ -1317,6 +1301,25 @@
     return uid;
 }
 
+Time
+HePhy::GetMaxDelayPpduSameUid(const WifiTxVector& txVector)
+{
+    auto heConfiguration = m_wifiPhy->GetDevice()->GetHeConfiguration();
+    NS_ASSERT(heConfiguration);
+    // DoStartReceivePayload(), which is called when we start receiving the Data field,
+    // computes the max offset among TB PPDUs based on the begin OFDMA payload RX events,
+    // which are scheduled by StartReceivePreamble() when starting the reception of the
+    // OFDMA portion. Therefore, the maximum delay cannot exceed the duration of the
+    // training fields that are between the start of the OFDMA portion and the start
+    // of the Data field.
+    auto maxDelay = GetDuration(WIFI_PPDU_FIELD_TRAINING, txVector);
+    if (heConfiguration->GetMaxTbPpduDelay().IsStrictlyPositive())
+    {
+        maxDelay = Min(maxDelay, heConfiguration->GetMaxTbPpduDelay());
+    }
+    return maxDelay;
+}
+
 Ptr<SpectrumValue>
 HePhy::GetTxPowerSpectralDensity(double txPowerW, Ptr<const WifiPpdu> ppdu) const
 {
@@ -1368,7 +1371,7 @@
         }
         else
         {
-            const auto band = GetRuBandForTx(txVector, GetStaId(ppdu));
+            const auto band = GetRuBandForTx(txVector, GetStaId(ppdu)).indices;
             return WifiSpectrumValueHelper::CreateHeMuOfdmTxPowerSpectralDensity(
                 centerFrequency,
                 channelWidth,
@@ -1781,41 +1784,6 @@
     return 6500631;
 }
 
-uint32_t
-HePhy::GetSigBFieldSize(const WifiTxVector& txVector)
-{
-    NS_ASSERT(txVector.GetModulationClass() >= WIFI_MOD_CLASS_HE);
-    NS_ASSERT(ns3::IsDlMu(txVector.GetPreambleType()));
-
-    // Compute the number of bits used by common field.
-    // Assume that compression bit in HE-SIG-A is not set (i.e. not
-    // full band MU-MIMO); the field is present.
-    auto bw = txVector.GetChannelWidth();
-    auto commonFieldSize = 4 /* CRC */ + 6 /* tail */;
-    if (bw <= 40)
-    {
-        commonFieldSize += 8; // only one allocation subfield
-    }
-    else
-    {
-        commonFieldSize += 8 * (bw / 40) /* one allocation field per 40 MHz */ + 1 /* center RU */;
-    }
-
-    auto numStaPerContentChannel = txVector.GetNumRusPerHeSigBContentChannel();
-    auto maxNumStaPerContentChannel =
-        std::max(numStaPerContentChannel.first, numStaPerContentChannel.second);
-    auto maxNumUserBlockFields = maxNumStaPerContentChannel /
-                                 2; // handle last user block with single user, if any, further down
-    std::size_t userSpecificFieldSize =
-        maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */);
-    if (maxNumStaPerContentChannel % 2 != 0)
-    {
-        userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */;
-    }
-
-    return commonFieldSize + userSpecificFieldSize;
-}
-
 bool
 HePhy::CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txChannelWidth) const
 {
@@ -1873,6 +1841,85 @@
     return PhyEntity::GetRxPpduFromTxPpdu(ppdu);
 }
 
+WifiSpectrumBandIndices
+HePhy::ConvertHeRuSubcarriers(uint16_t bandWidth,
+                              uint16_t guardBandwidth,
+                              uint32_t subcarrierSpacing,
+                              HeRu::SubcarrierRange subcarrierRange,
+                              uint8_t bandIndex)
+{
+    WifiSpectrumBandIndices convertedSubcarriers;
+    uint32_t nGuardBands =
+        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / subcarrierSpacing) + 0.5);
+    uint32_t centerFrequencyIndex = 0;
+    switch (bandWidth)
+    {
+    case 20:
+        centerFrequencyIndex = (nGuardBands / 2) + 6 + 122;
+        break;
+    case 40:
+        centerFrequencyIndex = (nGuardBands / 2) + 12 + 244;
+        break;
+    case 80:
+        centerFrequencyIndex = (nGuardBands / 2) + 12 + 500;
+        break;
+    case 160:
+        centerFrequencyIndex = (nGuardBands / 2) + 12 + 1012;
+        break;
+    default:
+        NS_FATAL_ERROR("ChannelWidth " << bandWidth << " unsupported");
+        break;
+    }
+
+    size_t numBandsInBand = static_cast<size_t>(bandWidth * 1e6 / subcarrierSpacing);
+    centerFrequencyIndex += numBandsInBand * bandIndex;
+
+    convertedSubcarriers.first = centerFrequencyIndex + subcarrierRange.first;
+    convertedSubcarriers.second = centerFrequencyIndex + subcarrierRange.second;
+    return convertedSubcarriers;
+}
+
+HePhy::RuBands
+HePhy::GetRuBands(Ptr<const WifiPhy> phy, uint16_t channelWidth, uint16_t guardBandwidth)
+{
+    HePhy::RuBands ruBands{};
+    for (uint16_t bw = 160; bw >= 20; bw = bw / 2)
+    {
+        for (uint32_t i = 0; i < (channelWidth / bw); ++i)
+        {
+            for (uint32_t type = 0; type < 7; type++)
+            {
+                HeRu::RuType ruType = static_cast<HeRu::RuType>(type);
+                std::size_t nRus = HeRu::GetNRus(bw, ruType);
+                for (std::size_t phyIndex = 1; phyIndex <= nRus; phyIndex++)
+                {
+                    HeRu::SubcarrierGroup group = HeRu::GetSubcarrierGroup(bw, ruType, phyIndex);
+                    HeRu::SubcarrierRange subcarrierRange =
+                        std::make_pair(group.front().first, group.back().second);
+                    const auto bandIndices = ConvertHeRuSubcarriers(bw,
+                                                                    guardBandwidth,
+                                                                    phy->GetSubcarrierSpacing(),
+                                                                    subcarrierRange,
+                                                                    i);
+                    const auto bandFrequencies = phy->ConvertIndicesToFrequencies(bandIndices);
+                    WifiSpectrumBandInfo band = {bandIndices, bandFrequencies};
+                    std::size_t index =
+                        (bw == 160 && phyIndex > nRus / 2 ? phyIndex - nRus / 2 : phyIndex);
+                    const auto p20Index = phy->GetOperatingChannel().GetPrimaryChannelIndex(20);
+                    bool primary80IsLower80 = (p20Index < bw / 40);
+                    bool primary80 = (bw < 160 || ruType == HeRu::RU_2x996_TONE ||
+                                      (primary80IsLower80 && phyIndex <= nRus / 2) ||
+                                      (!primary80IsLower80 && phyIndex > nRus / 2));
+                    HeRu::RuSpec ru(ruType, index, primary80);
+                    NS_ABORT_IF(ru.GetPhyIndex(bw, p20Index) != phyIndex);
+                    ruBands.insert({band, ru});
+                }
+            }
+        }
+    }
+    return ruBands;
+}
+
 } // namespace ns3
 
 namespace
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-phy.h ns-3.39/src/wifi/model/he/he-phy.h
--- ns-3.38/src/wifi/model/he/he-phy.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-phy.h	2023-07-05 14:23:48.113428400 -0700
@@ -172,7 +172,7 @@
      *
      * \return the RU band in the TX spectrum
      */
-    WifiSpectrumBand GetRuBandForTx(const WifiTxVector& txVector, uint16_t staId) const;
+    WifiSpectrumBandInfo GetRuBandForTx(const WifiTxVector& txVector, uint16_t staId) const;
     /**
      * Get the band in the RX spectrum associated with the RU used by the PSDU
      * transmitted to/by a given STA in a DL MU PPDU/HE TB PPDU
@@ -182,7 +182,7 @@
      *
      * \return the RU band in the RX spectrum
      */
-    WifiSpectrumBand GetRuBandForRx(const WifiTxVector& txVector, uint16_t staId) const;
+    WifiSpectrumBandInfo GetRuBandForRx(const WifiTxVector& txVector, uint16_t staId) const;
     /**
      * Get the band used to transmit the non-OFDMA part of an HE TB PPDU.
      *
@@ -191,7 +191,7 @@
      *
      * \return the spectrum band used to transmit the non-OFDMA part of an HE TB PPDU
      */
-    WifiSpectrumBand GetNonOfdmaBand(const WifiTxVector& txVector, uint16_t staId) const;
+    WifiSpectrumBandInfo GetNonOfdmaBand(const WifiTxVector& txVector, uint16_t staId) const;
     /**
      * Get the width in MHz of the non-OFDMA portion of an HE TB PPDU
      *
@@ -424,11 +424,47 @@
     static bool IsAllowed(const WifiTxVector& txVector);
 
     /**
-     * Get variable length HE SIG-B field size based on TX Vector
-     * \param txVector WiFi TX Vector
-     * \return field size in bytes
+     * Create and return the HE MCS corresponding to
+     * the provided index.
+     * This method binds all the callbacks used by WifiMode.
+     *
+     * \param index the index of the MCS
+     * \return an HE MCS
      */
-    static uint32_t GetSigBFieldSize(const WifiTxVector& txVector);
+    static WifiMode CreateHeMcs(uint8_t index);
+
+    /**
+     * \param bandWidth the width (MHz) of the band used for the OFDMA transmission. Must be
+     *                  a multiple of 20 MHz
+     * \param guardBandwidth width of the guard band (MHz)
+     * \param subcarrierSpacing the subcarrier spacing (MHz)
+     * \param subcarrierRange the subcarrier range of the HE RU
+     * \param bandIndex the index (starting at 0) of the band within the operating channel
+     * \return the converted subcarriers
+     *
+     * This is a helper function to convert HE RU subcarriers, which are relative to the center
+     * frequency subcarrier, to the indexes used by the Spectrum model.
+     */
+    static WifiSpectrumBandIndices ConvertHeRuSubcarriers(uint16_t bandWidth,
+                                                          uint16_t guardBandwidth,
+                                                          uint32_t subcarrierSpacing,
+                                                          HeRu::SubcarrierRange subcarrierRange,
+                                                          uint8_t bandIndex = 0);
+
+    /// Map a spectrum band associated with an RU to the RU specification
+    using RuBands = std::map<WifiSpectrumBandInfo, HeRu::RuSpec>;
+
+    /**
+     * Static function to compute the RU bands that belong to a given channel width.
+     *
+     * \param phy the PHY that issued the function call
+     * \param channelWidth the channelWidth the channel width in MHz
+     * \param guardBandwidth width of the guard band in MHz
+     * \returns the computed RU bands that belong to the channel width
+     */
+    static RuBands GetRuBands(Ptr<const WifiPhy> phy,
+                              uint16_t channelWidth,
+                              uint16_t guardBandwidth);
 
   protected:
     PhyFieldRxStatus ProcessSig(Ptr<Event> event,
@@ -437,8 +473,8 @@
     Ptr<Event> DoGetEvent(Ptr<const WifiPpdu> ppdu, RxPowerWattPerChannelBand& rxPowersW) override;
     bool IsConfigSupported(Ptr<const WifiPpdu> ppdu) const override;
     Time DoStartReceivePayload(Ptr<Event> event) override;
-    std::pair<uint16_t, WifiSpectrumBand> GetChannelWidthAndBand(const WifiTxVector& txVector,
-                                                                 uint16_t staId) const override;
+    std::pair<uint16_t, WifiSpectrumBandInfo> GetChannelWidthAndBand(const WifiTxVector& txVector,
+                                                                     uint16_t staId) const override;
     void RxPayloadSucceeded(Ptr<const WifiPsdu> psdu,
                             RxSignalInfo rxSignalInfo,
                             const WifiTxVector& txVector,
@@ -451,6 +487,7 @@
     void DoResetReceive(Ptr<Event> event) override;
     void DoAbortCurrentReception(WifiPhyRxfailureReason reason) override;
     uint64_t ObtainNextUid(const WifiTxVector& txVector) override;
+    Time GetMaxDelayPpduSameUid(const WifiTxVector& txVector) override;
     Ptr<SpectrumValue> GetTxPowerSpectralDensity(double txPowerW,
                                                  Ptr<const WifiPpdu> ppdu) const override;
     uint32_t GetMaxPsduSize() const override;
@@ -480,6 +517,12 @@
     virtual PhyFieldRxStatus ProcessSigB(Ptr<Event> event, PhyFieldRxStatus status);
 
     /**
+     * \param txVector the transmission parameters
+     * \return the number of bits of the HE-SIG-B
+     */
+    virtual uint32_t GetSigBSize(const WifiTxVector& txVector) const;
+
+    /**
      * Start receiving the PSDU (i.e. the first symbol of the PSDU has arrived) of an OFDMA
      * transmission. This function is called upon the RX event corresponding to the OFDMA part of
      * the MU PPDU.
@@ -583,16 +626,6 @@
     std::vector<Time> GetPer20MHzDurations(const Ptr<const WifiPpdu> ppdu);
 
     /**
-     * Create and return the HE MCS corresponding to
-     * the provided index.
-     * This method binds all the callbacks used by WifiMode.
-     *
-     * \param index the index of the MCS
-     * \return an HE MCS
-     */
-    static WifiMode CreateHeMcs(uint8_t index);
-
-    /**
      * Given a PPDU duration value, the TXVECTOR used to transmit the PPDU and
      * the PHY band, compute a valid PPDU duration considering the number and
      * duration of symbols, the preamble duration and the guard interval.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-ppdu.cc ns-3.39/src/wifi/model/he/he-ppdu.cc
--- ns-3.38/src/wifi/model/he/he-ppdu.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-ppdu.cc	2023-07-05 14:23:48.113428400 -0700
@@ -24,6 +24,7 @@
 #include "he-phy.h"
 
 #include "ns3/log.h"
+#include "ns3/wifi-phy-operating-channel.h"
 #include "ns3/wifi-phy.h"
 #include "ns3/wifi-psdu.h"
 #include "ns3/wifi-utils.h"
@@ -50,83 +51,57 @@
 
 HePpdu::HePpdu(const WifiConstPsduMap& psdus,
                const WifiTxVector& txVector,
-               uint16_t txCenterFreq,
+               const WifiPhyOperatingChannel& channel,
                Time ppduDuration,
-               WifiPhyBand band,
                uint64_t uid,
                TxPsdFlag flag)
     : OfdmPpdu(psdus.begin()->second,
                txVector,
-               txCenterFreq,
-               band,
+               channel,
                uid,
-               false) // don't instantiate LSigHeader of OfdmPpdu
+               false), // don't instantiate LSigHeader of OfdmPpdu
+      m_txPsdFlag(flag)
 {
-    NS_LOG_FUNCTION(this << psdus << txVector << txCenterFreq << ppduDuration << band << uid
-                         << flag);
+    NS_LOG_FUNCTION(this << psdus << txVector << channel << ppduDuration << uid << flag);
 
     // overwrite with map (since only first element used by OfdmPpdu)
     m_psdus.begin()->second = nullptr;
     m_psdus.clear();
     m_psdus = psdus;
-    if (txVector.IsMu())
-    {
-        for (const auto& heMuUserInfo : txVector.GetHeMuUserInfoMap())
-        {
-            auto [it, ret] = m_muUserInfos.emplace(heMuUserInfo);
-            NS_ABORT_MSG_IF(!ret, "STA-ID " << heMuUserInfo.first << " already present");
-        }
-        m_contentChannelAlloc = txVector.GetContentChannelAllocation();
-        m_ruAllocation = txVector.GetRuAllocation();
-    }
     SetPhyHeaders(txVector, ppduDuration);
-    SetTxPsdFlag(flag);
 }
 
 HePpdu::HePpdu(Ptr<const WifiPsdu> psdu,
                const WifiTxVector& txVector,
-               uint16_t txCenterFreq,
+               const WifiPhyOperatingChannel& channel,
                Time ppduDuration,
-               WifiPhyBand band,
                uint64_t uid)
     : OfdmPpdu(psdu,
                txVector,
-               txCenterFreq,
-               band,
+               channel,
                uid,
-               false) // don't instantiate LSigHeader of OfdmPpdu
+               false), // don't instantiate LSigHeader of OfdmPpdu
+      m_txPsdFlag(PSD_NON_HE_PORTION)
 {
-    NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << ppduDuration << band << uid);
+    NS_LOG_FUNCTION(this << psdu << txVector << channel << ppduDuration << uid);
     NS_ASSERT(!IsMu());
     SetPhyHeaders(txVector, ppduDuration);
-    SetTxPsdFlag(PSD_NON_HE_PORTION);
 }
 
 void
 HePpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
 {
     NS_LOG_FUNCTION(this << txVector << ppduDuration);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    LSigHeader lSig;
-    SetLSigHeader(lSig, ppduDuration);
-
-    HeSigHeader heSig;
-    SetHeSigHeader(heSig, txVector);
-
-    m_phyHeaders->AddHeader(heSig);
-    m_phyHeaders->AddHeader(lSig);
-#else
-    SetLSigHeader(m_lSig, ppduDuration);
-    SetHeSigHeader(m_heSig, txVector);
-#endif
+    SetLSigHeader(ppduDuration);
+    SetHeSigHeader(txVector);
 }
 
 void
-HePpdu::SetLSigHeader(LSigHeader& lSig, Time ppduDuration) const
+HePpdu::SetLSigHeader(Time ppduDuration)
 {
     uint8_t sigExtension = 0;
-    if (m_band == WIFI_PHY_BAND_2_4GHZ)
+    NS_ASSERT(m_operatingChannel.IsSet());
+    if (m_operatingChannel.GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
     {
         sigExtension = 6;
     }
@@ -137,27 +112,47 @@
                              4.0) *
                         3) -
                        3 - m);
-    lSig.SetLength(length);
+    m_lSig.SetLength(length);
 }
 
 void
-HePpdu::SetHeSigHeader(HeSigHeader& heSig, const WifiTxVector& txVector) const
+HePpdu::SetHeSigHeader(const WifiTxVector& txVector)
 {
-    if (ns3::IsDlMu(m_preamble))
-    {
-        heSig.SetMuFlag(true);
-        heSig.SetMcs(txVector.GetSigBMode().GetMcsValue());
-    }
-    else if (!ns3::IsUlMu(m_preamble))
+    const auto bssColor = txVector.GetBssColor();
+    NS_ASSERT(bssColor < 64);
+    if (ns3::IsUlMu(m_preamble))
     {
-        heSig.SetMcs(txVector.GetMode().GetMcsValue());
-        heSig.SetNStreams(txVector.GetNss());
+        m_heSig.emplace<HeTbSigHeader>(HeTbSigHeader{
+            .m_bssColor = bssColor,
+            .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth())});
+    }
+    else if (ns3::IsDlMu(m_preamble))
+    {
+        const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(20);
+        m_heSig.emplace<HeMuSigHeader>(HeMuSigHeader{
+            .m_bssColor = bssColor,
+            .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth()),
+            .m_sigBMcs = txVector.GetSigBMode().GetMcsValue(),
+            .m_giLtfSize = GetGuardIntervalAndNltfEncoding(txVector.GetGuardInterval(),
+                                                           2 /*NLTF currently unused*/),
+            .m_ruAllocation = txVector.GetRuAllocation(p20Index),
+            .m_contentChannels = GetHeSigBContentChannels(txVector, p20Index),
+            .m_center26ToneRuIndication =
+                (txVector.GetChannelWidth() >= 80)
+                    ? std::optional{txVector.GetCenter26ToneRuIndication()}
+                    : std::nullopt});
     }
-    heSig.SetBssColor(txVector.GetBssColor());
-    heSig.SetChannelWidth(txVector.GetChannelWidth());
-    if (!txVector.IsUlMu())
+    else
     {
-        heSig.SetGuardIntervalAndLtfSize(txVector.GetGuardInterval(), 2 /*NLTF currently unused*/);
+        const auto mcs = txVector.GetMode().GetMcsValue();
+        NS_ASSERT(mcs <= 11);
+        m_heSig.emplace<HeSuSigHeader>(HeSuSigHeader{
+            .m_bssColor = bssColor,
+            .m_mcs = mcs,
+            .m_bandwidth = GetChannelWidthEncodingFromMhz(txVector.GetChannelWidth()),
+            .m_giLtfSize = GetGuardIntervalAndNltfEncoding(txVector.GetGuardInterval(),
+                                                           2 /*NLTF currently unused*/),
+            .m_nsts = GetNstsEncodingFromNss(txVector.GetNss())});
     }
 }
 
@@ -166,59 +161,115 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    auto phyHeaders = m_phyHeaders->Copy();
-
-    LSigHeader lSig;
-    if (phyHeaders->RemoveHeader(lSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing L-SIG header in HE PPDU");
-    }
-
-    HeSigHeader heSig;
-    if (phyHeaders->PeekHeader(heSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing HE-SIG header in HE PPDU");
-    }
-
-    SetTxVectorFromPhyHeaders(txVector, lSig, heSig);
-#else
-    SetTxVectorFromPhyHeaders(txVector, m_lSig, m_heSig);
-#endif
-
+    SetTxVectorFromPhyHeaders(txVector);
     return txVector;
 }
 
 void
-HePpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
-                                  const LSigHeader& lSig,
-                                  const HeSigHeader& heSig) const
-{
-    txVector.SetChannelWidth(heSig.GetChannelWidth());
-    txVector.SetBssColor(heSig.GetBssColor());
-    txVector.SetLength(lSig.GetLength());
+HePpdu::SetTxVectorFromPhyHeaders(WifiTxVector& txVector) const
+{
+    txVector.SetLength(m_lSig.GetLength());
     txVector.SetAggregation(m_psdus.size() > 1 || m_psdus.begin()->second->IsAggregate());
     if (!IsMu())
     {
-        txVector.SetMode(HePhy::GetHeMcs(heSig.GetMcs()));
-        txVector.SetNss(heSig.GetNStreams());
-    }
-    if (!IsUlMu())
-    {
-        txVector.SetGuardInterval(heSig.GetGuardInterval());
-    }
-    if (IsDlMu())
-    {
-        for (const auto& muUserInfo : m_muUserInfos)
+        auto heSigHeader = std::get_if<HeSuSigHeader>(&m_heSig);
+        NS_ASSERT(heSigHeader && (heSigHeader->m_format == 1));
+        txVector.SetMode(HePhy::GetHeMcs(heSigHeader->m_mcs));
+        txVector.SetNss(GetNssFromNstsEncoding(heSigHeader->m_nsts));
+        txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(heSigHeader->m_bandwidth));
+        txVector.SetGuardInterval(GetGuardIntervalFromEncoding(heSigHeader->m_giLtfSize));
+        txVector.SetBssColor(heSigHeader->m_bssColor);
+    }
+    else if (IsUlMu())
+    {
+        auto heSigHeader = std::get_if<HeTbSigHeader>(&m_heSig);
+        NS_ASSERT(heSigHeader && (heSigHeader->m_format == 0));
+        txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(heSigHeader->m_bandwidth));
+        txVector.SetBssColor(heSigHeader->m_bssColor);
+    }
+    else if (IsDlMu())
+    {
+        auto heSigHeader = std::get_if<HeMuSigHeader>(&m_heSig);
+        NS_ASSERT(heSigHeader);
+        txVector.SetChannelWidth(GetChannelWidthMhzFromEncoding(heSigHeader->m_bandwidth));
+        txVector.SetGuardInterval(GetGuardIntervalFromEncoding(heSigHeader->m_giLtfSize));
+        txVector.SetBssColor(heSigHeader->m_bssColor);
+        SetHeMuUserInfos(txVector, heSigHeader->m_ruAllocation, heSigHeader->m_contentChannels);
+        txVector.SetSigBMode(HePhy::GetVhtMcs(heSigHeader->m_sigBMcs));
+        const auto p20Index = m_operatingChannel.GetPrimaryChannelIndex(20);
+        txVector.SetRuAllocation(heSigHeader->m_ruAllocation, p20Index);
+        if (heSigHeader->m_center26ToneRuIndication.has_value())
         {
-            txVector.SetHeMuUserInfo(muUserInfo.first, muUserInfo.second);
+            txVector.SetCenter26ToneRuIndication(heSigHeader->m_center26ToneRuIndication.value());
         }
     }
-    if (txVector.IsDlMu())
-    {
-        txVector.SetSigBMode(HePhy::GetVhtMcs(heSig.GetMcs()));
-        txVector.SetRuAllocation(m_ruAllocation);
+}
+
+void
+HePpdu::SetHeMuUserInfos(WifiTxVector& txVector,
+                         const RuAllocation& ruAllocation,
+                         const HeSigBContentChannels& contentChannels) const
+{
+    auto contentChannelIndex = 0;
+    for (const auto& contentChannel : contentChannels)
+    {
+        auto numRusLeft = 0;
+        auto ruAllocIndex = contentChannelIndex;
+        for (const auto& userInfo : contentChannel)
+        {
+            if (userInfo.staId == NO_USER_STA_ID)
+            {
+                continue;
+            }
+            auto ruSpecs = HeRu::GetRuSpecs(ruAllocation.at(ruAllocIndex));
+            if (ruSpecs.empty())
+            {
+                continue;
+            }
+            if (numRusLeft == 0)
+            {
+                numRusLeft = ruSpecs.size();
+            }
+            auto ruIndex = (ruSpecs.size() - numRusLeft);
+            auto ruSpec = ruSpecs.at(ruIndex);
+            auto ruType = ruSpec.GetRuType();
+            if ((ruAllocation.size() == 8) && (ruType == HeRu::RU_996_TONE) &&
+                (std::all_of(
+                    contentChannel.cbegin(),
+                    contentChannel.cend(),
+                    [&userInfo](const auto& item) { return userInfo.staId == item.staId; })))
+            {
+                NS_ASSERT(txVector.GetChannelWidth() == 160);
+                ruType = HeRu::RU_2x996_TONE;
+            }
+            const auto ruBw = HeRu::GetBandwidth(ruType);
+            auto primary80 = ruAllocIndex < 4;
+            auto num20MhzSubchannelsInRu = (ruBw < 20) ? 1 : (ruBw / 20);
+            auto numRuAllocsInContentChannel = std::max(1, num20MhzSubchannelsInRu / 2);
+            auto ruIndexOffset = (ruBw < 20) ? (ruSpecs.size() * ruAllocIndex)
+                                             : (ruAllocIndex / num20MhzSubchannelsInRu);
+            if (!primary80)
+            {
+                ruIndexOffset -= HeRu::GetRusOfType(80, ruType).size();
+            }
+            if (!txVector.IsAllocated(userInfo.staId))
+            {
+                txVector.SetHeMuUserInfo(userInfo.staId,
+                                         {{ruType, ruSpec.GetIndex() + ruIndexOffset, primary80},
+                                          userInfo.mcs,
+                                          userInfo.nss});
+            }
+            if (ruType == HeRu::RU_2x996_TONE)
+            {
+                return;
+            }
+            numRusLeft--;
+            if (numRusLeft == 0)
+            {
+                ruAllocIndex += (2 * numRuAllocsInContentChannel);
+            }
+        }
+        contentChannelIndex++;
     }
 }
 
@@ -227,34 +278,21 @@
 {
     Time ppduDuration = Seconds(0);
     const WifiTxVector& txVector = GetTxVector();
-
-    uint16_t length = 0;
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    LSigHeader lSig;
-    m_phyHeaders->PeekHeader(lSig);
-    length = lSig.GetLength();
-#else
-    length = m_lSig.GetLength();
-#endif
-
-    Time tSymbol = NanoSeconds(12800 + txVector.GetGuardInterval());
-    Time preambleDuration = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
-    uint8_t sigExtension = 0;
-    if (m_band == WIFI_PHY_BAND_2_4GHZ)
-    {
-        sigExtension = 6;
-    }
+    const auto length = m_lSig.GetLength();
+    const auto tSymbol = NanoSeconds(12800 + txVector.GetGuardInterval());
+    const auto preambleDuration = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
+    NS_ASSERT(m_operatingChannel.IsSet());
+    uint8_t sigExtension = (m_operatingChannel.GetPhyBand() == WIFI_PHY_BAND_2_4GHZ) ? 6 : 0;
     uint8_t m = IsDlMu() ? 1 : 2;
     // Equation 27-11 of IEEE P802.11ax/D4.0
-    Time calculatedDuration =
+    const auto calculatedDuration =
         MicroSeconds(((ceil(static_cast<double>(length + 3 + m) / 3)) * 4) + 20 + sigExtension);
     NS_ASSERT(calculatedDuration > preambleDuration);
     uint32_t nSymbols =
         floor(static_cast<double>((calculatedDuration - preambleDuration).GetNanoSeconds() -
                                   (sigExtension * 1000)) /
               tSymbol.GetNanoSeconds());
-    ppduDuration = preambleDuration + (nSymbols * tSymbol) + MicroSeconds(sigExtension);
-    return ppduDuration;
+    return (preambleDuration + (nSymbols * tSymbol) + MicroSeconds(sigExtension));
 }
 
 Ptr<WifiPpdu>
@@ -304,29 +342,23 @@
         return m_psdus.at(SU_STA_ID);
     }
 
-    uint8_t ppduBssColor = 0;
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    auto phyHeaders = m_phyHeaders->Copy();
-    LSigHeader lSig;
-    phyHeaders->RemoveHeader(lSig);
-    HeSigHeader heSig;
-    phyHeaders->RemoveHeader(heSig);
-    ppduBssColor = heSig.GetBssColor();
-#else
-    ppduBssColor = m_heSig.GetBssColor();
-#endif
-
     if (IsUlMu())
     {
+        auto heSigHeader = std::get_if<HeTbSigHeader>(&m_heSig);
+        NS_ASSERT(heSigHeader);
         NS_ASSERT(m_psdus.size() == 1);
-        if (bssColor == 0 || ppduBssColor == 0 || (bssColor == ppduBssColor))
+        if ((bssColor == 0) || (heSigHeader->m_bssColor == 0) ||
+            (bssColor == heSigHeader->m_bssColor))
         {
             return m_psdus.cbegin()->second;
         }
     }
     else
     {
-        if (bssColor == 0 || ppduBssColor == 0 || (bssColor == ppduBssColor))
+        auto heSigHeader = std::get_if<HeMuSigHeader>(&m_heSig);
+        NS_ASSERT(heSigHeader);
+        if ((bssColor == 0) || (heSigHeader->m_bssColor == 0) ||
+            (bssColor == heSigHeader->m_bssColor))
         {
             const auto it = m_psdus.find(staId);
             if (it != m_psdus.cend())
@@ -413,151 +445,198 @@
     }
 }
 
-bool
-HePpdu::IsAllocated(uint16_t staId) const
+std::pair<std::size_t, std::size_t>
+HePpdu::GetNumRusPerHeSigBContentChannel(uint16_t channelWidth, const RuAllocation& ruAllocation)
 {
-    return (m_muUserInfos.find(staId) != m_muUserInfos.cend());
+    // MU-MIMO is not handled for now, i.e. one station per RU
+    NS_ASSERT_MSG(!ruAllocation.empty(), "RU allocation is not set");
+    NS_ASSERT_MSG(ruAllocation.size() == channelWidth / 20,
+                  "RU allocation is not consistent with packet bandwidth");
+
+    std::pair<std::size_t /* number of RUs in content channel 1 */,
+              std::size_t /* number of RUs in content channel 2 */>
+        chSize{0, 0};
+
+    switch (channelWidth)
+    {
+    case 40:
+        chSize.second += HeRu::GetRuSpecs(ruAllocation[1]).size();
+        [[fallthrough]];
+    case 20:
+        chSize.first += HeRu::GetRuSpecs(ruAllocation[0]).size();
+        break;
+    default:
+        for (auto n = 0; n < channelWidth / 20;)
+        {
+            chSize.first += HeRu::GetRuSpecs(ruAllocation[n]).size();
+            if (ruAllocation[n] >= 208)
+            {
+                // 996 tone RU occupies 80 MHz
+                n += 4;
+                continue;
+            }
+            n += 2;
+        }
+        for (auto n = 0; n < channelWidth / 20;)
+        {
+            chSize.second += HeRu::GetRuSpecs(ruAllocation[n + 1]).size();
+            if (ruAllocation[n + 1] >= 208)
+            {
+                // 996 tone RU occupies 80 MHz
+                n += 4;
+                continue;
+            }
+            n += 2;
+        }
+        break;
+    }
+    return chSize;
 }
 
-bool
-HePpdu::IsStaInContentChannel(uint16_t staId, std::size_t channelId) const
+HePpdu::HeSigBContentChannels
+HePpdu::GetHeSigBContentChannels(const WifiTxVector& txVector, uint8_t p20Index)
 {
-    NS_ASSERT_MSG(channelId < m_contentChannelAlloc.size(),
-                  "Invalid content channel ID " << channelId);
-    const auto& channelAlloc = m_contentChannelAlloc.at(channelId);
-    return (std::find(channelAlloc.cbegin(), channelAlloc.cend(), staId) != channelAlloc.cend());
-}
+    HeSigBContentChannels contentChannels{{}};
 
-std::string
-HePpdu::PrintPayload() const
-{
-    std::ostringstream ss;
-    if (IsMu())
+    const auto channelWidth = txVector.GetChannelWidth();
+    if (channelWidth > 20)
     {
-        ss << m_psdus;
-        ss << ", " << m_txPsdFlag;
+        contentChannels.emplace_back();
     }
-    else
+
+    const auto& orderedMap = txVector.GetUserInfoMapOrderedByRus(p20Index);
+    for (const auto& [ru, staId] : orderedMap)
     {
-        ss << "PSDU=" << m_psdus.at(SU_STA_ID) << " ";
-    }
-    return ss.str();
-}
+        auto ruType = ru.GetRuType();
+        auto ruIdx = ru.GetIndex();
+        const auto& userInfo = txVector.GetHeMuUserInfo(staId);
+        NS_ASSERT(ru == userInfo.ru);
 
-HePpdu::HeSigHeader::HeSigHeader()
-    : m_format(1),
-      m_bssColor(0),
-      m_ul_dl(0),
-      m_mcs(0),
-      m_spatialReuse(0),
-      m_bandwidth(0),
-      m_gi_ltf_size(0),
-      m_nsts(0),
-      m_mu(false)
-{
-}
+        if (ruType > HeRu::RU_242_TONE)
+        {
+            for (auto i = 0; i < ((ruType == HeRu::RU_2x996_TONE) ? 2 : 1); ++i)
+            {
+                contentChannels[0].push_back({staId, userInfo.nss, userInfo.mcs});
+                contentChannels[1].push_back({staId, userInfo.nss, userInfo.mcs});
+            }
+            continue;
+        }
 
-TypeId
-HePpdu::HeSigHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::HeSigHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<HeSigHeader>();
-    return tid;
-}
+        std::size_t numRus = (ruType >= HeRu::RU_242_TONE)
+                                 ? 1
+                                 : HeRu::m_heRuSubcarrierGroups.at({20, ruType}).size();
+        if (((ruIdx - 1) / numRus) % 2 == 0)
+        {
+            contentChannels.at(0).push_back({staId, userInfo.nss, userInfo.mcs});
+        }
+        else
+        {
+            contentChannels.at(1).push_back({staId, userInfo.nss, userInfo.mcs});
+        }
+    }
 
-TypeId
-HePpdu::HeSigHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
+    // Add unassigned RUs
+    auto numNumRusPerHeSigBContentChannel =
+        GetNumRusPerHeSigBContentChannel(channelWidth, txVector.GetRuAllocation(p20Index));
+    std::size_t contentChannelIndex = 1;
+    for (auto& contentChannel : contentChannels)
+    {
+        const auto totalUsersInContentChannel = (contentChannelIndex == 1)
+                                                    ? numNumRusPerHeSigBContentChannel.first
+                                                    : numNumRusPerHeSigBContentChannel.second;
+        NS_ASSERT(contentChannel.size() <= totalUsersInContentChannel);
+        std::size_t unallocatedRus = totalUsersInContentChannel - contentChannel.size();
+        for (std::size_t i = 0; i < unallocatedRus; i++)
+        {
+            contentChannel.push_back({NO_USER_STA_ID, 0, 0});
+        }
+        contentChannelIndex++;
+    }
 
-void
-HePpdu::HeSigHeader::Print(std::ostream& os) const
-{
-    os << "MCS=" << +m_mcs << " CHANNEL_WIDTH=" << GetChannelWidth() << " GI=" << GetGuardInterval()
-       << " NSTS=" << +m_nsts << " BSSColor=" << +m_bssColor << " MU=" << +m_mu;
+    return contentChannels;
 }
 
 uint32_t
-HePpdu::HeSigHeader::GetSerializedSize() const
+HePpdu::GetSigBFieldSize(uint16_t channelWidth, const RuAllocation& ruAllocation)
 {
-    uint32_t size = 0;
-    size += 4; // HE-SIG-A1
-    size += 4; // HE-SIG-A2
-    if (m_mu)
+    // Compute the number of bits used by common field.
+    // Assume that compression bit in HE-SIG-A is not set (i.e. not
+    // full band MU-MIMO); the field is present.
+    auto commonFieldSize = 4 /* CRC */ + 6 /* tail */;
+    if (channelWidth <= 40)
     {
-        size += 1; // HE-SIG-B
+        commonFieldSize += 8; // only one allocation subfield
+    }
+    else
+    {
+        commonFieldSize +=
+            8 * (channelWidth / 40) /* one allocation field per 40 MHz */ + 1 /* center RU */;
     }
-    return size;
-}
-
-void
-HePpdu::HeSigHeader::SetMuFlag(bool mu)
-{
-    m_mu = mu;
-}
 
-void
-HePpdu::HeSigHeader::SetMcs(uint8_t mcs)
-{
-    // NS_ASSERT (mcs <= 11); // TODO: reactivate once EHT PHY headers are implemented
-    m_mcs = mcs;
-}
+    auto numRusPerContentChannel = GetNumRusPerHeSigBContentChannel(channelWidth, ruAllocation);
+    auto maxNumRusPerContentChannel =
+        std::max(numRusPerContentChannel.first, numRusPerContentChannel.second);
+    auto maxNumUserBlockFields = maxNumRusPerContentChannel /
+                                 2; // handle last user block with single user, if any, further down
+    std::size_t userSpecificFieldSize =
+        maxNumUserBlockFields * (2 * 21 /* user fields (2 users) */ + 4 /* tail */ + 6 /* CRC */);
+    if (maxNumRusPerContentChannel % 2 != 0)
+    {
+        userSpecificFieldSize += 21 /* last user field */ + 4 /* CRC */ + 6 /* tail */;
+    }
 
-uint8_t
-HePpdu::HeSigHeader::GetMcs() const
-{
-    return m_mcs;
+    return commonFieldSize + userSpecificFieldSize;
 }
 
-void
-HePpdu::HeSigHeader::SetBssColor(uint8_t bssColor)
+std::string
+HePpdu::PrintPayload() const
 {
-    NS_ASSERT(bssColor < 64);
-    m_bssColor = bssColor;
+    std::ostringstream ss;
+    if (IsMu())
+    {
+        ss << m_psdus;
+        ss << ", " << m_txPsdFlag;
+    }
+    else
+    {
+        ss << "PSDU=" << m_psdus.at(SU_STA_ID) << " ";
+    }
+    return ss.str();
 }
 
 uint8_t
-HePpdu::HeSigHeader::GetBssColor() const
-{
-    return m_bssColor;
-}
-
-void
-HePpdu::HeSigHeader::SetChannelWidth(uint16_t channelWidth)
+HePpdu::GetChannelWidthEncodingFromMhz(uint16_t channelWidth)
 {
     if (channelWidth == 160)
     {
-        m_bandwidth = 3;
+        return 3;
     }
     else if (channelWidth == 80)
     {
-        m_bandwidth = 2;
+        return 2;
     }
     else if (channelWidth == 40)
     {
-        m_bandwidth = 1;
+        return 1;
     }
     else
     {
-        m_bandwidth = 0;
+        return 0;
     }
 }
 
 uint16_t
-HePpdu::HeSigHeader::GetChannelWidth() const
+HePpdu::GetChannelWidthMhzFromEncoding(uint8_t bandwidth)
 {
-    if (m_bandwidth == 3)
+    if (bandwidth == 3)
     {
         return 160;
     }
-    else if (m_bandwidth == 2)
+    else if (bandwidth == 2)
     {
         return 80;
     }
-    else if (m_bandwidth == 1)
+    else if (bandwidth == 1)
     {
         return 40;
     }
@@ -567,36 +646,36 @@
     }
 }
 
-void
-HePpdu::HeSigHeader::SetGuardIntervalAndLtfSize(uint16_t gi, uint8_t ltf)
+uint8_t
+HePpdu::GetGuardIntervalAndNltfEncoding(uint16_t gi, uint8_t nltf)
 {
-    if (gi == 800 && ltf == 1)
+    if (gi == 800 && nltf == 1)
     {
-        m_gi_ltf_size = 0;
+        return 0;
     }
-    else if (gi == 800 && ltf == 2)
+    else if (gi == 800 && nltf == 2)
     {
-        m_gi_ltf_size = 1;
+        return 1;
     }
-    else if (gi == 1600 && ltf == 2)
+    else if (gi == 1600 && nltf == 2)
     {
-        m_gi_ltf_size = 2;
+        return 2;
     }
     else
     {
-        m_gi_ltf_size = 3;
+        return 3;
     }
 }
 
 uint16_t
-HePpdu::HeSigHeader::GetGuardInterval() const
+HePpdu::GetGuardIntervalFromEncoding(uint8_t giAndNltfSize)
 {
-    if (m_gi_ltf_size == 3)
+    if (giAndNltfSize == 3)
     {
         // we currently do not consider DCM nor STBC fields
         return 3200;
     }
-    else if (m_gi_ltf_size == 2)
+    else if (giAndNltfSize == 2)
     {
         return 1600;
     }
@@ -606,77 +685,17 @@
     }
 }
 
-void
-HePpdu::HeSigHeader::SetNStreams(uint8_t nStreams)
-{
-    NS_ASSERT(nStreams <= 8);
-    m_nsts = (nStreams - 1);
-}
-
 uint8_t
-HePpdu::HeSigHeader::GetNStreams() const
+HePpdu::GetNstsEncodingFromNss(uint8_t nss)
 {
-    return (m_nsts + 1);
+    NS_ASSERT(nss <= 8);
+    return nss - 1;
 }
 
-void
-HePpdu::HeSigHeader::Serialize(Buffer::Iterator start) const
-{
-    // HE-SIG-A1
-    uint8_t byte = m_format & 0x01;
-    byte |= ((m_ul_dl & 0x01) << 2);
-    byte |= ((m_mcs & 0x0f) << 3);
-    start.WriteU8(byte);
-    uint16_t bytes = (m_bssColor & 0x3f);
-    bytes |= (0x01 << 6); // Reserved set to 1
-    bytes |= ((m_spatialReuse & 0x0f) << 7);
-    bytes |= ((m_bandwidth & 0x03) << 11);
-    bytes |= ((m_gi_ltf_size & 0x03) << 13);
-    bytes |= ((m_nsts & 0x01) << 15);
-    start.WriteU16(bytes);
-    start.WriteU8((m_nsts >> 1) & 0x03);
-
-    // HE-SIG-A2
-    uint32_t sigA2 = 0;
-    sigA2 |= (0x01 << 14); // Set Reserved bit #14 to 1
-    start.WriteU32(sigA2);
-
-    if (m_mu)
-    {
-        // HE-SIG-B
-        start.WriteU8(0);
-    }
-}
-
-uint32_t
-HePpdu::HeSigHeader::Deserialize(Buffer::Iterator start)
+uint8_t
+HePpdu::GetNssFromNstsEncoding(uint8_t nsts)
 {
-    Buffer::Iterator i = start;
-
-    // HE-SIG-A1
-    uint8_t byte = i.ReadU8();
-    m_format = (byte & 0x01);
-    m_ul_dl = ((byte >> 2) & 0x01);
-    m_mcs = ((byte >> 3) & 0x0f);
-    uint16_t bytes = i.ReadU16();
-    m_bssColor = (bytes & 0x3f);
-    m_spatialReuse = ((bytes >> 7) & 0x0f);
-    m_bandwidth = ((bytes >> 11) & 0x03);
-    m_gi_ltf_size = ((bytes >> 13) & 0x03);
-    m_nsts = ((bytes >> 15) & 0x01);
-    byte = i.ReadU8();
-    m_nsts |= (byte & 0x03) << 1;
-
-    // HE-SIG-A2
-    i.ReadU32();
-
-    if (m_mu)
-    {
-        // HE-SIG-B
-        i.ReadU8();
-    }
-
-    return i.GetDistanceFrom(start);
+    return nsts + 1;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-ppdu.h ns-3.39/src/wifi/model/he/he-ppdu.h
--- ns-3.38/src/wifi/model/he/he-ppdu.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-ppdu.h	2023-07-05 14:23:48.113428400 -0700
@@ -24,6 +24,8 @@
 
 #include "ns3/ofdm-ppdu.h"
 
+#include <variant>
+
 /**
  * \file
  * \ingroup wifi
@@ -33,6 +35,9 @@
 namespace ns3
 {
 
+/// HE SIG-B Content Channels
+constexpr size_t WIFI_MAX_NUM_HE_SIGB_CONTENT_CHANNELS = 2;
+
 class WifiPsdu;
 
 /**
@@ -44,109 +49,61 @@
 class HePpdu : public OfdmPpdu
 {
   public:
+    /// User Specific Fields in HE-SIG-Bs.
+    struct HeSigBUserSpecificField
+    {
+        uint16_t staId : 11; ///< STA-ID
+        uint8_t nss : 4;     ///< number of spatial streams
+        uint8_t mcs : 4;     ///< MCS index
+    };
+
+    /// HE SIG-B Content Channels
+    using HeSigBContentChannels = std::vector<std::vector<HeSigBUserSpecificField>>;
+
     /**
-     * HE-SIG PHY header (HE-SIG-A1/A2/B)
+     * HE-SIG PHY header for HE SU PPDUs (HE-SIG-A1/A2)
      */
-    class HeSigHeader : public Header
+    struct HeSuSigHeader
     {
-      public:
-        HeSigHeader();
+        uint8_t m_format{1};    ///< Format bit
+        uint8_t m_bssColor{0};  ///< BSS color field
+        uint8_t m_mcs{0};       ///< MCS field
+        uint8_t m_bandwidth{0}; ///< Bandwidth field
+        uint8_t m_giLtfSize{0}; ///< GI+LTF Size field
+        uint8_t m_nsts{0};      ///< NSTS
+    };                          // struct HeSuSigHeader
 
-        /**
-         * \brief Get the type ID.
-         * \return the object 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;
-
-        /**
-         * Set the Multi-User (MU) flag.
-         *
-         * \param mu the MU flag
-         */
-        void SetMuFlag(bool mu);
-
-        /**
-         * Fill the MCS field of HE-SIG-A1.
-         *
-         * \param mcs the MCS field of HE-SIG-A1
-         */
-        void SetMcs(uint8_t mcs);
-        /**
-         * Return the MCS field of HE-SIG-A1.
-         *
-         * \return the MCS field of HE-SIG-A1
-         */
-        uint8_t GetMcs() const;
-        /**
-         * Fill the BSS Color field of HE-SIG-A1.
-         *
-         * \param bssColor the BSS Color value
-         */
-        void SetBssColor(uint8_t bssColor);
-        /**
-         * Return the BSS Color field in the HE-SIG-A1.
-         *
-         * \return the BSS Color field in the HE-SIG-A1
-         */
-        uint8_t GetBssColor() const;
-        /**
-         * Fill the channel width field of HE-SIG-A1 (in MHz).
-         *
-         * \param channelWidth the channel width (in MHz)
-         */
-        void SetChannelWidth(uint16_t channelWidth);
-        /**
-         * Return the channel width (in MHz).
-         *
-         * \return the channel width (in MHz)
-         */
-        uint16_t GetChannelWidth() const;
-        /**
-         * Fill the GI + LTF size field of HE-SIG-A1.
-         *
-         * \param gi the guard interval (in nanoseconds)
-         * \param ltf the sequence of HE-LTF
-         */
-        void SetGuardIntervalAndLtfSize(uint16_t gi, uint8_t ltf);
-        /**
-         * Return the guard interval (in nanoseconds).
-         *
-         * \return the guard interval (in nanoseconds)
-         */
-        uint16_t GetGuardInterval() const;
-        /**
-         * Fill the number of streams field of HE-SIG-A1.
-         *
-         * \param nStreams the number of streams
-         */
-        void SetNStreams(uint8_t nStreams);
-        /**
-         * Return the number of streams.
-         *
-         * \return the number of streams
-         */
-        uint8_t GetNStreams() const;
-
-      private:
-        // HE-SIG-A1 fields
-        uint8_t m_format;       ///< Format bit
-        uint8_t m_bssColor;     ///< BSS color field
-        uint8_t m_ul_dl;        ///< UL/DL bit
-        uint8_t m_mcs;          ///< MCS field
-        uint8_t m_spatialReuse; ///< Spatial Reuse field
-        uint8_t m_bandwidth;    ///< Bandwidth field
-        uint8_t m_gi_ltf_size;  ///< GI+LTF Size field
-        uint8_t m_nsts;         ///< NSTS
-
-        /// This is used to decide whether MU SIG-B should be added or not
-        bool m_mu;
-    }; // class HeSigHeader
+    /**
+     * HE-SIG PHY header for HE TB PPDUs (HE-SIG-A1/A2)
+     */
+    struct HeTbSigHeader
+    {
+        uint8_t m_format{0};    ///< Format bit
+        uint8_t m_bssColor{0};  ///< BSS color field
+        uint8_t m_bandwidth{0}; ///< Bandwidth field
+    };                          // struct HeTbSigHeader
+
+    /**
+     * HE-SIG PHY header for HE MU PPDUs (HE-SIG-A1/A2/B)
+     */
+    struct HeMuSigHeader
+    {
+        // HE-SIG-A fields
+        uint8_t m_bssColor{0};  ///< BSS color field
+        uint8_t m_bandwidth{0}; ///< Bandwidth field
+        uint8_t m_sigBMcs{0};   ///< HE-SIG-B MCS
+        uint8_t m_giLtfSize{0}; ///< GI+LTF Size field
+
+        // HE-SIG-B fields
+        RuAllocation m_ruAllocation; //!< RU allocations that are going to be carried in SIG-B
+                                     //!< common subfields
+        HeSigBContentChannels m_contentChannels; //!< HE SIG-B Content Channels
+        std::optional<Center26ToneRuIndication>
+            m_center26ToneRuIndication; //!< center 26 tone RU indication in SIG-B common subfields
+    };                                  // struct HeMuSigHeader
+
+    /// type of the HE-SIG PHY header
+    using HeSigHeader = std::variant<std::monostate, HeSuSigHeader, HeTbSigHeader, HeMuSigHeader>;
 
     /**
      * The transmit power spectral density flag, namely used
@@ -163,16 +120,14 @@
      *
      * \param psdu the PHY payload (PSDU)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param ppduDuration the transmission duration of this PPDU
-     * \param band the WifiPhyBand used for the transmission of this PPDU
      * \param uid the unique ID of this PPDU
      */
     HePpdu(Ptr<const WifiPsdu> psdu,
            const WifiTxVector& txVector,
-           uint16_t txCenterFreq,
+           const WifiPhyOperatingChannel& channel,
            Time ppduDuration,
-           WifiPhyBand band,
            uint64_t uid);
     /**
      * Create an MU HE PPDU, storing a map of PSDUs.
@@ -181,17 +136,15 @@
      *
      * \param psdus the PHY payloads (PSDUs)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param ppduDuration the transmission duration of this PPDU
-     * \param band the WifiPhyBand used for the transmission of this PPDU
      * \param uid the unique ID of this PPDU or of the triggering PPDU if this is an HE TB PPDU
      * \param flag the flag indicating the type of Tx PSD to build
      */
     HePpdu(const WifiConstPsduMap& psdus,
            const WifiTxVector& txVector,
-           uint16_t txCenterFreq,
+           const WifiPhyOperatingChannel& channel,
            Time ppduDuration,
-           WifiPhyBand band,
            uint64_t uid,
            TxPsdFlag flag);
 
@@ -234,66 +187,111 @@
     void UpdateTxVectorForUlMu(const std::optional<WifiTxVector>& trigVector) const;
 
     /**
-     * Check if STA ID is in HE SIG-B Content Channel ID
-     * \param staId STA ID
-     * \param channelId Content Channel ID
-     * \return true if STA ID in content channel ID, false otherwise
+     * Get the number of RUs per HE-SIG-B content channel.
+     * This is applicable only for MU. MU-MIMO (i.e. multiple stations
+     * per RU) is not supported yet.
+     * See section 27.3.10.8.3 of IEEE 802.11ax draft 4.0.
+     *
+     * \param channelWidth the channel width occupied by the PPDU (in MHz)
+     * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz
+     * \return a pair containing the number of RUs in each HE-SIG-B content channel (resp. 1 and 2)
+     */
+    static std::pair<std::size_t, std::size_t> GetNumRusPerHeSigBContentChannel(
+        uint16_t channelWidth,
+        const RuAllocation& ruAllocation);
+
+    /**
+     * Get the HE SIG-B content channels for a given PPDU
+     * IEEE 802.11ax-2021 27.3.11.8.2 HE-SIG-B content channels
+     *
+     * \param txVector the TXVECTOR used for the PPDU
+     * \param p20Index the index of the primary20 channel
+     * \return HE-SIG-B content channels
      */
-    bool IsStaInContentChannel(uint16_t staId, size_t channelId) const;
+    static HeSigBContentChannels GetHeSigBContentChannels(const WifiTxVector& txVector,
+                                                          uint8_t p20Index);
 
     /**
-     * Check if STA ID is allocated
-     * \param staId STA ID
-     * \return true if allocated, false otherwise
+     * Get variable length HE SIG-B field size
+     * \param channelWidth the channel width occupied by the PPDU (in MHz)
+     * \param ruAllocation 8 bit RU_ALLOCATION per 20 MHz
+     * \return field size in bytes
      */
-    bool IsAllocated(uint16_t staId) const;
+    static uint32_t GetSigBFieldSize(uint16_t channelWidth, const RuAllocation& ruAllocation);
 
   protected:
     /**
      * Fill in the TXVECTOR from PHY headers.
      *
      * \param txVector the TXVECTOR to fill in
-     * \param lSig the L-SIG header
-     * \param heSig the HE-SIG header
      */
-    virtual void SetTxVectorFromPhyHeaders(WifiTxVector& txVector,
-                                           const LSigHeader& lSig,
-                                           const HeSigHeader& heSig) const;
+    virtual void SetTxVectorFromPhyHeaders(WifiTxVector& txVector) const;
 
-#ifndef NS3_BUILD_PROFILE_DEBUG
-    HeSigHeader m_heSig; //!< the HE-SIG PHY header
-#endif
-    mutable TxPsdFlag m_txPsdFlag; //!< the transmit power spectral density flag
+    /**
+     * Reconstruct HeMuUserInfoMap from HE-SIG-B header.
+     *
+     * \param txVector the TXVECTOR to set its HeMuUserInfoMap
+     * \param ruAllocation the RU_ALLOCATION per 20 MHz
+     * \param contentChannels the HE-SIG-B content channels
+     */
+    void SetHeMuUserInfos(WifiTxVector& txVector,
+                          const RuAllocation& ruAllocation,
+                          const HeSigBContentChannels& contentChannels) const;
 
-    WifiTxVector::HeMuUserInfoMap m_muUserInfos; //!< HE MU specific per-user information (to be
-                                                 //!< removed once HE-SIG-B headers are implemented)
-    ContentChannelAllocation
-        m_contentChannelAlloc; //!< HE SIG-B Content Channel allocation (to be removed once HE-SIG-B
-                               //!< headers are implemented)
-    RuAllocation m_ruAllocation; //!< RU_ALLOCATION in SIG-B common field (to be removed once
-                                 //!< HE-SIG-B headers are implemented)
+    /**
+     * Convert channel width expressed in MHz to bandwidth field encoding in HE-SIG-A.
+     *
+     * \param channelWidth the channel width in MHz
+     * \return the value used to encode the bandwidth field in HE-SIG-A
+     */
+    static uint8_t GetChannelWidthEncodingFromMhz(uint16_t channelWidth);
 
-  private:
-    std::string PrintPayload() const override;
-    WifiTxVector DoGetTxVector() const override;
+    /**
+     * Convert number of spatial streams to NSTS field encoding in HE-SIG-A.
+     *
+     * \param nss the number of spatial streams
+     * \return the value used to encode the NSTS field in HE-SIG-A
+     */
+    static uint8_t GetNstsEncodingFromNss(uint8_t nss);
 
     /**
-     * Return true if the PPDU is a MU PPDU
-     * \return true if the PPDU is a MU PPDU
+     * Convert guard interval (in ns) and NLTF to its encoding in HE-SIG-A.
+     *
+     * \param gi the guard interval in nanoseconds
+     * \param nltf the the number of long training symbols
+     * \return the value used to encode the NSTS field in HE-SIG-A
      */
-    virtual bool IsMu() const;
+    static uint8_t GetGuardIntervalAndNltfEncoding(uint16_t gi, uint8_t nltf);
 
     /**
-     * Return true if the PPDU is a DL MU PPDU
-     * \return true if the PPDU is a DL MU PPDU
+     * Convert number of spatial streams from NSTS field encoding in HE-SIG-A.
+     *
+     * \param nsts the value of the NSTS field in HE-SIG-A
+     * \return the number of spatial streams
      */
-    virtual bool IsDlMu() const;
+    static uint8_t GetNssFromNstsEncoding(uint8_t nsts);
 
     /**
-     * Return true if the PPDU is an UL MU PPDU
-     * \return true if the PPDU is an UL MU PPDU
+     * Convert channel width expressed in MHz from bandwidth field encoding in HE-SIG-A.
+     *
+     * \param bandwidth the value of the bandwidth field in HE-SIG-A
+     * \return the channel width in MHz
      */
-    virtual bool IsUlMu() const;
+    static uint16_t GetChannelWidthMhzFromEncoding(uint8_t bandwidth);
+
+    /**
+     * Convert guard interval (in ns) from its encoding in HE-SIG-A.
+     *
+     * \param giAndNltfSize the value used to encode the guard interval and NLTF field in HE-SIG-A
+     * \return the guard interval in nanoseconds
+     */
+    static uint16_t GetGuardIntervalFromEncoding(uint8_t giAndNltfSize);
+
+    mutable TxPsdFlag m_txPsdFlag; //!< the transmit power spectral density flag
+
+  private:
+    std::string PrintPayload() const override;
+    WifiTxVector DoGetTxVector() const override;
 
     /**
      * Fill in the PHY headers.
@@ -301,24 +299,42 @@
      * \param txVector the TXVECTOR that was used for this PPDU
      * \param ppduDuration the transmission duration of this PPDU
      */
-    virtual void SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration);
+    void SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration);
 
     /**
      * Fill in the L-SIG header.
      *
-     * \param lSig the L-SIG header to fill in
      * \param ppduDuration the transmission duration of this PPDU
      */
-    virtual void SetLSigHeader(LSigHeader& lSig, Time ppduDuration) const;
+    void SetLSigHeader(Time ppduDuration);
 
     /**
      * Fill in the HE-SIG header.
      *
-     * \param heSig the HE-SIG header to fill in
      * \param txVector the TXVECTOR that was used for this PPDU
      */
-    void SetHeSigHeader(HeSigHeader& heSig, const WifiTxVector& txVector) const;
-}; // class HePpdu
+    void SetHeSigHeader(const WifiTxVector& txVector);
+
+    /**
+     * Return true if the PPDU is a MU PPDU
+     * \return true if the PPDU is a MU PPDU
+     */
+    virtual bool IsMu() const;
+
+    /**
+     * Return true if the PPDU is a DL MU PPDU
+     * \return true if the PPDU is a DL MU PPDU
+     */
+    virtual bool IsDlMu() const;
+
+    /**
+     * Return true if the PPDU is an UL MU PPDU
+     * \return true if the PPDU is an UL MU PPDU
+     */
+    virtual bool IsUlMu() const;
+
+    HeSigHeader m_heSig; //!< the HE-SIG PHY header
+};                       // class HePpdu
 
 /**
  * \brief Stream insertion operator.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-ru.cc ns-3.39/src/wifi/model/he/he-ru.cc
--- ns-3.38/src/wifi/model/he/he-ru.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-ru.cc	2023-07-05 14:23:48.113428400 -0700
@@ -370,6 +370,24 @@
     // clang-format on
 };
 
+HeRu::RuSpecCompare::RuSpecCompare(uint16_t channelWidth, uint8_t p20Index)
+    : m_channelWidth(channelWidth),
+      m_p20Index(p20Index)
+{
+}
+
+bool
+HeRu::RuSpecCompare::operator()(const HeRu::RuSpec& lhs, const HeRu::RuSpec& rhs) const
+{
+    const auto lhsIndex = lhs.GetPhyIndex(m_channelWidth, m_p20Index);
+    const auto rhsIndex = rhs.GetPhyIndex(m_channelWidth, m_p20Index);
+    const auto lhsStartTone =
+        HeRu::GetSubcarrierGroup(m_channelWidth, lhs.GetRuType(), lhsIndex).front().first;
+    const auto rhsStartTone =
+        HeRu::GetSubcarrierGroup(m_channelWidth, rhs.GetRuType(), rhsIndex).front().first;
+    return lhsStartTone < rhsStartTone;
+}
+
 std::vector<HeRu::RuSpec>
 HeRu::GetRuSpecs(uint8_t ruAllocation)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/he-ru.h ns-3.39/src/wifi/model/he/he-ru.h
--- ns-3.38/src/wifi/model/he/he-ru.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/he-ru.h	2023-07-05 14:23:48.113428400 -0700
@@ -128,6 +128,34 @@
     };
 
     /**
+     * Struct providing a function call operator to compare two RUs.
+     */
+    struct RuSpecCompare
+    {
+        /**
+         * Constructor.
+         *
+         * \param channelWidth the channel width in MHz
+         * \param p20Index the index of the primary20 channel
+         */
+        RuSpecCompare(uint16_t channelWidth, uint8_t p20Index);
+        /**
+         * Function call operator.
+         *
+         * \param lhs left hand side RU
+         * \param rhs right hand side RU
+         * \return true if the left hand side RU has its leftmost tone at a lower
+         *         frequency than the leftmost tone of the right hand side RU,
+         *         false otherwise
+         */
+        bool operator()(const RuSpec& lhs, const RuSpec& rhs) const;
+
+      private:
+        uint16_t m_channelWidth; ///< The channel width in MHz
+        uint8_t m_p20Index;      ///< Primary20 channel index
+    };
+
+    /**
      * Get the number of distinct RUs of the given type (number of tones)
      * available in a HE PPDU of the given bandwidth.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/obss-pd-algorithm.cc ns-3.39/src/wifi/model/he/obss-pd-algorithm.cc
--- ns-3.38/src/wifi/model/he/obss-pd-algorithm.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/obss-pd-algorithm.cc	2023-07-05 14:23:48.113428400 -0700
@@ -19,9 +19,8 @@
 
 #include "obss-pd-algorithm.h"
 
-#include "he-phy.h"
-
 #include "ns3/double.h"
+#include "ns3/eht-phy.h"
 #include "ns3/log.h"
 #include "ns3/wifi-net-device.h"
 #include "ns3/wifi-phy.h"
@@ -85,6 +84,13 @@
 {
     NS_LOG_FUNCTION(this << device);
     m_device = device;
+    auto phy = device->GetPhy();
+    if (phy->GetStandard() >= WIFI_STANDARD_80211be)
+    {
+        auto ehtPhy = DynamicCast<EhtPhy>(device->GetPhy()->GetPhyEntity(WIFI_MOD_CLASS_EHT));
+        NS_ASSERT(ehtPhy);
+        ehtPhy->SetObssPdAlgorithm(this);
+    }
     auto hePhy = DynamicCast<HePhy>(device->GetPhy()->GetPhyEntity(WIFI_MOD_CLASS_HE));
     NS_ASSERT(hePhy);
     hePhy->SetObssPdAlgorithm(this);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/he/rr-multi-user-scheduler.cc ns-3.39/src/wifi/model/he/rr-multi-user-scheduler.cc
--- ns-3.38/src/wifi/model/he/rr-multi-user-scheduler.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/he/rr-multi-user-scheduler.cc	2023-07-05 14:23:48.113428400 -0700
@@ -180,7 +180,7 @@
 
 template <class Func>
 WifiTxVector
-RrMultiUserScheduler::GetTxVectorForUlMu(Func canbeSolicited)
+RrMultiUserScheduler::GetTxVectorForUlMu(Func canBeSolicited)
 {
     NS_LOG_FUNCTION(this);
 
@@ -214,7 +214,7 @@
     {
         NS_LOG_DEBUG("Next candidate STA (MAC=" << staIt->address << ", AID=" << staIt->aid << ")");
 
-        if (!canbeSolicited(*staIt))
+        if (!canBeSolicited(*staIt))
         {
             NS_LOG_DEBUG("Skipping station based on provided function object");
             staIt++;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-capabilities.cc ns-3.39/src/wifi/model/ht/ht-capabilities.cc
--- ns-3.38/src/wifi/model/ht/ht-capabilities.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/ht/ht-capabilities.cc	2023-07-05 14:23:48.113428400 -0700
@@ -98,6 +98,17 @@
 }
 
 void
+HtCapabilities::Print(std::ostream& os) const
+{
+    os << "HT Capabilities=" << bool(GetLdpc()) << "|" << bool(GetSupportedChannelWidth()) << "|"
+       << bool(GetShortGuardInterval20()) << "|";
+    for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
+    {
+        os << IsSupportedMcs(i) << " ";
+    }
+}
+
+void
 HtCapabilities::SetLdpc(uint8_t ldpc)
 {
     m_ldpc = ldpc;
@@ -222,11 +233,7 @@
 bool
 HtCapabilities::IsSupportedMcs(uint8_t mcs) const
 {
-    if (m_rxMcsBitmask[mcs] == 1)
-    {
-        return true;
-    }
-    return false;
+    return m_rxMcsBitmask[mcs] == 1;
 }
 
 uint8_t
@@ -238,7 +245,7 @@
 
         for (uint8_t mcs = (nRx - 1) * 8; mcs <= maxMcs; mcs++)
         {
-            if (IsSupportedMcs(mcs) == false)
+            if (!IsSupportedMcs(mcs))
             {
                 return (nRx - 1);
             }
@@ -506,16 +513,4 @@
     return length;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const HtCapabilities& htcapabilities)
-{
-    os << bool(htcapabilities.GetLdpc()) << "|" << bool(htcapabilities.GetSupportedChannelWidth())
-       << "|" << bool(htcapabilities.GetShortGuardInterval20()) << "|";
-    for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
-    {
-        os << htcapabilities.IsSupportedMcs(i) << " ";
-    }
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-capabilities.h ns-3.39/src/wifi/model/ht/ht-capabilities.h
--- ns-3.38/src/wifi/model/ht/ht-capabilities.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/ht/ht-capabilities.h	2023-07-05 14:23:48.113428400 -0700
@@ -271,6 +271,7 @@
     uint16_t GetInformationFieldSize() const override;
     void SerializeInformationField(Buffer::Iterator start) const override;
     uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
+    void Print(std::ostream& os) const override;
 
     // HT Capabilities Info field
     uint8_t m_ldpc;                  ///< LDPC
@@ -322,8 +323,8 @@
     uint8_t m_implicitTxBfCapable;                  ///< implicit transmit BF capable
     uint8_t m_calibration;                          ///< calibration
     uint8_t m_explicitCsiTxBfCapable;               ///< explicit CSI transmit BF capable
-    uint8_t m_explicitNoncompressedSteeringCapable; ///< explicit non compressed steeering capable
-    uint8_t m_explicitCompressedSteeringCapable;    ///< explicit compressed steeering capable
+    uint8_t m_explicitNoncompressedSteeringCapable; ///< explicit non compressed steering capable
+    uint8_t m_explicitCompressedSteeringCapable;    ///< explicit compressed steering capable
     uint8_t m_explicitTxBfCsiFeedback;              ///< explicit transmit BF CSI feedback
     uint8_t
         m_explicitNoncompressedBfFeedbackCapable;  ///< explicit non compressed BF feedback capable
@@ -350,16 +351,6 @@
     uint8_t m_reservedASel;                             ///< reserved ASEL
 };
 
-/**
- * output stream output operator
- *
- * \param os output stream
- * \param htcapabilities the HT capabilities
- *
- * \returns output stream
- */
-std::ostream& operator<<(std::ostream& os, const HtCapabilities& htcapabilities);
-
 } // namespace ns3
 
 #endif /* HT_CAPABILITY_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.cc ns-3.39/src/wifi/model/ht/ht-frame-exchange-manager.cc
--- ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/ht/ht-frame-exchange-manager.cc	2023-07-05 14:23:48.113428400 -0700
@@ -20,11 +20,13 @@
 #include "ht-frame-exchange-manager.h"
 
 #include "ns3/abort.h"
+#include "ns3/assert.h"
 #include "ns3/ctrl-headers.h"
 #include "ns3/log.h"
 #include "ns3/mgt-headers.h"
 #include "ns3/recipient-block-ack-agreement.h"
 #include "ns3/snr-tag.h"
+#include "ns3/sta-wifi-mac.h"
 #include "ns3/wifi-mac-queue.h"
 #include "ns3/wifi-utils.h"
 
@@ -118,7 +120,7 @@
     }
     else
     {
-        WifiContainerQueueId queueId{WIFI_QOSDATA_UNICAST_QUEUE, recipient, tid};
+        WifiContainerQueueId queueId{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, recipient, tid};
         uint32_t packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId);
         establish =
             ((qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
@@ -131,14 +133,16 @@
     return establish;
 }
 
-void
+bool
 HtFrameExchangeManager::SendAddBaRequest(Mac48Address dest,
                                          uint8_t tid,
                                          uint16_t startingSeq,
                                          uint16_t timeout,
-                                         bool immediateBAck)
+                                         bool immediateBAck,
+                                         Time availableTime)
 {
-    NS_LOG_FUNCTION(this << dest << +tid << startingSeq << timeout << immediateBAck);
+    NS_LOG_FUNCTION(this << dest << +tid << startingSeq << timeout << immediateBAck
+                         << availableTime);
     NS_LOG_DEBUG("Send ADDBA request to " << dest);
 
     WifiMacHeader hdr;
@@ -191,12 +195,16 @@
     WifiTxParameters txParams;
     txParams.m_txVector =
         GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
-    txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
-    txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
+    if (!TryAddMpdu(mpdu, txParams, availableTime))
+    {
+        NS_LOG_DEBUG("Not enough time to send the ADDBA Request frame");
+        return false;
+    }
 
     // Wifi MAC queue scheduler is expected to prioritize management frames
     m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(mpdu);
     SendMpduWithProtection(mpdu, txParams);
+    return true;
 }
 
 void
@@ -379,12 +387,12 @@
             (hdr.IsRetry()
                  ? hdr.GetSequenceNumber()
                  : m_txMiddle->GetNextSeqNumberByTidAndAddress(hdr.GetQosTid(), hdr.GetAddr1()));
-        SendAddBaRequest(hdr.GetAddr1(),
-                         hdr.GetQosTid(),
-                         startingSeq,
-                         edca->GetBlockAckInactivityTimeout(),
-                         true);
-        return true;
+        return SendAddBaRequest(hdr.GetAddr1(),
+                                hdr.GetQosTid(),
+                                startingSeq,
+                                edca->GetBlockAckInactivityTimeout(),
+                                true,
+                                availableTime);
     }
 
     // Use SendDataFrame if we can try aggregation
@@ -417,110 +425,98 @@
     auto queue = m_mac->GetTxopQueue(ac);
     queue->WipeAllExpiredMpdus();
 
-    // in case of MLD, we need to check both the queue of control frames that need to be sent
-    // on our link and the queue of control frames that can be sent on any link. We start with
-    // the former, but we check the latter even if a suitable frame is found in the former
-    // (because a BAR that can be sent on any link to a recipient is no longer needed after
-    // sending a BAR to that recipient on our link).
-
-    std::list<WifiContainerQueueId> queueIds{{WIFI_CTL_QUEUE, m_self, std::nullopt}};
-    if (m_self != m_mac->GetAddress())
-    {
-        // add the MLD address
-        queueIds.emplace_back(WIFI_CTL_QUEUE, m_mac->GetAddress(), std::nullopt);
-    }
+    Ptr<WifiMpdu> bar;
+    Ptr<WifiMpdu> prevBar;
     Ptr<WifiMpdu> selectedBar;
-    uint8_t selectedTid = 0;
 
-    for (const auto& queueId : queueIds)
-    {
-        Ptr<WifiMpdu> bar;
-        Ptr<WifiMpdu> prevBar;
+    // we could iterate over all the scheduler's queues and ignore those that do not contain
+    // control frames, but it's more efficient to peek frames until we get frames that are
+    // not control frames, given that control frames have the highest priority
+    while ((bar = queue->PeekFirstAvailable(m_linkId, prevBar)) && bar && bar->GetHeader().IsCtl())
+    {
+        if (bar->GetHeader().IsBlockAckReq())
+        {
+            CtrlBAckRequestHeader reqHdr;
+            bar->GetPacket()->PeekHeader(reqHdr);
+            auto tid = reqHdr.GetTidInfo();
+            Mac48Address recipient = bar->GetHeader().GetAddr1();
+            auto recipientMld = m_mac->GetMldAddress(recipient);
+
+            // the scheduler should not return a BlockAckReq that cannot be sent on this link:
+            // either the TA address is the address of this link or it is the MLD address and
+            // the RA field is the MLD address of a device we can communicate with on this link
+            NS_ASSERT_MSG(bar->GetHeader().GetAddr2() == m_self ||
+                              (bar->GetHeader().GetAddr2() == m_mac->GetAddress() && recipientMld &&
+                               GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient)),
+                          "Cannot use link " << +m_linkId << " to send BAR: " << *bar);
+
+            if (optAddress &&
+                (GetWifiRemoteStationManager()->GetMldAddress(*optAddress).value_or(*optAddress) !=
+                     GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient) ||
+                 optTid != tid))
+            {
+                NS_LOG_DEBUG("BAR " << *bar
+                                    << " cannot be returned because it is not addressed"
+                                       " to the given station for the given TID");
+                prevBar = bar;
+                continue;
+            }
 
-        while ((bar = queue->PeekByQueueId(queueId, prevBar)))
-        {
-            if (bar->GetHeader().IsBlockAckReq())
+            auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid);
+            if (!agreement)
+            {
+                NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid
+                                                  << " was torn down");
+                queue->Remove(bar);
+                continue;
+            }
+            // update BAR if the starting sequence number changed
+            if (auto seqNo = agreement->get().GetStartingSequence();
+                reqHdr.GetStartingSequence() != seqNo)
             {
-                CtrlBAckRequestHeader reqHdr;
-                bar->GetPacket()->PeekHeader(reqHdr);
-                auto tid = reqHdr.GetTidInfo();
-                Mac48Address recipient = bar->GetHeader().GetAddr1();
+                reqHdr.SetStartingSequence(seqNo);
+                Ptr<Packet> packet = Create<Packet>();
+                packet->AddHeader(reqHdr);
+                auto updatedBar = Create<WifiMpdu>(packet, bar->GetHeader(), bar->GetTimestamp());
+                queue->Replace(bar, updatedBar);
+                bar = updatedBar;
+            }
+            // bar is the BlockAckReq to send
+            selectedBar = bar;
 
-                if (selectedBar)
+            // if the selected BAR is intended to be sent on this specific link and the recipient
+            // is an MLD, remove the BAR (if any) for this BA agreement that can be sent on any
+            // link (because a BAR that can be sent on any link to a recipient is no longer
+            // needed after sending a BAR to that recipient on this link)
+            if (bar->GetHeader().GetAddr2() == m_self && recipientMld)
+            {
+                WifiContainerQueueId queueId{WIFI_CTL_QUEUE,
+                                             WIFI_UNICAST,
+                                             *recipientMld,
+                                             std::nullopt};
+                Ptr<WifiMpdu> otherBar;
+                while ((otherBar = queue->PeekByQueueId(queueId, otherBar)))
                 {
-                    NS_ASSERT_MSG(std::get<Mac48Address>(queueId) != m_self,
-                                  "We shall not keep iterating over the control frames that need"
-                                  "to be sent on a specific link after selecting a BAR to send");
-                    // if this BlockAckReq is addressed to the same recipient as the selected BAR,
-                    // remove it from the queue and stop iterating over this queue; otherwise,
-                    // move on to the next frame in the queue
-                    if (GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient) ==
-                            selectedBar->GetHeader().GetAddr1() &&
-                        selectedTid == tid)
+                    if (otherBar->GetHeader().IsBlockAckReq())
                     {
-                        queue->Remove(bar);
-                        break;
+                        CtrlBAckRequestHeader otherReqHdr;
+                        otherBar->GetPacket()->PeekHeader(otherReqHdr);
+                        if (otherReqHdr.GetTidInfo() == tid)
+                        {
+                            queue->Remove(otherBar);
+                            break;
+                        }
                     }
-                    prevBar = bar;
-                    continue;
-                }
-
-                // if this BlockAckReq can be sent on any link, we have to check that this link
-                // has been setup with the recipient (this may happen, e.g., if we are an AP MLD
-                // and we have setup a subset of our links with a non-AP MLD)
-                if (bar->GetHeader().GetAddr2() != m_self && m_mac->GetMldAddress(recipient) &&
-                    !GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient))
-                {
-                    prevBar = bar;
-                    continue;
-                }
-
-                if (optAddress &&
-                    (GetWifiRemoteStationManager()
-                             ->GetMldAddress(*optAddress)
-                             .value_or(*optAddress) !=
-                         GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(
-                             recipient) ||
-                     optTid != tid))
-                {
-                    NS_LOG_DEBUG("BAR " << *bar
-                                        << " cannot be returned because it is not addressed"
-                                           " to the given station for the given TID");
-                    prevBar = bar;
-                    continue;
                 }
-
-                auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid);
-                if (!agreement)
-                {
-                    NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid
-                                                      << " was torn down");
-                    queue->Remove(bar);
-                    continue;
-                }
-                // update BAR if the starting sequence number changed
-                if (auto seqNo = agreement->get().GetStartingSequence();
-                    reqHdr.GetStartingSequence() != seqNo)
-                {
-                    reqHdr.SetStartingSequence(seqNo);
-                    Ptr<Packet> packet = Create<Packet>();
-                    packet->AddHeader(reqHdr);
-                    auto updatedBar = Create<WifiMpdu>(packet, bar->GetHeader());
-                    queue->Replace(bar, updatedBar);
-                    bar = updatedBar;
-                }
-                // bar is the BlockAckReq to send
-                selectedBar = bar;
-                selectedTid = tid;
-                break;
-            }
-            if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar)
-            {
-                return bar;
             }
-            // not a BAR nor a Trigger Frame, continue
-            prevBar = bar;
+            break;
         }
+        if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar)
+        {
+            return bar;
+        }
+        // not a BAR nor a Trigger Frame, continue
+        prevBar = bar;
     }
 
     if (!selectedBar)
@@ -531,7 +527,10 @@
         {
             if (queue->PeekByTidAndAddress(tid, recipient))
             {
-                selectedBar = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
+                auto [reqHdr, hdr] = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
+                auto pkt = Create<Packet>();
+                pkt->AddHeader(reqHdr);
+                selectedBar = Create<WifiMpdu>(pkt, hdr);
                 baManager->RemoveFromSendBarIfDataQueuedList(recipient, tid);
                 queue->Enqueue(selectedBar);
                 break;
@@ -577,25 +576,8 @@
     WifiTxParameters txParams;
     txParams.m_txVector =
         GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
-    txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
-    txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
 
-    NS_ABORT_IF(txParams.m_acknowledgment->method != WifiAcknowledgment::BLOCK_ACK);
-
-    WifiBlockAck* blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
-    CalculateAcknowledgmentTime(blockAcknowledgment);
-    // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
-    txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
-
-    Time barTxDuration = m_phy->CalculateTxDuration(mpdu->GetSize(),
-                                                    blockAcknowledgment->blockAckTxVector,
-                                                    m_phy->GetPhyBand());
-
-    // if the available time is limited and we are not transmitting the initial
-    // frame of the TXOP, we have to check that this frame and its response fit
-    // within the given time limits
-    if (availableTime != Time::Min() && !initialFrame &&
-        barTxDuration + m_phy->GetSifs() + blockAcknowledgment->acknowledgmentTime > availableTime)
+    if (!TryAddMpdu(mpdu, txParams, availableTime))
     {
         NS_LOG_DEBUG("Not enough time to send the BAR frame returned by the Block Ack Manager");
         return false;
@@ -961,22 +943,21 @@
         }
     }
 
-    if (m_txParams.m_protection->method == WifiProtection::RTS_CTS)
-    {
-        SendRts(m_txParams);
-    }
-    else if (m_txParams.m_protection->method == WifiProtection::CTS_TO_SELF)
-    {
-        SendCtsToSelf(m_txParams);
-    }
-    else if (m_txParams.m_protection->method == WifiProtection::NONE)
+    StartProtection(m_txParams);
+}
+
+void
+HtFrameExchangeManager::ProtectionCompleted()
+{
+    NS_LOG_FUNCTION(this);
+    if (m_psdu)
     {
+        m_protectedStas.merge(m_sentRtsTo);
+        m_sentRtsTo.clear();
         SendPsdu();
+        return;
     }
-    else
-    {
-        NS_ABORT_MSG("Unknown protection type");
-    }
+    QosFrameExchangeManager::ProtectionCompleted();
 }
 
 void
@@ -1052,7 +1033,8 @@
         uint8_t tid = *tids.begin();
 
         Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
-        GetBaManager(tid)->ScheduleBar(edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid));
+        auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid);
+        GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
 
         Simulator::Schedule(txDuration, &HtFrameExchangeManager::TransmissionSucceeded, this);
     }
@@ -1084,6 +1066,23 @@
 {
     NS_LOG_FUNCTION(this << psdu);
 
+    for (const auto& mpdu : *PeekPointer(psdu))
+    {
+        auto& hdr = mpdu->GetHeader();
+
+        if (hdr.IsQosData() && hdr.HasData())
+        {
+            auto tid = hdr.GetQosTid();
+            m_mac->GetQosTxop(tid)->CompleteMpduTx(mpdu);
+        }
+    }
+}
+
+void
+HtFrameExchangeManager::FinalizeMacHeader(Ptr<const WifiPsdu> psdu)
+{
+    NS_LOG_FUNCTION(this << psdu);
+
     // use an array to avoid computing the queue size for every MPDU in the PSDU
     std::array<std::optional<uint8_t>, 8> queueSizeForTid;
 
@@ -1094,7 +1093,7 @@
         if (hdr.IsQosData())
         {
             uint8_t tid = hdr.GetQosTid();
-            Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
+            auto edca = m_mac->GetQosTxop(tid);
 
             if (m_mac->GetTypeOfStation() == STA && (m_setQosQueueSize || hdr.IsQosEosp()))
             {
@@ -1107,13 +1106,10 @@
                 hdr.SetQosEosp();
                 hdr.SetQosQueueSize(queueSizeForTid[tid].value());
             }
-
-            if (hdr.HasData())
-            {
-                edca->CompleteMpduTx(mpdu);
-            }
         }
     }
+
+    QosFrameExchangeManager::FinalizeMacHeader(psdu);
 }
 
 void
@@ -1133,6 +1129,7 @@
     NS_LOG_FUNCTION(this << psdu << txVector);
 
     NS_LOG_DEBUG("Transmitting a PSDU: " << *psdu << " TXVECTOR: " << txVector);
+    FinalizeMacHeader(psdu);
     NotifyTxToEdca(psdu);
 
     if (psdu->IsAggregate())
@@ -1412,7 +1409,8 @@
             else
             {
                 // missed block ack after data frame with Implicit BAR Ack policy
-                GetBaManager(tid)->ScheduleBar(edca->PrepareBlockAckRequest(recipient, tid));
+                auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid);
+                GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
             }
             resetCw = false;
         }
@@ -1542,7 +1540,9 @@
 
             m_txTimer.Cancel();
             m_channelAccessManager->NotifyCtsTimeoutResetNow();
-            Simulator::Schedule(m_phy->GetSifs(), &HtFrameExchangeManager::SendPsdu, this);
+            Simulator::Schedule(m_phy->GetSifs(),
+                                &HtFrameExchangeManager::ProtectionCompleted,
+                                this);
         }
         else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
                  m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK && hdr.GetAddr1() == m_self)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.h ns-3.39/src/wifi/model/ht/ht-frame-exchange-manager.h
--- ns-3.38/src/wifi/model/ht/ht-frame-exchange-manager.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/ht/ht-frame-exchange-manager.h	2023-07-05 14:23:48.113428400 -0700
@@ -197,8 +197,10 @@
     void RetransmitMpduAfterMissedAck(Ptr<WifiMpdu> mpdu) const override;
     void ReleaseSequenceNumbers(Ptr<const WifiPsdu> psdu) const override;
     void ForwardMpduDown(Ptr<WifiMpdu> mpdu, WifiTxVector& txVector) override;
+    void FinalizeMacHeader(Ptr<const WifiPsdu> psdu) override;
     void CtsTimeout(Ptr<WifiMpdu> rts, const WifiTxVector& txVector) override;
     void TransmissionSucceeded() override;
+    void ProtectionCompleted() override;
 
     /**
      * Get a PSDU containing the given MPDU
@@ -315,12 +317,16 @@
      * \param startingSeq the BA agreement starting sequence number
      * \param timeout timeout value.
      * \param immediateBAck flag to indicate whether immediate BlockAck is used.
+     * \param availableTime the amount of time allowed for the frame exchange. Equals
+     *                      Time::Min() in case the TXOP limit is null
+     * \return true if ADDBA Request frame is transmitted, false otherwise
      */
-    void SendAddBaRequest(Mac48Address recipient,
+    bool SendAddBaRequest(Mac48Address recipient,
                           uint8_t tid,
                           uint16_t startingSeq,
                           uint16_t timeout,
-                          bool immediateBAck);
+                          bool immediateBAck,
+                          Time availableTime);
 
     /**
      * Create a BlockAck frame with header equal to <i>blockAck</i> and start its transmission.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-operation.cc ns-3.39/src/wifi/model/ht/ht-operation.cc
--- ns-3.38/src/wifi/model/ht/ht-operation.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/ht/ht-operation.cc	2023-07-05 14:23:48.113428400 -0700
@@ -62,6 +62,23 @@
     return IE_HT_OPERATION;
 }
 
+void
+HtOperation::Print(std::ostream& os) const
+{
+    os << "HT Operation=" << bool(GetPrimaryChannel()) << "|" << +GetSecondaryChannelOffset() << "|"
+       << bool(GetStaChannelWidth()) << "|" << bool(GetRifsMode()) << "|" << +GetHtProtection()
+       << "|" << bool(GetNonGfHtStasPresent()) << "|" << bool(GetObssNonHtStasPresent()) << "|"
+       << bool(GetDualBeacon()) << "|" << bool(GetDualCtsProtection()) << "|"
+       << bool(GetStbcBeacon()) << "|" << bool(GetLSigTxopProtectionFullSupport()) << "|"
+       << bool(GetPcoActive()) << "|" << bool(GetPhase()) << "|" << GetRxHighestSupportedDataRate()
+       << "|" << bool(GetTxMcsSetDefined()) << "|" << bool(GetTxRxMcsSetUnequal()) << "|"
+       << +GetTxMaxNSpatialStreams() << "|" << bool(GetTxUnequalModulation()) << "|";
+    for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
+    {
+        os << IsSupportedMcs(i) << " ";
+    }
+}
+
 uint16_t
 HtOperation::GetInformationFieldSize() const
 {
@@ -263,11 +280,7 @@
 bool
 HtOperation::IsSupportedMcs(uint8_t mcs) const
 {
-    if (m_rxMcsBitmask[mcs] == 1)
-    {
-        return true;
-    }
-    return false;
+    return m_rxMcsBitmask[mcs] == 1;
 }
 
 uint16_t
@@ -456,26 +469,4 @@
     return length;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const HtOperation& htOperation)
-{
-    os << bool(htOperation.GetPrimaryChannel()) << "|" << +htOperation.GetSecondaryChannelOffset()
-       << "|" << bool(htOperation.GetStaChannelWidth()) << "|" << bool(htOperation.GetRifsMode())
-       << "|" << +htOperation.GetHtProtection() << "|" << bool(htOperation.GetNonGfHtStasPresent())
-       << "|" << bool(htOperation.GetObssNonHtStasPresent()) << "|"
-       << bool(htOperation.GetDualBeacon()) << "|" << bool(htOperation.GetDualCtsProtection())
-       << "|" << bool(htOperation.GetStbcBeacon()) << "|"
-       << bool(htOperation.GetLSigTxopProtectionFullSupport()) << "|"
-       << bool(htOperation.GetPcoActive()) << "|" << bool(htOperation.GetPhase()) << "|"
-       << htOperation.GetRxHighestSupportedDataRate() << "|"
-       << bool(htOperation.GetTxMcsSetDefined()) << "|" << bool(htOperation.GetTxRxMcsSetUnequal())
-       << "|" << +htOperation.GetTxMaxNSpatialStreams() << "|"
-       << bool(htOperation.GetTxUnequalModulation()) << "|";
-    for (uint8_t i = 0; i < MAX_SUPPORTED_MCS; i++)
-    {
-        os << htOperation.IsSupportedMcs(i) << " ";
-    }
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-operation.h ns-3.39/src/wifi/model/ht/ht-operation.h
--- ns-3.38/src/wifi/model/ht/ht-operation.h	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/ht/ht-operation.h	2023-07-05 14:23:48.113428400 -0700
@@ -54,6 +54,7 @@
 
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Set the Primary Channel field in the HT Operation information element.
@@ -394,16 +395,6 @@
     uint8_t m_rxMcsBitmask[MAX_SUPPORTED_MCS]; ///< receive MCS bitmask
 };
 
-/**
- * output stream output operator
- *
- * \param os output stream
- * \param htOperation the HT operation
- *
- * \returns output stream
- */
-std::ostream& operator<<(std::ostream& os, const HtOperation& htOperation);
-
 } // namespace ns3
 
 #endif /* HT_OPERATION_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-phy.cc ns-3.39/src/wifi/model/ht/ht-phy.cc
--- ns-3.38/src/wifi/model/ht/ht-phy.cc	2023-03-16 20:54:23.856869200 -0700
+++ ns-3.39/src/wifi/model/ht/ht-phy.cc	2023-07-05 14:23:48.113428400 -0700
@@ -381,10 +381,8 @@
     NS_LOG_FUNCTION(this << psdus << txVector << ppduDuration);
     return Create<HtPpdu>(psdus.begin()->second,
                           txVector,
-                          m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                              txVector.GetChannelWidth()),
+                          m_wifiPhy->GetOperatingChannel(),
                           ppduDuration,
-                          m_wifiPhy->GetPhyBand(),
                           ObtainNextUid(txVector));
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-ppdu.cc ns-3.39/src/wifi/model/ht/ht-ppdu.cc
--- ns-3.38/src/wifi/model/ht/ht-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/ht/ht-ppdu.cc	2023-07-05 14:23:48.113428400 -0700
@@ -24,6 +24,7 @@
 #include "ht-phy.h"
 
 #include "ns3/log.h"
+#include "ns3/wifi-phy-operating-channel.h"
 #include "ns3/wifi-phy.h"
 #include "ns3/wifi-psdu.h"
 #include "ns3/wifi-utils.h"
@@ -35,18 +36,16 @@
 
 HtPpdu::HtPpdu(Ptr<const WifiPsdu> psdu,
                const WifiTxVector& txVector,
-               uint16_t txCenterFreq,
+               const WifiPhyOperatingChannel& channel,
                Time ppduDuration,
-               WifiPhyBand band,
                uint64_t uid)
     : OfdmPpdu(psdu,
                txVector,
-               txCenterFreq,
-               band,
+               channel,
                uid,
                false) // don't instantiate LSigHeader of OfdmPpdu
 {
-    NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << ppduDuration << band << uid);
+    NS_LOG_FUNCTION(this << psdu << txVector << channel << ppduDuration << uid);
     SetPhyHeaders(txVector, ppduDuration, psdu->GetSize());
 }
 
@@ -54,27 +53,16 @@
 HtPpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration, std::size_t psduSize)
 {
     NS_LOG_FUNCTION(this << txVector << ppduDuration << psduSize);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    LSigHeader lSig;
-    SetLSigHeader(lSig, ppduDuration);
-
-    HtSigHeader htSig;
-    SetHtSigHeader(htSig, txVector, psduSize);
-
-    m_phyHeaders->AddHeader(htSig);
-    m_phyHeaders->AddHeader(lSig);
-#else
     SetLSigHeader(m_lSig, ppduDuration);
     SetHtSigHeader(m_htSig, txVector, psduSize);
-#endif
 }
 
 void
 HtPpdu::SetLSigHeader(LSigHeader& lSig, Time ppduDuration) const
 {
     uint8_t sigExtension = 0;
-    if (m_band == WIFI_PHY_BAND_2_4GHZ)
+    NS_ASSERT(m_operatingChannel.IsSet());
+    if (m_operatingChannel.GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
     {
         sigExtension = 6;
     }
@@ -102,27 +90,7 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    auto phyHeaders = m_phyHeaders->Copy();
-
-    LSigHeader lSig;
-    if (phyHeaders->RemoveHeader(lSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing L-SIG header in HT PPDU");
-    }
-
-    HtSigHeader htSig;
-    if (phyHeaders->RemoveHeader(htSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing HT-SIG header in HT PPDU");
-    }
-
-    SetTxVectorFromPhyHeaders(txVector, lSig, htSig);
-#else
     SetTxVectorFromPhyHeaders(txVector, m_lSig, m_htSig);
-#endif
-
     return txVector;
 }
 
@@ -141,25 +109,10 @@
 Time
 HtPpdu::GetTxDuration() const
 {
-    Time ppduDuration = Seconds(0);
     const WifiTxVector& txVector = GetTxVector();
-
-    uint16_t htLength = 0;
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    auto phyHeaders = m_phyHeaders->Copy();
-
-    LSigHeader lSig;
-    phyHeaders->RemoveHeader(lSig);
-    HtSigHeader htSig;
-    phyHeaders->RemoveHeader(htSig);
-
-    htLength = htSig.GetHtLength();
-#else
-    htLength = m_htSig.GetHtLength();
-#endif
-
-    ppduDuration = WifiPhy::CalculateTxDuration(htLength, txVector, m_band);
-    return ppduDuration;
+    const auto htLength = m_htSig.GetHtLength();
+    NS_ASSERT(m_operatingChannel.IsSet());
+    return WifiPhy::CalculateTxDuration(htLength, txVector, m_operatingChannel.GetPhyBand());
 }
 
 Ptr<WifiPpdu>
@@ -177,35 +130,6 @@
 {
 }
 
-TypeId
-HtPpdu::HtSigHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::HtSigHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<HtSigHeader>();
-    return tid;
-}
-
-TypeId
-HtPpdu::HtSigHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-HtPpdu::HtSigHeader::Print(std::ostream& os) const
-{
-    os << "MCS=" << +m_mcs << " HT_LENGTH=" << m_htLength << " CHANNEL_WIDTH=" << GetChannelWidth()
-       << " SGI=" << +m_sgi << " AGGREGATION=" << +m_aggregation;
-}
-
-uint32_t
-HtPpdu::HtSigHeader::GetSerializedSize() const
-{
-    return 6;
-}
-
 void
 HtPpdu::HtSigHeader::SetMcs(uint8_t mcs)
 {
@@ -267,33 +191,4 @@
     return m_sgi;
 }
 
-void
-HtPpdu::HtSigHeader::Serialize(Buffer::Iterator start) const
-{
-    uint8_t byte = m_mcs;
-    byte |= ((m_cbw20_40 & 0x01) << 7);
-    start.WriteU8(byte);
-    start.WriteU16(m_htLength);
-    byte = (0x01 << 2); // Set Reserved bit #2 to 1
-    byte |= ((m_aggregation & 0x01) << 3);
-    byte |= ((m_sgi & 0x01) << 7);
-    start.WriteU8(byte);
-    start.WriteU16(0);
-}
-
-uint32_t
-HtPpdu::HtSigHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    uint8_t byte = i.ReadU8();
-    m_mcs = byte & 0x7f;
-    m_cbw20_40 = ((byte >> 7) & 0x01);
-    m_htLength = i.ReadU16();
-    byte = i.ReadU8();
-    m_aggregation = ((byte >> 3) & 0x01);
-    m_sgi = ((byte >> 7) & 0x01);
-    i.ReadU16();
-    return i.GetDistanceFrom(start);
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ht/ht-ppdu.h ns-3.39/src/wifi/model/ht/ht-ppdu.h
--- ns-3.38/src/wifi/model/ht/ht-ppdu.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/ht/ht-ppdu.h	2023-07-05 14:23:48.113428400 -0700
@@ -48,24 +48,12 @@
      * HT PHY header (HT-SIG1/2).
      * See section 19.3.9 in IEEE 802.11-2016.
      */
-    class HtSigHeader : public Header
+    class HtSigHeader
     {
       public:
         HtSigHeader();
 
         /**
-         * \brief Get the type ID.
-         * \return the object 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;
-
-        /**
          * Fill the MCS field of HT-SIG.
          *
          * \param mcs the MCS field of HT-SIG
@@ -139,16 +127,14 @@
      *
      * \param psdu the PHY payload (PSDU)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param ppduDuration the transmission duration of this PPDU
-     * \param band the WifiPhyBand used for the transmission of this PPDU
      * \param uid the unique ID of this PPDU
      */
     HtPpdu(Ptr<const WifiPsdu> psdu,
            const WifiTxVector& txVector,
-           uint16_t txCenterFreq,
+           const WifiPhyOperatingChannel& channel,
            Time ppduDuration,
-           WifiPhyBand band,
            uint64_t uid);
 
     Time GetTxDuration() const override;
@@ -196,10 +182,8 @@
                                    const LSigHeader& lSig,
                                    const HtSigHeader& htSig) const;
 
-#ifndef NS3_BUILD_PROFILE_DEBUG
     HtSigHeader m_htSig; //!< the HT-SIG PHY header
-#endif
-}; // class HtPpdu
+};                       // class HtPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/interference-helper.cc ns-3.39/src/wifi/model/interference-helper.cc
--- ns-3.38/src/wifi/model/interference-helper.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/interference-helper.cc	2023-07-05 14:23:48.113428400 -0700
@@ -28,6 +28,7 @@
 #include "ns3/log.h"
 #include "ns3/packet.h"
 #include "ns3/simulator.h"
+#include "ns3/wifi-phy-operating-channel.h"
 
 #include <algorithm>
 #include <numeric>
@@ -90,19 +91,18 @@
 {
     NS_ASSERT(!m_rxPowerW.empty());
     // The total RX power corresponds to the maximum over all the bands
-    auto it = std::max_element(
-        m_rxPowerW.begin(),
-        m_rxPowerW.end(),
-        [](const std::pair<WifiSpectrumBand, double>& p1,
-           const std::pair<WifiSpectrumBand, double>& p2) { return p1.second < p2.second; });
+    auto it =
+        std::max_element(m_rxPowerW.cbegin(),
+                         m_rxPowerW.cend(),
+                         [](const auto& p1, const auto& p2) { return p1.second < p2.second; });
     return it->second;
 }
 
 double
-Event::GetRxPowerW(WifiSpectrumBand band) const
+Event::GetRxPowerW(const WifiSpectrumBandInfo& band) const
 {
-    auto it = m_rxPowerW.find(band);
-    NS_ASSERT(it != m_rxPowerW.end());
+    const auto it = m_rxPowerW.find(band);
+    NS_ASSERT(it != m_rxPowerW.cend());
     return it->second;
 }
 
@@ -208,7 +208,12 @@
 InterferenceHelper::DoDispose()
 {
     NS_LOG_FUNCTION(this);
-    RemoveBands();
+    for (auto it : m_niChanges)
+    {
+        it.second.clear();
+    }
+    m_niChanges.clear();
+    m_firstPowers.clear();
     m_errorRateModel = nullptr;
 }
 
@@ -232,34 +237,75 @@
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_QOSDATA);
     hdr.SetQosTid(0);
-    Ptr<WifiPpdu> fakePpdu =
-        Create<WifiPpdu>(Create<WifiPsdu>(Create<Packet>(0), hdr), WifiTxVector(), 0);
+    Ptr<WifiPpdu> fakePpdu = Create<WifiPpdu>(Create<WifiPsdu>(Create<Packet>(0), hdr),
+                                              WifiTxVector(),
+                                              WifiPhyOperatingChannel());
     Add(fakePpdu, WifiTxVector(), duration, rxPowerW);
 }
 
-void
-InterferenceHelper::RemoveBands()
+bool
+InterferenceHelper::HasBands() const
 {
-    NS_LOG_FUNCTION(this);
-    for (auto it : m_niChangesPerBand)
-    {
-        it.second.clear();
-    }
-    m_niChangesPerBand.clear();
-    m_firstPowerPerBand.clear();
+    return !m_niChanges.empty();
+}
+
+bool
+InterferenceHelper::HasBand(const WifiSpectrumBandInfo& band) const
+{
+    return (m_niChanges.count(band) > 0);
 }
 
 void
-InterferenceHelper::AddBand(WifiSpectrumBand band)
+InterferenceHelper::AddBand(const WifiSpectrumBandInfo& band)
 {
-    NS_LOG_FUNCTION(this << band.first << band.second);
-    NS_ASSERT(m_niChangesPerBand.find(band) == m_niChangesPerBand.end());
+    NS_LOG_FUNCTION(this << band);
+    NS_ASSERT(m_niChanges.count(band) == 0);
+    NS_ASSERT(m_firstPowers.count(band) == 0);
     NiChanges niChanges;
-    auto result = m_niChangesPerBand.insert({band, niChanges});
+    auto result = m_niChanges.insert({band, niChanges});
     NS_ASSERT(result.second);
     // Always have a zero power noise event in the list
     AddNiChangeEvent(Time(0), NiChange(0.0, nullptr), result.first);
-    m_firstPowerPerBand.insert({band, 0.0});
+    m_firstPowers.insert({band, 0.0});
+}
+
+void
+InterferenceHelper::UpdateBands(const std::vector<WifiSpectrumBandInfo>& bands,
+                                const FrequencyRange& freqRange)
+{
+    NS_LOG_FUNCTION(this << freqRange);
+    for (auto it = m_niChanges.begin(); it != m_niChanges.end();)
+    {
+        if (!IsBandInFrequencyRange(it->first, freqRange))
+        {
+            it++;
+            continue;
+        }
+        const auto frequencies = it->first.frequencies;
+        const auto found =
+            std::find_if(bands.cbegin(), bands.cend(), [frequencies](const auto& item) {
+                return frequencies == item.frequencies;
+            }) != std::end(bands);
+        if (!found)
+        {
+            // band does not belong to the new bands, erase it
+            m_firstPowers.erase(it->first);
+            it->second.clear();
+            it = m_niChanges.erase(it);
+        }
+        else
+        {
+            it++;
+        }
+    }
+    for (const auto& band : bands)
+    {
+        if (!HasBand(band))
+        {
+            // this is a new band, add it
+            AddBand(band);
+        }
+    }
 }
 
 void
@@ -287,11 +333,12 @@
 }
 
 Time
-InterferenceHelper::GetEnergyDuration(double energyW, WifiSpectrumBand band)
+InterferenceHelper::GetEnergyDuration(double energyW, const WifiSpectrumBandInfo& band)
 {
+    NS_LOG_FUNCTION(this << energyW << band);
     Time now = Simulator::Now();
-    auto niIt = m_niChangesPerBand.find(band);
-    NS_ASSERT(niIt != m_niChangesPerBand.end());
+    auto niIt = m_niChanges.find(band);
+    NS_ABORT_IF(niIt == m_niChanges.end());
     auto i = GetPreviousPosition(now, niIt);
     Time end = i->first;
     for (; i != niIt->second.end(); ++i)
@@ -310,11 +357,10 @@
 InterferenceHelper::AppendEvent(Ptr<Event> event, bool isStartOfdmaRxing)
 {
     NS_LOG_FUNCTION(this << event << isStartOfdmaRxing);
-    for (const auto& it : event->GetRxPowerWPerBand())
+    for (const auto& [band, power] : event->GetRxPowerWPerBand())
     {
-        WifiSpectrumBand band = it.first;
-        auto niIt = m_niChangesPerBand.find(band);
-        NS_ASSERT(niIt != m_niChangesPerBand.end());
+        auto niIt = m_niChanges.find(band);
+        NS_ABORT_IF(niIt == m_niChanges.end());
         double previousPowerStart = 0;
         double previousPowerEnd = 0;
         auto previousPowerPosition = GetPreviousPosition(event->GetStartTime(), niIt);
@@ -322,23 +368,23 @@
         previousPowerEnd = GetPreviousPosition(event->GetEndTime(), niIt)->second.GetPower();
         if (!m_rxing)
         {
-            m_firstPowerPerBand.find(band)->second = previousPowerStart;
+            m_firstPowers.find(band)->second = previousPowerStart;
             // Always leave the first zero power noise event in the list
             niIt->second.erase(++(niIt->second.begin()), ++previousPowerPosition);
         }
         else if (isStartOfdmaRxing)
         {
-            // When the first UL-OFDMA payload is received, we need to set m_firstPowerPerBand
+            // When the first UL-OFDMA payload is received, we need to set m_firstPowers
             // so that it takes into account interferences that arrived between the start of the
             // UL MU transmission and the start of UL-OFDMA payload.
-            m_firstPowerPerBand.find(band)->second = previousPowerStart;
+            m_firstPowers.find(band)->second = previousPowerStart;
         }
         auto first =
             AddNiChangeEvent(event->GetStartTime(), NiChange(previousPowerStart, event), niIt);
         auto last = AddNiChangeEvent(event->GetEndTime(), NiChange(previousPowerEnd, event), niIt);
         for (auto i = first; i != last; ++i)
         {
-            i->second.AddPower(it.second);
+            i->second.AddPower(power);
         }
     }
 }
@@ -348,16 +394,15 @@
 {
     NS_LOG_FUNCTION(this << event);
     // This is called for UL MU events, in order to scale power as long as UL MU PPDUs arrive
-    for (const auto& it : rxPower)
+    for (const auto& [band, power] : rxPower)
     {
-        WifiSpectrumBand band = it.first;
-        auto niIt = m_niChangesPerBand.find(band);
-        NS_ASSERT(niIt != m_niChangesPerBand.end());
+        auto niIt = m_niChanges.find(band);
+        NS_ABORT_IF(niIt == m_niChanges.end());
         auto first = GetPreviousPosition(event->GetStartTime(), niIt);
         auto last = GetPreviousPosition(event->GetEndTime(), niIt);
         for (auto i = first; i != last; ++i)
         {
-            i->second.AddPower(it.second);
+            i->second.AddPower(power);
         }
     }
     event->UpdateRxPowerW(rxPower);
@@ -398,21 +443,21 @@
 double
 InterferenceHelper::CalculateNoiseInterferenceW(Ptr<Event> event,
                                                 NiChangesPerBand* nis,
-                                                WifiSpectrumBand band) const
+                                                const WifiSpectrumBandInfo& band) const
 {
-    NS_LOG_FUNCTION(this << band.first << band.second);
-    auto firstPower_it = m_firstPowerPerBand.find(band);
-    NS_ASSERT(firstPower_it != m_firstPowerPerBand.end());
+    NS_LOG_FUNCTION(this << band);
+    auto firstPower_it = m_firstPowers.find(band);
+    NS_ABORT_IF(firstPower_it == m_firstPowers.end());
     double noiseInterferenceW = firstPower_it->second;
-    auto niIt = m_niChangesPerBand.find(band);
-    NS_ASSERT(niIt != m_niChangesPerBand.end());
+    auto niIt = m_niChanges.find(band);
+    NS_ABORT_IF(niIt == m_niChanges.end());
     auto it = niIt->second.find(event->GetStartTime());
     for (; it != niIt->second.end() && it->first < Simulator::Now(); ++it)
     {
         noiseInterferenceW = it->second.GetPower() - event->GetRxPowerW(band);
     }
     it = niIt->second.find(event->GetStartTime());
-    NS_ASSERT(it != niIt->second.end());
+    NS_ABORT_IF(it == niIt->second.end());
     for (; it != niIt->second.end() && it->second.GetEvent() != event; ++it)
     {
         ;
@@ -477,12 +522,11 @@
 InterferenceHelper::CalculatePayloadPer(Ptr<const Event> event,
                                         uint16_t channelWidth,
                                         NiChangesPerBand* nis,
-                                        WifiSpectrumBand band,
+                                        const WifiSpectrumBandInfo& band,
                                         uint16_t staId,
                                         std::pair<Time, Time> window) const
 {
-    NS_LOG_FUNCTION(this << channelWidth << band.first << band.second << staId << window.first
-                         << window.second);
+    NS_LOG_FUNCTION(this << channelWidth << band << staId << window.first << window.second);
     double psr = 1.0; /* Packet Success Rate */
     const auto& niIt = nis->find(band)->second;
     auto j = niIt.cbegin();
@@ -498,7 +542,8 @@
     }
     Time windowStart = phyPayloadStart + window.first;
     Time windowEnd = phyPayloadStart + window.second;
-    double noiseInterferenceW = m_firstPowerPerBand.find(band)->second;
+    NS_ABORT_IF(m_firstPowers.count(band) == 0);
+    double noiseInterferenceW = m_firstPowers.at(band);
     double powerW = event->GetRxPowerW(band);
     while (++j != niIt.cend())
     {
@@ -548,10 +593,10 @@
     Ptr<const Event> event,
     NiChangesPerBand* nis,
     uint16_t channelWidth,
-    WifiSpectrumBand band,
+    const WifiSpectrumBandInfo& band,
     PhyEntity::PhyHeaderSections phyHeaderSections) const
 {
-    NS_LOG_FUNCTION(this << band.first << band.second);
+    NS_LOG_FUNCTION(this << band);
     double psr = 1.0; /* Packet Success Rate */
     auto niIt = nis->find(band)->second;
     auto j = niIt.begin();
@@ -564,7 +609,8 @@
     }
 
     Time previous = j->first;
-    double noiseInterferenceW = m_firstPowerPerBand.find(band)->second;
+    NS_ABORT_IF(m_firstPowers.count(band) == 0);
+    double noiseInterferenceW = m_firstPowers.at(band);
     double powerW = event->GetRxPowerW(band);
     while (++j != niIt.end())
     {
@@ -610,10 +656,10 @@
 InterferenceHelper::CalculatePhyHeaderPer(Ptr<const Event> event,
                                           NiChangesPerBand* nis,
                                           uint16_t channelWidth,
-                                          WifiSpectrumBand band,
+                                          const WifiSpectrumBandInfo& band,
                                           WifiPpduField header) const
 {
-    NS_LOG_FUNCTION(this << band.first << band.second << header);
+    NS_LOG_FUNCTION(this << band << header);
     auto niIt = nis->find(band)->second;
     auto phyEntity = WifiPhy::GetStaticPhyEntity(event->GetTxVector().GetModulationClass());
 
@@ -635,15 +681,15 @@
     return 1 - psr;
 }
 
-struct PhyEntity::SnrPer
+PhyEntity::SnrPer
 InterferenceHelper::CalculatePayloadSnrPer(Ptr<Event> event,
                                            uint16_t channelWidth,
-                                           WifiSpectrumBand band,
+                                           const WifiSpectrumBandInfo& band,
                                            uint16_t staId,
                                            std::pair<Time, Time> relativeMpduStartStop) const
 {
-    NS_LOG_FUNCTION(this << channelWidth << band.first << band.second << staId
-                         << relativeMpduStartStop.first << relativeMpduStartStop.second);
+    NS_LOG_FUNCTION(this << channelWidth << band << staId << relativeMpduStartStop.first
+                         << relativeMpduStartStop.second);
     NiChangesPerBand ni;
     double noiseInterferenceW = CalculateNoiseInterferenceW(event, &ni, band);
     double snr = CalculateSnr(event->GetRxPowerW(band),
@@ -663,7 +709,7 @@
 InterferenceHelper::CalculateSnr(Ptr<Event> event,
                                  uint16_t channelWidth,
                                  uint8_t nss,
-                                 WifiSpectrumBand band) const
+                                 const WifiSpectrumBandInfo& band) const
 {
     NiChangesPerBand ni;
     double noiseInterferenceW = CalculateNoiseInterferenceW(event, &ni, band);
@@ -671,13 +717,13 @@
     return snr;
 }
 
-struct PhyEntity::SnrPer
+PhyEntity::SnrPer
 InterferenceHelper::CalculatePhyHeaderSnrPer(Ptr<Event> event,
                                              uint16_t channelWidth,
-                                             WifiSpectrumBand band,
+                                             const WifiSpectrumBandInfo& band,
                                              WifiPpduField header) const
 {
-    NS_LOG_FUNCTION(this << band.first << band.second << header);
+    NS_LOG_FUNCTION(this << band << header);
     NiChangesPerBand ni;
     double noiseInterferenceW = CalculateNoiseInterferenceW(event, &ni, band);
     double snr = CalculateSnr(event->GetRxPowerW(band), noiseInterferenceW, channelWidth, 1);
@@ -690,19 +736,6 @@
     return PhyEntity::SnrPer(snr, per);
 }
 
-void
-InterferenceHelper::EraseEvents()
-{
-    for (auto niIt = m_niChangesPerBand.begin(); niIt != m_niChangesPerBand.end(); ++niIt)
-    {
-        niIt->second.clear();
-        // Always have a zero power noise event in the list
-        AddNiChangeEvent(Time(0), NiChange(0.0, nullptr), niIt);
-        m_firstPowerPerBand.at(niIt->first) = 0.0;
-    }
-    m_rxing = false;
-}
-
 InterferenceHelper::NiChanges::iterator
 InterferenceHelper::GetNextPosition(Time moment, NiChangesPerBand::iterator niIt)
 {
@@ -733,18 +766,30 @@
 }
 
 void
-InterferenceHelper::NotifyRxEnd(Time endTime)
+InterferenceHelper::NotifyRxEnd(Time endTime, const FrequencyRange& freqRange)
 {
-    NS_LOG_FUNCTION(this << endTime);
+    NS_LOG_FUNCTION(this << endTime << freqRange);
     m_rxing = false;
-    // Update m_firstPowerPerBand for frame capture
-    for (auto niIt = m_niChangesPerBand.begin(); niIt != m_niChangesPerBand.end(); ++niIt)
+    // Update m_firstPowers for frame capture
+    for (auto niIt = m_niChanges.begin(); niIt != m_niChanges.end(); ++niIt)
     {
+        if (!IsBandInFrequencyRange(niIt->first, freqRange))
+        {
+            continue;
+        }
         NS_ASSERT(niIt->second.size() > 1);
         auto it = GetPreviousPosition(endTime, niIt);
         it--;
-        m_firstPowerPerBand.find(niIt->first)->second = it->second.GetPower();
+        m_firstPowers.find(niIt->first)->second = it->second.GetPower();
     }
 }
 
+bool
+InterferenceHelper::IsBandInFrequencyRange(const WifiSpectrumBandInfo& band,
+                                           const FrequencyRange& freqRange) const
+{
+    return ((band.frequencies.second > (freqRange.minFrequency * 1e6)) &&
+            (band.frequencies.first < (freqRange.maxFrequency * 1e6)));
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/interference-helper.h ns-3.39/src/wifi/model/interference-helper.h
--- ns-3.38/src/wifi/model/interference-helper.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/interference-helper.h	2023-07-05 14:23:48.113428400 -0700
@@ -90,7 +90,7 @@
      * \param band the band for which the power should be returned
      * \return the received power (W) for a given band
      */
-    double GetRxPowerW(WifiSpectrumBand band) const;
+    double GetRxPowerW(const WifiSpectrumBandInfo& band) const;
     /**
      * Return the received power (W) for all bands.
      *
@@ -147,14 +147,26 @@
     /**
      * Add a frequency band.
      *
-     * \param band the band to be created
+     * \param band the band to be added
      */
-    void AddBand(WifiSpectrumBand band);
+    void AddBand(const WifiSpectrumBandInfo& band);
 
     /**
-     * Remove the frequency bands.
+     * Check whether bands are already tracked by this interference helper.
+     *
+     * \return true if bands are tracked by this interference helper, false otherwise
+     */
+    bool HasBands() const;
+
+    /**
+     * Update the frequency bands that belongs to a given frequency range when the spectrum model is
+     * changed.
+     *
+     * \param bands the bands to be added in the new spectrum model
+     * \param freqRange the frequency range the bands belong to
      */
-    void RemoveBands();
+    void UpdateBands(const std::vector<WifiSpectrumBandInfo>& bands,
+                     const FrequencyRange& freqRange);
 
     /**
      * Set the noise figure.
@@ -191,7 +203,7 @@
      *          energy on the medium for a given band will
      *          be higher than the requested threshold.
      */
-    Time GetEnergyDuration(double energyW, WifiSpectrumBand band);
+    Time GetEnergyDuration(double energyW, const WifiSpectrumBandInfo& band);
 
     /**
      * Add the PPDU-related signal to interference helper.
@@ -234,12 +246,11 @@
      *
      * \return struct of SNR and PER (with PER being evaluated over the provided time window)
      */
-    struct PhyEntity::SnrPer CalculatePayloadSnrPer(
-        Ptr<Event> event,
-        uint16_t channelWidth,
-        WifiSpectrumBand band,
-        uint16_t staId,
-        std::pair<Time, Time> relativeMpduStartStop) const;
+    PhyEntity::SnrPer CalculatePayloadSnrPer(Ptr<Event> event,
+                                             uint16_t channelWidth,
+                                             const WifiSpectrumBandInfo& band,
+                                             uint16_t staId,
+                                             std::pair<Time, Time> relativeMpduStartStop) const;
     /**
      * Calculate the SNIR for the event (starting from now until the event end).
      *
@@ -253,7 +264,7 @@
     double CalculateSnr(Ptr<Event> event,
                         uint16_t channelWidth,
                         uint8_t nss,
-                        WifiSpectrumBand band) const;
+                        const WifiSpectrumBandInfo& band) const;
     /**
      * Calculate the SNIR at the start of the PHY header and accumulate
      * all SNIR changes in the SNIR vector.
@@ -265,10 +276,10 @@
      *
      * \return struct of SNR and PER
      */
-    struct PhyEntity::SnrPer CalculatePhyHeaderSnrPer(Ptr<Event> event,
-                                                      uint16_t channelWidth,
-                                                      WifiSpectrumBand band,
-                                                      WifiPpduField header) const;
+    PhyEntity::SnrPer CalculatePhyHeaderSnrPer(Ptr<Event> event,
+                                               uint16_t channelWidth,
+                                               const WifiSpectrumBandInfo& band,
+                                               WifiPpduField header) const;
 
     /**
      * Notify that RX has started.
@@ -278,12 +289,9 @@
      * Notify that RX has ended.
      *
      * \param endTime the end time of the signal
+     * \param freqRange the frequency range in which the received signal event had been detected
      */
-    void NotifyRxEnd(Time endTime);
-    /**
-     * Erase all events.
-     */
-    void EraseEvents();
+    void NotifyRxEnd(Time endTime, const FrequencyRange& freqRange);
 
     /**
      * Update event to scale its received power (W) per band.
@@ -386,12 +394,35 @@
     /**
      * typedef for a multimap of NiChange
      */
-    typedef std::multimap<Time, NiChange> NiChanges;
+    using NiChanges = std::multimap<Time, NiChange>;
 
     /**
      * Map of NiChanges per band
      */
-    typedef std::map<WifiSpectrumBand, NiChanges> NiChangesPerBand;
+    using NiChangesPerBand = std::map<WifiSpectrumBandInfo, NiChanges>;
+
+    /**
+     * Map of first power per band
+     */
+    using FirstPowerPerBand = std::map<WifiSpectrumBandInfo, double>;
+
+    /**
+     * Check whether a given band is tracked by this interference helper.
+     *
+     * \param band the band to be checked
+     * \return true if the band is already tracked by this interference helper, false otherwise
+     */
+    bool HasBand(const WifiSpectrumBandInfo& band) const;
+
+    /**
+     * Check whether a given band belongs to a given frequency range.
+     *
+     * \param band the band to be checked
+     * \param freqRange the frequency range to check whether the band belong to
+     * \return true if the band belongs to the frequency range, false otherwise
+     */
+    bool IsBandInFrequencyRange(const WifiSpectrumBandInfo& band,
+                                const FrequencyRange& freqRange) const;
 
     /**
      * Append the given Event.
@@ -413,7 +444,7 @@
      */
     double CalculateNoiseInterferenceW(Ptr<Event> event,
                                        NiChangesPerBand* nis,
-                                       WifiSpectrumBand band) const;
+                                       const WifiSpectrumBandInfo& band) const;
     /**
      * Calculate the error rate of the given PHY payload only in the provided time
      * window (thus enabling per MPDU PER information). The PHY payload can be divided into
@@ -431,7 +462,7 @@
     double CalculatePayloadPer(Ptr<const Event> event,
                                uint16_t channelWidth,
                                NiChangesPerBand* nis,
-                               WifiSpectrumBand band,
+                               const WifiSpectrumBandInfo& band,
                                uint16_t staId,
                                std::pair<Time, Time> window) const;
     /**
@@ -449,7 +480,7 @@
     double CalculatePhyHeaderPer(Ptr<const Event> event,
                                  NiChangesPerBand* nis,
                                  uint16_t channelWidth,
-                                 WifiSpectrumBand band,
+                                 const WifiSpectrumBandInfo& band,
                                  WifiPpduField header) const;
     /**
      * Calculate the success rate of the PHY header sections for the provided event.
@@ -465,15 +496,15 @@
     double CalculatePhyHeaderSectionPsr(Ptr<const Event> event,
                                         NiChangesPerBand* nis,
                                         uint16_t channelWidth,
-                                        WifiSpectrumBand band,
+                                        const WifiSpectrumBandInfo& band,
                                         PhyEntity::PhyHeaderSections phyHeaderSections) const;
 
     double m_noiseFigure;                 //!< noise figure (linear)
     Ptr<ErrorRateModel> m_errorRateModel; //!< error rate model
-    uint8_t m_numRxAntennas; //!< the number of RX antennas in the corresponding receiver
-    NiChangesPerBand m_niChangesPerBand;                    //!< NI Changes for each band
-    std::map<WifiSpectrumBand, double> m_firstPowerPerBand; //!< first power of each band in watts
-    bool m_rxing; //!< flag whether it is in receiving state
+    uint8_t m_numRxAntennas;         //!< the number of RX antennas in the corresponding receiver
+    NiChangesPerBand m_niChanges;    //!< NI Changes for each band
+    FirstPowerPerBand m_firstPowers; //!< first power of each band in watts
+    bool m_rxing;                    //!< flag whether it is in receiving state
 
     /**
      * Returns an iterator to the first NiChange that is later than moment
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/mac-rx-middle.cc ns-3.39/src/wifi/model/mac-rx-middle.cc
--- ns-3.38/src/wifi/model/mac-rx-middle.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/mac-rx-middle.cc	2023-07-05 14:23:48.113428400 -0700
@@ -134,15 +134,8 @@
      */
     bool IsNextFragment(uint16_t sequenceControl) const
     {
-        if ((sequenceControl >> 4) == (m_lastSequenceControl >> 4) &&
-            (sequenceControl & 0x0f) == ((m_lastSequenceControl & 0x0f) + 1))
-        {
-            return true;
-        }
-        else
-        {
-            return false;
-        }
+        return (sequenceControl >> 4) == (m_lastSequenceControl >> 4) &&
+               (sequenceControl & 0x0f) == ((m_lastSequenceControl & 0x0f) + 1);
     }
 
     /**
@@ -230,11 +223,7 @@
 MacRxMiddle::IsDuplicate(const WifiMacHeader* hdr, OriginatorRxStatus* originator) const
 {
     NS_LOG_FUNCTION(hdr << originator);
-    if (hdr->IsRetry() && originator->GetLastSequenceControl() == hdr->GetSequenceControl())
-    {
-        return true;
-    }
-    return false;
+    return hdr->IsRetry() && originator->GetLastSequenceControl() == hdr->GetSequenceControl();
 }
 
 Ptr<const Packet>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/mgt-headers.cc ns-3.39/src/wifi/model/mgt-headers.cc
--- ns-3.38/src/wifi/model/mgt-headers.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/mgt-headers.cc	2023-07-05 14:23:48.113428400 -0700
@@ -22,6 +22,7 @@
 #include "mgt-headers.h"
 
 #include "ns3/address-utils.h"
+#include "ns3/packet.h"
 #include "ns3/simulator.h"
 
 namespace ns3
@@ -33,169 +34,6 @@
 
 NS_OBJECT_ENSURE_REGISTERED(MgtProbeRequestHeader);
 
-MgtProbeRequestHeader::~MgtProbeRequestHeader()
-{
-}
-
-void
-MgtProbeRequestHeader::SetSsid(const Ssid& ssid)
-{
-    m_ssid = ssid;
-}
-
-void
-MgtProbeRequestHeader::SetSsid(Ssid&& ssid)
-{
-    m_ssid = std::move(ssid);
-}
-
-const Ssid&
-MgtProbeRequestHeader::GetSsid() const
-{
-    return m_ssid;
-}
-
-void
-MgtProbeRequestHeader::SetSupportedRates(const SupportedRates& rates)
-{
-    m_rates = rates;
-}
-
-void
-MgtProbeRequestHeader::SetSupportedRates(SupportedRates&& rates)
-{
-    m_rates = std::move(rates);
-}
-
-void
-MgtProbeRequestHeader::SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities)
-{
-    m_extendedCapability = extendedCapabilities;
-}
-
-void
-MgtProbeRequestHeader::SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities)
-{
-    m_extendedCapability = std::move(extendedCapabilities);
-}
-
-const std::optional<ExtendedCapabilities>&
-MgtProbeRequestHeader::GetExtendedCapabilities() const
-{
-    return m_extendedCapability;
-}
-
-void
-MgtProbeRequestHeader::SetHtCapabilities(const HtCapabilities& htCapabilities)
-{
-    m_htCapability = htCapabilities;
-}
-
-void
-MgtProbeRequestHeader::SetHtCapabilities(HtCapabilities&& htCapabilities)
-{
-    m_htCapability = std::move(htCapabilities);
-}
-
-const std::optional<HtCapabilities>&
-MgtProbeRequestHeader::GetHtCapabilities() const
-{
-    return m_htCapability;
-}
-
-void
-MgtProbeRequestHeader::SetVhtCapabilities(const VhtCapabilities& vhtCapabilities)
-{
-    m_vhtCapability = vhtCapabilities;
-}
-
-void
-MgtProbeRequestHeader::SetVhtCapabilities(VhtCapabilities&& vhtCapabilities)
-{
-    m_vhtCapability = std::move(vhtCapabilities);
-}
-
-const std::optional<VhtCapabilities>&
-MgtProbeRequestHeader::GetVhtCapabilities() const
-{
-    return m_vhtCapability;
-}
-
-void
-MgtProbeRequestHeader::SetHeCapabilities(const HeCapabilities& heCapabilities)
-{
-    m_heCapability = heCapabilities;
-}
-
-void
-MgtProbeRequestHeader::SetHeCapabilities(HeCapabilities&& heCapabilities)
-{
-    m_heCapability = std::move(heCapabilities);
-}
-
-const std::optional<HeCapabilities>&
-MgtProbeRequestHeader::GetHeCapabilities() const
-{
-    return m_heCapability;
-}
-
-void
-MgtProbeRequestHeader::SetEhtCapabilities(const EhtCapabilities& ehtCapabilities)
-{
-    m_ehtCapability = ehtCapabilities;
-}
-
-void
-MgtProbeRequestHeader::SetEhtCapabilities(EhtCapabilities&& ehtCapabilities)
-{
-    m_ehtCapability = std::move(ehtCapabilities);
-}
-
-const std::optional<EhtCapabilities>&
-MgtProbeRequestHeader::GetEhtCapabilities() const
-{
-    return m_ehtCapability;
-}
-
-const SupportedRates&
-MgtProbeRequestHeader::GetSupportedRates() const
-{
-    return m_rates;
-}
-
-uint32_t
-MgtProbeRequestHeader::GetSerializedSize() const
-{
-    uint32_t size = 0;
-    size += m_ssid.GetSerializedSize();
-    size += m_rates.GetSerializedSize();
-    if (m_rates.GetNRates() > 8)
-    {
-        size += m_rates.extended->GetSerializedSize();
-    }
-    if (m_extendedCapability.has_value())
-    {
-        size += m_extendedCapability->GetSerializedSize();
-    }
-    if (m_htCapability.has_value())
-    {
-        size += m_htCapability->GetSerializedSize();
-    }
-    if (m_vhtCapability.has_value())
-    {
-        size += m_vhtCapability->GetSerializedSize();
-    }
-    if (m_heCapability.has_value())
-    {
-        size += m_heCapability->GetSerializedSize();
-    }
-    if (m_ehtCapability.has_value())
-    {
-        size += m_ehtCapability->GetSerializedSize();
-    }
-    return size;
-}
-
 TypeId
 MgtProbeRequestHeader::GetTypeId()
 {
@@ -212,107 +50,26 @@
     return GetTypeId();
 }
 
-void
-MgtProbeRequestHeader::Print(std::ostream& os) const
-{
-    os << "ssid=" << m_ssid << ", "
-       << "rates=" << m_rates << ", ";
-    if (m_extendedCapability.has_value())
-    {
-        os << "Extended Capabilities=" << *m_extendedCapability << " , ";
-    }
-    if (m_htCapability.has_value())
-    {
-        os << "HT Capabilities=" << *m_htCapability << " , ";
-    }
-    if (m_vhtCapability.has_value())
-    {
-        os << "VHT Capabilities=" << *m_vhtCapability << " , ";
-    }
-    if (m_heCapability.has_value())
-    {
-        os << "HE Capabilities=" << *m_heCapability << " , ";
-    }
-    if (m_ehtCapability.has_value())
-    {
-        os << "EHT Capabilities=" << *m_ehtCapability;
-    }
-}
-
-void
-MgtProbeRequestHeader::Serialize(Buffer::Iterator start) const
-{
-    Buffer::Iterator i = start;
-    i = m_ssid.Serialize(i);
-    i = m_rates.Serialize(i);
-    if (m_rates.GetNRates() > 8)
-    {
-        i = m_rates.extended->Serialize(i);
-    }
-    if (m_extendedCapability.has_value())
-    {
-        i = m_extendedCapability->Serialize(i);
-    }
-    if (m_htCapability.has_value())
-    {
-        i = m_htCapability->Serialize(i);
-    }
-    if (m_vhtCapability.has_value())
-    {
-        i = m_vhtCapability->Serialize(i);
-    }
-    if (m_heCapability.has_value())
-    {
-        i = m_heCapability->Serialize(i);
-    }
-    if (m_ehtCapability.has_value())
-    {
-        i = m_ehtCapability->Serialize(i);
-    }
-}
-
-uint32_t
-MgtProbeRequestHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    i = m_ssid.Deserialize(i);
-    i = m_rates.Deserialize(i);
-    i = WifiInformationElement::DeserializeIfPresent(m_rates.extended, i, &m_rates);
-    i = WifiInformationElement::DeserializeIfPresent(m_extendedCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_htCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_vhtCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_heCapability, i);
-    const bool is2_4Ghz = m_rates.IsSupportedRate(
-        1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band
-                                 // Capabilities IE once the later is implemented
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, i, is2_4Ghz, m_heCapability);
-    return i.GetDistanceFrom(start);
-}
-
 /***********************************************************
  *          Probe Response
  ***********************************************************/
 
 NS_OBJECT_ENSURE_REGISTERED(MgtProbeResponseHeader);
 
-MgtProbeResponseHeader::MgtProbeResponseHeader()
-{
-}
-
-MgtProbeResponseHeader::~MgtProbeResponseHeader()
-{
-}
-
-uint64_t
-MgtProbeResponseHeader::GetTimestamp() const
+TypeId
+MgtProbeResponseHeader::GetTypeId()
 {
-    return m_timestamp;
+    static TypeId tid = TypeId("ns3::MgtProbeResponseHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtProbeResponseHeader>();
+    return tid;
 }
 
-const Ssid&
-MgtProbeResponseHeader::GetSsid() const
+TypeId
+MgtProbeResponseHeader::GetInstanceTypeId() const
 {
-    return m_ssid;
+    return GetTypeId();
 }
 
 uint64_t
@@ -321,576 +78,59 @@
     return m_beaconInterval;
 }
 
-const SupportedRates&
-MgtProbeResponseHeader::GetSupportedRates() const
-{
-    return m_rates;
-}
-
-void
-MgtProbeResponseHeader::SetCapabilities(const CapabilityInformation& capabilities)
-{
-    m_capability = capabilities;
-}
-
-void
-MgtProbeResponseHeader::SetCapabilities(CapabilityInformation&& capabilities)
-{
-    m_capability = std::move(capabilities);
-}
-
-const CapabilityInformation&
-MgtProbeResponseHeader::GetCapabilities() const
-{
-    return m_capability;
-}
-
-void
-MgtProbeResponseHeader::SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities)
-{
-    m_extendedCapability = extendedCapabilities;
-}
-
-void
-MgtProbeResponseHeader::SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities)
-{
-    m_extendedCapability = std::move(extendedCapabilities);
-}
-
-const std::optional<ExtendedCapabilities>&
-MgtProbeResponseHeader::GetExtendedCapabilities() const
-{
-    return m_extendedCapability;
-}
-
-void
-MgtProbeResponseHeader::SetHtCapabilities(const HtCapabilities& htCapabilities)
-{
-    m_htCapability = htCapabilities;
-}
-
-void
-MgtProbeResponseHeader::SetHtCapabilities(HtCapabilities&& htCapabilities)
-{
-    m_htCapability = std::move(htCapabilities);
-}
-
-const std::optional<HtCapabilities>&
-MgtProbeResponseHeader::GetHtCapabilities() const
-{
-    return m_htCapability;
-}
-
-void
-MgtProbeResponseHeader::SetHtOperation(const HtOperation& htOperation)
-{
-    m_htOperation = htOperation;
-}
-
-void
-MgtProbeResponseHeader::SetHtOperation(HtOperation&& htOperation)
-{
-    m_htOperation = std::move(htOperation);
-}
-
-const std::optional<HtOperation>&
-MgtProbeResponseHeader::GetHtOperation() const
-{
-    return m_htOperation;
-}
-
-void
-MgtProbeResponseHeader::SetVhtCapabilities(const VhtCapabilities& vhtCapabilities)
-{
-    m_vhtCapability = vhtCapabilities;
-}
-
-void
-MgtProbeResponseHeader::SetVhtCapabilities(VhtCapabilities&& vhtCapabilities)
-{
-    m_vhtCapability = std::move(vhtCapabilities);
-}
-
-const std::optional<VhtCapabilities>&
-MgtProbeResponseHeader::GetVhtCapabilities() const
-{
-    return m_vhtCapability;
-}
-
-void
-MgtProbeResponseHeader::SetVhtOperation(const VhtOperation& vhtOperation)
-{
-    m_vhtOperation = vhtOperation;
-}
-
-void
-MgtProbeResponseHeader::SetVhtOperation(VhtOperation&& vhtOperation)
-{
-    m_vhtOperation = std::move(vhtOperation);
-}
-
-const std::optional<VhtOperation>&
-MgtProbeResponseHeader::GetVhtOperation() const
-{
-    return m_vhtOperation;
-}
-
-void
-MgtProbeResponseHeader::SetHeCapabilities(const HeCapabilities& heCapabilities)
-{
-    m_heCapability = heCapabilities;
-}
-
-void
-MgtProbeResponseHeader::SetHeCapabilities(HeCapabilities&& heCapabilities)
-{
-    m_heCapability = std::move(heCapabilities);
-}
-
-const std::optional<HeCapabilities>&
-MgtProbeResponseHeader::GetHeCapabilities() const
-{
-    return m_heCapability;
-}
-
-void
-MgtProbeResponseHeader::SetHeOperation(const HeOperation& heOperation)
-{
-    m_heOperation = heOperation;
-}
-
-void
-MgtProbeResponseHeader::SetHeOperation(HeOperation&& heOperation)
-{
-    m_heOperation = std::move(heOperation);
-}
-
-const std::optional<HeOperation>&
-MgtProbeResponseHeader::GetHeOperation() const
-{
-    return m_heOperation;
-}
-
-void
-MgtProbeResponseHeader::SetEhtCapabilities(const EhtCapabilities& ehtCapabilities)
-{
-    m_ehtCapability = ehtCapabilities;
-}
-
-void
-MgtProbeResponseHeader::SetEhtCapabilities(EhtCapabilities&& ehtCapabilities)
-{
-    m_ehtCapability = std::move(ehtCapabilities);
-}
-
-const std::optional<EhtCapabilities>&
-MgtProbeResponseHeader::GetEhtCapabilities() const
-{
-    return m_ehtCapability;
-}
-
-void
-MgtProbeResponseHeader::SetEhtOperation(const EhtOperation& ehtOperation)
-{
-    m_ehtOperation = ehtOperation;
-}
-
-void
-MgtProbeResponseHeader::SetEhtOperation(EhtOperation&& ehtOperation)
-{
-    m_ehtOperation = std::move(ehtOperation);
-}
-
-const std::optional<EhtOperation>&
-MgtProbeResponseHeader::GetEhtOperation() const
-{
-    return m_ehtOperation;
-}
-
-void
-MgtProbeResponseHeader::SetSsid(const Ssid& ssid)
-{
-    m_ssid = ssid;
-}
-
-void
-MgtProbeResponseHeader::SetSsid(Ssid&& ssid)
-{
-    m_ssid = std::move(ssid);
-}
-
 void
 MgtProbeResponseHeader::SetBeaconIntervalUs(uint64_t us)
 {
     m_beaconInterval = us;
 }
 
-void
-MgtProbeResponseHeader::SetSupportedRates(const SupportedRates& rates)
-{
-    m_rates = rates;
-}
-
-void
-MgtProbeResponseHeader::SetSupportedRates(SupportedRates&& rates)
-{
-    m_rates = std::move(rates);
-}
-
-void
-MgtProbeResponseHeader::SetDsssParameterSet(const DsssParameterSet& dsssParameterSet)
-{
-    m_dsssParameterSet = dsssParameterSet;
-}
-
-void
-MgtProbeResponseHeader::SetDsssParameterSet(DsssParameterSet&& dsssParameterSet)
-{
-    m_dsssParameterSet = std::move(dsssParameterSet);
-}
-
-const std::optional<DsssParameterSet>&
-MgtProbeResponseHeader::GetDsssParameterSet() const
-{
-    return m_dsssParameterSet;
-}
-
-void
-MgtProbeResponseHeader::SetErpInformation(const ErpInformation& erpInformation)
-{
-    m_erpInformation = erpInformation;
-}
-
-void
-MgtProbeResponseHeader::SetErpInformation(ErpInformation&& erpInformation)
-{
-    m_erpInformation = std::move(erpInformation);
-}
-
-const std::optional<ErpInformation>&
-MgtProbeResponseHeader::GetErpInformation() const
-{
-    return m_erpInformation;
-}
-
-void
-MgtProbeResponseHeader::SetEdcaParameterSet(const EdcaParameterSet& edcaParameters)
-{
-    m_edcaParameterSet = edcaParameters;
-}
-
-void
-MgtProbeResponseHeader::SetEdcaParameterSet(EdcaParameterSet&& edcaParameters)
-{
-    m_edcaParameterSet = std::move(edcaParameters);
-}
-
-void
-MgtProbeResponseHeader::SetMuEdcaParameterSet(const MuEdcaParameterSet& muEdcaParameters)
-{
-    m_muEdcaParameterSet = muEdcaParameters;
-}
-
-void
-MgtProbeResponseHeader::SetMuEdcaParameterSet(MuEdcaParameterSet&& muEdcaParameters)
-{
-    m_muEdcaParameterSet = std::move(muEdcaParameters);
-}
-
-void
-MgtProbeResponseHeader::SetReducedNeighborReport(const ReducedNeighborReport& reducedNeighborReport)
-{
-    m_reducedNeighborReport = reducedNeighborReport;
-}
-
-void
-MgtProbeResponseHeader::SetReducedNeighborReport(ReducedNeighborReport&& reducedNeighborReport)
-{
-    m_reducedNeighborReport = std::move(reducedNeighborReport);
-}
-
-void
-MgtProbeResponseHeader::SetMultiLinkElement(const MultiLinkElement& multiLinkElement)
-{
-    m_multiLinkElement = multiLinkElement;
-}
-
-void
-MgtProbeResponseHeader::SetMultiLinkElement(MultiLinkElement&& multiLinkElement)
-{
-    m_multiLinkElement = std::move(multiLinkElement);
-}
-
-const std::optional<EdcaParameterSet>&
-MgtProbeResponseHeader::GetEdcaParameterSet() const
-{
-    return m_edcaParameterSet;
-}
-
-const std::optional<MuEdcaParameterSet>&
-MgtProbeResponseHeader::GetMuEdcaParameterSet() const
-{
-    return m_muEdcaParameterSet;
-}
-
-const std::optional<ReducedNeighborReport>&
-MgtProbeResponseHeader::GetReducedNeighborReport() const
-{
-    return m_reducedNeighborReport;
-}
-
-const std::optional<MultiLinkElement>&
-MgtProbeResponseHeader::GetMultiLinkElement() const
+const CapabilityInformation&
+MgtProbeResponseHeader::Capabilities() const
 {
-    return m_multiLinkElement;
+    return m_capability;
 }
 
-TypeId
-MgtProbeResponseHeader::GetTypeId()
+CapabilityInformation&
+MgtProbeResponseHeader::Capabilities()
 {
-    static TypeId tid = TypeId("ns3::MgtProbeResponseHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtProbeResponseHeader>();
-    return tid;
+    return m_capability;
 }
 
-TypeId
-MgtProbeResponseHeader::GetInstanceTypeId() const
+uint64_t
+MgtProbeResponseHeader::GetTimestamp() const
 {
-    return GetTypeId();
+    return m_timestamp;
 }
 
 uint32_t
-MgtProbeResponseHeader::GetSerializedSize() const
+MgtProbeResponseHeader::GetSerializedSizeImpl() const
 {
-    uint32_t size = 0;
-    size += 8; // timestamp
-    size += 2; // beacon interval
+    uint32_t size = 8 /* timestamp */ + 2 /* beacon interval */;
     size += m_capability.GetSerializedSize();
-    size += m_ssid.GetSerializedSize();
-    size += m_rates.GetSerializedSize();
-    if (m_dsssParameterSet.has_value())
-    {
-        size += m_dsssParameterSet->GetSerializedSize();
-    }
-    if (m_erpInformation.has_value())
-    {
-        size += m_erpInformation->GetSerializedSize();
-    }
-    if (m_rates.GetNRates() > 8)
-    {
-        size += m_rates.extended->GetSerializedSize();
-    }
-    if (m_edcaParameterSet.has_value())
-    {
-        size += m_edcaParameterSet->GetSerializedSize();
-    }
-    if (m_extendedCapability.has_value())
-    {
-        size += m_extendedCapability->GetSerializedSize();
-    }
-    if (m_htCapability.has_value())
-    {
-        size += m_htCapability->GetSerializedSize();
-    }
-    if (m_htOperation.has_value())
-    {
-        size += m_htOperation->GetSerializedSize();
-    }
-    if (m_vhtCapability.has_value())
-    {
-        size += m_vhtCapability->GetSerializedSize();
-    }
-    if (m_vhtOperation.has_value())
-    {
-        size += m_vhtOperation->GetSerializedSize();
-    }
-    if (m_reducedNeighborReport.has_value())
-    {
-        size += m_reducedNeighborReport->GetSerializedSize();
-    }
-    if (m_heCapability.has_value())
-    {
-        size += m_heCapability->GetSerializedSize();
-    }
-    if (m_heOperation.has_value())
-    {
-        size += m_heOperation->GetSerializedSize();
-    }
-    if (m_muEdcaParameterSet.has_value())
-    {
-        size += m_muEdcaParameterSet->GetSerializedSize();
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        size += m_multiLinkElement->GetSerializedSize();
-    }
-    if (m_ehtCapability.has_value())
-    {
-        size += m_ehtCapability->GetSerializedSize();
-    }
-    if (m_ehtOperation.has_value())
-    {
-        size += m_ehtOperation->GetSerializedSize();
-    }
+    size += WifiMgtHeader<MgtProbeResponseHeader, ProbeResponseElems>::GetSerializedSizeImpl();
     return size;
 }
 
 void
-MgtProbeResponseHeader::Print(std::ostream& os) const
-{
-    os << "ssid=" << m_ssid << ", "
-       << "rates=" << m_rates << ", ";
-    if (m_erpInformation.has_value())
-    {
-        os << "ERP information=" << *m_erpInformation << ", ";
-    }
-    if (m_extendedCapability.has_value())
-    {
-        os << "Extended Capabilities=" << *m_extendedCapability << " , ";
-    }
-    if (m_htCapability.has_value())
-    {
-        os << "HT Capabilities=" << *m_htCapability << " , ";
-    }
-    if (m_htOperation.has_value())
-    {
-        os << "HT Operation=" << *m_htOperation << " , ";
-    }
-    if (m_vhtCapability.has_value())
-    {
-        os << "VHT Capabilities=" << *m_vhtCapability << " , ";
-    }
-    if (m_vhtOperation.has_value())
-    {
-        os << "VHT Operation=" << *m_vhtOperation << " , ";
-    }
-    if (m_heCapability.has_value())
-    {
-        os << "HE Capabilities=" << *m_heCapability << " , ";
-    }
-    if (m_heOperation.has_value())
-    {
-        os << "HE Operation=" << *m_heOperation << " , ";
-    }
-    if (m_ehtCapability.has_value())
-    {
-        os << "EHT Capabilities=" << *m_ehtCapability;
-    }
-    if (m_ehtOperation.has_value())
-    {
-        os << "EHT Operation=" << *m_ehtOperation;
-    }
-}
-
-void
-MgtProbeResponseHeader::Serialize(Buffer::Iterator start) const
+MgtProbeResponseHeader::SerializeImpl(Buffer::Iterator start) const
 {
     Buffer::Iterator i = start;
     i.WriteHtolsbU64(Simulator::Now().GetMicroSeconds());
     i.WriteHtolsbU16(static_cast<uint16_t>(m_beaconInterval / 1024));
     i = m_capability.Serialize(i);
-    i = m_ssid.Serialize(i);
-    i = m_rates.Serialize(i);
-    if (m_dsssParameterSet.has_value())
-    {
-        i = m_dsssParameterSet->Serialize(i);
-    }
-    if (m_erpInformation.has_value())
-    {
-        i = m_erpInformation->Serialize(i);
-    }
-    if (m_rates.GetNRates() > 8)
-    {
-        i = m_rates.extended->Serialize(i);
-    }
-    if (m_edcaParameterSet.has_value())
-    {
-        i = m_edcaParameterSet->Serialize(i);
-    }
-    if (m_extendedCapability.has_value())
-    {
-        i = m_extendedCapability->Serialize(i);
-    }
-    if (m_htCapability.has_value())
-    {
-        i = m_htCapability->Serialize(i);
-    }
-    if (m_htOperation.has_value())
-    {
-        i = m_htOperation->Serialize(i);
-    }
-    if (m_vhtCapability.has_value())
-    {
-        i = m_vhtCapability->Serialize(i);
-    }
-    if (m_vhtOperation.has_value())
-    {
-        i = m_vhtOperation->Serialize(i);
-    }
-    if (m_reducedNeighborReport.has_value())
-    {
-        i = m_reducedNeighborReport->Serialize(i);
-    }
-    if (m_heCapability.has_value())
-    {
-        i = m_heCapability->Serialize(i);
-    }
-    if (m_heOperation.has_value())
-    {
-        i = m_heOperation->Serialize(i);
-    }
-    if (m_muEdcaParameterSet.has_value())
-    {
-        i = m_muEdcaParameterSet->Serialize(i);
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        i = m_multiLinkElement->Serialize(i);
-    }
-    if (m_ehtCapability.has_value())
-    {
-        i = m_ehtCapability->Serialize(i);
-    }
-    if (m_ehtOperation.has_value())
-    {
-        i = m_ehtOperation->Serialize(i);
-    }
+    WifiMgtHeader<MgtProbeResponseHeader, ProbeResponseElems>::SerializeImpl(i);
 }
 
 uint32_t
-MgtProbeResponseHeader::Deserialize(Buffer::Iterator start)
+MgtProbeResponseHeader::DeserializeImpl(Buffer::Iterator start)
 {
-    Buffer::Iterator tmp;
     Buffer::Iterator i = start;
     m_timestamp = i.ReadLsbtohU64();
     m_beaconInterval = i.ReadLsbtohU16();
     m_beaconInterval *= 1024;
     i = m_capability.Deserialize(i);
-    i = m_ssid.Deserialize(i);
-    i = m_rates.Deserialize(i);
-    i = WifiInformationElement::DeserializeIfPresent(m_dsssParameterSet, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_erpInformation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_rates.extended, i, &m_rates);
-    i = WifiInformationElement::DeserializeIfPresent(m_edcaParameterSet, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_extendedCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_htCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_htOperation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_vhtCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_vhtOperation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_reducedNeighborReport, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_heCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_heOperation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_muEdcaParameterSet, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement, i, WIFI_MAC_MGT_BEACON);
-    const bool is2_4Ghz = m_rates.IsSupportedRate(
-        1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band
-                                 // Capabilities IE once the later is implemented
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, i, is2_4Ghz, m_heCapability);
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtOperation, i);
-
-    return i.GetDistanceFrom(start);
+    auto distance = i.GetDistanceFrom(start);
+    return distance + WifiMgtHeader<MgtProbeResponseHeader, ProbeResponseElems>::DeserializeImpl(i);
 }
 
 /***********************************************************
@@ -916,37 +156,26 @@
 
 NS_OBJECT_ENSURE_REGISTERED(MgtAssocRequestHeader);
 
-MgtAssocRequestHeader::MgtAssocRequestHeader()
-    : m_listenInterval(0)
-{
-}
-
-MgtAssocRequestHeader::~MgtAssocRequestHeader()
-{
-}
-
-void
-MgtAssocRequestHeader::SetSsid(const Ssid& ssid)
-{
-    m_ssid = ssid;
-}
-
-void
-MgtAssocRequestHeader::SetSsid(Ssid&& ssid)
+TypeId
+MgtAssocRequestHeader::GetTypeId()
 {
-    m_ssid = std::move(ssid);
+    static TypeId tid = TypeId("ns3::MgtAssocRequestHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtAssocRequestHeader>();
+    return tid;
 }
 
-void
-MgtAssocRequestHeader::SetSupportedRates(const SupportedRates& rates)
+TypeId
+MgtAssocRequestHeader::GetInstanceTypeId() const
 {
-    m_rates = rates;
+    return GetTypeId();
 }
 
-void
-MgtAssocRequestHeader::SetSupportedRates(SupportedRates&& rates)
+uint16_t
+MgtAssocRequestHeader::GetListenInterval() const
 {
-    m_rates = std::move(rates);
+    return m_listenInterval;
 }
 
 void
@@ -955,292 +184,102 @@
     m_listenInterval = interval;
 }
 
-void
-MgtAssocRequestHeader::SetCapabilities(const CapabilityInformation& capabilities)
-{
-    m_capability = capabilities;
-}
-
-void
-MgtAssocRequestHeader::SetCapabilities(CapabilityInformation&& capabilities)
-{
-    m_capability = std::move(capabilities);
-}
-
 const CapabilityInformation&
-MgtAssocRequestHeader::GetCapabilities() const
+MgtAssocRequestHeader::Capabilities() const
 {
     return m_capability;
 }
 
-void
-MgtAssocRequestHeader::SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities)
-{
-    m_extendedCapability = extendedCapabilities;
-}
-
-void
-MgtAssocRequestHeader::SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities)
+CapabilityInformation&
+MgtAssocRequestHeader::Capabilities()
 {
-    m_extendedCapability = std::move(extendedCapabilities);
-}
-
-const std::optional<ExtendedCapabilities>&
-MgtAssocRequestHeader::GetExtendedCapabilities() const
-{
-    return m_extendedCapability;
-}
-
-void
-MgtAssocRequestHeader::SetHtCapabilities(const HtCapabilities& htCapabilities)
-{
-    m_htCapability = htCapabilities;
-}
-
-void
-MgtAssocRequestHeader::SetHtCapabilities(HtCapabilities&& htCapabilities)
-{
-    m_htCapability = std::move(htCapabilities);
-}
-
-const std::optional<HtCapabilities>&
-MgtAssocRequestHeader::GetHtCapabilities() const
-{
-    return m_htCapability;
-}
-
-void
-MgtAssocRequestHeader::SetVhtCapabilities(const VhtCapabilities& vhtCapabilities)
-{
-    m_vhtCapability = vhtCapabilities;
-}
-
-void
-MgtAssocRequestHeader::SetVhtCapabilities(VhtCapabilities&& vhtCapabilities)
-{
-    m_vhtCapability = std::move(vhtCapabilities);
-}
-
-const std::optional<VhtCapabilities>&
-MgtAssocRequestHeader::GetVhtCapabilities() const
-{
-    return m_vhtCapability;
-}
-
-void
-MgtAssocRequestHeader::SetHeCapabilities(const HeCapabilities& heCapabilities)
-{
-    m_heCapability = heCapabilities;
-}
-
-void
-MgtAssocRequestHeader::SetHeCapabilities(HeCapabilities&& heCapabilities)
-{
-    m_heCapability = std::move(heCapabilities);
-}
-
-const std::optional<HeCapabilities>&
-MgtAssocRequestHeader::GetHeCapabilities() const
-{
-    return m_heCapability;
-}
-
-void
-MgtAssocRequestHeader::SetEhtCapabilities(const EhtCapabilities& ehtCapabilities)
-{
-    m_ehtCapability = ehtCapabilities;
-}
-
-void
-MgtAssocRequestHeader::SetEhtCapabilities(EhtCapabilities&& ehtCapabilities)
-{
-    m_ehtCapability = std::move(ehtCapabilities);
-}
-
-const std::optional<EhtCapabilities>&
-MgtAssocRequestHeader::GetEhtCapabilities() const
-{
-    return m_ehtCapability;
-}
-
-void
-MgtAssocRequestHeader::SetMultiLinkElement(const MultiLinkElement& multiLinkElement)
-{
-    m_multiLinkElement = multiLinkElement;
-}
-
-void
-MgtAssocRequestHeader::SetMultiLinkElement(MultiLinkElement&& multiLinkElement)
-{
-    m_multiLinkElement = std::move(multiLinkElement);
-}
-
-const std::optional<MultiLinkElement>&
-MgtAssocRequestHeader::GetMultiLinkElement() const
-{
-    return m_multiLinkElement;
-}
-
-const Ssid&
-MgtAssocRequestHeader::GetSsid() const
-{
-    return m_ssid;
-}
-
-const SupportedRates&
-MgtAssocRequestHeader::GetSupportedRates() const
-{
-    return m_rates;
-}
-
-uint16_t
-MgtAssocRequestHeader::GetListenInterval() const
-{
-    return m_listenInterval;
+    return m_capability;
 }
 
-TypeId
-MgtAssocRequestHeader::GetTypeId()
+uint32_t
+MgtAssocRequestHeader::GetSerializedSizeImpl() const
 {
-    static TypeId tid = TypeId("ns3::MgtAssocRequestHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtAssocRequestHeader>();
-    return tid;
-}
+    SetMleContainingFrame();
 
-TypeId
-MgtAssocRequestHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
+    uint32_t size = 0;
+    size += m_capability.GetSerializedSize();
+    size += 2; // listen interval
+    size += WifiMgtHeader<MgtAssocRequestHeader, AssocRequestElems>::GetSerializedSizeImpl();
+    return size;
 }
 
 uint32_t
-MgtAssocRequestHeader::GetSerializedSize() const
+MgtAssocRequestHeader::GetSerializedSizeInPerStaProfileImpl(
+    const MgtAssocRequestHeader& frame) const
 {
     uint32_t size = 0;
     size += m_capability.GetSerializedSize();
-    size += 2;
-    size += m_ssid.GetSerializedSize();
-    size += m_rates.GetSerializedSize();
-    if (m_rates.GetNRates() > 8)
-    {
-        size += m_rates.extended->GetSerializedSize();
-    }
-    if (m_extendedCapability.has_value())
-    {
-        size += m_extendedCapability->GetSerializedSize();
-    }
-    if (m_htCapability.has_value())
-    {
-        size += m_htCapability->GetSerializedSize();
-    }
-    if (m_vhtCapability.has_value())
-    {
-        size += m_vhtCapability->GetSerializedSize();
-    }
-    if (m_heCapability.has_value())
-    {
-        size += m_heCapability->GetSerializedSize();
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        size += m_multiLinkElement->GetSerializedSize();
-    }
-    if (m_ehtCapability.has_value())
-    {
-        size += m_ehtCapability->GetSerializedSize();
-    }
+    size +=
+        MgtHeaderInPerStaProfile<MgtAssocRequestHeader,
+                                 AssocRequestElems>::GetSerializedSizeInPerStaProfileImpl(frame);
     return size;
 }
 
 void
-MgtAssocRequestHeader::Print(std::ostream& os) const
+MgtAssocRequestHeader::SerializeImpl(Buffer::Iterator start) const
 {
-    os << "ssid=" << m_ssid << ", "
-       << "rates=" << m_rates << ", ";
-    if (m_extendedCapability.has_value())
-    {
-        os << "Extended Capabilities=" << *m_extendedCapability << " , ";
-    }
-    if (m_htCapability.has_value())
-    {
-        os << "HT Capabilities=" << *m_htCapability << " , ";
-    }
-    if (m_vhtCapability.has_value())
-    {
-        os << "VHT Capabilities=" << *m_vhtCapability << " , ";
-    }
-    if (m_heCapability.has_value())
-    {
-        os << "HE Capabilities=" << *m_heCapability << " , ";
-    }
-    if (m_ehtCapability.has_value())
-    {
-        os << "EHT Capabilities=" << *m_ehtCapability;
-    }
+    SetMleContainingFrame();
+
+    Buffer::Iterator i = start;
+    i = m_capability.Serialize(i);
+    i.WriteHtolsbU16(m_listenInterval);
+    WifiMgtHeader<MgtAssocRequestHeader, AssocRequestElems>::SerializeImpl(i);
 }
 
 void
-MgtAssocRequestHeader::Serialize(Buffer::Iterator start) const
+MgtAssocRequestHeader::SerializeInPerStaProfileImpl(Buffer::Iterator start,
+                                                    const MgtAssocRequestHeader& frame) const
 {
     Buffer::Iterator i = start;
     i = m_capability.Serialize(i);
-    i.WriteHtolsbU16(m_listenInterval);
-    i = m_ssid.Serialize(i);
-    i = m_rates.Serialize(i);
-    if (m_rates.GetNRates() > 8)
-    {
-        i = m_rates.extended->Serialize(i);
-    }
-    if (m_extendedCapability.has_value())
-    {
-        i = m_extendedCapability->Serialize(i);
-    }
-    if (m_htCapability.has_value())
-    {
-        i = m_htCapability->Serialize(i);
-    }
-    if (m_vhtCapability.has_value())
-    {
-        i = m_vhtCapability->Serialize(i);
-    }
-    if (m_heCapability.has_value())
-    {
-        i = m_heCapability->Serialize(i);
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        i = m_multiLinkElement->Serialize(i);
-    }
-    if (m_ehtCapability.has_value())
+    MgtHeaderInPerStaProfile<MgtAssocRequestHeader,
+                             AssocRequestElems>::SerializeInPerStaProfileImpl(i, frame);
+}
+
+uint32_t
+MgtAssocRequestHeader::DeserializeImpl(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    i = m_capability.Deserialize(i);
+    m_listenInterval = i.ReadLsbtohU16();
+    auto distance = i.GetDistanceFrom(start) +
+                    WifiMgtHeader<MgtAssocRequestHeader, AssocRequestElems>::DeserializeImpl(i);
+    if (auto& mle = Get<MultiLinkElement>())
     {
-        i = m_ehtCapability->Serialize(i);
+        for (std::size_t id = 0; id < mle->GetNPerStaProfileSubelements(); id++)
+        {
+            auto& perStaProfile = mle->GetPerStaProfile(id);
+            if (perStaProfile.HasAssocRequest())
+            {
+                auto& frameInPerStaProfile =
+                    std::get<std::reference_wrapper<MgtAssocRequestHeader>>(
+                        perStaProfile.GetAssocRequest())
+                        .get();
+                frameInPerStaProfile.CopyIesFromContainingFrame(*this);
+            }
+        }
     }
+    return distance;
 }
 
 uint32_t
-MgtAssocRequestHeader::Deserialize(Buffer::Iterator start)
+MgtAssocRequestHeader::DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
+                                                        uint16_t length,
+                                                        const MgtAssocRequestHeader& frame)
 {
-    Buffer::Iterator tmp;
     Buffer::Iterator i = start;
     i = m_capability.Deserialize(i);
-    m_listenInterval = i.ReadLsbtohU16();
-    i = m_ssid.Deserialize(i);
-    i = m_rates.Deserialize(i);
-    i = WifiInformationElement::DeserializeIfPresent(m_rates.extended, i, &m_rates);
-    i = WifiInformationElement::DeserializeIfPresent(m_extendedCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_htCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_vhtCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_heCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement,
-                                                     i,
-                                                     WIFI_MAC_MGT_ASSOCIATION_REQUEST);
-    const bool is2_4Ghz = m_rates.IsSupportedRate(
-        1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band
-                                 // Capabilities IE once the later is implemented
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, i, is2_4Ghz, m_heCapability);
-    return i.GetDistanceFrom(start);
+    m_listenInterval = frame.m_listenInterval;
+    auto distance = i.GetDistanceFrom(start);
+    NS_ASSERT_MSG(distance <= length,
+                  "Bytes read (" << distance << ") exceed expected number (" << length << ")");
+    return distance + MgtHeaderInPerStaProfile<MgtAssocRequestHeader, AssocRequestElems>::
+                          DeserializeFromPerStaProfileImpl(i, length - distance, frame);
 }
 
 /***********************************************************
@@ -1249,37 +288,26 @@
 
 NS_OBJECT_ENSURE_REGISTERED(MgtReassocRequestHeader);
 
-MgtReassocRequestHeader::MgtReassocRequestHeader()
-    : m_currentApAddr(Mac48Address())
-{
-}
-
-MgtReassocRequestHeader::~MgtReassocRequestHeader()
-{
-}
-
-void
-MgtReassocRequestHeader::SetSsid(const Ssid& ssid)
-{
-    m_ssid = ssid;
-}
-
-void
-MgtReassocRequestHeader::SetSsid(Ssid&& ssid)
+TypeId
+MgtReassocRequestHeader::GetTypeId()
 {
-    m_ssid = std::move(ssid);
+    static TypeId tid = TypeId("ns3::MgtReassocRequestHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtReassocRequestHeader>();
+    return tid;
 }
 
-void
-MgtReassocRequestHeader::SetSupportedRates(const SupportedRates& rates)
+TypeId
+MgtReassocRequestHeader::GetInstanceTypeId() const
 {
-    m_rates = rates;
+    return GetTypeId();
 }
 
-void
-MgtReassocRequestHeader::SetSupportedRates(SupportedRates&& rates)
+uint16_t
+MgtReassocRequestHeader::GetListenInterval() const
 {
-    m_rates = std::move(rates);
+    return m_listenInterval;
 }
 
 void
@@ -1288,148 +316,16 @@
     m_listenInterval = interval;
 }
 
-void
-MgtReassocRequestHeader::SetCapabilities(const CapabilityInformation& capabilities)
-{
-    m_capability = capabilities;
-}
-
-void
-MgtReassocRequestHeader::SetCapabilities(CapabilityInformation&& capabilities)
-{
-    m_capability = std::move(capabilities);
-}
-
 const CapabilityInformation&
-MgtReassocRequestHeader::GetCapabilities() const
+MgtReassocRequestHeader::Capabilities() const
 {
     return m_capability;
 }
 
-void
-MgtReassocRequestHeader::SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities)
-{
-    m_extendedCapability = extendedCapabilities;
-}
-
-void
-MgtReassocRequestHeader::SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities)
-{
-    m_extendedCapability = std::move(extendedCapabilities);
-}
-
-const std::optional<ExtendedCapabilities>&
-MgtReassocRequestHeader::GetExtendedCapabilities() const
-{
-    return m_extendedCapability;
-}
-
-void
-MgtReassocRequestHeader::SetHtCapabilities(const HtCapabilities& htCapabilities)
-{
-    m_htCapability = htCapabilities;
-}
-
-void
-MgtReassocRequestHeader::SetHtCapabilities(HtCapabilities&& htCapabilities)
+CapabilityInformation&
+MgtReassocRequestHeader::Capabilities()
 {
-    m_htCapability = std::move(htCapabilities);
-}
-
-const std::optional<HtCapabilities>&
-MgtReassocRequestHeader::GetHtCapabilities() const
-{
-    return m_htCapability;
-}
-
-void
-MgtReassocRequestHeader::SetVhtCapabilities(const VhtCapabilities& vhtCapabilities)
-{
-    m_vhtCapability = vhtCapabilities;
-}
-
-void
-MgtReassocRequestHeader::SetVhtCapabilities(VhtCapabilities&& vhtCapabilities)
-{
-    m_vhtCapability = std::move(vhtCapabilities);
-}
-
-const std::optional<VhtCapabilities>&
-MgtReassocRequestHeader::GetVhtCapabilities() const
-{
-    return m_vhtCapability;
-}
-
-void
-MgtReassocRequestHeader::SetHeCapabilities(const HeCapabilities& heCapabilities)
-{
-    m_heCapability = heCapabilities;
-}
-
-void
-MgtReassocRequestHeader::SetHeCapabilities(HeCapabilities&& heCapabilities)
-{
-    m_heCapability = std::move(heCapabilities);
-}
-
-const std::optional<HeCapabilities>&
-MgtReassocRequestHeader::GetHeCapabilities() const
-{
-    return m_heCapability;
-}
-
-void
-MgtReassocRequestHeader::SetEhtCapabilities(const EhtCapabilities& ehtCapabilities)
-{
-    m_ehtCapability = ehtCapabilities;
-}
-
-void
-MgtReassocRequestHeader::SetEhtCapabilities(EhtCapabilities&& ehtCapabilities)
-{
-    m_ehtCapability = std::move(ehtCapabilities);
-}
-
-const std::optional<EhtCapabilities>&
-MgtReassocRequestHeader::GetEhtCapabilities() const
-{
-    return m_ehtCapability;
-}
-
-void
-MgtReassocRequestHeader::SetMultiLinkElement(const MultiLinkElement& multiLinkElement)
-{
-    m_multiLinkElement = multiLinkElement;
-}
-
-void
-MgtReassocRequestHeader::SetMultiLinkElement(MultiLinkElement&& multiLinkElement)
-{
-    m_multiLinkElement = std::move(multiLinkElement);
-}
-
-const std::optional<MultiLinkElement>&
-MgtReassocRequestHeader::GetMultiLinkElement() const
-{
-    return m_multiLinkElement;
-}
-
-const Ssid&
-MgtReassocRequestHeader::GetSsid() const
-{
-    return m_ssid;
-}
-
-const SupportedRates&
-MgtReassocRequestHeader::GetSupportedRates() const
-{
-    return m_rates;
-}
-
-uint16_t
-MgtReassocRequestHeader::GetListenInterval() const
-{
-    return m_listenInterval;
+    return m_capability;
 }
 
 void
@@ -1438,152 +334,101 @@
     m_currentApAddr = currentApAddr;
 }
 
-TypeId
-MgtReassocRequestHeader::GetTypeId()
+uint32_t
+MgtReassocRequestHeader::GetSerializedSizeImpl() const
 {
-    static TypeId tid = TypeId("ns3::MgtReassocRequestHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtReassocRequestHeader>();
-    return tid;
-}
+    SetMleContainingFrame();
 
-TypeId
-MgtReassocRequestHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
+    uint32_t size = 0;
+    size += m_capability.GetSerializedSize();
+    size += 2; // listen interval
+    size += 6; // current AP address
+    size += WifiMgtHeader<MgtReassocRequestHeader, AssocRequestElems>::GetSerializedSizeImpl();
+    return size;
 }
 
 uint32_t
-MgtReassocRequestHeader::GetSerializedSize() const
+MgtReassocRequestHeader::GetSerializedSizeInPerStaProfileImpl(
+    const MgtReassocRequestHeader& frame) const
 {
     uint32_t size = 0;
     size += m_capability.GetSerializedSize();
-    size += 2; // listen interval
-    size += 6; // current AP address
-    size += m_ssid.GetSerializedSize();
-    size += m_rates.GetSerializedSize();
-    if (m_rates.GetNRates() > 8)
-    {
-        size += m_rates.extended->GetSerializedSize();
-    }
-    if (m_extendedCapability.has_value())
-    {
-        size += m_extendedCapability->GetSerializedSize();
-    }
-    if (m_htCapability.has_value())
-    {
-        size += m_htCapability->GetSerializedSize();
-    }
-    if (m_vhtCapability.has_value())
-    {
-        size += m_vhtCapability->GetSerializedSize();
-    }
-    if (m_heCapability.has_value())
-    {
-        size += m_heCapability->GetSerializedSize();
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        size += m_multiLinkElement->GetSerializedSize();
-    }
-    if (m_ehtCapability.has_value())
-    {
-        size += m_ehtCapability->GetSerializedSize();
-    }
+    size +=
+        MgtHeaderInPerStaProfile<MgtReassocRequestHeader,
+                                 AssocRequestElems>::GetSerializedSizeInPerStaProfileImpl(frame);
     return size;
 }
 
 void
-MgtReassocRequestHeader::Print(std::ostream& os) const
+MgtReassocRequestHeader::PrintImpl(std::ostream& os) const
 {
-    os << "current AP address=" << m_currentApAddr << ", "
-       << "ssid=" << m_ssid << ", "
-       << "rates=" << m_rates << ", ";
-    if (m_extendedCapability.has_value())
-    {
-        os << "Extended Capabilities=" << *m_extendedCapability << " , ";
-    }
-    if (m_htCapability.has_value())
-    {
-        os << "HT Capabilities=" << *m_htCapability << " , ";
-    }
-    if (m_vhtCapability.has_value())
-    {
-        os << "VHT Capabilities=" << *m_vhtCapability << " , ";
-    }
-    if (m_heCapability.has_value())
-    {
-        os << "HE Capabilities=" << *m_heCapability << " , ";
-    }
-    if (m_ehtCapability.has_value())
-    {
-        os << "EHT Capabilities=" << *m_ehtCapability;
-    }
+    os << "current AP address=" << m_currentApAddr << ", ";
+    WifiMgtHeader<MgtReassocRequestHeader, AssocRequestElems>::PrintImpl(os);
 }
 
 void
-MgtReassocRequestHeader::Serialize(Buffer::Iterator start) const
+MgtReassocRequestHeader::SerializeImpl(Buffer::Iterator start) const
 {
+    SetMleContainingFrame();
+
     Buffer::Iterator i = start;
     i = m_capability.Serialize(i);
     i.WriteHtolsbU16(m_listenInterval);
     WriteTo(i, m_currentApAddr);
-    i = m_ssid.Serialize(i);
-    i = m_rates.Serialize(i);
-    if (m_rates.GetNRates() > 8)
-    {
-        i = m_rates.extended->Serialize(i);
-    }
-    if (m_extendedCapability.has_value())
-    {
-        i = m_extendedCapability->Serialize(i);
-    }
-    if (m_htCapability.has_value())
-    {
-        i = m_htCapability->Serialize(i);
-    }
-    if (m_vhtCapability.has_value())
-    {
-        i = m_vhtCapability->Serialize(i);
-    }
-    if (m_heCapability.has_value())
-    {
-        i = m_heCapability->Serialize(i);
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        i = m_multiLinkElement->Serialize(i);
-    }
-    if (m_ehtCapability.has_value())
-    {
-        i = m_ehtCapability->Serialize(i);
-    }
+    WifiMgtHeader<MgtReassocRequestHeader, AssocRequestElems>::SerializeImpl(i);
+}
+
+void
+MgtReassocRequestHeader::SerializeInPerStaProfileImpl(Buffer::Iterator start,
+                                                      const MgtReassocRequestHeader& frame) const
+{
+    Buffer::Iterator i = start;
+    i = m_capability.Serialize(i);
+    MgtHeaderInPerStaProfile<MgtReassocRequestHeader,
+                             AssocRequestElems>::SerializeInPerStaProfileImpl(i, frame);
 }
 
 uint32_t
-MgtReassocRequestHeader::Deserialize(Buffer::Iterator start)
+MgtReassocRequestHeader::DeserializeImpl(Buffer::Iterator start)
 {
-    Buffer::Iterator tmp;
     Buffer::Iterator i = start;
     i = m_capability.Deserialize(i);
     m_listenInterval = i.ReadLsbtohU16();
     ReadFrom(i, m_currentApAddr);
-    i = m_ssid.Deserialize(i);
-    i = m_rates.Deserialize(i);
-    i = WifiInformationElement::DeserializeIfPresent(m_rates.extended, i, &m_rates);
-    i = WifiInformationElement::DeserializeIfPresent(m_extendedCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_htCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_vhtCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_heCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement,
-                                                     i,
-                                                     WIFI_MAC_MGT_REASSOCIATION_REQUEST);
-    const bool is2_4Ghz = m_rates.IsSupportedRate(
-        1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band
-                                 // Capabilities IE once the later is implemented
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, i, is2_4Ghz, m_heCapability);
-    return i.GetDistanceFrom(start);
+    auto distance = i.GetDistanceFrom(start) +
+                    WifiMgtHeader<MgtReassocRequestHeader, AssocRequestElems>::DeserializeImpl(i);
+    if (auto& mle = Get<MultiLinkElement>())
+    {
+        for (std::size_t id = 0; id < mle->GetNPerStaProfileSubelements(); id++)
+        {
+            auto& perStaProfile = mle->GetPerStaProfile(id);
+            if (perStaProfile.HasReassocRequest())
+            {
+                auto& frameInPerStaProfile =
+                    std::get<std::reference_wrapper<MgtReassocRequestHeader>>(
+                        perStaProfile.GetAssocRequest())
+                        .get();
+                frameInPerStaProfile.CopyIesFromContainingFrame(*this);
+            }
+        }
+    }
+    return distance;
+}
+
+uint32_t
+MgtReassocRequestHeader::DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
+                                                          uint16_t length,
+                                                          const MgtReassocRequestHeader& frame)
+{
+    Buffer::Iterator i = start;
+    i = m_capability.Deserialize(i);
+    m_listenInterval = frame.m_listenInterval;
+    m_currentApAddr = frame.m_currentApAddr;
+    auto distance = i.GetDistanceFrom(start);
+    NS_ASSERT_MSG(distance <= length,
+                  "Bytes read (" << distance << ") exceed expected number (" << length << ")");
+    return distance + MgtHeaderInPerStaProfile<MgtReassocRequestHeader, AssocRequestElems>::
+                          DeserializeFromPerStaProfileImpl(i, length - distance, frame);
 }
 
 /***********************************************************
@@ -1592,13 +437,20 @@
 
 NS_OBJECT_ENSURE_REGISTERED(MgtAssocResponseHeader);
 
-MgtAssocResponseHeader::MgtAssocResponseHeader()
-    : m_aid(0)
+TypeId
+MgtAssocResponseHeader::GetTypeId()
 {
+    static TypeId tid = TypeId("ns3::MgtAssocResponseHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtAssocResponseHeader>();
+    return tid;
 }
 
-MgtAssocResponseHeader::~MgtAssocResponseHeader()
+TypeId
+MgtAssocResponseHeader::GetInstanceTypeId() const
 {
+    return GetTypeId();
 }
 
 StatusCode
@@ -1607,226 +459,22 @@
     return m_code;
 }
 
-const SupportedRates&
-MgtAssocResponseHeader::GetSupportedRates() const
-{
-    return m_rates;
-}
-
 void
 MgtAssocResponseHeader::SetStatusCode(StatusCode code)
 {
     m_code = code;
 }
 
-void
-MgtAssocResponseHeader::SetSupportedRates(const SupportedRates& rates)
-{
-    m_rates = rates;
-}
-
-void
-MgtAssocResponseHeader::SetSupportedRates(SupportedRates&& rates)
-{
-    m_rates = std::move(rates);
-}
-
-void
-MgtAssocResponseHeader::SetCapabilities(const CapabilityInformation& capabilities)
-{
-    m_capability = capabilities;
-}
-
-void
-MgtAssocResponseHeader::SetCapabilities(CapabilityInformation&& capabilities)
-{
-    m_capability = std::move(capabilities);
-}
-
 const CapabilityInformation&
-MgtAssocResponseHeader::GetCapabilities() const
+MgtAssocResponseHeader::Capabilities() const
 {
     return m_capability;
 }
 
-void
-MgtAssocResponseHeader::SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities)
-{
-    m_extendedCapability = extendedCapabilities;
-}
-
-void
-MgtAssocResponseHeader::SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities)
-{
-    m_extendedCapability = std::move(extendedCapabilities);
-}
-
-const std::optional<ExtendedCapabilities>&
-MgtAssocResponseHeader::GetExtendedCapabilities() const
-{
-    return m_extendedCapability;
-}
-
-void
-MgtAssocResponseHeader::SetHtCapabilities(const HtCapabilities& htCapabilities)
-{
-    m_htCapability = htCapabilities;
-}
-
-void
-MgtAssocResponseHeader::SetHtCapabilities(HtCapabilities&& htCapabilities)
-{
-    m_htCapability = std::move(htCapabilities);
-}
-
-const std::optional<HtCapabilities>&
-MgtAssocResponseHeader::GetHtCapabilities() const
-{
-    return m_htCapability;
-}
-
-void
-MgtAssocResponseHeader::SetHtOperation(const HtOperation& htOperation)
-{
-    m_htOperation = htOperation;
-}
-
-void
-MgtAssocResponseHeader::SetHtOperation(HtOperation&& htOperation)
-{
-    m_htOperation = std::move(htOperation);
-}
-
-const std::optional<HtOperation>&
-MgtAssocResponseHeader::GetHtOperation() const
-{
-    return m_htOperation;
-}
-
-void
-MgtAssocResponseHeader::SetVhtCapabilities(const VhtCapabilities& vhtCapabilities)
+CapabilityInformation&
+MgtAssocResponseHeader::Capabilities()
 {
-    m_vhtCapability = vhtCapabilities;
-}
-
-void
-MgtAssocResponseHeader::SetVhtCapabilities(VhtCapabilities&& vhtCapabilities)
-{
-    m_vhtCapability = std::move(vhtCapabilities);
-}
-
-const std::optional<VhtCapabilities>&
-MgtAssocResponseHeader::GetVhtCapabilities() const
-{
-    return m_vhtCapability;
-}
-
-void
-MgtAssocResponseHeader::SetVhtOperation(const VhtOperation& vhtOperation)
-{
-    m_vhtOperation = vhtOperation;
-}
-
-void
-MgtAssocResponseHeader::SetVhtOperation(VhtOperation&& vhtOperation)
-{
-    m_vhtOperation = std::move(vhtOperation);
-}
-
-const std::optional<VhtOperation>&
-MgtAssocResponseHeader::GetVhtOperation() const
-{
-    return m_vhtOperation;
-}
-
-void
-MgtAssocResponseHeader::SetHeCapabilities(const HeCapabilities& heCapabilities)
-{
-    m_heCapability = heCapabilities;
-}
-
-void
-MgtAssocResponseHeader::SetHeCapabilities(HeCapabilities&& heCapabilities)
-{
-    m_heCapability = std::move(heCapabilities);
-}
-
-const std::optional<HeCapabilities>&
-MgtAssocResponseHeader::GetHeCapabilities() const
-{
-    return m_heCapability;
-}
-
-void
-MgtAssocResponseHeader::SetHeOperation(const HeOperation& heOperation)
-{
-    m_heOperation = heOperation;
-}
-
-void
-MgtAssocResponseHeader::SetHeOperation(HeOperation&& heOperation)
-{
-    m_heOperation = std::move(heOperation);
-}
-
-const std::optional<HeOperation>&
-MgtAssocResponseHeader::GetHeOperation() const
-{
-    return m_heOperation;
-}
-
-void
-MgtAssocResponseHeader::SetEhtCapabilities(const EhtCapabilities& ehtCapabilities)
-{
-    m_ehtCapability = ehtCapabilities;
-}
-
-void
-MgtAssocResponseHeader::SetEhtCapabilities(EhtCapabilities&& ehtCapabilities)
-{
-    m_ehtCapability = std::move(ehtCapabilities);
-}
-
-const std::optional<EhtCapabilities>&
-MgtAssocResponseHeader::GetEhtCapabilities() const
-{
-    return m_ehtCapability;
-}
-
-void
-MgtAssocResponseHeader::SetEhtOperation(const EhtOperation& ehtOperation)
-{
-    m_ehtOperation = ehtOperation;
-}
-
-void
-MgtAssocResponseHeader::SetEhtOperation(EhtOperation&& ehtOperation)
-{
-    m_ehtOperation = std::move(ehtOperation);
-}
-
-const std::optional<EhtOperation>&
-MgtAssocResponseHeader::GetEhtOperation() const
-{
-    return m_ehtOperation;
-}
-
-void
-MgtAssocResponseHeader::SetMultiLinkElement(const MultiLinkElement& multiLinkElement)
-{
-    m_multiLinkElement = multiLinkElement;
-}
-
-void
-MgtAssocResponseHeader::SetMultiLinkElement(MultiLinkElement&& multiLinkElement)
-{
-    m_multiLinkElement = std::move(multiLinkElement);
-}
-
-const std::optional<MultiLinkElement>&
-MgtAssocResponseHeader::GetMultiLinkElement() const
-{
-    return m_multiLinkElement;
+    return m_capability;
 }
 
 void
@@ -1841,286 +489,101 @@
     return m_aid;
 }
 
-void
-MgtAssocResponseHeader::SetErpInformation(const ErpInformation& erpInformation)
-{
-    m_erpInformation = erpInformation;
-}
-
-void
-MgtAssocResponseHeader::SetErpInformation(ErpInformation&& erpInformation)
-{
-    m_erpInformation = std::move(erpInformation);
-}
-
-const std::optional<ErpInformation>&
-MgtAssocResponseHeader::GetErpInformation() const
-{
-    return m_erpInformation;
-}
-
-void
-MgtAssocResponseHeader::SetEdcaParameterSet(const EdcaParameterSet& edcaparameters)
-{
-    m_edcaParameterSet = edcaparameters;
-}
-
-void
-MgtAssocResponseHeader::SetEdcaParameterSet(EdcaParameterSet&& edcaparameters)
-{
-    m_edcaParameterSet = std::move(edcaparameters);
-}
-
-void
-MgtAssocResponseHeader::SetMuEdcaParameterSet(const MuEdcaParameterSet& muEdcaParameters)
-{
-    m_muEdcaParameterSet = muEdcaParameters;
-}
-
-void
-MgtAssocResponseHeader::SetMuEdcaParameterSet(MuEdcaParameterSet&& muEdcaParameters)
-{
-    m_muEdcaParameterSet = std::move(muEdcaParameters);
-}
-
-const std::optional<EdcaParameterSet>&
-MgtAssocResponseHeader::GetEdcaParameterSet() const
-{
-    return m_edcaParameterSet;
-}
-
-const std::optional<MuEdcaParameterSet>&
-MgtAssocResponseHeader::GetMuEdcaParameterSet() const
-{
-    return m_muEdcaParameterSet;
-}
-
-TypeId
-MgtAssocResponseHeader::GetTypeId()
+uint32_t
+MgtAssocResponseHeader::GetSerializedSizeImpl() const
 {
-    static TypeId tid = TypeId("ns3::MgtAssocResponseHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<MgtAssocResponseHeader>();
-    return tid;
-}
+    SetMleContainingFrame();
 
-TypeId
-MgtAssocResponseHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
+    uint32_t size = 0;
+    size += m_capability.GetSerializedSize();
+    size += m_code.GetSerializedSize();
+    size += 2; // aid
+    size += WifiMgtHeader<MgtAssocResponseHeader, AssocResponseElems>::GetSerializedSizeImpl();
+    return size;
 }
 
 uint32_t
-MgtAssocResponseHeader::GetSerializedSize() const
+MgtAssocResponseHeader::GetSerializedSizeInPerStaProfileImpl(
+    const MgtAssocResponseHeader& frame) const
 {
     uint32_t size = 0;
     size += m_capability.GetSerializedSize();
     size += m_code.GetSerializedSize();
-    size += 2; // aid
-    size += m_rates.GetSerializedSize();
-    if (m_erpInformation.has_value())
-    {
-        size += m_erpInformation->GetSerializedSize();
-    }
-    if (m_rates.GetNRates() > 8)
-    {
-        size += m_rates.extended->GetSerializedSize();
-    }
-    if (m_edcaParameterSet.has_value())
-    {
-        size += m_edcaParameterSet->GetSerializedSize();
-    }
-    if (m_extendedCapability.has_value())
-    {
-        size += m_extendedCapability->GetSerializedSize();
-    }
-    if (m_htCapability.has_value())
-    {
-        size += m_htCapability->GetSerializedSize();
-    }
-    if (m_htOperation.has_value())
-    {
-        size += m_htOperation->GetSerializedSize();
-    }
-    if (m_vhtCapability.has_value())
-    {
-        size += m_vhtCapability->GetSerializedSize();
-    }
-    if (m_vhtOperation.has_value())
-    {
-        size += m_vhtOperation->GetSerializedSize();
-    }
-    if (m_heCapability.has_value())
-    {
-        size += m_heCapability->GetSerializedSize();
-    }
-    if (m_heOperation.has_value())
-    {
-        size += m_heOperation->GetSerializedSize();
-    }
-    if (m_muEdcaParameterSet.has_value())
-    {
-        size += m_muEdcaParameterSet->GetSerializedSize();
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        size += m_multiLinkElement->GetSerializedSize();
-    }
-    if (m_ehtCapability.has_value())
-    {
-        size += m_ehtCapability->GetSerializedSize();
-    }
-    if (m_ehtOperation.has_value())
-    {
-        size += m_ehtOperation->GetSerializedSize();
-    }
+    size +=
+        MgtHeaderInPerStaProfile<MgtAssocResponseHeader,
+                                 AssocResponseElems>::GetSerializedSizeInPerStaProfileImpl(frame);
     return size;
 }
 
 void
-MgtAssocResponseHeader::Print(std::ostream& os) const
+MgtAssocResponseHeader::PrintImpl(std::ostream& os) const
 {
     os << "status code=" << m_code << ", "
-       << "aid=" << m_aid << ", "
-       << "rates=" << m_rates << ", ";
-    if (m_erpInformation.has_value())
-    {
-        os << "ERP information=" << *m_erpInformation << ", ";
-    }
-    if (m_extendedCapability.has_value())
-    {
-        os << "Extended Capabilities=" << *m_extendedCapability << " , ";
-    }
-    if (m_htCapability.has_value())
-    {
-        os << "HT Capabilities=" << *m_htCapability << " , ";
-    }
-    if (m_htOperation.has_value())
-    {
-        os << "HT Operation=" << *m_htOperation << " , ";
-    }
-    if (m_vhtCapability.has_value())
-    {
-        os << "VHT Capabilities=" << *m_vhtCapability << " , ";
-    }
-    if (m_vhtOperation.has_value())
-    {
-        os << "VHT Operation=" << *m_vhtOperation << " , ";
-    }
-    if (m_heCapability.has_value())
-    {
-        os << "HE Capabilities=" << *m_heCapability << " , ";
-    }
-    if (m_heOperation.has_value())
-    {
-        os << "HE Operation=" << *m_heOperation << " , ";
-    }
-    if (m_ehtCapability.has_value())
-    {
-        os << "EHT Capabilities=" << *m_ehtCapability;
-    }
-    if (m_ehtOperation.has_value())
-    {
-        os << "EHT Operation=" << *m_ehtOperation;
-    }
+       << "aid=" << m_aid << ", ";
+    WifiMgtHeader<MgtAssocResponseHeader, AssocResponseElems>::PrintImpl(os);
 }
 
 void
-MgtAssocResponseHeader::Serialize(Buffer::Iterator start) const
+MgtAssocResponseHeader::SerializeImpl(Buffer::Iterator start) const
 {
+    SetMleContainingFrame();
+
     Buffer::Iterator i = start;
     i = m_capability.Serialize(i);
     i = m_code.Serialize(i);
     i.WriteHtolsbU16(m_aid);
-    i = m_rates.Serialize(i);
-    if (m_erpInformation.has_value())
-    {
-        i = m_erpInformation->Serialize(i);
-    }
-    if (m_rates.GetNRates() > 8)
-    {
-        i = m_rates.extended->Serialize(i);
-    }
-    if (m_edcaParameterSet.has_value())
-    {
-        i = m_edcaParameterSet->Serialize(i);
-    }
-    if (m_extendedCapability.has_value())
-    {
-        i = m_extendedCapability->Serialize(i);
-    }
-    if (m_htCapability.has_value())
-    {
-        i = m_htCapability->Serialize(i);
-    }
-    if (m_htOperation.has_value())
-    {
-        i = m_htOperation->Serialize(i);
-    }
-    if (m_vhtCapability.has_value())
-    {
-        i = m_vhtCapability->Serialize(i);
-    }
-    if (m_vhtOperation.has_value())
-    {
-        i = m_vhtOperation->Serialize(i);
-    }
-    if (m_heCapability.has_value())
-    {
-        i = m_heCapability->Serialize(i);
-    }
-    if (m_heOperation.has_value())
-    {
-        i = m_heOperation->Serialize(i);
-    }
-    if (m_muEdcaParameterSet.has_value())
-    {
-        i = m_muEdcaParameterSet->Serialize(i);
-    }
-    if (m_multiLinkElement.has_value())
-    {
-        i = m_multiLinkElement->Serialize(i);
-    }
-    if (m_ehtCapability.has_value())
-    {
-        i = m_ehtCapability->Serialize(i);
-    }
-    if (m_ehtOperation.has_value())
+    WifiMgtHeader<MgtAssocResponseHeader, AssocResponseElems>::SerializeImpl(i);
+}
+
+void
+MgtAssocResponseHeader::SerializeInPerStaProfileImpl(Buffer::Iterator start,
+                                                     const MgtAssocResponseHeader& frame) const
+{
+    Buffer::Iterator i = start;
+    i = m_capability.Serialize(i);
+    i = m_code.Serialize(i);
+    MgtHeaderInPerStaProfile<MgtAssocResponseHeader,
+                             AssocResponseElems>::SerializeInPerStaProfileImpl(i, frame);
+}
+
+uint32_t
+MgtAssocResponseHeader::DeserializeImpl(Buffer::Iterator start)
+{
+    Buffer::Iterator i = start;
+    i = m_capability.Deserialize(i);
+    i = m_code.Deserialize(i);
+    m_aid = i.ReadLsbtohU16();
+    auto distance = i.GetDistanceFrom(start) +
+                    WifiMgtHeader<MgtAssocResponseHeader, AssocResponseElems>::DeserializeImpl(i);
+    if (auto& mle = Get<MultiLinkElement>())
     {
-        i = m_ehtOperation->Serialize(i);
+        for (std::size_t id = 0; id < mle->GetNPerStaProfileSubelements(); id++)
+        {
+            auto& perStaProfile = mle->GetPerStaProfile(id);
+            if (perStaProfile.HasAssocResponse())
+            {
+                auto& frameInPerStaProfile = perStaProfile.GetAssocResponse();
+                frameInPerStaProfile.CopyIesFromContainingFrame(*this);
+            }
+        }
     }
+    return distance;
 }
 
 uint32_t
-MgtAssocResponseHeader::Deserialize(Buffer::Iterator start)
+MgtAssocResponseHeader::DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
+                                                         uint16_t length,
+                                                         const MgtAssocResponseHeader& frame)
 {
-    Buffer::Iterator tmp;
     Buffer::Iterator i = start;
     i = m_capability.Deserialize(i);
     i = m_code.Deserialize(i);
-    m_aid = i.ReadLsbtohU16();
-    i = m_rates.Deserialize(i);
-    i = WifiInformationElement::DeserializeIfPresent(m_erpInformation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_rates.extended, i, &m_rates);
-    i = WifiInformationElement::DeserializeIfPresent(m_edcaParameterSet, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_extendedCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_htCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_htOperation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_vhtCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_vhtOperation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_heCapability, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_heOperation, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_muEdcaParameterSet, i);
-    i = WifiInformationElement::DeserializeIfPresent(m_multiLinkElement,
-                                                     i,
-                                                     WIFI_MAC_MGT_ASSOCIATION_RESPONSE);
-    const bool is2_4Ghz = m_rates.IsSupportedRate(
-        1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band
-                                 // Capabilities IE once the later is implemented
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtCapability, i, is2_4Ghz, m_heCapability);
-    i = WifiInformationElement::DeserializeIfPresent(m_ehtOperation, i);
-    return i.GetDistanceFrom(start);
+    m_aid = frame.m_aid;
+    auto distance = i.GetDistanceFrom(start);
+    NS_ASSERT_MSG(distance <= length,
+                  "Bytes read (" << distance << ") exceed expected number (" << length << ")");
+    return distance + MgtHeaderInPerStaProfile<MgtAssocResponseHeader, AssocResponseElems>::
+                          DeserializeFromPerStaProfileImpl(i, length - distance, frame);
 }
 
 /**********************************************************
@@ -2181,6 +644,10 @@
         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;
     }
@@ -2212,6 +679,8 @@
         return FST;
     case UNPROTECTED_DMG:
         return UNPROTECTED_DMG;
+    case PROTECTED_EHT:
+        return PROTECTED_EHT;
     case VENDOR_SPECIFIC_ACTION:
         return VENDOR_SPECIFIC_ACTION;
     default:
@@ -2357,11 +826,11 @@
         case MDA_SETUP_REPLY:
             retval.meshAction = MDA_SETUP_REPLY;
             break;
-        case MDAOP_ADVERTISMENT_REQUEST:
-            retval.meshAction = MDAOP_ADVERTISMENT_REQUEST;
+        case MDAOP_ADVERTISEMENT_REQUEST:
+            retval.meshAction = MDAOP_ADVERTISEMENT_REQUEST;
             break;
-        case MDAOP_ADVERTISMENTS:
-            retval.meshAction = MDAOP_ADVERTISMENTS;
+        case MDAOP_ADVERTISEMENTS:
+            retval.meshAction = MDAOP_ADVERTISEMENTS;
             break;
         case MDAOP_SET_TEARDOWN:
             retval.meshAction = MDAOP_SET_TEARDOWN;
@@ -2522,6 +991,46 @@
         }
         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 */
@@ -2545,76 +1054,205 @@
     return GetTypeId();
 }
 
-std::string
-WifiActionHeader::CategoryValueToString(CategoryValue value) const
+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
 {
-    switch (value)
+#define CASE_ACTION_VALUE(x)                                                                       \
+    case x:                                                                                        \
+        os << #x << "]";                                                                           \
+        break;
+
+    switch (m_category)
     {
     case QOS:
-        return "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:
-        return "BlockAck";
+        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:
-        return "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:
-        return "RadioMeasurement";
+        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:
-        return "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:
-        return "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:
-        return "SelfProtected";
+        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:
-        return "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:
-        return "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:
-        return "UnprotectedDmg";
+        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:
-        return "VendorSpecificAction";
+        os << "VENDOR_SPECIFIC_ACTION";
+        break;
     default:
-        std::ostringstream convert;
-        convert << value;
-        return convert.str();
-    }
-}
-
-std::string
-WifiActionHeader::SelfProtectedActionValueToString(SelfProtectedActionValue value) const
-{
-    if (value == PEER_LINK_OPEN)
-    {
-        return "PeerLinkOpen";
-    }
-    else if (value == PEER_LINK_CONFIRM)
-    {
-        return "PeerLinkConfirm";
-    }
-    else if (value == PEER_LINK_CLOSE)
-    {
-        return "PeerLinkClose";
-    }
-    else if (value == GROUP_KEY_INFORM)
-    {
-        return "GroupKeyInform";
-    }
-    else if (value == GROUP_KEY_ACK)
-    {
-        return "GroupKeyAck";
-    }
-    else
-    {
-        std::ostringstream convert;
-        convert << value;
-        return convert.str();
+        NS_FATAL_ERROR("Unknown action value");
     }
-}
-
-void
-WifiActionHeader::Print(std::ostream& os) const
-{
-    os << "category=" << CategoryValueToString((CategoryValue)m_category)
-       << ", value=" << SelfProtectedActionValueToString((SelfProtectedActionValue)m_actionValue);
+#undef CASE_ACTION_VALUE
 }
 
 uint32_t
@@ -3089,4 +1727,162 @@
     m_tid = (params >> 12) & 0x0f;
 }
 
+/***************************************************
+ *     EMLSR Operating Mode Notification
+ ****************************************************/
+
+NS_OBJECT_ENSURE_REGISTERED(MgtEmlOperatingModeNotification);
+
+TypeId
+MgtEmlOperatingModeNotification::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::MgtEmlOperatingModeNotification")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtEmlOperatingModeNotification>();
+    return tid;
+}
+
+TypeId
+MgtEmlOperatingModeNotification::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+void
+MgtEmlOperatingModeNotification::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
+MgtEmlOperatingModeNotification::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
+MgtEmlOperatingModeNotification::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
+MgtEmlOperatingModeNotification::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
+MgtEmlOperatingModeNotification::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>
+MgtEmlOperatingModeNotification::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.38/src/wifi/model/mgt-headers.h ns-3.39/src/wifi/model/mgt-headers.h
--- ns-3.38/src/wifi/model/mgt-headers.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/mgt-headers.h	2023-07-05 14:23:48.113428400 -0700
@@ -29,6 +29,7 @@
 #include "ssid.h"
 #include "status-code.h"
 #include "supported-rates.h"
+#include "wifi-mgt-header.h"
 
 #include "ns3/dsss-parameter-set.h"
 #include "ns3/eht-capabilities.h"
@@ -41,41 +42,137 @@
 #include "ns3/mac48-address.h"
 #include "ns3/mu-edca-parameter-set.h"
 #include "ns3/multi-link-element.h"
+#include "ns3/tid-to-link-mapping-element.h"
 #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):
+ *
+ * An AP affiliated with an AP MLD shall not include a Timestamp field, a Beacon Interval field,
+ * an AID field, a BSS Max Idle Period element, a Neighbor Report element, a Reduced Neighbor
+ * Report element, a Multiple BSSID element, TIM element, Multiple BSSID-Index element, Multiple
+ * BSSID Configuration element, TID-to-Link Mapping element, Multi-Link Traffic Indication element
+ * or another Multi- Link element in the STA Profile field of the Basic Multi-Link element.
+ *
+ * A non-AP STA affiliated with a non-AP MLD shall not include a Listen Interval field, a Current
+ * AP Address field, an SSID element, BSS Max Idle Period element or another Multi-Link element in
+ * the STA Profile field of the Basic Multi-Link element.
+ */
+
+/** \copydoc CanBeInPerStaProfile */
+template <>
+struct CanBeInPerStaProfile<ReducedNeighborReport> : std::false_type
+{
+};
+
+/** \copydoc CanBeInPerStaProfile */
+template <>
+struct CanBeInPerStaProfile<TidToLinkMapping> : std::false_type
+{
+};
+
+/** \copydoc CanBeInPerStaProfile */
+template <>
+struct CanBeInPerStaProfile<MultiLinkElement> : std::false_type
+{
+};
+
+/** \copydoc CanBeInPerStaProfile */
+template <>
+struct CanBeInPerStaProfile<Ssid> : std::false_type
+{
+};
+
+/// List of Information Elements included in Probe Request frames
+using ProbeRequestElems = std::tuple<Ssid,
+                                     SupportedRates,
+                                     std::optional<ExtendedSupportedRatesIE>,
+                                     std::optional<HtCapabilities>,
+                                     std::optional<ExtendedCapabilities>,
+                                     std::optional<VhtCapabilities>,
+                                     std::optional<HeCapabilities>,
+                                     std::optional<EhtCapabilities>>;
+
+/// List of Information Elements included in Probe Response frames
+using ProbeResponseElems = std::tuple<Ssid,
+                                      SupportedRates,
+                                      std::optional<DsssParameterSet>,
+                                      std::optional<ErpInformation>,
+                                      std::optional<ExtendedSupportedRatesIE>,
+                                      std::optional<EdcaParameterSet>,
+                                      std::optional<HtCapabilities>,
+                                      std::optional<HtOperation>,
+                                      std::optional<ExtendedCapabilities>,
+                                      std::optional<VhtCapabilities>,
+                                      std::optional<VhtOperation>,
+                                      std::optional<ReducedNeighborReport>,
+                                      std::optional<HeCapabilities>,
+                                      std::optional<HeOperation>,
+                                      std::optional<MuEdcaParameterSet>,
+                                      std::optional<MultiLinkElement>,
+                                      std::optional<EhtCapabilities>,
+                                      std::optional<EhtOperation>,
+                                      std::optional<TidToLinkMapping>>;
+
+/// List of Information Elements included in Association Request frames
+using AssocRequestElems = std::tuple<Ssid,
+                                     SupportedRates,
+                                     std::optional<ExtendedSupportedRatesIE>,
+                                     std::optional<HtCapabilities>,
+                                     std::optional<ExtendedCapabilities>,
+                                     std::optional<VhtCapabilities>,
+                                     std::optional<HeCapabilities>,
+                                     std::optional<MultiLinkElement>,
+                                     std::optional<EhtCapabilities>,
+                                     std::optional<TidToLinkMapping>>;
+
+/// List of Information Elements included in Association Response frames
+using AssocResponseElems = std::tuple<SupportedRates,
+                                      std::optional<ExtendedSupportedRatesIE>,
+                                      std::optional<EdcaParameterSet>,
+                                      std::optional<HtCapabilities>,
+                                      std::optional<HtOperation>,
+                                      std::optional<ExtendedCapabilities>,
+                                      std::optional<VhtCapabilities>,
+                                      std::optional<VhtOperation>,
+                                      std::optional<HeCapabilities>,
+                                      std::optional<HeOperation>,
+                                      std::optional<MuEdcaParameterSet>,
+                                      std::optional<MultiLinkElement>,
+                                      std::optional<EhtCapabilities>,
+                                      std::optional<EhtOperation>,
+                                      std::optional<TidToLinkMapping>>;
+
 /**
  * \ingroup wifi
  * Implement the header for management frames of type association request.
  */
-class MgtAssocRequestHeader : public Header
+class MgtAssocRequestHeader
+    : public MgtHeaderInPerStaProfile<MgtAssocRequestHeader, AssocRequestElems>
 {
-  public:
-    MgtAssocRequestHeader();
-    ~MgtAssocRequestHeader() override;
+    friend class WifiMgtHeader<MgtAssocRequestHeader, AssocRequestElems>;
+    friend class MgtHeaderInPerStaProfile<MgtAssocRequestHeader, AssocRequestElems>;
 
-    /**
-     * Set the Service Set Identifier (SSID).
-     *
-     * \param ssid SSID
-     */
-    void SetSsid(const Ssid& ssid);
-
-    /** \copydoc SetSsid */
-    void SetSsid(Ssid&& ssid);
+  public:
+    ~MgtAssocRequestHeader() override = default;
 
     /**
-     * Set the supported rates.
-     *
-     * \param rates the supported rates
+     * Register this type.
+     * \return The TypeId.
      */
-    void SetSupportedRates(const SupportedRates& rates);
+    static TypeId GetTypeId();
 
-    /** \copydoc SetSupportedRates */
-    void SetSupportedRates(SupportedRates&& rates);
+    /** \copydoc Header::GetInstanceTypeId */
+    TypeId GetInstanceTypeId() const override;
 
     /**
      * Set the listen interval.
@@ -83,192 +180,83 @@
      * \param interval the listen interval
      */
     void SetListenInterval(uint16_t interval);
-
-    /**
-     * Set the Capability information.
-     *
-     * \param capabilities Capability information
-     */
-    void SetCapabilities(const CapabilityInformation& capabilities);
-
-    /** \copydoc SetCapabilities */
-    void SetCapabilities(CapabilityInformation&& capabilities);
-
     /**
-     * Set the Extended Capabilities.
+     * Return the listen interval.
      *
-     * \param extendedCapabilities the Extended Capabilities
+     * \return the listen interval
      */
-    void SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities);
-
-    /** \copydoc SetExtendedCapabilities */
-    void SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities);
-
+    uint16_t GetListenInterval() const;
     /**
-     * Set the HT capabilities.
-     *
-     * \param htCapabilities HT capabilities
+     * \return a reference to the Capability information
      */
-    void SetHtCapabilities(const HtCapabilities& htCapabilities);
-
-    /** \copydoc SetHtCapabilities */
-    void SetHtCapabilities(HtCapabilities&& htCapabilities);
-
+    CapabilityInformation& Capabilities();
     /**
-     * Set the VHT capabilities.
-     *
-     * \param vhtCapabilities VHT capabilities
+     * \return a const reference to the Capability information
      */
-    void SetVhtCapabilities(const VhtCapabilities& vhtCapabilities);
+    const CapabilityInformation& Capabilities() const;
 
-    /** \copydoc SetVhtCapabilities */
-    void SetVhtCapabilities(VhtCapabilities&& vhtCapabilities);
+  protected:
+    /** \copydoc Header::GetSerializedSize */
+    uint32_t GetSerializedSizeImpl() const;
+    /** \copydoc Header::Serialize */
+    void SerializeImpl(Buffer::Iterator start) const;
+    /** \copydoc Header::Deserialize */
+    uint32_t DeserializeImpl(Buffer::Iterator start);
 
     /**
-     * Set the HE capabilities.
-     *
-     * \param heCapabilities HE capabilities
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes that are needed to serialize this header into a Per-STA Profile
+     *         subelement of the Multi-Link Element
      */
-    void SetHeCapabilities(const HeCapabilities& heCapabilities);
-
-    /** \copydoc SetHeCapabilities */
-    void SetHeCapabilities(HeCapabilities&& heCapabilities);
+    uint32_t GetSerializedSizeInPerStaProfileImpl(const MgtAssocRequestHeader& frame) const;
 
     /**
-     * Set the EHT capabilities.
+     * Serialize this header into a Per-STA Profile subelement of a Multi-Link Element
      *
-     * \param ehtCapabilities EHT capabilities
+     * \param start an iterator which points to where the header should be written
+     * \param frame the frame containing the Multi-Link Element
      */
-    void SetEhtCapabilities(const EhtCapabilities& ehtCapabilities);
-
-    /** \copydoc SetEhtCapabilities */
-    void SetEhtCapabilities(EhtCapabilities&& ehtCapabilities);
+    void SerializeInPerStaProfileImpl(Buffer::Iterator start,
+                                      const MgtAssocRequestHeader& frame) const;
 
     /**
-     * Set the Multi-Link Element information element
-     *
-     * \param multiLinkElement the Multi-Link Element information element
-     */
-    void SetMultiLinkElement(const MultiLinkElement& multiLinkElement);
-
-    /** \copydoc SetMultiLinkElement */
-    void SetMultiLinkElement(MultiLinkElement&& multiLinkElement);
-
-    /**
-     * Return the Capability information.
-     *
-     * \return Capability information
-     */
-    const CapabilityInformation& GetCapabilities() const;
-    /**
-     * Return the extended capabilities, if present.
-     *
-     * \return the extended capabilities, if present
-     */
-    const std::optional<ExtendedCapabilities>& GetExtendedCapabilities() const;
-    /**
-     * Return the HT capabilities, if present.
-     *
-     * \return HT capabilities, if present
-     */
-    const std::optional<HtCapabilities>& GetHtCapabilities() const;
-    /**
-     * Return the VHT capabilities, if present.
-     *
-     * \return VHT capabilities, if present
-     */
-    const std::optional<VhtCapabilities>& GetVhtCapabilities() const;
-    /**
-     * Return the HE capabilities, if present.
-     *
-     * \return HE capabilities, if present
-     */
-    const std::optional<HeCapabilities>& GetHeCapabilities() const;
-    /**
-     * Return the EHT capabilities, if present.
-     *
-     * \return EHT capabilities, if present
-     */
-    const std::optional<EhtCapabilities>& GetEhtCapabilities() const;
-    /**
-     * Return the Service Set Identifier (SSID).
-     *
-     * \return SSID
-     */
-    const Ssid& GetSsid() const;
-    /**
-     * Return the supported rates.
-     *
-     * \return the supported rates
-     */
-    const SupportedRates& GetSupportedRates() const;
-    /**
-     * Return the listen interval.
-     *
-     * \return the listen interval
-     */
-    uint16_t GetListenInterval() const;
-    /**
-     * Return the Multi-Link Element information element, if present.
+     * Deserialize this header from a Per-STA Profile subelement of a Multi-Link Element.
      *
-     * \return the Multi-Link Element information element, if present
+     * \param start an iterator which points to where the header should be read from
+     * \param length the expected number of bytes to read
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes read
      */
-    const std::optional<MultiLinkElement>& GetMultiLinkElement() const;
-
-    /**
-     * 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;
+    uint32_t DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
+                                              uint16_t length,
+                                              const MgtAssocRequestHeader& frame);
 
   private:
-    Ssid m_ssid;                                              //!< Service Set ID (SSID)
-    SupportedRates m_rates;                                   //!< List of supported rates
-    CapabilityInformation m_capability;                       //!< Capability information
-    std::optional<ExtendedCapabilities> m_extendedCapability; //!< Extended capabilities
-    std::optional<HtCapabilities> m_htCapability;             //!< HT capabilities
-    std::optional<VhtCapabilities> m_vhtCapability;           //!< VHT capabilities
-    std::optional<HeCapabilities> m_heCapability;             //!< HE capabilities
-    uint16_t m_listenInterval;                                //!< listen interval
-    std::optional<EhtCapabilities> m_ehtCapability;           //!< EHT capabilities
-    std::optional<MultiLinkElement> m_multiLinkElement;       //!< Multi-Link Element
+    CapabilityInformation m_capability; //!< Capability information
+    uint16_t m_listenInterval{0};       //!< listen interval
 };
 
 /**
  * \ingroup wifi
  * Implement the header for management frames of type reassociation request.
  */
-class MgtReassocRequestHeader : public Header
+class MgtReassocRequestHeader
+    : public MgtHeaderInPerStaProfile<MgtReassocRequestHeader, AssocRequestElems>
 {
-  public:
-    MgtReassocRequestHeader();
-    ~MgtReassocRequestHeader() override;
-
-    /**
-     * Set the Service Set Identifier (SSID).
-     *
-     * \param ssid SSID
-     */
-    void SetSsid(const Ssid& ssid);
+    friend class WifiMgtHeader<MgtReassocRequestHeader, AssocRequestElems>;
+    friend class MgtHeaderInPerStaProfile<MgtReassocRequestHeader, AssocRequestElems>;
 
-    /** \copydoc SetSsid */
-    void SetSsid(Ssid&& ssid);
+  public:
+    ~MgtReassocRequestHeader() override = default;
 
     /**
-     * Set the supported rates.
-     *
-     * \param rates the supported rates
+     * Register this type.
+     * \return The TypeId.
      */
-    void SetSupportedRates(const SupportedRates& rates);
+    static TypeId GetTypeId();
 
-    /** \copydoc SetSupportedRates */
-    void SetSupportedRates(SupportedRates&& rates);
+    /** \copydoc Header::GetInstanceTypeId */
+    TypeId GetInstanceTypeId() const override;
 
     /**
      * Set the listen interval.
@@ -276,178 +264,92 @@
      * \param interval the listen interval
      */
     void SetListenInterval(uint16_t interval);
-
-    /**
-     * Set the Capability information.
-     *
-     * \param capabilities Capability information
-     */
-    void SetCapabilities(const CapabilityInformation& capabilities);
-
-    /** \copydoc SetCapabilities */
-    void SetCapabilities(CapabilityInformation&& capabilities);
-
     /**
-     * Set the Extended Capabilities.
+     * Return the listen interval.
      *
-     * \param extendedCapabilities the Extended Capabilities
+     * \return the listen interval
      */
-    void SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities);
-
-    /** \copydoc SetExtendedCapabilities */
-    void SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities);
-
+    uint16_t GetListenInterval() const;
     /**
-     * Set the HT capabilities.
-     *
-     * \param htCapabilities HT capabilities
+     * \return a reference to the Capability information
      */
-    void SetHtCapabilities(const HtCapabilities& htCapabilities);
-
-    /** \copydoc SetHtCapabilities */
-    void SetHtCapabilities(HtCapabilities&& htCapabilities);
-
+    CapabilityInformation& Capabilities();
     /**
-     * Set the VHT capabilities.
-     *
-     * \param vhtCapabilities VHT capabilities
+     * \return a const reference to the Capability information
      */
-    void SetVhtCapabilities(const VhtCapabilities& vhtCapabilities);
-
-    /** \copydoc SetVhtCapabilities */
-    void SetVhtCapabilities(VhtCapabilities&& vhtCapabilities);
-
+    const CapabilityInformation& Capabilities() const;
     /**
-     * Set the HE capabilities.
+     * Set the address of the current access point.
      *
-     * \param heCapabilities HE capabilities
+     * \param currentApAddr address of the current access point
      */
-    void SetHeCapabilities(const HeCapabilities& heCapabilities);
+    void SetCurrentApAddress(Mac48Address currentApAddr);
 
-    /** \copydoc SetHeCapabilities */
-    void SetHeCapabilities(HeCapabilities&& heCapabilities);
+  protected:
+    /** \copydoc Header::GetSerializedSize */
+    uint32_t GetSerializedSizeImpl() const;
+    /** \copydoc Header::Serialize */
+    void SerializeImpl(Buffer::Iterator start) const;
+    /** \copydoc Header::Deserialize */
+    uint32_t DeserializeImpl(Buffer::Iterator start);
+    /** \copydoc Header::Print */
+    void PrintImpl(std::ostream& os) const;
 
     /**
-     * Set the EHT capabilities.
-     *
-     * \param ehtCapabilities EHT capabilities
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes that are needed to serialize this header into a Per-STA Profile
+     *         subelement of the Multi-Link Element
      */
-    void SetEhtCapabilities(const EhtCapabilities& ehtCapabilities);
-
-    /** \copydoc SetEhtCapabilities */
-    void SetEhtCapabilities(EhtCapabilities&& ehtCapabilities);
+    uint32_t GetSerializedSizeInPerStaProfileImpl(const MgtReassocRequestHeader& frame) const;
 
     /**
-     * Set the Multi-Link Element information element
+     * Serialize this header into a Per-STA Profile subelement of a Multi-Link Element
      *
-     * \param multiLinkElement the Multi-Link Element information element
+     * \param start an iterator which points to where the header should be written
+     * \param frame the frame containing the Multi-Link Element
      */
-    void SetMultiLinkElement(const MultiLinkElement& multiLinkElement);
+    void SerializeInPerStaProfileImpl(Buffer::Iterator start,
+                                      const MgtReassocRequestHeader& frame) const;
 
-    /** \copydoc SetMultiLinkElement */
-    void SetMultiLinkElement(MultiLinkElement&& multiLinkElement);
-
-    /**
-     * Return the Capability information.
-     *
-     * \return Capability information
-     */
-    const CapabilityInformation& GetCapabilities() const;
-    /**
-     * Return the extended capabilities, if present.
-     *
-     * \return the extended capabilities, if present
-     */
-    const std::optional<ExtendedCapabilities>& GetExtendedCapabilities() const;
-    /**
-     * Return the HT capabilities, if present.
-     *
-     * \return HT capabilities, if present
-     */
-    const std::optional<HtCapabilities>& GetHtCapabilities() const;
-    /**
-     * Return the VHT capabilities, if present.
-     *
-     * \return VHT capabilities, if present
-     */
-    const std::optional<VhtCapabilities>& GetVhtCapabilities() const;
-    /**
-     * Return the HE capabilities, if present.
-     *
-     * \return HE capabilities, if present
-     */
-    const std::optional<HeCapabilities>& GetHeCapabilities() const;
-    /**
-     * Return the EHT capabilities, if present.
-     *
-     * \return EHT capabilities, if present
-     */
-    const std::optional<EhtCapabilities>& GetEhtCapabilities() const;
-    /**
-     * Return the Service Set Identifier (SSID).
-     *
-     * \return SSID
-     */
-    const Ssid& GetSsid() const;
-    /**
-     * Return the supported rates.
-     *
-     * \return the supported rates
-     */
-    const SupportedRates& GetSupportedRates() const;
     /**
-     * Return the Multi-Link Element information element, if present.
+     * Deserialize this header from a Per-STA Profile subelement of a Multi-Link Element.
      *
-     * \return the Multi-Link Element information element, if present
+     * \param start an iterator which points to where the header should be read from
+     * \param length the expected number of bytes to read
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes read
      */
-    const std::optional<MultiLinkElement>& GetMultiLinkElement() const;
-    /**
-     * Return the listen interval.
-     *
-     * \return the listen interval
-     */
-    uint16_t GetListenInterval() const;
-    /**
-     * Set the address of the current access point.
-     *
-     * \param currentApAddr address of the current access point
-     */
-    void SetCurrentApAddress(Mac48Address currentApAddr);
-
-    /**
-     * 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;
+    uint32_t DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
+                                              uint16_t length,
+                                              const MgtReassocRequestHeader& frame);
 
   private:
     Mac48Address m_currentApAddr;       //!< Address of the current access point
-    Ssid m_ssid;                        //!< Service Set ID (SSID)
-    SupportedRates m_rates;             //!< List of supported rates
     CapabilityInformation m_capability; //!< Capability information
-    std::optional<ExtendedCapabilities> m_extendedCapability; //!< Extended capabilities
-    std::optional<HtCapabilities> m_htCapability;             //!< HT capabilities
-    std::optional<VhtCapabilities> m_vhtCapability;           //!< VHT capabilities
-    std::optional<HeCapabilities> m_heCapability;             //!< HE capabilities
-    uint16_t m_listenInterval;                                //!< listen interval
-    std::optional<EhtCapabilities> m_ehtCapability;           //!< EHT capabilities
-    std::optional<MultiLinkElement> m_multiLinkElement;       //!< Multi-Link Element
+    uint16_t m_listenInterval{0};       //!< listen interval
 };
 
 /**
  * \ingroup wifi
  * Implement the header for management frames of type association and reassociation response.
  */
-class MgtAssocResponseHeader : public Header
+class MgtAssocResponseHeader
+    : public MgtHeaderInPerStaProfile<MgtAssocResponseHeader, AssocResponseElems>
 {
+    friend class WifiMgtHeader<MgtAssocResponseHeader, AssocResponseElems>;
+    friend class MgtHeaderInPerStaProfile<MgtAssocResponseHeader, AssocResponseElems>;
+
   public:
-    MgtAssocResponseHeader();
-    ~MgtAssocResponseHeader() override;
+    ~MgtAssocResponseHeader() override = default;
+
+    /**
+     * Register this type.
+     * \return The TypeId.
+     */
+    static TypeId GetTypeId();
+
+    /** \copydoc Header::GetInstanceTypeId */
+    TypeId GetInstanceTypeId() const override;
 
     /**
      * Return the status code.
@@ -456,77 +358,19 @@
      */
     StatusCode GetStatusCode();
     /**
-     * Return the supported rates.
-     *
-     * \return the supported rates
-     */
-    const SupportedRates& GetSupportedRates() const;
-    /**
-     * Return the Capability information.
-     *
-     * \return Capability information
-     */
-    const CapabilityInformation& GetCapabilities() const;
-    /**
-     * Return the extended capabilities, if present.
-     *
-     * \return the extended capabilities, if present
-     */
-    const std::optional<ExtendedCapabilities>& GetExtendedCapabilities() const;
-    /**
-     * Return the HT capabilities, if present.
-     *
-     * \return HT capabilities, if present
-     */
-    const std::optional<HtCapabilities>& GetHtCapabilities() const;
-    /**
-     * Return the HT operation, if present.
-     *
-     * \return HT operation, if present
-     */
-    const std::optional<HtOperation>& GetHtOperation() const;
-    /**
-     * Return the VHT capabilities, if present.
-     *
-     * \return VHT capabilities, if present
-     */
-    const std::optional<VhtCapabilities>& GetVhtCapabilities() const;
-    /**
-     * Return the VHT operation, if present.
-     *
-     * \return VHT operation, if present
-     */
-    const std::optional<VhtOperation>& GetVhtOperation() const;
-    /**
-     * Return the HE capabilities, if present.
-     *
-     * \return HE capabilities, if present
-     */
-    const std::optional<HeCapabilities>& GetHeCapabilities() const;
-    /**
-     * Return the HE operation, if present.
-     *
-     * \return HE operation, if present
-     */
-    const std::optional<HeOperation>& GetHeOperation() const;
-    /**
-     * Return the EHT capabilities, if present.
+     * Set the status code.
      *
-     * \return EHT capabilities, if present
+     * \param code the status code
      */
-    const std::optional<EhtCapabilities>& GetEhtCapabilities() const;
+    void SetStatusCode(StatusCode code);
     /**
-     * Return the EHT operation, if present.
-     *
-     * \return EHT operation, if present
+     * \return a reference to the Capability information
      */
-    const std::optional<EhtOperation>& GetEhtOperation() const;
+    CapabilityInformation& Capabilities();
     /**
-     * Return the Multi-Link Element information element, if present.
-     *
-     * \return the Multi-Link Element information element, if present
+     * \return a const reference to the Capability information
      */
-    const std::optional<MultiLinkElement>& GetMultiLinkElement() const;
+    const CapabilityInformation& Capabilities() const;
     /**
      * Return the association ID.
      *
@@ -534,383 +378,95 @@
      */
     uint16_t GetAssociationId() const;
     /**
-     * Return the ERP information, if present.
-     *
-     * \return the ERP information, if present
-     */
-    const std::optional<ErpInformation>& GetErpInformation() const;
-    /**
-     * Return the EDCA Parameter Set, if present.
-     *
-     * \return the EDCA Parameter Set, if present
-     */
-    const std::optional<EdcaParameterSet>& GetEdcaParameterSet() const;
-    /**
-     * Return the MU EDCA Parameter Set, if present.
-     *
-     * \return the MU EDCA Parameter Set, if present
-     */
-    const std::optional<MuEdcaParameterSet>& GetMuEdcaParameterSet() const;
-    /**
-     * Set the Capability information.
-     *
-     * \param capabilities Capability information
-     */
-    void SetCapabilities(const CapabilityInformation& capabilities);
-
-    /** \copydoc SetCapabilities */
-    void SetCapabilities(CapabilityInformation&& capabilities);
-
-    /**
-     * Set the extended capabilities.
-     *
-     * \param extendedCapabilities the extended capabilities
-     */
-    void SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities);
-
-    /** \copydoc SetExtendedCapabilities */
-    void SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities);
-
-    /**
-     * Set the VHT operation.
-     *
-     * \param vhtOperation VHT operation
-     */
-    void SetVhtOperation(const VhtOperation& vhtOperation);
-
-    /** \copydoc SetVhtOperation */
-    void SetVhtOperation(VhtOperation&& vhtOperation);
-
-    /**
-     * Set the VHT capabilities.
-     *
-     * \param vhtCapabilities VHT capabilities
-     */
-    void SetVhtCapabilities(const VhtCapabilities& vhtCapabilities);
-
-    /** \copydoc SetVhtCapabilities */
-    void SetVhtCapabilities(VhtCapabilities&& vhtCapabilities);
-
-    /**
-     * Set the HT capabilities.
-     *
-     * \param htCapabilities HT capabilities
-     */
-    void SetHtCapabilities(const HtCapabilities& htCapabilities);
-
-    /** \copydoc SetHtCapabilities */
-    void SetHtCapabilities(HtCapabilities&& htCapabilities);
-
-    /**
-     * Set the HT operation.
-     *
-     * \param htOperation HT operation
-     */
-    void SetHtOperation(const HtOperation& htOperation);
-
-    /** \copydoc SetHtOperation */
-    void SetHtOperation(HtOperation&& htOperation);
-
-    /**
-     * Set the supported rates.
-     *
-     * \param rates the supported rates
-     */
-    void SetSupportedRates(const SupportedRates& rates);
-
-    /** \copydoc SetSupportedRates */
-    void SetSupportedRates(SupportedRates&& rates);
-
-    /**
-     * Set the status code.
-     *
-     * \param code the status code
-     */
-    void SetStatusCode(StatusCode code);
-
-    /**
      * Set the association ID.
      *
      * \param aid the association ID
      */
     void SetAssociationId(uint16_t aid);
 
-    /**
-     * Set the ERP information.
-     *
-     * \param erpInformation the ERP information
-     */
-    void SetErpInformation(const ErpInformation& erpInformation);
-
-    /** \copydoc SetErpInformation */
-    void SetErpInformation(ErpInformation&& erpInformation);
+  protected:
+    /** \copydoc Header::GetSerializedSize */
+    uint32_t GetSerializedSizeImpl() const;
+    /** \copydoc Header::Serialize */
+    void SerializeImpl(Buffer::Iterator start) const;
+    /** \copydoc Header::Deserialize */
+    uint32_t DeserializeImpl(Buffer::Iterator start);
+    /** \copydoc Header::Print */
+    void PrintImpl(std::ostream& os) const;
 
     /**
-     * Set the EDCA Parameter Set.
-     *
-     * \param edcaParameterSet the EDCA Parameter Set
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes that are needed to serialize this header into a Per-STA Profile
+     *         subelement of the Multi-Link Element
      */
-    void SetEdcaParameterSet(const EdcaParameterSet& edcaParameterSet);
-
-    /** \copydoc SetEdcaParameterSet */
-    void SetEdcaParameterSet(EdcaParameterSet&& edcaParameterSet);
+    uint32_t GetSerializedSizeInPerStaProfileImpl(const MgtAssocResponseHeader& frame) const;
 
     /**
-     * Set the MU EDCA Parameter Set.
+     * Serialize this header into a Per-STA Profile subelement of a Multi-Link Element
      *
-     * \param muEdcaParameterSet the MU EDCA Parameter Set
+     * \param start an iterator which points to where the header should be written
+     * \param frame the frame containing the Multi-Link Element
      */
-    void SetMuEdcaParameterSet(const MuEdcaParameterSet& muEdcaParameterSet);
-
-    /** \copydoc SetMuEdcaParameterSet */
-    void SetMuEdcaParameterSet(MuEdcaParameterSet&& muEdcaParameterSet);
+    void SerializeInPerStaProfileImpl(Buffer::Iterator start,
+                                      const MgtAssocResponseHeader& frame) const;
 
     /**
-     * Set the HE capabilities.
+     * Deserialize this header from a Per-STA Profile subelement of a Multi-Link Element.
      *
-     * \param heCapabilities HE capabilities
+     * \param start an iterator which points to where the header should be read from
+     * \param length the expected number of bytes to read
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes read
      */
-    void SetHeCapabilities(const HeCapabilities& heCapabilities);
-
-    /** \copydoc SetHeCapabilities */
-    void SetHeCapabilities(HeCapabilities&& heCapabilities);
-
-    /**
-     * Set the HE operation.
-     *
-     * \param heOperation HE operation
-     */
-    void SetHeOperation(const HeOperation& heOperation);
-
-    /** \copydoc SetHeOperation */
-    void SetHeOperation(HeOperation&& heOperation);
-
-    /**
-     * Set the EHT capabilities.
-     *
-     * \param ehtCapabilities EHT capabilities
-     */
-    void SetEhtCapabilities(const EhtCapabilities& ehtCapabilities);
-
-    /** \copydoc SetEhtCapabilities */
-    void SetEhtCapabilities(EhtCapabilities&& ehtCapabilities);
-
-    /**
-     * Set the EHT operation.
-     *
-     * \param ehtOperation HE operation
-     */
-    void SetEhtOperation(const EhtOperation& ehtOperation);
-
-    /** \copydoc SetEhtOperation */
-    void SetEhtOperation(EhtOperation&& ehtOperation);
-
-    /**
-     * Set the Multi-Link Element information element
-     *
-     * \param multiLinkElement the Multi-Link Element information element
-     */
-    void SetMultiLinkElement(const MultiLinkElement& multiLinkElement);
-
-    /** \copydoc SetMultiLinkElement */
-    void SetMultiLinkElement(MultiLinkElement&& multiLinkElement);
-
-    /**
-     * 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;
+    uint32_t DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
+                                              uint16_t length,
+                                              const MgtAssocResponseHeader& frame);
 
   private:
-    SupportedRates m_rates;                                   //!< List of supported rates
-    CapabilityInformation m_capability;                       //!< Capability information
-    StatusCode m_code;                                        //!< Status code
-    uint16_t m_aid;                                           //!< AID
-    std::optional<ExtendedCapabilities> m_extendedCapability; //!< extended capabilities
-    std::optional<HtCapabilities> m_htCapability;             //!< HT capabilities
-    std::optional<HtOperation> m_htOperation;                 //!< HT operation
-    std::optional<VhtCapabilities> m_vhtCapability;           //!< VHT capabilities
-    std::optional<VhtOperation> m_vhtOperation;               //!< VHT operation
-    std::optional<ErpInformation> m_erpInformation;           //!< ERP information
-    std::optional<EdcaParameterSet> m_edcaParameterSet;       //!< EDCA Parameter Set
-    std::optional<HeCapabilities> m_heCapability;             //!< HE capabilities
-    std::optional<HeOperation> m_heOperation;                 //!< HE operation
-    std::optional<MuEdcaParameterSet> m_muEdcaParameterSet;   //!< MU EDCA Parameter Set
-    std::optional<EhtCapabilities> m_ehtCapability;           //!< EHT capabilities
-    std::optional<EhtOperation> m_ehtOperation;               //!< EHT Operation
-    std::optional<MultiLinkElement> m_multiLinkElement;       //!< Multi-Link Element
+    CapabilityInformation m_capability; //!< Capability information
+    StatusCode m_code;                  //!< Status code
+    uint16_t m_aid{0};                  //!< AID
 };
 
 /**
  * \ingroup wifi
  * Implement the header for management frames of type probe request.
  */
-class MgtProbeRequestHeader : public Header
+class MgtProbeRequestHeader : public WifiMgtHeader<MgtProbeRequestHeader, ProbeRequestElems>
 {
   public:
-    ~MgtProbeRequestHeader() override;
-
-    /**
-     * Set the Service Set Identifier (SSID).
-     *
-     * \param ssid SSID
-     */
-    void SetSsid(const Ssid& ssid);
-
-    /** \copydoc SetSsid */
-    void SetSsid(Ssid&& ssid);
-
-    /**
-     * Set the supported rates.
-     *
-     * \param rates the supported rates
-     */
-    void SetSupportedRates(const SupportedRates& rates);
-
-    /** \copydoc SetSupportedRates */
-    void SetSupportedRates(SupportedRates&& rates);
-
-    /**
-     * Set the extended capabilities.
-     *
-     * \param extendedCapabilities the extended capabilities
-     */
-    void SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities);
-
-    /** \copydoc SetExtendedCapabilities */
-    void SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities);
-
-    /**
-     * Set the HT capabilities.
-     *
-     * \param htCapabilities HT capabilities
-     */
-    void SetHtCapabilities(const HtCapabilities& htCapabilities);
-
-    /** \copydoc SetHtCapabilities */
-    void SetHtCapabilities(HtCapabilities&& htCapabilities);
-
-    /**
-     * Set the VHT capabilities.
-     *
-     * \param vhtCapabilities VHT capabilities
-     */
-    void SetVhtCapabilities(const VhtCapabilities& vhtCapabilities);
-
-    /** \copydoc SetVhtCapabilities */
-    void SetVhtCapabilities(VhtCapabilities&& vhtCapabilities);
-
-    /**
-     * Set the HE capabilities.
-     *
-     * \param heCapabilities HE capabilities
-     */
-    void SetHeCapabilities(const HeCapabilities& heCapabilities);
-
-    /** \copydoc SetHeCapabilities */
-    void SetHeCapabilities(HeCapabilities&& heCapabilities);
-
-    /**
-     * Set the EHT capabilities.
-     *
-     * \param ehtCapabilities EHT capabilities
-     */
-    void SetEhtCapabilities(const EhtCapabilities& ehtCapabilities);
-
-    /** \copydoc SetEhtCapabilities */
-    void SetEhtCapabilities(EhtCapabilities&& ehtCapabilities);
-
-    /**
-     * Return the Service Set Identifier (SSID).
-     *
-     * \return SSID
-     */
-    const Ssid& GetSsid() const;
-
-    /**
-     * Return the supported rates.
-     *
-     * \return the supported rates
-     */
-    const SupportedRates& GetSupportedRates() const;
-
-    /**
-     * Return the extended capabilities, if present.
-     *
-     * \return the extended capabilities, if present
-     */
-    const std::optional<ExtendedCapabilities>& GetExtendedCapabilities() const;
-
-    /**
-     * Return the HT capabilities, if present.
-     *
-     * \return HT capabilities, if present
-     */
-    const std::optional<HtCapabilities>& GetHtCapabilities() const;
-
-    /**
-     * Return the VHT capabilities, if present.
-     *
-     * \return VHT capabilities, if present
-     */
-    const std::optional<VhtCapabilities>& GetVhtCapabilities() const;
-
-    /**
-     * Return the HE capabilities, if present.
-     *
-     * \return HE capabilities, if present
-     */
-    const std::optional<HeCapabilities>& GetHeCapabilities() const;
-
-    /**
-     * Return the EHT capabilities, if present.
-     *
-     * \return EHT capabilities, if present
-     */
-    const std::optional<EhtCapabilities>& GetEhtCapabilities() const;
+    ~MgtProbeRequestHeader() override = 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;
 
-  private:
-    Ssid m_ssid;                                              //!< Service Set ID (SSID)
-    SupportedRates m_rates;                                   //!< List of supported rates
-    std::optional<ExtendedCapabilities> m_extendedCapability; //!< extended capabilities
-    std::optional<HtCapabilities> m_htCapability;             //!< HT capabilities
-    std::optional<VhtCapabilities> m_vhtCapability;           //!< VHT capabilities
-    std::optional<HeCapabilities> m_heCapability;             //!< HE capabilities
-    std::optional<EhtCapabilities> m_ehtCapability;           //!< EHT capabilities
+    /** \copydoc Header::GetInstanceTypeId */
+    TypeId GetInstanceTypeId() const override;
 };
 
 /**
  * \ingroup wifi
  * Implement the header for management frames of type probe response.
  */
-class MgtProbeResponseHeader : public Header
+class MgtProbeResponseHeader : public WifiMgtHeader<MgtProbeResponseHeader, ProbeResponseElems>
 {
+    friend class WifiMgtHeader<MgtProbeResponseHeader, ProbeResponseElems>;
+
   public:
-    MgtProbeResponseHeader();
-    ~MgtProbeResponseHeader() override;
+    ~MgtProbeResponseHeader() override = default;
 
     /**
-     * Return the Service Set Identifier (SSID).
-     *
-     * \return SSID
+     * Register this type.
+     * \return The TypeId.
      */
-    const Ssid& GetSsid() const;
+    static TypeId GetTypeId();
+
+    /** \copydoc Header::GetInstanceTypeId */
+    TypeId GetInstanceTypeId() const override;
+
     /**
      * Return the beacon interval in microseconds unit.
      *
@@ -918,295 +474,19 @@
      */
     uint64_t GetBeaconIntervalUs() const;
     /**
-     * Return the supported rates.
-     *
-     * \return the supported rates
-     */
-    const SupportedRates& GetSupportedRates() const;
-    /**
-     * Return the Capability information.
-     *
-     * \return Capability information
-     */
-    const CapabilityInformation& GetCapabilities() const;
-    /**
-     * Return the DSSS Parameter Set, if present.
-     *
-     * \return the DSSS Parameter Set, if present
-     */
-    const std::optional<DsssParameterSet>& GetDsssParameterSet() const;
-    /**
-     * Return the extended capabilities, if present.
-     *
-     * \return the extended capabilities, if present
-     */
-    const std::optional<ExtendedCapabilities>& GetExtendedCapabilities() const;
-    /**
-     * Return the HT capabilities, if present.
-     *
-     * \return HT capabilities, if present
-     */
-    const std::optional<HtCapabilities>& GetHtCapabilities() const;
-    /**
-     * Return the HT operation, if present.
-     *
-     * \return HT operation, if present
-     */
-    const std::optional<HtOperation>& GetHtOperation() const;
-    /**
-     * Return the VHT capabilities, if present.
-     *
-     * \return VHT capabilities, if present
-     */
-    const std::optional<VhtCapabilities>& GetVhtCapabilities() const;
-    /**
-     * Return the VHT operation, if present.
-     *
-     * \return VHT operation, if present
-     */
-    const std::optional<VhtOperation>& GetVhtOperation() const;
-    /**
-     * Return the HE capabilities, if present.
-     *
-     * \return HE capabilities, if present
-     */
-    const std::optional<HeCapabilities>& GetHeCapabilities() const;
-    /**
-     * Return the HE operation, if present.
-     *
-     * \return HE operation, if present
-     */
-    const std::optional<HeOperation>& GetHeOperation() const;
-    /**
-     * Return the EHT capabilities, if present.
-     *
-     * \return EHT capabilities, if present
-     */
-    const std::optional<EhtCapabilities>& GetEhtCapabilities() const;
-    /**
-     * Return the EHT operation, if present.
-     *
-     * \return EHT operation, if present
-     */
-    const std::optional<EhtOperation>& GetEhtOperation() const;
-    /**
-     * Return the ERP information, if present.
-     *
-     * \return the ERP information, if present
-     */
-    const std::optional<ErpInformation>& GetErpInformation() const;
-    /**
-     * Return the EDCA Parameter Set, if present.
-     *
-     * \return the EDCA Parameter Set, if present
-     */
-    const std::optional<EdcaParameterSet>& GetEdcaParameterSet() const;
-    /**
-     * Return the MU EDCA Parameter Set, if present.
-     *
-     * \return the MU EDCA Parameter Set, if present
-     */
-    const std::optional<MuEdcaParameterSet>& GetMuEdcaParameterSet() const;
-
-    /**
-     * Return the Reduced Neighbor Report information element, if present.
-     *
-     * \return the Reduced Neighbor Report information element, if present
-     */
-    const std::optional<ReducedNeighborReport>& GetReducedNeighborReport() const;
-    /**
-     * Return the Multi-Link Element information element, if present.
-     *
-     * \return the Multi-Link Element information element, if present
-     */
-    const std::optional<MultiLinkElement>& GetMultiLinkElement() const;
-    /**
-     * Set the Capability information.
-     *
-     * \param capabilities Capability information
-     */
-    void SetCapabilities(const CapabilityInformation& capabilities);
-
-    /** \copydoc SetCapabilities */
-    void SetCapabilities(CapabilityInformation&& capabilities);
-
-    /**
-     * Set the extended capabilities.
-     *
-     * \param extendedCapabilities the extended capabilities
-     */
-    void SetExtendedCapabilities(const ExtendedCapabilities& extendedCapabilities);
-
-    /** \copydoc SetExtendedCapabilities */
-    void SetExtendedCapabilities(ExtendedCapabilities&& extendedCapabilities);
-
-    /**
-     * Set the HT capabilities.
-     *
-     * \param htCapabilities HT capabilities
-     */
-    void SetHtCapabilities(const HtCapabilities& htCapabilities);
-
-    /** \copydoc SetHtCapabilities */
-    void SetHtCapabilities(HtCapabilities&& htCapabilities);
-
-    /**
-     * Set the HT operation.
-     *
-     * \param htOperation HT operation
-     */
-    void SetHtOperation(const HtOperation& htOperation);
-
-    /** \copydoc SetHtOperation */
-    void SetHtOperation(HtOperation&& htOperation);
-
-    /**
-     * Set the VHT capabilities.
-     *
-     * \param vhtCapabilities VHT capabilities
-     */
-    void SetVhtCapabilities(const VhtCapabilities& vhtCapabilities);
-
-    /** \copydoc SetVhtCapabilities */
-    void SetVhtCapabilities(VhtCapabilities&& vhtCapabilities);
-
-    /**
-     * Set the VHT operation.
-     *
-     * \param vhtOperation VHT operation
-     */
-    void SetVhtOperation(const VhtOperation& vhtOperation);
-
-    /** \copydoc SetVhtOperation */
-    void SetVhtOperation(VhtOperation&& vhtOperation);
-
-    /**
-     * Set the HE capabilities.
-     *
-     * \param heCapabilities HE capabilities
-     */
-    void SetHeCapabilities(const HeCapabilities& heCapabilities);
-
-    /** \copydoc SetHeCapabilities */
-    void SetHeCapabilities(HeCapabilities&& heCapabilities);
-
-    /**
-     * Set the HE operation.
-     *
-     * \param heOperation HE operation
-     */
-    void SetHeOperation(const HeOperation& heOperation);
-
-    /** \copydoc SetHeOperation */
-    void SetHeOperation(HeOperation&& heOperation);
-
-    /**
-     * Set the EHT capabilities.
-     *
-     * \param ehtCapabilities EHT capabilities
-     */
-    void SetEhtCapabilities(const EhtCapabilities& ehtCapabilities);
-
-    /** \copydoc SetEhtCapabilities */
-    void SetEhtCapabilities(EhtCapabilities&& ehtCapabilities);
-
-    /**
-     * Set the EHT operation.
-     *
-     * \param ehtOperation HE operation
-     */
-    void SetEhtOperation(const EhtOperation& ehtOperation);
-
-    /** \copydoc SetEhtOperation */
-    void SetEhtOperation(EhtOperation&& ehtOperation);
-
-    /**
-     * Set the Service Set Identifier (SSID).
-     *
-     * \param ssid SSID
-     */
-    void SetSsid(const Ssid& ssid);
-
-    /** \copydoc SetSsid */
-    void SetSsid(Ssid&& ssid);
-
-    /**
      * Set the beacon interval in microseconds unit.
      *
      * \param us beacon interval in microseconds unit
      */
     void SetBeaconIntervalUs(uint64_t us);
-
     /**
-     * Set the supported rates.
-     *
-     * \param rates the supported rates
+     * \return a reference to the Capability information
      */
-    void SetSupportedRates(const SupportedRates& rates);
-
-    /** \copydoc SetSupportedRates */
-    void SetSupportedRates(SupportedRates&& rates);
-
+    CapabilityInformation& Capabilities();
     /**
-     * Set the DSSS Parameter Set.
-     *
-     * \param dsssParameterSet the DSSS Parameter Set
+     * \return a const reference to the Capability information
      */
-    void SetDsssParameterSet(const DsssParameterSet& dsssParameterSet);
-
-    /** \copydoc SetDsssParameterSet */
-    void SetDsssParameterSet(DsssParameterSet&& dsssParameterSet);
-
-    /**
-     * Set the ERP information.
-     *
-     * \param erpInformation the ERP information
-     */
-    void SetErpInformation(const ErpInformation& erpInformation);
-
-    /** \copydoc SetErpInformation */
-    void SetErpInformation(ErpInformation&& erpInformation);
-
-    /**
-     * Set the EDCA Parameter Set.
-     *
-     * \param edcaParameterSet the EDCA Parameter Set
-     */
-    void SetEdcaParameterSet(const EdcaParameterSet& edcaParameterSet);
-
-    /** \copydoc SetEdcaParameterSet */
-    void SetEdcaParameterSet(EdcaParameterSet&& edcaParameterSet);
-
-    /**
-     * Set the MU EDCA Parameter Set.
-     *
-     * \param muEdcaParameterSet the MU EDCA Parameter Set
-     */
-    void SetMuEdcaParameterSet(const MuEdcaParameterSet& muEdcaParameterSet);
-
-    /** \copydoc SetMuEdcaParameterSet */
-    void SetMuEdcaParameterSet(MuEdcaParameterSet&& muEdcaParameterSet);
-
-    /**
-     * Set the Reduced Neighbor Report information element
-     *
-     * \param reducedNeighborReport the Reduced Neighbor Report information element
-     */
-    void SetReducedNeighborReport(const ReducedNeighborReport& reducedNeighborReport);
-
-    /** \copydoc SetReducedNeighborReport */
-    void SetReducedNeighborReport(ReducedNeighborReport&& reducedNeighborReport);
-
-    /**
-     * Set the Multi-Link Element information element
-     *
-     * \param multiLinkElement the Multi-Link Element information element
-     */
-    void SetMultiLinkElement(const MultiLinkElement& multiLinkElement);
-
-    /** \copydoc SetMultiLinkElement */
-    void SetMultiLinkElement(MultiLinkElement&& multiLinkElement);
-
+    const CapabilityInformation& Capabilities() const;
     /**
      * Return the time stamp.
      *
@@ -1214,39 +494,18 @@
      */
     uint64_t GetTimestamp() const;
 
-    /**
-     * 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;
+  protected:
+    /** \copydoc Header::GetSerializedSize */
+    uint32_t GetSerializedSizeImpl() const;
+    /** \copydoc Header::Serialize*/
+    void SerializeImpl(Buffer::Iterator start) const;
+    /** \copydoc Header::Deserialize */
+    uint32_t DeserializeImpl(Buffer::Iterator start);
 
   private:
-    uint64_t m_timestamp;                                     //!< Timestamp
-    Ssid m_ssid;                                              //!< Service set ID (SSID)
-    uint64_t m_beaconInterval;                                //!< Beacon interval
-    SupportedRates m_rates;                                   //!< List of supported rates
-    CapabilityInformation m_capability;                       //!< Capability information
-    std::optional<DsssParameterSet> m_dsssParameterSet;       //!< DSSS Parameter Set
-    std::optional<ExtendedCapabilities> m_extendedCapability; //!< extended capabilities
-    std::optional<HtCapabilities> m_htCapability;             //!< HT capabilities
-    std::optional<HtOperation> m_htOperation;                 //!< HT operation
-    std::optional<VhtCapabilities> m_vhtCapability;           //!< VHT capabilities
-    std::optional<VhtOperation> m_vhtOperation;               //!< VHT operation
-    std::optional<HeCapabilities> m_heCapability;             //!< HE capabilities
-    std::optional<HeOperation> m_heOperation;                 //!< HE operation
-    std::optional<ErpInformation> m_erpInformation;           //!< ERP information
-    std::optional<EdcaParameterSet> m_edcaParameterSet;       //!< EDCA Parameter Set
-    std::optional<MuEdcaParameterSet> m_muEdcaParameterSet;   //!< MU EDCA Parameter Set
-    std::optional<EhtCapabilities> m_ehtCapability;           //!< EHT capabilities
-    std::optional<EhtOperation> m_ehtOperation;               //!< EHT Operation
-    std::optional<ReducedNeighborReport>
-        m_reducedNeighborReport;                        //!< Reduced Neighbor Report information
-    std::optional<MultiLinkElement> m_multiLinkElement; //!< Multi-Link Element
+    uint64_t m_timestamp;               //!< Timestamp
+    uint64_t m_beaconInterval;          //!< Beacon interval
+    CapabilityInformation m_capability; //!< Capability information
 };
 
 /**
@@ -1256,7 +515,8 @@
 class MgtBeaconHeader : public MgtProbeResponseHeader
 {
   public:
-    /** Register this type. */
+    ~MgtBeaconHeader() override = default;
+
     /**
      * Register this type.
      * \return The TypeId.
@@ -1287,7 +547,7 @@
      */
 
     /// CategoryValue enumeration
-    enum CategoryValue // table 8-38 staring from IEEE 802.11, Part11, (Year 2012)
+    enum CategoryValue // table 9-51 of IEEE 802.11-2020
     {
         QOS = 1,
         BLOCK_ACK = 3,
@@ -1299,6 +559,7 @@
         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,
@@ -1354,9 +615,9 @@
             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_ADVERTISMENT_REQUEST =
+        MDAOP_ADVERTISEMENT_REQUEST =
             6, // Action Value:6 in Category 13: Mesh MCCA-Advertisement-Request (not used so far)
-        MDAOP_ADVERTISMENTS = 7,       // Action Value:7 in Category 13: Mesh (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)
@@ -1439,6 +700,24 @@
     };
 
     /**
+     * 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 {
@@ -1452,6 +731,7 @@
         DmgActionValue dmgAction;                           ///< dmg
         FstActionValue fstAction;                           ///< fst
         UnprotectedDmgActionValue unprotectedDmgAction;     ///< unprotected dmg
+        ProtectedEhtActionValue protectedEhtAction;         ///< protected eht
     } ActionValue;                                          ///< the action value
 
     /**
@@ -1476,6 +756,22 @@
     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.
      */
@@ -1487,18 +783,6 @@
     uint32_t Deserialize(Buffer::Iterator start) override;
 
   private:
-    /**
-     * Category value to string function
-     * \param value the category value
-     * \returns the category value string
-     */
-    std::string CategoryValueToString(CategoryValue value) const;
-    /**
-     * Self protected action value to string function
-     * \param value the protected action value
-     * \returns the self protected action value string
-     */
-    std::string SelfProtectedActionValueToString(SelfProtectedActionValue value) const;
     uint8_t m_category;    //!< Category of the action
     uint8_t m_actionValue; //!< Action value
 };
@@ -1821,6 +1105,65 @@
     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 MgtEmlOperatingModeNotification : public Header
+{
+  public:
+    MgtEmlOperatingModeNotification() = 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.38/src/wifi/model/non-ht/dsss-phy.cc ns-3.39/src/wifi/model/non-ht/dsss-phy.cc
--- ns-3.38/src/wifi/model/non-ht/dsss-phy.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/dsss-phy.cc	2023-07-05 14:23:48.113428400 -0700
@@ -200,8 +200,7 @@
     NS_LOG_FUNCTION(this << psdus << txVector << ppduDuration);
     return Create<DsssPpdu>(psdus.begin()->second,
                             txVector,
-                            m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                                txVector.GetChannelWidth()),
+                            m_wifiPhy->GetOperatingChannel(),
                             ppduDuration,
                             ObtainNextUid(txVector));
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/dsss-ppdu.cc ns-3.39/src/wifi/model/non-ht/dsss-ppdu.cc
--- ns-3.38/src/wifi/model/non-ht/dsss-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/dsss-ppdu.cc	2023-07-05 14:23:48.113428400 -0700
@@ -24,6 +24,7 @@
 #include "dsss-phy.h"
 
 #include "ns3/log.h"
+#include "ns3/wifi-phy-operating-channel.h"
 #include "ns3/wifi-phy.h"
 #include "ns3/wifi-psdu.h"
 
@@ -34,12 +35,12 @@
 
 DsssPpdu::DsssPpdu(Ptr<const WifiPsdu> psdu,
                    const WifiTxVector& txVector,
-                   uint16_t txCenterFreq,
+                   const WifiPhyOperatingChannel& channel,
                    Time ppduDuration,
                    uint64_t uid)
-    : WifiPpdu(psdu, txVector, txCenterFreq, uid)
+    : WifiPpdu(psdu, txVector, channel, uid)
 {
-    NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << ppduDuration << uid);
+    NS_LOG_FUNCTION(this << psdu << txVector << channel << ppduDuration << uid);
     SetPhyHeaders(txVector, ppduDuration);
 }
 
@@ -47,14 +48,7 @@
 DsssPpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
 {
     NS_LOG_FUNCTION(this << txVector);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    DsssSigHeader dsssSig;
-    SetDsssHeader(dsssSig, txVector, ppduDuration);
-    m_phyHeaders->AddHeader(dsssSig);
-#else
     SetDsssHeader(m_dsssSig, txVector, ppduDuration);
-#endif
 }
 
 void
@@ -73,19 +67,7 @@
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
     txVector.SetChannelWidth(22);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    DsssSigHeader dsssSig;
-    if (m_phyHeaders->PeekHeader(dsssSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing DSSS SIG PHY header in DSSS PPDU");
-    }
-
-    SetTxVectorFromDsssHeader(txVector, dsssSig);
-#else
     SetTxVectorFromDsssHeader(txVector, m_dsssSig);
-#endif
-
     return txVector;
 }
 
@@ -98,18 +80,9 @@
 Time
 DsssPpdu::GetTxDuration() const
 {
-    Time ppduDuration = Seconds(0);
     const WifiTxVector& txVector = GetTxVector();
-    uint16_t length = 0;
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    DsssSigHeader dsssSig;
-    m_phyHeaders->PeekHeader(dsssSig);
-    length = dsssSig.GetLength();
-#else
-    length = m_dsssSig.GetLength();
-#endif
-    ppduDuration = MicroSeconds(length) + WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
-    return ppduDuration;
+    const auto length = m_dsssSig.GetLength();
+    return (MicroSeconds(length) + WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector));
 }
 
 Ptr<WifiPpdu>
@@ -124,34 +97,6 @@
 {
 }
 
-TypeId
-DsssPpdu::DsssSigHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::DsssSigHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<DsssSigHeader>();
-    return tid;
-}
-
-TypeId
-DsssPpdu::DsssSigHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-DsssPpdu::DsssSigHeader::Print(std::ostream& os) const
-{
-    os << "SIGNAL=" << GetRate() << " LENGTH=" << m_length;
-}
-
-uint32_t
-DsssPpdu::DsssSigHeader::GetSerializedSize() const
-{
-    return 6;
-}
-
 void
 DsssPpdu::DsssSigHeader::SetRate(uint64_t rate)
 {
@@ -218,24 +163,4 @@
     return m_length;
 }
 
-void
-DsssPpdu::DsssSigHeader::Serialize(Buffer::Iterator start) const
-{
-    start.WriteU8(m_rate);
-    start.WriteU8(0); /* SERVICE */
-    start.WriteU16(m_length);
-    start.WriteU16(0); /* CRC */
-}
-
-uint32_t
-DsssPpdu::DsssSigHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    m_rate = i.ReadU8();
-    i.ReadU8(); /* SERVICE */
-    m_length = i.ReadU16();
-    i.ReadU16(); /* CRC */
-    return i.GetDistanceFrom(start);
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/dsss-ppdu.h ns-3.39/src/wifi/model/non-ht/dsss-ppdu.h
--- ns-3.38/src/wifi/model/non-ht/dsss-ppdu.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/dsss-ppdu.h	2023-07-05 14:23:48.113428400 -0700
@@ -22,7 +22,6 @@
 #ifndef DSSS_PPDU_H
 #define DSSS_PPDU_H
 
-#include "ns3/header.h"
 #include "ns3/wifi-ppdu.h"
 
 /**
@@ -49,24 +48,12 @@
      * DSSS SIG PHY header.
      * See section 16.2.2 in IEEE 802.11-2016.
      */
-    class DsssSigHeader : public Header
+    class DsssSigHeader
     {
       public:
         DsssSigHeader();
 
         /**
-         * \brief Get the type ID.
-         * \return the object 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;
-
-        /**
          * Fill the RATE field of L-SIG (in bit/s).
          *
          * \param rate the RATE field of L-SIG expressed in bit/s
@@ -101,13 +88,13 @@
      *
      * \param psdu the PHY payload (PSDU)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param ppduDuration the transmission duration of this PPDU
      * \param uid the unique ID of this PPDU
      */
     DsssPpdu(Ptr<const WifiPsdu> psdu,
              const WifiTxVector& txVector,
-             uint16_t txCenterFreq,
+             const WifiPhyOperatingChannel& channel,
              Time ppduDuration,
              uint64_t uid);
 
@@ -145,10 +132,8 @@
     virtual void SetTxVectorFromDsssHeader(WifiTxVector& txVector,
                                            const DsssSigHeader& dsssSig) const;
 
-#ifndef NS3_BUILD_PROFILE_DEBUG
     DsssSigHeader m_dsssSig; //!< the DSSS SIG PHY header
-#endif
-}; // class DsssPpdu
+};                           // class DsssPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/erp-ofdm-phy.cc ns-3.39/src/wifi/model/non-ht/erp-ofdm-phy.cc
--- ns-3.38/src/wifi/model/non-ht/erp-ofdm-phy.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/erp-ofdm-phy.cc	2023-07-05 14:23:48.117428300 -0700
@@ -116,9 +116,7 @@
     return Create<ErpOfdmPpdu>(
         psdus.begin()->second,
         txVector,
-        m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-            txVector.GetChannelWidth()),
-        m_wifiPhy->GetPhyBand(),
+        m_wifiPhy->GetOperatingChannel(),
         m_wifiPhy->GetLatestPhyEntity()->ObtainNextUid(
             txVector)); // use latest PHY entity to handle MU-RTS sent with non-HT rate
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.cc ns-3.39/src/wifi/model/non-ht/erp-ofdm-ppdu.cc
--- ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/erp-ofdm-ppdu.cc	2023-07-05 14:23:48.117428300 -0700
@@ -23,6 +23,7 @@
 #include "erp-ofdm-phy.h"
 
 #include "ns3/log.h"
+#include "ns3/wifi-phy-operating-channel.h"
 #include "ns3/wifi-psdu.h"
 
 namespace ns3
@@ -32,12 +33,11 @@
 
 ErpOfdmPpdu::ErpOfdmPpdu(Ptr<const WifiPsdu> psdu,
                          const WifiTxVector& txVector,
-                         uint16_t txCenterFreq,
-                         WifiPhyBand band,
+                         const WifiPhyOperatingChannel& channel,
                          uint64_t uid)
-    : OfdmPpdu(psdu, txVector, txCenterFreq, band, uid, true) // add LSigHeader of OfdmPpdu
+    : OfdmPpdu(psdu, txVector, channel, uid, true) // add LSigHeader of OfdmPpdu
 {
-    NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << band << uid);
+    NS_LOG_FUNCTION(this << psdu << txVector << channel << uid);
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.h ns-3.39/src/wifi/model/non-ht/erp-ofdm-ppdu.h
--- ns-3.38/src/wifi/model/non-ht/erp-ofdm-ppdu.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/erp-ofdm-ppdu.h	2023-07-05 14:23:48.117428300 -0700
@@ -49,14 +49,12 @@
      *
      * \param psdu the PHY payload (PSDU)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
-     * \param band the WifiPhyBand used for the transmission of this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param uid the unique ID of this PPDU
      */
     ErpOfdmPpdu(Ptr<const WifiPsdu> psdu,
                 const WifiTxVector& txVector,
-                uint16_t txCenterFreq,
-                WifiPhyBand band,
+                const WifiPhyOperatingChannel& channel,
                 uint64_t uid);
 
     Ptr<WifiPpdu> Copy() const override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/ofdm-phy.cc ns-3.39/src/wifi/model/non-ht/ofdm-phy.cc
--- ns-3.38/src/wifi/model/non-ht/ofdm-phy.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/ofdm-phy.cc	2023-07-05 14:23:48.117428300 -0700
@@ -298,9 +298,7 @@
     return Create<OfdmPpdu>(
         psdus.begin()->second,
         txVector,
-        m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-            txVector.GetChannelWidth()),
-        m_wifiPhy->GetPhyBand(),
+        m_wifiPhy->GetOperatingChannel(),
         m_wifiPhy->GetLatestPhyEntity()->ObtainNextUid(
             txVector)); // use latest PHY entity to handle MU-RTS sent with non-HT rate
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.cc ns-3.39/src/wifi/model/non-ht/ofdm-ppdu.cc
--- ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/ofdm-ppdu.cc	2023-07-05 14:23:48.117428300 -0700
@@ -24,6 +24,7 @@
 #include "ofdm-phy.h"
 
 #include "ns3/log.h"
+#include "ns3/wifi-phy-operating-channel.h"
 #include "ns3/wifi-phy.h"
 #include "ns3/wifi-psdu.h"
 
@@ -34,15 +35,13 @@
 
 OfdmPpdu::OfdmPpdu(Ptr<const WifiPsdu> psdu,
                    const WifiTxVector& txVector,
-                   uint16_t txCenterFreq,
-                   WifiPhyBand band,
+                   const WifiPhyOperatingChannel& channel,
                    uint64_t uid,
                    bool instantiateLSig /* = true */)
-    : WifiPpdu(psdu, txVector, txCenterFreq, uid),
-      m_band(band),
+    : WifiPpdu(psdu, txVector, channel, uid),
       m_channelWidth(txVector.IsNonHtDuplicate() ? 20 : txVector.GetChannelWidth())
 {
-    NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << band << uid);
+    NS_LOG_FUNCTION(this << psdu << txVector << channel << uid);
     if (instantiateLSig)
     {
         SetPhyHeaders(txVector, psdu->GetSize());
@@ -53,14 +52,7 @@
 OfdmPpdu::SetPhyHeaders(const WifiTxVector& txVector, std::size_t psduSize)
 {
     NS_LOG_FUNCTION(this << txVector << psduSize);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    LSigHeader lSig;
-    SetLSigHeader(lSig, txVector, psduSize);
-    m_phyHeaders->AddHeader(lSig);
-#else
     SetLSigHeader(m_lSig, txVector, psduSize);
-#endif
 }
 
 void
@@ -75,19 +67,7 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    LSigHeader lSig;
-    if (m_phyHeaders->PeekHeader(lSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing L-SIG in PPDU");
-    }
-
-    SetTxVectorFromLSigHeader(txVector, lSig);
-#else
     SetTxVectorFromLSigHeader(txVector, m_lSig);
-#endif
-
     return txVector;
 }
 
@@ -104,15 +84,9 @@
 OfdmPpdu::GetTxDuration() const
 {
     const WifiTxVector& txVector = GetTxVector();
-    uint16_t length = 0;
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    LSigHeader lSig;
-    m_phyHeaders->PeekHeader(lSig);
-    length = lSig.GetLength();
-#else
-    length = m_lSig.GetLength();
-#endif
-    return WifiPhy::CalculateTxDuration(length, txVector, m_band);
+    const auto length = m_lSig.GetLength();
+    NS_ASSERT(m_operatingChannel.IsSet());
+    return WifiPhy::CalculateTxDuration(length, txVector, m_operatingChannel.GetPhyBand());
 }
 
 Ptr<WifiPpdu>
@@ -127,34 +101,6 @@
 {
 }
 
-TypeId
-OfdmPpdu::LSigHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::LSigHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<LSigHeader>();
-    return tid;
-}
-
-TypeId
-OfdmPpdu::LSigHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-OfdmPpdu::LSigHeader::Print(std::ostream& os) const
-{
-    os << "SIGNAL=" << GetRate() << " LENGTH=" << m_length;
-}
-
-uint32_t
-OfdmPpdu::LSigHeader::GetSerializedSize() const
-{
-    return 3;
-}
-
 void
 OfdmPpdu::LSigHeader::SetRate(uint64_t rate, uint16_t channelWidth)
 {
@@ -266,33 +212,4 @@
     return m_length;
 }
 
-void
-OfdmPpdu::LSigHeader::Serialize(Buffer::Iterator start) const
-{
-    uint8_t byte = 0;
-    uint16_t bytes = 0;
-
-    byte |= m_rate;
-    byte |= (m_length & 0x07) << 5;
-    start.WriteU8(byte);
-
-    bytes |= (m_length & 0x0ff8) >> 3;
-    start.WriteU16(bytes);
-}
-
-uint32_t
-OfdmPpdu::LSigHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-
-    uint8_t byte = i.ReadU8();
-    m_rate = byte & 0x0f;
-    m_length = (byte >> 5) & 0x07;
-
-    uint16_t bytes = i.ReadU16();
-    m_length |= (bytes << 3) & 0x0ff8;
-
-    return i.GetDistanceFrom(start);
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.h ns-3.39/src/wifi/model/non-ht/ofdm-ppdu.h
--- ns-3.38/src/wifi/model/non-ht/ofdm-ppdu.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/non-ht/ofdm-ppdu.h	2023-07-05 14:23:48.117428300 -0700
@@ -22,7 +22,6 @@
 #ifndef OFDM_PPDU_H
 #define OFDM_PPDU_H
 
-#include "ns3/header.h"
 #include "ns3/wifi-phy-band.h"
 #include "ns3/wifi-ppdu.h"
 
@@ -51,24 +50,12 @@
      * OFDM and ERP OFDM L-SIG PHY header.
      * See section 17.3.4 in IEEE 802.11-2016.
      */
-    class LSigHeader : public Header
+    class LSigHeader
     {
       public:
         LSigHeader();
 
         /**
-         * \brief Get the type ID.
-         * \return the object 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;
-
-        /**
          * Fill the RATE field of L-SIG (in bit/s).
          *
          * \param rate the RATE field of L-SIG expressed in bit/s
@@ -105,16 +92,14 @@
      *
      * \param psdu the PHY payload (PSDU)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
-     * \param band the WifiPhyBand used for the transmission of this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param uid the unique ID of this PPDU
      * \param instantiateLSig flag used to instantiate LSigHeader (set LSigHeader's
      *                        rate and length), should be disabled by child classes
      */
     OfdmPpdu(Ptr<const WifiPsdu> psdu,
              const WifiTxVector& txVector,
-             uint16_t txCenterFreq,
-             WifiPhyBand band,
+             const WifiPhyOperatingChannel& channel,
              uint64_t uid,
              bool instantiateLSig = true);
 
@@ -122,10 +107,7 @@
     Ptr<WifiPpdu> Copy() const override;
 
   protected:
-    WifiPhyBand m_band; //!< the WifiPhyBand used to transmit that PPDU
-#ifndef NS3_BUILD_PROFILE_DEBUG
     LSigHeader m_lSig; //!< the L-SIG PHY header
-#endif
 
   private:
     WifiTxVector DoGetTxVector() const override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-inheritance.cc ns-3.39/src/wifi/model/non-inheritance.cc
--- ns-3.38/src/wifi/model/non-inheritance.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/non-inheritance.cc	2023-07-05 14:23:48.117428300 -0700
@@ -0,0 +1,106 @@
+/*
+ * 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 "non-inheritance.h"
+
+#include <algorithm>
+#include <iterator>
+
+namespace ns3
+{
+
+WifiInformationElementId
+NonInheritance::ElementId() const
+{
+    return IE_EXTENSION;
+}
+
+WifiInformationElementId
+NonInheritance::ElementIdExt() const
+{
+    return IE_EXT_NON_INHERITANCE;
+}
+
+void
+NonInheritance::Print(std::ostream& os) const
+{
+    os << "NonInheritance=[";
+    std::copy(m_elemIdList.cbegin(), m_elemIdList.cend(), std::ostream_iterator<uint16_t>(os, " "));
+    os << "][";
+    std::copy(m_elemIdExtList.cbegin(),
+              m_elemIdExtList.cend(),
+              std::ostream_iterator<uint16_t>(os, " "));
+    os << "]";
+}
+
+uint16_t
+NonInheritance::GetInformationFieldSize() const
+{
+    uint16_t size = 1; // Element ID Extension
+    size += 1 /* Length */ + m_elemIdList.size();
+    size += 1 /* Length */ + m_elemIdExtList.size();
+    return size;
+}
+
+void
+NonInheritance::SerializeInformationField(Buffer::Iterator start) const
+{
+    start.WriteU8(m_elemIdList.size());
+    for (const auto id : m_elemIdList)
+    {
+        start.WriteU8(id);
+    }
+    start.WriteU8(m_elemIdExtList.size());
+    for (const auto id : m_elemIdExtList)
+    {
+        start.WriteU8(id);
+    }
+}
+
+uint16_t
+NonInheritance::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
+{
+    auto i = start;
+    auto count = i.ReadU8();
+    for (uint8_t j = 0; j < count; j++)
+    {
+        m_elemIdList.emplace(i.ReadU8());
+    }
+    count = i.ReadU8();
+    for (uint8_t j = 0; j < count; j++)
+    {
+        m_elemIdExtList.emplace(i.ReadU8());
+    }
+    return i.GetDistanceFrom(start);
+}
+
+void
+NonInheritance::Add(uint8_t elemId, uint8_t elemIdExt)
+{
+    elemId != IE_EXTENSION ? m_elemIdList.insert(elemId) : m_elemIdExtList.insert(elemIdExt);
+}
+
+bool
+NonInheritance::IsPresent(uint8_t elemId, uint8_t elemIdExt) const
+{
+    return elemId != IE_EXTENSION ? m_elemIdList.count(elemId) == 1
+                                  : m_elemIdExtList.count(elemIdExt) == 1;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/non-inheritance.h ns-3.39/src/wifi/model/non-inheritance.h
--- ns-3.38/src/wifi/model/non-inheritance.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/non-inheritance.h	2023-07-05 14:23:48.117428300 -0700
@@ -0,0 +1,69 @@
+/*
+ * 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 NON_INHERITANCE_H
+#define NON_INHERITANCE_H
+
+#include "wifi-information-element.h"
+
+#include <set>
+
+namespace ns3
+{
+
+/**
+ * \ingroup wifi
+ *
+ * The IEEE 802.11 Non-Inheritance Information Element
+ */
+class NonInheritance : public WifiInformationElement
+{
+  public:
+    WifiInformationElementId ElementId() const override;
+    WifiInformationElementId ElementIdExt() const override;
+    void Print(std::ostream& os) const override;
+
+    /**
+     * Add the Information Element specified by the given Element ID and Element ID Extension.
+     *
+     * \param elemId the given Element ID
+     * \param elemIdExt the given Element ID Extension
+     */
+    void Add(uint8_t elemId, uint8_t elemIdExt = 0);
+
+    /**
+     * \param elemId the given Element ID
+     * \param elemIdExt the given Element ID Extension
+     * \return whether the Information Element specified by the given Element ID and
+     *         Element ID Extension is present
+     */
+    bool IsPresent(uint8_t elemId, uint8_t elemIdExt = 0) const;
+
+    std::set<uint8_t> m_elemIdList;    //!< list of unique Element ID values (in increasing order)
+    std::set<uint8_t> m_elemIdExtList; //!< list of unique Element ID Extension values
+
+  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 /* NON_INHERITANCE_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/phy-entity.cc ns-3.39/src/wifi/model/phy-entity.cc
--- ns-3.38/src/wifi/model/phy-entity.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/phy-entity.cc	2023-07-05 14:23:48.117428300 -0700
@@ -246,8 +246,7 @@
                    "version in the amendment-specific subclasses instead!");
     return Create<WifiPpdu>(psdus.begin()->second,
                             txVector,
-                            m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                                txVector.GetChannelWidth())); // should be overloaded
+                            m_wifiPhy->GetOperatingChannel()); // should be overloaded
 }
 
 Time
@@ -401,16 +400,13 @@
                                 Time rxDuration)
 {
     // The total RX power corresponds to the maximum over all the bands
-    auto it = std::max_element(
-        rxPowersW.begin(),
-        rxPowersW.end(),
-        [](const std::pair<WifiSpectrumBand, double>& p1,
-           const std::pair<WifiSpectrumBand, double>& p2) { return p1.second < p2.second; });
+    auto it =
+        std::max_element(rxPowersW.begin(), rxPowersW.end(), [](const auto& p1, const auto& p2) {
+            return p1.second < p2.second;
+        });
     NS_LOG_FUNCTION(this << ppdu << it->second);
 
-    Ptr<Event> event = m_wifiPhy->GetPhyEntityForPpdu(ppdu)->DoGetEvent(
-        ppdu,
-        rxPowersW); // use latest PHY entity to handle MU-RTS sent with non-HT rate
+    auto event = DoGetEvent(ppdu, rxPowersW);
     if (!event)
     {
         // PPDU should be simply considered as interference (once it has been accounted for in
@@ -826,7 +822,7 @@
     }
 }
 
-std::pair<uint16_t, WifiSpectrumBand>
+std::pair<uint16_t, WifiSpectrumBandInfo>
 PhyEntity::GetChannelWidthAndBand(const WifiTxVector& txVector, uint16_t /* staId */) const
 {
     uint16_t channelWidth = GetRxChannelWidth(txVector);
@@ -851,26 +847,53 @@
 Ptr<Event>
 PhyEntity::DoGetEvent(Ptr<const WifiPpdu> ppdu, RxPowerWattPerChannelBand& rxPowersW)
 {
-    Ptr<Event> event =
-        CreateInterferenceEvent(ppdu, ppdu->GetTxVector(), ppdu->GetTxDuration(), rxPowersW);
-
+    Ptr<Event> event;
     // We store all incoming preamble events, and a decision is made at the end of the preamble
     // detection window.
-    auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
-    NS_ASSERT(m_wifiPhy->m_currentPreambleEvents.find(uidPreamblePair) ==
-              m_wifiPhy->m_currentPreambleEvents.end());
-    m_wifiPhy->m_currentPreambleEvents.insert({uidPreamblePair, event});
+    const auto uidPreamblePair = std::make_pair(ppdu->GetUid(), ppdu->GetPreamble());
+    const auto& currentPreambleEvents = GetCurrentPreambleEvents();
+    const auto it = currentPreambleEvents.find(uidPreamblePair);
+    if (it != currentPreambleEvents.cend())
+    {
+        // received another signal with the same content
+        NS_LOG_DEBUG("Received another PPDU for UID " << ppdu->GetUid());
+        event = it->second;
+        const auto maxDelay =
+            m_wifiPhy->GetPhyEntityForPpdu(ppdu)->GetMaxDelayPpduSameUid(ppdu->GetTxVector());
+        if (Simulator::Now() - event->GetStartTime() > maxDelay)
+        {
+            // This PPDU arrived too late to be decoded properly. The PPDU is dropped and added as
+            // interference
+            event = CreateInterferenceEvent(ppdu, ppdu->GetTxDuration(), rxPowersW);
+            NS_LOG_DEBUG("Drop PPDU that arrived too late");
+            m_wifiPhy->NotifyRxDrop(GetAddressedPsduInPpdu(ppdu), PPDU_TOO_LATE);
+        }
+        else
+        {
+            // Update received power of the event associated to that transmission
+            UpdateInterferenceEvent(event, rxPowersW);
+        }
+        return nullptr;
+    }
+    else
+    {
+        event = CreateInterferenceEvent(ppdu, ppdu->GetTxDuration(), rxPowersW);
+        AddPreambleEvent(event);
+    }
     return event;
 }
 
 Ptr<Event>
 PhyEntity::CreateInterferenceEvent(Ptr<const WifiPpdu> ppdu,
-                                   const WifiTxVector& txVector,
                                    Time duration,
                                    RxPowerWattPerChannelBand& rxPower,
                                    bool isStartOfdmaRxing /* = false */)
 {
-    return m_wifiPhy->m_interference->Add(ppdu, txVector, duration, rxPower, isStartOfdmaRxing);
+    return m_wifiPhy->m_interference->Add(ppdu,
+                                          ppdu->GetTxVector(),
+                                          duration,
+                                          rxPower,
+                                          isStartOfdmaRxing);
 }
 
 void
@@ -882,7 +905,7 @@
 void
 PhyEntity::NotifyInterferenceRxEndAndClear(bool reset)
 {
-    m_wifiPhy->m_interference->NotifyRxEnd(Simulator::Now());
+    m_wifiPhy->m_interference->NotifyRxEnd(Simulator::Now(), m_wifiPhy->GetCurrentFrequencyRange());
     m_signalNoiseMap.clear();
     m_statusPerMpduMap.clear();
     for (const auto& endOfMpduEvent : m_endOfMpduEvents)
@@ -958,7 +981,8 @@
         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
-        m_wifiPhy->m_interference->NotifyRxEnd(maxEvent->GetStartTime());
+        m_wifiPhy->m_interference->NotifyRxEnd(maxEvent->GetStartTime(),
+                                               m_wifiPhy->GetCurrentFrequencyRange());
         // Make sure InterferenceHelper keeps recording events
         m_wifiPhy->m_interference->NotifyRxStart();
         return;
@@ -1000,7 +1024,8 @@
                     // This is needed to cleanup the m_firstPowerPerBand so that the first power
                     // corresponds to the power at the start of the PPDU
                     m_wifiPhy->m_interference->NotifyRxEnd(
-                        m_wifiPhy->m_currentEvent->GetStartTime());
+                        m_wifiPhy->m_currentEvent->GetStartTime(),
+                        m_wifiPhy->GetCurrentFrequencyRange());
                 }
                 else
                 {
@@ -1044,7 +1069,8 @@
         if (m_wifiPhy->m_currentPreambleEvents.empty())
         {
             // Do not erase events if there are still pending preamble events to be processed
-            m_wifiPhy->m_interference->NotifyRxEnd(Simulator::Now());
+            m_wifiPhy->m_interference->NotifyRxEnd(Simulator::Now(),
+                                                   m_wifiPhy->GetCurrentFrequencyRange());
         }
         m_wifiPhy->m_currentEvent = nullptr;
         // Cancel preamble reception
@@ -1137,7 +1163,7 @@
     NS_LOG_FUNCTION(this << *event);
     DoResetReceive(event);
     NS_ASSERT(!m_wifiPhy->IsStateRx());
-    m_wifiPhy->m_interference->NotifyRxEnd(Simulator::Now());
+    m_wifiPhy->m_interference->NotifyRxEnd(Simulator::Now(), m_wifiPhy->GetCurrentFrequencyRange());
     NS_ASSERT(m_endRxPayloadEvents.size() == 1 && m_endRxPayloadEvents.front().IsExpired());
     m_endRxPayloadEvents.clear();
     m_wifiPhy->m_currentEvent = nullptr;
@@ -1170,7 +1196,7 @@
     return m_wifiPhy->m_currentEvent;
 }
 
-WifiSpectrumBand
+WifiSpectrumBandInfo
 PhyEntity::GetPrimaryBand(uint16_t bandWidth) const
 {
     if (m_wifiPhy->GetChannelWidth() % 20 != 0)
@@ -1181,7 +1207,7 @@
                               m_wifiPhy->m_operatingChannel.GetPrimaryChannelIndex(bandWidth));
 }
 
-WifiSpectrumBand
+WifiSpectrumBandInfo
 PhyEntity::GetSecondaryBand(uint16_t bandWidth) const
 {
     NS_ASSERT(m_wifiPhy->GetChannelWidth() >= 40);
@@ -1203,7 +1229,7 @@
 }
 
 Time
-PhyEntity::GetDelayUntilCcaEnd(double thresholdDbm, WifiSpectrumBand band)
+PhyEntity::GetDelayUntilCcaEnd(double thresholdDbm, const WifiSpectrumBandInfo& band)
 {
     return m_wifiPhy->m_interference->GetEnergyDuration(DbmToW(thresholdDbm), band);
 }
@@ -1263,6 +1289,12 @@
     return m_globalPpduUid++;
 }
 
+Time
+PhyEntity::GetMaxDelayPpduSameUid(const WifiTxVector& /*txVector*/)
+{
+    return Seconds(0);
+}
+
 uint16_t
 PhyEntity::GetCenterFrequencyForChannelWidth(const WifiTxVector& txVector) const
 {
@@ -1353,11 +1385,7 @@
     const auto txCenterFreq = ppdu->GetTxCenterFreq();
     const auto minTxFreq = txCenterFreq - txChannelWidth / 2;
     const auto maxTxFreq = txCenterFreq + txChannelWidth / 2;
-    if (p20MinFreq < minTxFreq || p20MaxFreq > maxTxFreq)
-    {
-        return false;
-    }
-    return true;
+    return p20MinFreq >= minTxFreq && p20MaxFreq <= maxTxFreq;
 }
 
 Ptr<const WifiPpdu>
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/phy-entity.h ns-3.39/src/wifi/model/phy-entity.h
--- ns-3.38/src/wifi/model/phy-entity.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/phy-entity.h	2023-07-05 14:23:48.117428300 -0700
@@ -75,7 +75,7 @@
 /**
  * A map of the received power (Watts) for each band
  */
-typedef std::map<WifiSpectrumBand, double> RxPowerWattPerChannelBand;
+using RxPowerWattPerChannelBand = std::map<WifiSpectrumBandInfo, double>;
 
 class WifiPsdu;
 class WifiPhy;
@@ -548,6 +548,15 @@
      */
     virtual uint64_t ObtainNextUid(const WifiTxVector& txVector);
 
+    /**
+     * Obtain the maximum time between two PPDUs with the same UID to consider they are identical
+     * and their power can be added construtively.
+     *
+     * \param txVector the TXVECTOR used for the transmission of the PPDUs
+     * \return the maximum time between two PPDUs with the same UID to decode them
+     */
+    virtual Time GetMaxDelayPpduSameUid(const WifiTxVector& txVector);
+
   protected:
     /**
      * A map of PPDU field elements per preamble type.
@@ -766,7 +775,7 @@
      * \param staId the station ID of the PSDU
      * \return a pair of channel width (MHz) and band
      */
-    virtual std::pair<uint16_t, WifiSpectrumBand> GetChannelWidthAndBand(
+    virtual std::pair<uint16_t, WifiSpectrumBandInfo> GetChannelWidthAndBand(
         const WifiTxVector& txVector,
         uint16_t staId) const;
 
@@ -827,10 +836,14 @@
      * Create an event using WifiPhy's InterferenceHelper class.
      * Wrapper used by child classes.
      *
-     * \copydoc InterferenceHelper::Add
+     * \param ppdu the PPDU
+     * \param duration the PPDU duration
+     * \param rxPower received power per band (W)
+     * \param isStartOfdmaRxing flag whether the event corresponds to the start of the OFDMA payload
+     * reception (only used for UL-OFDMA)
+     * \return the created event
      */
     Ptr<Event> CreateInterferenceEvent(Ptr<const WifiPpdu> ppdu,
-                                       const WifiTxVector& txVector,
                                        Time duration,
                                        RxPowerWattPerChannelBand& rxPower,
                                        bool isStartOfdmaRxing = false);
@@ -838,7 +851,8 @@
      * Update an event in WifiPhy's InterferenceHelper class.
      * Wrapper used by child classes.
      *
-     * \copydoc InterferenceHelper::UpdateEvent
+     * \param event the event to be updated
+     * \param rxPower the received power (W) per band to be added to the current event
      */
     void UpdateInterferenceEvent(Ptr<Event> event, const RxPowerWattPerChannelBand& rxPower);
     /**
@@ -879,27 +893,27 @@
     void NotifyPayloadBegin(const WifiTxVector& txVector, const Time& payloadDuration);
 
     /**
-     * If the operating channel width is a multiple of 20 MHz, return the start
-     * band index and the stop band index for the primary channel of the given
+     * If the operating channel width is a multiple of 20 MHz, return the info
+     * corresponding to the primary channel of the given
      * bandwidth (which must be a multiple of 20 MHz and not exceed the operating
      * channel width). Otherwise, this call is equivalent to GetBand with
      * <i>bandIndex</i> equal to zero.
      *
      * \param bandWidth the width of the band to be returned (MHz)
      *
-     * \return a pair of start and stop indexes that defines the band
+     * \return the info corresponding to the band
      */
-    WifiSpectrumBand GetPrimaryBand(uint16_t bandWidth) const;
+    WifiSpectrumBandInfo GetPrimaryBand(uint16_t bandWidth) const;
     /**
-     * If the channel bonding is used, return the start band index and the stop band index
-     * for the secondary channel of the given bandwidth (which must be a multiple of 20 MHz
+     * If the channel bonding is used, return the info corresponding to
+     * the secondary channel of the given bandwidth (which must be a multiple of 20 MHz
      * and not exceed the operating channel width).
      *
      * \param bandWidth the width of the band to be returned (MHz)
      *
-     * \return a pair of start and stop indexes that defines the band
+     * \return the info corresponding to the band
      */
-    WifiSpectrumBand GetSecondaryBand(uint16_t bandWidth) const;
+    WifiSpectrumBandInfo GetSecondaryBand(uint16_t bandWidth) const;
 
     /**
      * Return the channel width used to measure the RSSI.
@@ -925,7 +939,7 @@
      * \param band identify the requested band
      * \return the delay until CCA busy is ended
      */
-    Time GetDelayUntilCcaEnd(double thresholdDbm, WifiSpectrumBand band);
+    Time GetDelayUntilCcaEnd(double thresholdDbm, const WifiSpectrumBandInfo& band);
 
     /**
      * \param currentChannelWidth channel width of the current transmission (MHz)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/qos-blocked-destinations.cc ns-3.39/src/wifi/model/qos-blocked-destinations.cc
--- ns-3.38/src/wifi/model/qos-blocked-destinations.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/qos-blocked-destinations.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2005, 2009 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
- *
- * Author: Mirko Banchi <mk.banchi@gmail.com>
- */
-
-#include "qos-blocked-destinations.h"
-
-#include "ns3/mac48-address.h"
-
-namespace ns3
-{
-
-QosBlockedDestinations::QosBlockedDestinations()
-{
-}
-
-QosBlockedDestinations::~QosBlockedDestinations()
-{
-}
-
-bool
-QosBlockedDestinations::IsBlocked(Mac48Address dest, uint8_t tid) const
-{
-    return m_blockedQosPackets.find({dest, tid}) != m_blockedQosPackets.end();
-}
-
-void
-QosBlockedDestinations::Block(Mac48Address dest, uint8_t tid)
-{
-    m_blockedQosPackets.insert({dest, tid});
-}
-
-void
-QosBlockedDestinations::Unblock(Mac48Address dest, uint8_t tid)
-{
-    m_blockedQosPackets.erase({dest, tid});
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/qos-blocked-destinations.h ns-3.39/src/wifi/model/qos-blocked-destinations.h
--- ns-3.38/src/wifi/model/qos-blocked-destinations.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/qos-blocked-destinations.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,77 +0,0 @@
-/*
- * Copyright (c) 2005, 2009 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
- *
- * Author: Mirko Banchi <mk.banchi@gmail.com>
- */
-
-#ifndef QOS_BLOCKED_DESTINATIONS_H
-#define QOS_BLOCKED_DESTINATIONS_H
-
-#include "ns3/simple-ref-count.h"
-
-#include <set>
-
-namespace ns3
-{
-
-class Mac48Address;
-
-/**
- * Keep track of destination address - TID pairs that are waiting
- * for a BlockAck response.
- */
-class QosBlockedDestinations : public SimpleRefCount<QosBlockedDestinations>
-{
-  public:
-    QosBlockedDestinations();
-    ~QosBlockedDestinations();
-
-    /**
-     * Block the given destination address and TID from sending (e.g. pending
-     * BlockAck response).
-     *
-     * \param dest the destination MAC address
-     * \param tid the TID
-     */
-    void Block(Mac48Address dest, uint8_t tid);
-    /**
-     * Un-block the given destination address and TID (e.g. BlockAck
-     * response received).
-     *
-     * \param dest the destination MAC address
-     * \param tid the TID
-     */
-    void Unblock(Mac48Address dest, uint8_t tid);
-    /**
-     * Check if the given destination address and TID are blocked
-     * from sending (e.g. pending BlockAck response).
-     *
-     * \param dest the destination MAC address
-     * \param tid the TID
-     *
-     * \return true if the given destination address and TID are blocked from sending,
-     *         false otherwise
-     */
-    bool IsBlocked(Mac48Address dest, uint8_t tid) const;
-
-  private:
-    std::set<std::pair<Mac48Address, uint8_t>> m_blockedQosPackets; ///< blocked QoS packets
-};
-
-} // namespace ns3
-
-#endif /* QOS_BLOCKED_DESTINATIONS_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/qos-frame-exchange-manager.cc ns-3.39/src/wifi/model/qos-frame-exchange-manager.cc
--- ns-3.38/src/wifi/model/qos-frame-exchange-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/qos-frame-exchange-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -108,11 +108,14 @@
     {
         NS_LOG_DEBUG("Send CF-End frame");
         m_phy->Send(Create<WifiPsdu>(Create<Packet>(), cfEnd), cfEndTxVector);
-        Simulator::Schedule(txDuration, &Txop::NotifyChannelReleased, m_edca, m_linkId);
+        Simulator::Schedule(txDuration,
+                            &QosFrameExchangeManager::NotifyChannelReleased,
+                            this,
+                            m_edca);
         return true;
     }
 
-    m_edca->NotifyChannelReleased(m_linkId);
+    NotifyChannelReleased(m_edca);
     m_edca = nullptr;
     return false;
 }
@@ -128,7 +131,7 @@
     if (m_channelAccessManager->GetAccessGrantStart() - m_phy->GetSifs() >
         Simulator::Now() - m_phy->GetPifs())
     {
-        m_edca->NotifyChannelReleased(m_linkId);
+        NotifyChannelReleased(m_edca);
         m_edca = nullptr;
     }
     else
@@ -147,7 +150,7 @@
 
     NS_LOG_DEBUG("Cancel PIFS recovery being attempted by EDCAF " << m_edca);
     m_pifsRecoveryEvent.Cancel();
-    m_edca->NotifyChannelReleased(m_linkId);
+    NotifyChannelReleased(m_edca);
 }
 
 bool
@@ -230,7 +233,7 @@
 
             // TXOP not even started, return false
             NS_LOG_DEBUG("No frame transmitted");
-            m_edca->NotifyChannelReleased(m_linkId);
+            NotifyChannelReleased(m_edca);
             m_edca = nullptr;
             return false;
         }
@@ -257,7 +260,7 @@
     }
 
     NS_LOG_DEBUG("No frame transmitted");
-    m_edca->NotifyChannelReleased(m_linkId);
+    NotifyChannelReleased(m_edca);
     m_edca = nullptr;
     return false;
 }
@@ -582,7 +585,7 @@
     }
     else
     {
-        m_edca->NotifyChannelReleased(m_linkId);
+        NotifyChannelReleased(m_edca);
         m_edca = nullptr;
     }
     m_initialFrame = false;
@@ -605,7 +608,7 @@
         // The backoff procedure shall be invoked by an EDCAF when the transmission
         // of an MPDU in the initial PPDU of a TXOP fails (Sec. 10.22.2.2 of 802.11-2016)
         NS_LOG_DEBUG("TX of the initial frame of a TXOP failed: terminate TXOP");
-        m_edca->NotifyChannelReleased(m_linkId);
+        NotifyChannelReleased(m_edca);
         m_edca = nullptr;
     }
     else
@@ -713,6 +716,20 @@
 }
 
 void
+QosFrameExchangeManager::UpdateNav(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector)
+{
+    NS_LOG_FUNCTION(this << psdu << txVector);
+    if (psdu->GetHeader(0).IsCfEnd())
+    {
+        NS_LOG_DEBUG("Received CF-End, resetting NAV");
+        NavResetTimeout();
+        return;
+    }
+
+    FrameExchangeManager::UpdateNav(psdu, txVector);
+}
+
+void
 QosFrameExchangeManager::NavResetTimeout()
 {
     NS_LOG_FUNCTION(this);
@@ -732,13 +749,6 @@
     double rxSnr = rxSignalInfo.snr;
     const WifiMacHeader& hdr = mpdu->GetHeader();
 
-    if (hdr.IsCfEnd())
-    {
-        // reset NAV
-        NavResetTimeout();
-        return;
-    }
-
     if (hdr.IsRts())
     {
         NS_ABORT_MSG_IF(inAmpdu, "Received RTS as part of an A-MPDU");
@@ -779,11 +789,8 @@
                                 rxSnr);
         }
 
-        // Forward up the frame if it is not a QoS Null frame
-        if (hdr.HasData())
-        {
-            m_rxMiddle->Receive(mpdu, m_linkId);
-        }
+        // Forward up the frame
+        m_rxMiddle->Receive(mpdu, m_linkId);
 
         // the received data frame has been processed
         return;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/qos-frame-exchange-manager.h ns-3.39/src/wifi/model/qos-frame-exchange-manager.h
--- ns-3.38/src/wifi/model/qos-frame-exchange-manager.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/qos-frame-exchange-manager.h	2023-07-05 14:23:48.117428300 -0700
@@ -114,6 +114,7 @@
     void PreProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
     void PostProcessFrame(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
     void NavResetTimeout() override;
+    void UpdateNav(Ptr<const WifiPsdu> psdu, const WifiTxVector& txVector) override;
     Time GetFrameDurationId(const WifiMacHeader& header,
                             uint32_t size,
                             const WifiTxParameters& txParams,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/qos-txop.cc ns-3.39/src/wifi/model/qos-txop.cc
--- ns-3.38/src/wifi/model/qos-txop.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/qos-txop.cc	2023-07-05 14:23:48.117428300 -0700
@@ -28,7 +28,7 @@
 #include "mgt-headers.h"
 #include "mpdu-aggregator.h"
 #include "msdu-aggregator.h"
-#include "qos-blocked-destinations.h"
+#include "wifi-mac-queue-scheduler.h"
 #include "wifi-mac-queue.h"
 #include "wifi-mac-trailer.h"
 #include "wifi-phy.h"
@@ -108,13 +108,26 @@
       m_ac(ac)
 {
     NS_LOG_FUNCTION(this);
-    m_qosBlockedDestinations = Create<QosBlockedDestinations>();
     m_baManager = CreateObject<BlockAckManager>();
     m_baManager->SetQueue(m_queue);
     m_baManager->SetBlockDestinationCallback(
-        MakeCallback(&QosBlockedDestinations::Block, m_qosBlockedDestinations));
+        Callback<void, Mac48Address, uint8_t>([this](Mac48Address recipient, uint8_t tid) {
+            m_mac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::WAITING_ADDBA_RESP,
+                                                       m_ac,
+                                                       {WIFI_QOSDATA_QUEUE},
+                                                       recipient,
+                                                       m_mac->GetLocalAddress(recipient),
+                                                       {tid});
+        }));
     m_baManager->SetUnblockDestinationCallback(
-        MakeCallback(&QosBlockedDestinations::Unblock, m_qosBlockedDestinations));
+        Callback<void, Mac48Address, uint8_t>([this](Mac48Address recipient, uint8_t tid) {
+            m_mac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::WAITING_ADDBA_RESP,
+                                                         m_ac,
+                                                         {WIFI_QOSDATA_QUEUE},
+                                                         recipient,
+                                                         m_mac->GetLocalAddress(recipient),
+                                                         {tid});
+        }));
     m_queue->TraceConnectWithoutContext(
         "Expired",
         MakeCallback(&BlockAckManager::NotifyDiscardedMpdu, m_baManager));
@@ -134,7 +147,6 @@
         m_baManager->Dispose();
     }
     m_baManager = nullptr;
-    m_qosBlockedDestinations = nullptr;
     Txop::DoDispose();
 }
 
@@ -153,7 +165,7 @@
 uint8_t
 QosTxop::GetQosQueueSize(uint8_t tid, Mac48Address receiver) const
 {
-    WifiContainerQueueId queueId{WIFI_QOSDATA_UNICAST_QUEUE, receiver, tid};
+    WifiContainerQueueId queueId{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, receiver, tid};
     uint32_t bufferSize = m_queue->GetNBytes(queueId);
     // A queue size value of 254 is used for all sizes greater than 64 768 octets.
     uint8_t queueSize = static_cast<uint8_t>(std::ceil(std::min(bufferSize, 64769U) / 256.0));
@@ -275,7 +287,7 @@
     return m_baManager->GetOriginatorStartingSequence(address, tid);
 }
 
-Ptr<WifiMpdu>
+std::pair<CtrlBAckRequestHeader, WifiMacHeader>
 QosTxop::PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const
 {
     NS_LOG_FUNCTION(this << recipient << +tid);
@@ -285,8 +297,6 @@
 
     CtrlBAckRequestHeader reqHdr =
         m_baManager->GetBlockAckReqHeader(recipientMld.value_or(recipient), tid);
-    Ptr<Packet> bar = Create<Packet>();
-    bar->AddHeader(reqHdr);
 
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_CTL_BACKREQ);
@@ -297,7 +307,7 @@
     hdr.SetNoRetry();
     hdr.SetNoMoreFragments();
 
-    return Create<WifiMpdu>(bar, hdr);
+    return {reqHdr, hdr};
 }
 
 bool
@@ -311,7 +321,7 @@
 {
     // remove MSDUs with expired lifetime starting from the head of the queue
     m_queue->WipeAllExpiredMpdus();
-    bool queueIsNotEmpty = (bool)(m_queue->PeekFirstAvailable(linkId, m_qosBlockedDestinations));
+    bool queueIsNotEmpty = (bool)(m_queue->PeekFirstAvailable(linkId));
 
     NS_LOG_FUNCTION(this << queueIsNotEmpty);
     return queueIsNotEmpty;
@@ -347,12 +357,8 @@
         return false;
     }
 
-    if (QosUtilsIsOldPacket(GetBaStartingSequence(recipient, tid),
-                            mpdu->GetHeader().GetSequenceNumber()))
-    {
-        return true;
-    }
-    return false;
+    return QosUtilsIsOldPacket(GetBaStartingSequence(recipient, tid),
+                               mpdu->GetHeader().GetSequenceNumber());
 }
 
 Ptr<WifiMpdu>
@@ -364,13 +370,15 @@
     auto peek = [this, &linkId, &tid, &recipient, &mpdu]() -> Ptr<WifiMpdu> {
         if (tid == 8 && recipient.IsBroadcast()) // undefined TID and recipient
         {
-            return m_queue->PeekFirstAvailable(linkId, m_qosBlockedDestinations, mpdu);
+            return m_queue->PeekFirstAvailable(linkId, mpdu);
         }
-        if (m_qosBlockedDestinations->IsBlocked(recipient, tid))
+        WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, recipient, tid);
+        if (auto mask = m_mac->GetMacQueueScheduler()->GetQueueLinkMask(m_ac, queueId, linkId);
+            !mask || mask->none())
         {
-            return nullptr;
+            return m_queue->PeekByQueueId(queueId, mpdu);
         }
-        return m_queue->PeekByTidAndAddress(tid, recipient, mpdu);
+        return nullptr;
     };
 
     auto item = peek();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/qos-txop.h ns-3.39/src/wifi/model/qos-txop.h
--- ns-3.38/src/wifi/model/qos-txop.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/qos-txop.h	2023-07-05 14:23:48.117428300 -0700
@@ -31,7 +31,6 @@
 namespace ns3
 {
 
-class QosBlockedDestinations;
 class MgtAddBaResponseHeader;
 class MgtDelBaHeader;
 class AggregationCapableTransmissionListener;
@@ -137,14 +136,15 @@
     /**
      * \param recipient Address of recipient.
      * \param tid traffic ID.
-     * \return the BlockAckRequest to send
+     * \return the BlockAckRequest header and the MAC header for the BlockAckReq
      *
      * Prepare a BlockAckRequest to be sent to <i>recipient</i> for Traffic ID
      * <i>tid</i>. The header for the BlockAckRequest is requested to the QosTxop
      * corresponding to the given TID. A block ack agreement with the given recipient
      * for the given TID must have been established by such QosTxop.
      */
-    Ptr<WifiMpdu> PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const;
+    std::pair<CtrlBAckRequestHeader, WifiMacHeader> PrepareBlockAckRequest(Mac48Address recipient,
+                                                                           uint8_t tid) const;
 
     /* Event handlers */
     /**
@@ -464,9 +464,8 @@
      */
     bool IsQosOldPacket(Ptr<const WifiMpdu> mpdu);
 
-    AcIndex m_ac;                                         //!< the access category
-    Ptr<QosBlockedDestinations> m_qosBlockedDestinations; //!< the QoS blocked destinations
-    Ptr<BlockAckManager> m_baManager;                     //!< the block ack manager
+    AcIndex m_ac;                     //!< the access category
+    Ptr<BlockAckManager> m_baManager; //!< the block ack manager
     uint8_t m_blockAckThreshold; /**< the block ack threshold (use BA mechanism if number of packets
                                     in queue reaches this value. If this value is 0, block ack is
                                     never used. When A-MPDU is enabled, block ack mechanism is used
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/qos-utils.cc ns-3.39/src/wifi/model/qos-utils.cc
--- ns-3.38/src/wifi/model/qos-utils.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/qos-utils.cc	2023-07-05 14:23:48.117428300 -0700
@@ -139,19 +139,15 @@
     case 0:
     case 3:
         return AC_BE;
-        break;
     case 1:
     case 2:
         return AC_BK;
-        break;
     case 4:
     case 5:
         return AC_VI;
-        break;
     case 6:
     case 7:
         return AC_VO;
-        break;
     }
     return AC_UNDEF;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/aarfcd-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/aarfcd-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/aarfcd-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/aarfcd-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -366,7 +366,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/aarf-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/aarf-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/aarf-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/aarf-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -297,7 +297,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/amrr-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/amrr-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/amrr-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/amrr-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -411,7 +411,7 @@
     }
     UpdateMode(station);
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/aparf-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/aparf-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/aparf-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/aparf-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -70,13 +70,13 @@
                           "The minimum number of successful transmissions in \"High\" state to try "
                           "a new power or rate.",
                           UintegerValue(3),
-                          MakeUintegerAccessor(&AparfWifiManager::m_succesMax1),
+                          MakeUintegerAccessor(&AparfWifiManager::m_successMax1),
                           MakeUintegerChecker<uint32_t>())
             .AddAttribute("SuccessThreshold2",
                           "The minimum number of successful transmissions in \"Low\" state to try "
                           "a new power or rate.",
                           UintegerValue(10),
-                          MakeUintegerAccessor(&AparfWifiManager::m_succesMax2),
+                          MakeUintegerAccessor(&AparfWifiManager::m_successMax2),
                           MakeUintegerChecker<uint32_t>())
             .AddAttribute("FailThreshold",
                           "The minimum number of failed transmissions to try a new power or rate.",
@@ -162,7 +162,7 @@
     NS_LOG_FUNCTION(this);
     AparfWifiRemoteStation* station = new AparfWifiRemoteStation();
 
-    station->m_successThreshold = m_succesMax1;
+    station->m_successThreshold = m_successMax1;
     station->m_failThreshold = m_failMax;
     station->m_nSuccess = 0;
     station->m_nFailed = 0;
@@ -217,12 +217,12 @@
     if (station->m_aparfState == AparfWifiManager::Low)
     {
         station->m_aparfState = AparfWifiManager::High;
-        station->m_successThreshold = m_succesMax1;
+        station->m_successThreshold = m_successMax1;
     }
     else if (station->m_aparfState == AparfWifiManager::Spread)
     {
         station->m_aparfState = AparfWifiManager::Low;
-        station->m_successThreshold = m_succesMax2;
+        station->m_successThreshold = m_successMax2;
     }
 
     if (station->m_nFailed == station->m_failThreshold)
@@ -291,7 +291,7 @@
     else if (station->m_aparfState == AparfWifiManager::Spread)
     {
         station->m_aparfState = AparfWifiManager::High;
-        station->m_successThreshold = m_succesMax1;
+        station->m_successThreshold = m_successMax1;
     }
 
     if (station->m_nSuccess == station->m_successThreshold)
@@ -402,7 +402,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/aparf-wifi-manager.h ns-3.39/src/wifi/model/rate-control/aparf-wifi-manager.h
--- ns-3.38/src/wifi/model/rate-control/aparf-wifi-manager.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/aparf-wifi-manager.h	2023-07-05 14:23:48.117428300 -0700
@@ -92,10 +92,10 @@
      */
     void CheckInit(AparfWifiRemoteStation* station);
 
-    uint32_t m_succesMax1; //!< The minimum number of successful transmissions in \"High\" state to
-                           //!< try a new power or rate.
-    uint32_t m_succesMax2; //!< The minimum number of successful transmissions in \"Low\" state to
-                           //!< try a new power or rate.
+    uint32_t m_successMax1; //!< The minimum number of successful transmissions in \"High\" state to
+                            //!< try a new power or rate.
+    uint32_t m_successMax2; //!< The minimum number of successful transmissions in \"Low\" state to
+                            //!< try a new power or rate.
     uint32_t m_failMax;  //!< The minimum number of failed transmissions to try a new power or rate.
     uint32_t m_powerMax; //!< The maximum number of power changes.
     uint8_t m_powerInc;  //!< Step size for increment the power.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/arf-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/arf-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/arf-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/arf-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -274,7 +274,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/cara-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/cara-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/cara-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/cara-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -245,7 +245,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/minstrel-ht-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -511,20 +511,9 @@
     station->m_ampduLen = 0;
     station->m_ampduPacketCount = 0;
 
-    // If the device supports HT
-    if (GetHtSupported())
-    {
-        /**
-         * Assume the station is HT.
-         * When correct information available it will be checked.
-         */
-        station->m_isHt = true;
-    }
-    // Use the variable in the station to indicate that the device do not support HT
-    else
-    {
-        station->m_isHt = false;
-    }
+    // Use the variable in the station to indicate whether the device supports HT.
+    // When correct information available it will be checked.
+    station->m_isHt = GetHtSupported();
 
     return station;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/minstrel-ht-wifi-manager.h ns-3.39/src/wifi/model/rate-control/minstrel-ht-wifi-manager.h
--- ns-3.38/src/wifi/model/rate-control/minstrel-ht-wifi-manager.h	2023-03-16 20:54:23.860869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/minstrel-ht-wifi-manager.h	2023-07-05 14:23:48.117428300 -0700
@@ -161,7 +161,7 @@
  * Data structure for a table of groups. Each group is of type GroupInfo.
  * A vector of a GroupInfo.
  */
-typedef std::vector<struct GroupInfo> McsGroupData;
+typedef std::vector<GroupInfo> McsGroupData;
 
 /**
  * Constants for maximum values.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/minstrel-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/minstrel-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/minstrel-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/minstrel-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -424,7 +424,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/onoe-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/onoe-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/onoe-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/onoe-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -371,7 +371,7 @@
     }
     UpdateMode(station);
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/parf-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/parf-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/parf-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/parf-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -371,7 +371,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/rraa-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/rraa-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/rraa-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/rraa-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -395,7 +395,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
@@ -445,7 +445,7 @@
 {
     NS_LOG_FUNCTION(this << station);
     WifiRraaThresholds thresholds = GetThresholds(station, station->m_rateIndex);
-    double ploss = (station->m_nFailed / thresholds.m_ewnd);
+    auto ploss = (static_cast<double>(station->m_nFailed) / thresholds.m_ewnd);
     if (station->m_counter == 0 || ploss > thresholds.m_mtl)
     {
         if (ploss > thresholds.m_mtl)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/rate-control/rrpaa-wifi-manager.cc ns-3.39/src/wifi/model/rate-control/rrpaa-wifi-manager.cc
--- ns-3.38/src/wifi/model/rate-control/rrpaa-wifi-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/rate-control/rrpaa-wifi-manager.cc	2023-07-05 14:23:48.117428300 -0700
@@ -448,7 +448,7 @@
         channelWidth = 20;
     }
     WifiMode mode;
-    if (GetUseNonErpProtection() == false)
+    if (!GetUseNonErpProtection())
     {
         mode = GetSupported(station, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/simple-frame-capture-model.cc ns-3.39/src/wifi/model/simple-frame-capture-model.cc
--- ns-3.38/src/wifi/model/simple-frame-capture-model.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/simple-frame-capture-model.cc	2023-07-05 14:23:48.117428300 -0700
@@ -79,12 +79,8 @@
 SimpleFrameCaptureModel::CaptureNewFrame(Ptr<Event> currentEvent, Ptr<Event> newEvent) const
 {
     NS_LOG_FUNCTION(this);
-    if ((WToDbm(currentEvent->GetRxPowerW()) + GetMargin()) < WToDbm(newEvent->GetRxPowerW()) &&
-        (IsInCaptureWindow(currentEvent->GetStartTime())))
-    {
-        return true;
-    }
-    return false;
+    return WToDbm(currentEvent->GetRxPowerW()) + GetMargin() < WToDbm(newEvent->GetRxPowerW()) &&
+           IsInCaptureWindow(currentEvent->GetStartTime());
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/spectrum-wifi-phy.cc ns-3.39/src/wifi/model/spectrum-wifi-phy.cc
--- ns-3.38/src/wifi/model/spectrum-wifi-phy.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/spectrum-wifi-phy.cc	2023-07-05 14:23:48.121428500 -0700
@@ -32,11 +32,15 @@
 
 #include "ns3/boolean.h"
 #include "ns3/double.h"
+#include "ns3/he-phy.h"
 #include "ns3/log.h"
 #include "ns3/node.h"
 #include "ns3/simulator.h"
+#include "ns3/spectrum-channel.h"
 #include "ns3/wifi-net-device.h"
 
+#include <algorithm>
+
 namespace ns3
 {
 
@@ -58,6 +62,12 @@
                           MakeBooleanAccessor(&SpectrumWifiPhy::m_disableWifiReception),
                           MakeBooleanChecker())
             .AddAttribute(
+                "TrackSignalsFromInactiveInterfaces",
+                "Enable or disable tracking signals coming from inactive spectrum PHY interfaces",
+                BooleanValue(false),
+                MakeBooleanAccessor(&SpectrumWifiPhy::m_trackSignalsInactiveInterfaces),
+                MakeBooleanChecker())
+            .AddAttribute(
                 "TxMaskInnerBandMinimumRejection",
                 "Minimum rejection (dBr) for the inner band of the transmit spectrum mask",
                 DoubleValue(-20.0),
@@ -83,6 +93,8 @@
 }
 
 SpectrumWifiPhy::SpectrumWifiPhy()
+    : m_spectrumPhyInterfaces{},
+      m_currentSpectrumPhyInterface{nullptr}
 {
     NS_LOG_FUNCTION(this);
 }
@@ -96,11 +108,9 @@
 SpectrumWifiPhy::DoDispose()
 {
     NS_LOG_FUNCTION(this);
-    m_channel = nullptr;
-    m_wifiSpectrumPhyInterface = nullptr;
+    m_spectrumPhyInterfaces.clear();
+    m_currentSpectrumPhyInterface = nullptr;
     m_antenna = nullptr;
-    m_rxSpectrumModel = nullptr;
-    m_ruBands.clear();
     WifiPhy::DoDispose();
 }
 
@@ -109,59 +119,18 @@
 {
     NS_LOG_FUNCTION(this);
     WifiPhy::DoInitialize();
-    // This connection is deferred until frequency and channel width are set
-    if (m_channel && m_wifiSpectrumPhyInterface)
-    {
-        m_channel->AddRx(m_wifiSpectrumPhyInterface);
-    }
-    else
-    {
-        NS_FATAL_ERROR("SpectrumWifiPhy misses channel and WifiSpectrumPhyInterface objects at "
-                       "initialization time");
-    }
-}
-
-Ptr<const SpectrumModel>
-SpectrumWifiPhy::GetRxSpectrumModel()
-{
-    NS_LOG_FUNCTION(this);
-    if (m_rxSpectrumModel)
-    {
-        return m_rxSpectrumModel;
-    }
-    else
-    {
-        if (GetFrequency() == 0)
-        {
-            NS_LOG_DEBUG("Frequency is not set; returning 0");
-            return nullptr;
-        }
-        else
-        {
-            uint16_t channelWidth = GetChannelWidth();
-            NS_LOG_DEBUG("Creating spectrum model from frequency/width pair of ("
-                         << GetFrequency() << ", " << channelWidth << ")");
-            m_rxSpectrumModel =
-                WifiSpectrumValueHelper::GetSpectrumModel(GetFrequency(),
-                                                          channelWidth,
-                                                          GetBandBandwidth(),
-                                                          GetGuardBandwidth(channelWidth));
-            UpdateInterferenceHelperBands();
-        }
-    }
-    return m_rxSpectrumModel;
 }
 
 void
 SpectrumWifiPhy::UpdateInterferenceHelperBands()
 {
     NS_LOG_FUNCTION(this);
+    NS_ASSERT(!m_spectrumPhyInterfaces.empty());
     uint16_t channelWidth = GetChannelWidth();
-    m_interference->RemoveBands();
+    WifiSpectrumPhyInterface::WifiSpectrumBands bands{};
     if (channelWidth < 20)
     {
-        WifiSpectrumBand band = GetBand(channelWidth);
-        m_interference->AddBand(band);
+        bands.push_back(GetBand(channelWidth));
     }
     else
     {
@@ -169,59 +138,32 @@
         {
             for (uint32_t i = 0; i < (channelWidth / bw); ++i)
             {
-                m_interference->AddBand(GetBand(bw, i));
+                bands.push_back(GetBand(bw, i));
             }
         }
     }
+    WifiSpectrumPhyInterface::WifiSpectrumBands allBands{bands};
     if (GetStandard() >= WIFI_STANDARD_80211ax)
     {
-        // For a given RU type, some RUs over a channel occupy the same tones as
-        // the corresponding RUs over a subchannel, while some others not. For instance,
-        // the first nine 26-tone RUs over an 80 MHz channel occupy the same tones as
-        // the first nine 26-tone RUs over the lowest 40 MHz subchannel. Therefore, we
-        // need to store all the bands in a set (which removes duplicates) and then
-        // pass the elements in the set to AddBand (to which we cannot pass duplicates)
-        if (m_ruBands[channelWidth].empty())
+        auto&& ruBands = HePhy::GetRuBands(this, channelWidth, GetGuardBandwidth(channelWidth));
+        for (const auto& bandRuPair : ruBands)
         {
-            for (uint16_t bw = 160; bw >= 20; bw = bw / 2)
-            {
-                for (uint32_t i = 0; i < (channelWidth / bw); ++i)
-                {
-                    for (uint32_t type = 0; type < 7; type++)
-                    {
-                        HeRu::RuType ruType = static_cast<HeRu::RuType>(type);
-                        std::size_t nRus = HeRu::GetNRus(bw, ruType);
-                        for (std::size_t phyIndex = 1; phyIndex <= nRus; phyIndex++)
-                        {
-                            HeRu::SubcarrierGroup group =
-                                HeRu::GetSubcarrierGroup(bw, ruType, phyIndex);
-                            HeRu::SubcarrierRange range =
-                                std::make_pair(group.front().first, group.back().second);
-                            WifiSpectrumBand band =
-                                ConvertHeRuSubcarriers(bw,
-                                                       GetGuardBandwidth(channelWidth),
-                                                       range,
-                                                       i);
-                            std::size_t index =
-                                (bw == 160 && phyIndex > nRus / 2 ? phyIndex - nRus / 2 : phyIndex);
-                            bool primary80IsLower80 =
-                                (GetOperatingChannel().GetPrimaryChannelIndex(20) < bw / 40);
-                            bool primary80 = (bw < 160 || ruType == HeRu::RU_2x996_TONE ||
-                                              (primary80IsLower80 && phyIndex <= nRus / 2) ||
-                                              (!primary80IsLower80 && phyIndex > nRus / 2));
-                            HeRu::RuSpec ru(ruType, index, primary80);
-                            NS_ABORT_IF(ru.GetPhyIndex(bw,
-                                                       GetOperatingChannel().GetPrimaryChannelIndex(
-                                                           20)) != phyIndex);
-                            m_ruBands[channelWidth].insert({band, ru});
-                        }
-                    }
-                }
-            }
+            allBands.push_back(bandRuPair.first);
         }
-        for (const auto& bandRuPair : m_ruBands[channelWidth])
+        m_currentSpectrumPhyInterface->SetRuBands(std::move(ruBands));
+    }
+
+    m_currentSpectrumPhyInterface->SetBands(std::move(bands));
+
+    if (m_interference->HasBands())
+    {
+        m_interference->UpdateBands(allBands, GetCurrentFrequencyRange());
+    }
+    else
+    {
+        for (const auto& band : allBands)
         {
-            m_interference->AddBand(bandRuPair.first);
+            m_interference->AddBand(band);
         }
     }
 }
@@ -229,30 +171,76 @@
 Ptr<Channel>
 SpectrumWifiPhy::GetChannel() const
 {
-    return m_channel;
+    NS_ABORT_IF(!m_currentSpectrumPhyInterface);
+    return m_currentSpectrumPhyInterface->GetChannel();
 }
 
 void
-SpectrumWifiPhy::SetChannel(const Ptr<SpectrumChannel> channel)
+SpectrumWifiPhy::AddChannel(const Ptr<SpectrumChannel> channel, const FrequencyRange& freqRange)
 {
-    m_channel = channel;
+    NS_LOG_FUNCTION(this << channel << freqRange);
+
+    const auto foundOverlappingChannel =
+        std::any_of(m_spectrumPhyInterfaces.cbegin(),
+                    m_spectrumPhyInterfaces.cend(),
+                    [freqRange, channel](const auto& item) {
+                        const auto spectrumRange = item.first;
+                        const auto noOverlap =
+                            ((freqRange.minFrequency >= spectrumRange.maxFrequency) ||
+                             (freqRange.maxFrequency <= spectrumRange.minFrequency));
+                        return (!noOverlap);
+                    });
+    NS_ABORT_MSG_IF(foundOverlappingChannel,
+                    "Added a wifi spectrum channel that overlaps with another existing wifi "
+                    "spectrum channel");
+
+    auto wifiSpectrumPhyInterface = CreateObject<WifiSpectrumPhyInterface>(freqRange);
+    wifiSpectrumPhyInterface->SetSpectrumWifiPhy(this);
+    wifiSpectrumPhyInterface->SetChannel(channel);
+    if (GetDevice())
+    {
+        wifiSpectrumPhyInterface->SetDevice(GetDevice());
+    }
+    m_spectrumPhyInterfaces.emplace(freqRange, wifiSpectrumPhyInterface);
 }
 
 void
-SpectrumWifiPhy::ResetSpectrumModel()
+SpectrumWifiPhy::ResetSpectrumModelIfNeeded()
 {
     NS_LOG_FUNCTION(this);
-    NS_ASSERT_MSG(IsInitialized(), "Executing method before run-time");
-    uint16_t channelWidth = GetChannelWidth();
-    NS_LOG_DEBUG("Run-time change of spectrum model from frequency/width pair of ("
-                 << GetFrequency() << ", " << channelWidth << ")");
-    // Replace existing spectrum model with new one, and must call AddRx ()
-    // on the SpectrumChannel to provide this new spectrum model to it
-    m_rxSpectrumModel = WifiSpectrumValueHelper::GetSpectrumModel(GetFrequency(),
-                                                                  channelWidth,
-                                                                  GetBandBandwidth(),
-                                                                  GetGuardBandwidth(channelWidth));
-    m_channel->AddRx(m_wifiSpectrumPhyInterface);
+
+    // We have to reset the spectrum model because we changed RF channel. Consequently,
+    // we also have to add the spectrum interface to the spectrum channel again because
+    // MultiModelSpectrumChannel keeps spectrum interfaces in a map indexed by the RX
+    // spectrum model UID (which has changed after channel switching).
+    // Both SingleModelSpectrumChannel and MultiModelSpectrumChannel ensure not to keep
+    // duplicated spectrum interfaces (the latter removes the spectrum interface and adds
+    // it again in the entry associated with the new RX spectrum model UID)
+
+    const auto channelWidth = GetChannelWidth();
+    const auto centerFrequency = GetFrequency();
+    if (m_currentSpectrumPhyInterface->GetCenterFrequency() == centerFrequency)
+    {
+        // Center frequency has not changed for that interface, hence we do not need to
+        // reset the spectrum model nor update any band stored in the interference helper
+        if (!m_trackSignalsInactiveInterfaces)
+        {
+            // If we are not tracking signals from inactive interface,
+            // this means the spectrum interface has been disconnected
+            // from the spectrum channel and has to be connected back
+            m_currentSpectrumPhyInterface->GetChannel()->AddRx(m_currentSpectrumPhyInterface);
+        }
+        return;
+    }
+
+    // Replace existing spectrum model with new one
+    m_currentSpectrumPhyInterface->SetRxSpectrumModel(centerFrequency,
+                                                      channelWidth,
+                                                      GetSubcarrierSpacing(),
+                                                      GetGuardBandwidth(channelWidth));
+
+    m_currentSpectrumPhyInterface->GetChannel()->AddRx(m_currentSpectrumPhyInterface);
+
     UpdateInterferenceHelperBands();
 }
 
@@ -260,10 +248,45 @@
 SpectrumWifiPhy::DoChannelSwitch()
 {
     NS_LOG_FUNCTION(this);
+    const auto frequencyBefore = GetOperatingChannel().IsSet() ? GetFrequency() : 0;
+    const auto widthBefore = GetOperatingChannel().IsSet() ? GetChannelWidth() : 0;
     WifiPhy::DoChannelSwitch();
+    const auto frequencyAfter = GetFrequency();
+    const auto widthAfter = GetChannelWidth();
+    if ((frequencyBefore == frequencyAfter) && (widthBefore == widthAfter))
+    {
+        NS_LOG_DEBUG("Same RF channel as before, do nothing");
+        if (IsInitialized())
+        {
+            SwitchMaybeToCcaBusy(nullptr);
+        }
+        return;
+    }
+
+    auto newSpectrumPhyInterface = GetInterfaceCoveringChannelBand(frequencyAfter, widthAfter);
+    const auto interfaceChanged = (newSpectrumPhyInterface != m_currentSpectrumPhyInterface);
+
+    NS_ABORT_MSG_IF(!newSpectrumPhyInterface,
+                    "No spectrum channel covers frequency range ["
+                        << frequencyAfter - (widthAfter / 2) << " MHz - "
+                        << frequencyAfter + (widthAfter / 2) << " MHz]");
+
+    if (interfaceChanged)
+    {
+        NS_LOG_DEBUG("Switch to existing RF interface with frequency/width pair of ("
+                     << frequencyAfter << ", " << widthAfter << ")");
+        if (m_currentSpectrumPhyInterface && !m_trackSignalsInactiveInterfaces)
+        {
+            m_currentSpectrumPhyInterface->GetChannel()->RemoveRx(m_currentSpectrumPhyInterface);
+        }
+    }
+
+    m_currentSpectrumPhyInterface = newSpectrumPhyInterface;
+    ResetSpectrumModelIfNeeded();
+
     if (IsInitialized())
     {
-        ResetSpectrumModel();
+        SwitchMaybeToCcaBusy(nullptr);
     }
 }
 
@@ -274,9 +297,10 @@
 }
 
 void
-SpectrumWifiPhy::StartRx(Ptr<SpectrumSignalParameters> rxParams)
+SpectrumWifiPhy::StartRx(Ptr<SpectrumSignalParameters> rxParams,
+                         Ptr<const WifiSpectrumPhyInterface> interface)
 {
-    NS_LOG_FUNCTION(this << rxParams);
+    NS_LOG_FUNCTION(this << rxParams << interface);
     Time rxDuration = rxParams->duration;
     Ptr<SpectrumValue> receivedSignalPsd = rxParams->psd;
     NS_LOG_DEBUG("Received signal with PSD " << *receivedSignalPsd << " and duration "
@@ -293,76 +317,54 @@
     // spectral mask representing our filtering allows) to find the
     // total energy apparent to the "demodulator".
     // This is done per 20 MHz channel band.
-    uint16_t channelWidth = GetChannelWidth();
+    const auto channelWidth = interface ? interface->GetChannelWidth() : GetChannelWidth();
+    const auto& bands =
+        interface ? interface->GetBands() : m_currentSpectrumPhyInterface->GetBands();
     double totalRxPowerW = 0;
     RxPowerWattPerChannelBand rxPowerW;
 
-    if ((channelWidth == 5) || (channelWidth == 10))
-    {
-        WifiSpectrumBand filteredBand = GetBand(channelWidth);
+    std::size_t index = 0;
+    uint16_t prevBw = 0;
+    for (const auto& band : bands)
+    {
+        uint16_t bw = (band.frequencies.second - band.frequencies.first) / 1e6;
+        NS_ASSERT(bw <= channelWidth);
+        index = ((bw != prevBw) ? 0 : (index + 1));
         double rxPowerPerBandW =
-            WifiSpectrumValueHelper::GetBandPowerW(receivedSignalPsd, filteredBand);
-        NS_LOG_DEBUG("Signal power received (watts) before antenna gain: " << rxPowerPerBandW);
+            WifiSpectrumValueHelper::GetBandPowerW(receivedSignalPsd, band.indices);
+        NS_LOG_DEBUG("Signal power received (watts) before antenna gain for "
+                     << bw << " MHz channel band " << index << ": " << band);
         rxPowerPerBandW *= DbToRatio(GetRxGain());
-        totalRxPowerW += rxPowerPerBandW;
-        rxPowerW.insert({filteredBand, rxPowerPerBandW});
+        rxPowerW.insert({band, rxPowerPerBandW});
         NS_LOG_DEBUG("Signal power received after antenna gain for "
-                     << channelWidth << " MHz channel: " << rxPowerPerBandW << " W ("
+                     << bw << " MHz channel band " << index << ": " << rxPowerPerBandW << " W ("
                      << WToDbm(rxPowerPerBandW) << " dBm)");
-    }
-
-    // 20 MHz is handled apart since the totalRxPowerW is computed through it
-    for (uint16_t bw = 160; bw > 20; bw = bw / 2)
-    {
-        for (uint32_t i = 0; i < (channelWidth / bw); i++)
+        if (bw <= 20)
         {
-            NS_ASSERT(channelWidth >= bw);
-            WifiSpectrumBand filteredBand = GetBand(bw, i);
-            double rxPowerPerBandW =
-                WifiSpectrumValueHelper::GetBandPowerW(receivedSignalPsd, filteredBand);
-            NS_LOG_DEBUG("Signal power received (watts) before antenna gain for "
-                         << bw << " MHz channel band " << +i << ": " << rxPowerPerBandW);
-            rxPowerPerBandW *= DbToRatio(GetRxGain());
-            rxPowerW.insert({filteredBand, rxPowerPerBandW});
-            NS_LOG_DEBUG("Signal power received after antenna gain for "
-                         << bw << " MHz channel band " << +i << ": " << rxPowerPerBandW << " W ("
-                         << WToDbm(rxPowerPerBandW) << " dBm)");
+            totalRxPowerW += rxPowerPerBandW;
         }
-    }
-
-    for (uint32_t i = 0; i < (channelWidth / 20); i++)
-    {
-        WifiSpectrumBand filteredBand = GetBand(20, i);
-        double rxPowerPerBandW =
-            WifiSpectrumValueHelper::GetBandPowerW(receivedSignalPsd, filteredBand);
-        NS_LOG_DEBUG("Signal power received (watts) before antenna gain for 20 MHz channel band "
-                     << +i << ": " << rxPowerPerBandW);
-        rxPowerPerBandW *= DbToRatio(GetRxGain());
-        totalRxPowerW += rxPowerPerBandW;
-        rxPowerW.insert({filteredBand, rxPowerPerBandW});
-        NS_LOG_DEBUG("Signal power received after antenna gain for 20 MHz channel band "
-                     << +i << ": " << rxPowerPerBandW << " W (" << WToDbm(rxPowerPerBandW)
-                     << " dBm)");
+        prevBw = bw;
     }
 
     if (GetStandard() >= WIFI_STANDARD_80211ax)
     {
-        NS_ASSERT(!m_ruBands[channelWidth].empty());
-        for (const auto& bandRuPair : m_ruBands[channelWidth])
+        const auto& ruBands =
+            interface ? interface->GetRuBands() : m_currentSpectrumPhyInterface->GetRuBands();
+        NS_ASSERT(!ruBands.empty());
+        for (const auto& [band, ru] : ruBands)
         {
             double rxPowerPerBandW =
-                WifiSpectrumValueHelper::GetBandPowerW(receivedSignalPsd, bandRuPair.first);
+                WifiSpectrumValueHelper::GetBandPowerW(receivedSignalPsd, band.indices);
             NS_LOG_DEBUG("Signal power received (watts) before antenna gain for RU with type "
-                         << bandRuPair.second.GetRuType() << " and index "
-                         << bandRuPair.second.GetIndex() << " -> (" << bandRuPair.first.first
-                         << "; " << bandRuPair.first.second << "): " << rxPowerPerBandW);
+                         << ru.GetRuType() << " and index " << ru.GetIndex() << " -> ("
+                         << band.indices.first << "; " << band.indices.second
+                         << "): " << rxPowerPerBandW);
             rxPowerPerBandW *= DbToRatio(GetRxGain());
             NS_LOG_DEBUG("Signal power received after antenna gain for RU with type "
-                         << bandRuPair.second.GetRuType() << " and index "
-                         << bandRuPair.second.GetIndex() << " -> (" << bandRuPair.first.first
-                         << "; " << bandRuPair.first.second << "): " << rxPowerPerBandW << " W ("
-                         << WToDbm(rxPowerPerBandW) << " dBm)");
-            rxPowerW.insert({bandRuPair.first, rxPowerPerBandW});
+                         << ru.GetRuType() << " and index " << ru.GetIndex() << " -> ("
+                         << band.indices.first << "; " << band.indices.second << "): "
+                         << rxPowerPerBandW << " W (" << WToDbm(rxPowerPerBandW) << " dBm)");
+            rxPowerW.insert({band, rxPowerPerBandW});
         }
     }
 
@@ -382,6 +384,7 @@
         SwitchMaybeToCcaBusy(nullptr);
         return;
     }
+
     if (wifiRxParams && m_disableWifiReception)
     {
         NS_LOG_INFO("Received Wi-Fi signal but blocked from syncing");
@@ -389,6 +392,16 @@
         SwitchMaybeToCcaBusy(nullptr);
         return;
     }
+
+    if (m_trackSignalsInactiveInterfaces && interface &&
+        (interface != m_currentSpectrumPhyInterface))
+    {
+        NS_LOG_INFO("Received Wi-Fi signal from a non-active PHY interface");
+        m_interference->AddForeignSignal(rxDuration, rxPowerW);
+        SwitchMaybeToCcaBusy(nullptr);
+        return;
+    }
+
     // Do no further processing if signal is too weak
     // Current implementation assumes constant RX power over the PPDU duration
     // Compare received TX power per MHz to normalized RX sensitivity
@@ -424,7 +437,7 @@
     return GetPhyEntityForPpdu(ppdu)->GetRxPpduFromTxPpdu(ppdu);
 }
 
-Ptr<Object>
+Ptr<AntennaModel>
 SpectrumWifiPhy::GetAntenna() const
 {
     return m_antenna;
@@ -438,12 +451,14 @@
 }
 
 void
-SpectrumWifiPhy::CreateWifiSpectrumPhyInterface(Ptr<NetDevice> device)
+SpectrumWifiPhy::SetDevice(const Ptr<WifiNetDevice> device)
 {
     NS_LOG_FUNCTION(this << device);
-    m_wifiSpectrumPhyInterface = CreateObject<WifiSpectrumPhyInterface>();
-    m_wifiSpectrumPhyInterface->SetSpectrumWifiPhy(this);
-    m_wifiSpectrumPhyInterface->SetDevice(device);
+    WifiPhy::SetDevice(device);
+    for (auto& spectrumPhyInterface : m_spectrumPhyInterfaces)
+    {
+        spectrumPhyInterface.second->SetDevice(device);
+    }
 }
 
 void
@@ -457,52 +472,8 @@
 SpectrumWifiPhy::Transmit(Ptr<WifiSpectrumSignalParameters> txParams)
 {
     NS_LOG_FUNCTION(this << txParams);
-
-    // Finish configuration
-    NS_ASSERT_MSG(m_wifiSpectrumPhyInterface,
-                  "SpectrumPhy() is not set; maybe forgot to call CreateWifiSpectrumPhyInterface?");
-    txParams->txPhy = m_wifiSpectrumPhyInterface->GetObject<SpectrumPhy>();
-    txParams->txAntenna = m_antenna;
-
-    m_channel->StartTx(txParams);
-}
-
-uint32_t
-SpectrumWifiPhy::GetBandBandwidth() const
-{
-    uint32_t bandBandwidth = 0;
-    switch (GetStandard())
-    {
-    case WIFI_STANDARD_80211a:
-    case WIFI_STANDARD_80211g:
-    case WIFI_STANDARD_80211b:
-    case WIFI_STANDARD_80211n:
-    case WIFI_STANDARD_80211ac:
-        // Use OFDM subcarrier width of 312.5 KHz as band granularity
-        bandBandwidth = 312500;
-        break;
-    case WIFI_STANDARD_80211p:
-        if (GetChannelWidth() == 5)
-        {
-            // Use OFDM subcarrier width of 78.125 KHz as band granularity
-            bandBandwidth = 78125;
-        }
-        else
-        {
-            // Use OFDM subcarrier width of 156.25 KHz as band granularity
-            bandBandwidth = 156250;
-        }
-        break;
-    case WIFI_STANDARD_80211ax:
-    case WIFI_STANDARD_80211be:
-        // Use OFDM subcarrier width of 78.125 KHz as band granularity
-        bandBandwidth = 78125;
-        break;
-    default:
-        NS_FATAL_ERROR("Standard unknown: " << GetStandard());
-        break;
-    }
-    return bandBandwidth;
+    NS_ABORT_IF(!m_currentSpectrumPhyInterface);
+    m_currentSpectrumPhyInterface->StartTx(txParams);
 }
 
 uint16_t
@@ -527,67 +498,50 @@
     return guardBandwidth;
 }
 
-WifiSpectrumBand
-SpectrumWifiPhy::GetBand(uint16_t bandWidth, uint8_t bandIndex)
+WifiSpectrumBandInfo
+SpectrumWifiPhy::GetBand(uint16_t bandWidth, uint8_t bandIndex /* = 0 */)
 {
-    uint16_t channelWidth = GetChannelWidth();
-    uint32_t bandBandwidth = GetBandBandwidth();
-    size_t numBandsInChannel = static_cast<size_t>(channelWidth * 1e6 / bandBandwidth);
-    size_t numBandsInBand = static_cast<size_t>(bandWidth * 1e6 / bandBandwidth);
+    auto channelWidth = GetChannelWidth();
+    auto subcarrierSpacing = GetSubcarrierSpacing();
+    auto numBandsInChannel = static_cast<size_t>(channelWidth * 1e6 / subcarrierSpacing);
+    auto numBandsInBand = static_cast<size_t>(bandWidth * 1e6 / subcarrierSpacing);
     if (numBandsInBand % 2 == 0)
     {
         numBandsInChannel += 1; // symmetry around center frequency
     }
-    size_t totalNumBands = GetRxSpectrumModel()->GetNumBands();
+    NS_ABORT_IF(!m_currentSpectrumPhyInterface);
+    auto rxSpectrumModel = m_currentSpectrumPhyInterface->GetRxSpectrumModel();
+    size_t totalNumBands = rxSpectrumModel->GetNumBands();
     NS_ASSERT_MSG((numBandsInChannel % 2 == 1) && (totalNumBands % 2 == 1),
                   "Should have odd number of bands");
     NS_ASSERT_MSG((bandIndex * bandWidth) < channelWidth, "Band index is out of bound");
-    WifiSpectrumBand band;
-    band.first = ((totalNumBands - numBandsInChannel) / 2) + (bandIndex * numBandsInBand);
-    band.second = band.first + numBandsInBand - 1;
-    if (band.first >= totalNumBands / 2)
+    NS_ASSERT(totalNumBands >= numBandsInChannel);
+    auto startIndex = ((totalNumBands - numBandsInChannel) / 2) + (bandIndex * numBandsInBand);
+    auto stopIndex = startIndex + numBandsInBand - 1;
+    auto frequencies = ConvertIndicesToFrequencies({startIndex, stopIndex});
+    auto freqRange = m_currentSpectrumPhyInterface->GetFrequencyRange();
+    NS_ASSERT(frequencies.first >= (freqRange.minFrequency * 1e6));
+    NS_ASSERT(frequencies.second <= (freqRange.maxFrequency * 1e6));
+    NS_ASSERT((frequencies.second - frequencies.first) == (bandWidth * 1e6));
+    if (startIndex >= totalNumBands / 2)
     {
         // step past DC
-        band.first += 1;
+        startIndex += 1;
     }
-    return band;
+    return {{startIndex, stopIndex}, frequencies};
 }
 
-WifiSpectrumBand
-SpectrumWifiPhy::ConvertHeRuSubcarriers(uint16_t bandWidth,
-                                        uint16_t guardBandwidth,
-                                        HeRu::SubcarrierRange range,
-                                        uint8_t bandIndex) const
-{
-    WifiSpectrumBand convertedSubcarriers;
-    uint32_t nGuardBands =
-        static_cast<uint32_t>(((2 * guardBandwidth * 1e6) / GetBandBandwidth()) + 0.5);
-    uint32_t centerFrequencyIndex = 0;
-    switch (bandWidth)
-    {
-    case 20:
-        centerFrequencyIndex = (nGuardBands / 2) + 6 + 122;
-        break;
-    case 40:
-        centerFrequencyIndex = (nGuardBands / 2) + 12 + 244;
-        break;
-    case 80:
-        centerFrequencyIndex = (nGuardBands / 2) + 12 + 500;
-        break;
-    case 160:
-        centerFrequencyIndex = (nGuardBands / 2) + 12 + 1012;
-        break;
-    default:
-        NS_FATAL_ERROR("ChannelWidth " << bandWidth << " unsupported");
-        break;
-    }
-
-    size_t numBandsInBand = static_cast<size_t>(bandWidth * 1e6 / GetBandBandwidth());
-    centerFrequencyIndex += numBandsInBand * bandIndex;
-
-    convertedSubcarriers.first = centerFrequencyIndex + range.first;
-    convertedSubcarriers.second = centerFrequencyIndex + range.second;
-    return convertedSubcarriers;
+WifiSpectrumBandFrequencies
+SpectrumWifiPhy::ConvertIndicesToFrequencies(const WifiSpectrumBandIndices& indices) const
+{
+    NS_ABORT_IF(!m_currentSpectrumPhyInterface);
+    auto rxSpectrumModel = m_currentSpectrumPhyInterface->GetRxSpectrumModel();
+    auto startGuardBand = rxSpectrumModel->Begin();
+    auto startChannel = std::next(startGuardBand, indices.first);
+    auto endChannel = std::next(startGuardBand, indices.second + 1);
+    auto lowFreq = static_cast<uint64_t>(startChannel->fc);
+    auto highFreq = static_cast<uint64_t>(endChannel->fc);
+    return {lowFreq, highFreq};
 }
 
 std::tuple<double, double, double>
@@ -598,4 +552,35 @@
                            m_txMaskOuterBandMaximumRejection);
 }
 
+FrequencyRange
+SpectrumWifiPhy::GetCurrentFrequencyRange() const
+{
+    NS_ABORT_IF(!m_currentSpectrumPhyInterface);
+    return m_currentSpectrumPhyInterface->GetFrequencyRange();
+}
+
+Ptr<WifiSpectrumPhyInterface>
+SpectrumWifiPhy::GetInterfaceCoveringChannelBand(uint16_t frequency, uint16_t width) const
+{
+    const auto lowFreq = frequency - (width / 2);
+    const auto highFreq = frequency + (width / 2);
+    const auto it = std::find_if(m_spectrumPhyInterfaces.cbegin(),
+                                 m_spectrumPhyInterfaces.cend(),
+                                 [lowFreq, highFreq](const auto& item) {
+                                     return ((lowFreq >= item.first.minFrequency) &&
+                                             (highFreq <= item.first.maxFrequency));
+                                 });
+    if (it == std::end(m_spectrumPhyInterfaces))
+    {
+        return nullptr;
+    }
+    return it->second;
+}
+
+Ptr<WifiSpectrumPhyInterface>
+SpectrumWifiPhy::GetCurrentInterface() const
+{
+    return m_currentSpectrumPhyInterface;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/spectrum-wifi-phy.h ns-3.39/src/wifi/model/spectrum-wifi-phy.h
--- ns-3.38/src/wifi/model/spectrum-wifi-phy.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/spectrum-wifi-phy.h	2023-07-05 14:23:48.121428500 -0700
@@ -28,16 +28,17 @@
 #include "wifi-phy.h"
 
 #include "ns3/antenna-model.h"
-#include "ns3/spectrum-channel.h"
-#include "ns3/spectrum-model.h"
 
 #include <map>
+#include <optional>
 
 class SpectrumWifiPhyFilterTest;
 
 namespace ns3
 {
 
+class SpectrumChannel;
+struct SpectrumSignalParameters;
 class WifiSpectrumPhyInterface;
 struct WifiSpectrumSignalParameters;
 
@@ -69,35 +70,36 @@
     ~SpectrumWifiPhy() override;
 
     // Implementation of pure virtual method.
+    void SetDevice(const Ptr<WifiNetDevice> device) override;
     void StartTx(Ptr<const WifiPpdu> ppdu) override;
     Ptr<Channel> GetChannel() const override;
     uint16_t GetGuardBandwidth(uint16_t currentChannelWidth) const override;
     std::tuple<double, double, double> GetTxMaskRejectionParams() const override;
+    WifiSpectrumBandInfo GetBand(uint16_t bandWidth, uint8_t bandIndex = 0) override;
+    FrequencyRange GetCurrentFrequencyRange() const override;
+    WifiSpectrumBandFrequencies ConvertIndicesToFrequencies(
+        const WifiSpectrumBandIndices& indices) const override;
 
     /**
-     * Set the SpectrumChannel this SpectrumWifiPhy is to be connected to.
+     * Attach a SpectrumChannel to use for a given frequency range.
      *
-     * \param channel the SpectrumChannel this SpectrumWifiPhy is to be connected to
+     * \param channel the SpectrumChannel to attach
+     * \param freqRange the frequency range, bounded by a minFrequency and a maxFrequency in MHz
      */
-    void SetChannel(const Ptr<SpectrumChannel> channel);
+    void AddChannel(const Ptr<SpectrumChannel> channel,
+                    const FrequencyRange& freqRange = WHOLE_WIFI_SPECTRUM);
 
     /**
      * Input method for delivering a signal from the spectrum channel
      * and low-level PHY interface to this SpectrumWifiPhy instance.
      *
      * \param rxParams Input signal parameters
+     * \param interface the Spectrum PHY interface for which the signal has been detected
      */
-    void StartRx(Ptr<SpectrumSignalParameters> rxParams);
+    void StartRx(Ptr<SpectrumSignalParameters> rxParams,
+                 Ptr<const WifiSpectrumPhyInterface> interface);
 
     /**
-     * Method to encapsulate the creation of the WifiSpectrumPhyInterface
-     * object (used to bind the WifiSpectrumPhy to a SpectrumChannel) and
-     * to link it to this SpectrumWifiPhy instance
-     *
-     * \param device pointer to the NetDevice object including this new object
-     */
-    void CreateWifiSpectrumPhyInterface(Ptr<NetDevice> device);
-    /**
      * \param antenna an AntennaModel to include in the transmitted
      *                SpectrumSignalParameters (in case any objects downstream of the
      *                SpectrumWifiPhy wish to adjust signal properties based on the
@@ -114,18 +116,7 @@
      *
      * \return the AntennaModel used for reception
      */
-    Ptr<Object> GetAntenna() const;
-    /**
-     * \return the SpectrumModel that this SpectrumPhy expects to be used
-     *         for all SpectrumValues that are passed to StartRx. If 0 is
-     *         returned, it means that any model will be accepted.
-     */
-    Ptr<const SpectrumModel> GetRxSpectrumModel();
-
-    /**
-     * \return the width of each band (Hz)
-     */
-    uint32_t GetBandBandwidth() const;
+    Ptr<AntennaModel> GetAntenna() const;
 
     /**
      * Callback invoked when the PHY model starts to process a signal
@@ -160,42 +151,32 @@
      */
     Ptr<const WifiPpdu> GetRxPpduFromTxPpdu(Ptr<const WifiPpdu> ppdu);
 
+    /**
+     * Get the currently active spectrum PHY interface
+     *
+     * \return the current spectrum PHY interface
+     */
+    Ptr<WifiSpectrumPhyInterface> GetCurrentInterface() const;
+
   protected:
     void DoDispose() override;
     void DoInitialize() override;
 
-    /**
-     * Get the start band index and the stop band index for a given band
-     *
-     * \param bandWidth the width of the band to be returned (MHz)
-     * \param bandIndex the index of the band to be returned
-     *
-     * \return a pair of start and stop indexes that defines the band
-     */
-    WifiSpectrumBand GetBand(uint16_t bandWidth, uint8_t bandIndex = 0) override;
+    std::map<FrequencyRange, Ptr<WifiSpectrumPhyInterface>>
+        m_spectrumPhyInterfaces; //!< Spectrum PHY interfaces
+
+    Ptr<WifiSpectrumPhyInterface>
+        m_currentSpectrumPhyInterface; //!< The current Spectrum PHY interface (held for performance
+                                       //!< reasons)
 
   private:
     /**
-     * \param bandWidth the width (MHz) of the band used for the OFDMA transmission. Must be
-     *                  a multiple of 20 MHz
-     * \param guardBandwidth width of the guard band (MHz)
-     * \param range the subcarrier range of the HE RU
-     * \param bandIndex the index (starting at 0) of the band within the operating channel
-     * \return the converted subcarriers
-     *
-     * This is a helper function to convert HE RU subcarriers, which are relative to the center
-     * frequency subcarrier, to the indexes used by the Spectrum model.
+     * Perform run-time spectrum model change if the one used by the current Spectrum PHY interface
+     * has changed
      */
-    WifiSpectrumBand ConvertHeRuSubcarriers(uint16_t bandWidth,
-                                            uint16_t guardBandwidth,
-                                            HeRu::SubcarrierRange range,
-                                            uint8_t bandIndex = 0) const override;
+    void ResetSpectrumModelIfNeeded();
 
     /**
-     * Perform run-time spectrum model change
-     */
-    void ResetSpectrumModel();
-    /**
      * This function is called to update the bands handled by the InterferenceHelper.
      */
     void UpdateInterferenceHelperBands();
@@ -211,19 +192,22 @@
      */
     bool CanStartRx(Ptr<const WifiPpdu> ppdu, uint16_t txChannelWidth) const;
 
-    Ptr<SpectrumChannel> m_channel; //!< SpectrumChannel that this SpectrumWifiPhy is connected to
+    /**
+     * Get the spectrum PHY interface that covers a band portion of the RF channel
+     *
+     * \param frequency the center frequency in MHz of the RF channel band
+     * \param width the width in MHz of the RF channel band
+     * \return the spectrum PHY interface that covers the indicated band of the RF channel
+     */
+    Ptr<WifiSpectrumPhyInterface> GetInterfaceCoveringChannelBand(uint16_t frequency,
+                                                                  uint16_t width) const;
+
+    Ptr<AntennaModel> m_antenna; //!< antenna model
+
+    bool m_disableWifiReception;           //!< forces this PHY to fail to sync on any signal
+    bool m_trackSignalsInactiveInterfaces; //!< flag whether signals coming from inactive spectrum
+                                           //!< PHY interfaces are tracked
 
-    Ptr<WifiSpectrumPhyInterface> m_wifiSpectrumPhyInterface; //!< Spectrum PHY interface
-    Ptr<AntennaModel> m_antenna;                              //!< antenna model
-    mutable Ptr<const SpectrumModel> m_rxSpectrumModel;       //!< receive spectrum model
-
-    /// Map a spectrum band associated with an RU to the RU specification
-    typedef std::map<WifiSpectrumBand, HeRu::RuSpec> RuBand;
-
-    std::map<uint16_t, RuBand>
-        m_ruBands;               /**< For each channel width, store all the distinct spectrum
-                                      bands associated with every RU in a channel of that width */
-    bool m_disableWifiReception; //!< forces this PHY to fail to sync on any signal
     TracedCallback<bool, uint32_t, double, Time> m_signalCb; //!< Signal callback
 
     double m_txMaskInnerBandMinimumRejection; //!< The minimum rejection (in dBr) for the inner band
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ssid.cc ns-3.39/src/wifi/model/ssid.cc
--- ns-3.38/src/wifi/model/ssid.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/ssid.cc	2023-07-05 14:23:48.121428500 -0700
@@ -51,6 +51,12 @@
     }
 }
 
+void
+Ssid::Print(std::ostream& os) const
+{
+    os << "ssid=" << PeekString();
+}
+
 bool
 Ssid::IsEqual(const Ssid& o) const
 {
@@ -59,21 +65,13 @@
     {
         i++;
     }
-    if (m_ssid[i] != o.m_ssid[i])
-    {
-        return false;
-    }
-    return true;
+    return m_ssid[i] == o.m_ssid[i];
 }
 
 bool
 Ssid::IsBroadcast() const
 {
-    if (m_ssid[0] == 0)
-    {
-        return true;
-    }
-    return false;
+    return m_ssid[0] == 0;
 }
 
 char*
@@ -114,13 +112,6 @@
 
 ATTRIBUTE_HELPER_CPP(Ssid);
 
-std::ostream&
-operator<<(std::ostream& os, const Ssid& ssid)
-{
-    os << ssid.PeekString();
-    return os;
-}
-
 std::istream&
 operator>>(std::istream& is, Ssid& ssid)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/ssid.h ns-3.39/src/wifi/model/ssid.h
--- ns-3.38/src/wifi/model/ssid.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/ssid.h	2023-07-05 14:23:48.121428500 -0700
@@ -48,6 +48,7 @@
 
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Check if the two SSIDs are equal.
@@ -83,15 +84,6 @@
 };
 
 /**
- * Serialize SSID to the given ostream
- *
- * \param os the output stream
- * \param ssid the SSID
- *
- * \return std::ostream
- */
-std::ostream& operator<<(std::ostream& os, const Ssid& ssid);
-/**
  * Serialize from the given istream to this SSID.
  *
  * \param is the input stream
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/sta-wifi-mac.cc ns-3.39/src/wifi/model/sta-wifi-mac.cc
--- ns-3.38/src/wifi/model/sta-wifi-mac.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/sta-wifi-mac.cc	2023-07-05 14:23:48.121428500 -0700
@@ -27,13 +27,17 @@
 #include "qos-txop.h"
 #include "snr-tag.h"
 #include "wifi-assoc-manager.h"
+#include "wifi-mac-queue.h"
 #include "wifi-net-device.h"
 #include "wifi-phy.h"
 
+#include "ns3/eht-configuration.h"
+#include "ns3/emlsr-manager.h"
 #include "ns3/he-configuration.h"
 #include "ns3/ht-configuration.h"
 #include "ns3/log.h"
 #include "ns3/packet.h"
+#include "ns3/pair.h"
 #include "ns3/pointer.h"
 #include "ns3/random-variable-stream.h"
 #include "ns3/simulator.h"
@@ -94,6 +98,23 @@
                           StringValue("ns3::UniformRandomVariable[Min=50.0|Max=250.0]"),
                           MakePointerAccessor(&StaWifiMac::m_probeDelay),
                           MakePointerChecker<RandomVariableStream>())
+            .AddAttribute(
+                "PowerSaveMode",
+                "Enable/disable power save mode on the given link. The power management mode is "
+                "actually changed when the AP acknowledges a frame sent with the Power Management "
+                "field set to the value corresponding to the requested mode",
+                TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
+                PairValue<BooleanValue, UintegerValue>(),
+                MakePairAccessor<BooleanValue, UintegerValue>(&StaWifiMac::SetPowerSaveMode),
+                MakePairChecker<BooleanValue, UintegerValue>(MakeBooleanChecker(),
+                                                             MakeUintegerChecker<uint8_t>()))
+            .AddAttribute("PmModeSwitchTimeout",
+                          "If switching to a new Power Management mode is not completed within "
+                          "this amount of time, make another attempt at switching Power "
+                          "Management mode.",
+                          TimeValue(Seconds(0.1)),
+                          MakeTimeAccessor(&StaWifiMac::m_pmModeSwitchTimeout),
+                          MakeTimeChecker())
             .AddTraceSource("Assoc",
                             "Associated with an access point. If this is an MLD that associated "
                             "with an AP MLD, the AP MLD address is provided.",
@@ -142,6 +163,8 @@
 {
     NS_LOG_FUNCTION(this);
     StartScanning();
+    NS_ABORT_IF(!TraceConnectWithoutContext("AckedMpdu", MakeCallback(&StaWifiMac::TxOk, this)));
+    WifiMac::DoInitialize();
 }
 
 void
@@ -153,6 +176,11 @@
         m_assocManager->Dispose();
     }
     m_assocManager = nullptr;
+    if (m_emlsrManager)
+    {
+        m_emlsrManager->Dispose();
+    }
+    m_emlsrManager = nullptr;
     WifiMac::DoDispose();
 }
 
@@ -194,6 +222,20 @@
     m_assocManager->SetStaWifiMac(this);
 }
 
+void
+StaWifiMac::SetEmlsrManager(Ptr<EmlsrManager> emlsrManager)
+{
+    NS_LOG_FUNCTION(this << emlsrManager);
+    m_emlsrManager = emlsrManager;
+    m_emlsrManager->SetWifiMac(this);
+}
+
+Ptr<EmlsrManager>
+StaWifiMac::GetEmlsrManager() const
+{
+    return m_emlsrManager;
+}
+
 uint16_t
 StaWifiMac::GetAssociationId() const
 {
@@ -241,36 +283,38 @@
 }
 
 void
-StaWifiMac::SendProbeRequest()
+StaWifiMac::SendProbeRequest(uint8_t linkId)
 {
-    NS_LOG_FUNCTION(this);
+    NS_LOG_FUNCTION(this << linkId);
     WifiMacHeader hdr;
     hdr.SetType(WIFI_MAC_MGT_PROBE_REQUEST);
     hdr.SetAddr1(Mac48Address::GetBroadcast());
-    hdr.SetAddr2(GetAddress());
+    hdr.SetAddr2(GetFrameExchangeManager(linkId)->GetAddress());
     hdr.SetAddr3(Mac48Address::GetBroadcast());
     hdr.SetDsNotFrom();
     hdr.SetDsNotTo();
     Ptr<Packet> packet = Create<Packet>();
     MgtProbeRequestHeader probe;
-    probe.SetSsid(GetSsid());
-    probe.SetSupportedRates(GetSupportedRates(SINGLE_LINK_OP_ID));
+    probe.Get<Ssid>() = GetSsid();
+    auto supportedRates = GetSupportedRates(linkId);
+    probe.Get<SupportedRates>() = supportedRates.rates;
+    probe.Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
     if (GetHtSupported())
     {
-        probe.SetExtendedCapabilities(GetExtendedCapabilities());
-        probe.SetHtCapabilities(GetHtCapabilities(SINGLE_LINK_OP_ID));
+        probe.Get<ExtendedCapabilities>() = GetExtendedCapabilities();
+        probe.Get<HtCapabilities>() = GetHtCapabilities(linkId);
     }
-    if (GetVhtSupported(SINGLE_LINK_OP_ID))
+    if (GetVhtSupported(linkId))
     {
-        probe.SetVhtCapabilities(GetVhtCapabilities(SINGLE_LINK_OP_ID));
+        probe.Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
     }
     if (GetHeSupported())
     {
-        probe.SetHeCapabilities(GetHeCapabilities(SINGLE_LINK_OP_ID));
+        probe.Get<HeCapabilities>() = GetHeCapabilities(linkId);
     }
     if (GetEhtSupported())
     {
-        probe.SetEhtCapabilities(GetEhtCapabilities(SINGLE_LINK_OP_ID));
+        probe.Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
     }
     packet->AddHeader(probe);
 
@@ -311,26 +355,28 @@
 
     // lambda to set the fields of the (Re)Association Request
     auto fill = [&](auto&& frame) {
-        frame.SetSsid(GetSsid());
-        frame.SetSupportedRates(GetSupportedRates(linkId));
-        frame.SetCapabilities(GetCapabilities(linkId));
+        frame.template Get<Ssid>() = GetSsid();
+        auto supportedRates = GetSupportedRates(linkId);
+        frame.template Get<SupportedRates>() = supportedRates.rates;
+        frame.template Get<ExtendedSupportedRatesIE>() = supportedRates.extendedRates;
+        frame.Capabilities() = GetCapabilities(linkId);
         frame.SetListenInterval(0);
         if (GetHtSupported())
         {
-            frame.SetExtendedCapabilities(GetExtendedCapabilities());
-            frame.SetHtCapabilities(GetHtCapabilities(linkId));
+            frame.template Get<ExtendedCapabilities>() = GetExtendedCapabilities();
+            frame.template Get<HtCapabilities>() = GetHtCapabilities(linkId);
         }
         if (GetVhtSupported(linkId))
         {
-            frame.SetVhtCapabilities(GetVhtCapabilities(linkId));
+            frame.template Get<VhtCapabilities>() = GetVhtCapabilities(linkId);
         }
         if (GetHeSupported())
         {
-            frame.SetHeCapabilities(GetHeCapabilities(linkId));
+            frame.template Get<HeCapabilities>() = GetHeCapabilities(linkId);
         }
         if (GetEhtSupported())
         {
-            frame.SetEhtCapabilities(GetEhtCapabilities(linkId));
+            frame.template Get<EhtCapabilities>() = GetEhtCapabilities(linkId);
         }
     };
 
@@ -343,16 +389,28 @@
 {
     NS_LOG_FUNCTION(this << isReassoc << +linkId);
 
-    MultiLinkElement multiLinkElement(MultiLinkElement::BASIC_VARIANT,
-                                      isReassoc ? WIFI_MAC_MGT_REASSOCIATION_REQUEST
-                                                : WIFI_MAC_MGT_ASSOCIATION_REQUEST);
+    MultiLinkElement multiLinkElement(MultiLinkElement::BASIC_VARIANT);
     // The Common info field of the Basic Multi-Link element carried in the (Re)Association
     // Request frame shall include the MLD MAC address, the MLD Capabilities and Operations,
     // and the EML Capabilities subfields, and shall not include the Link ID Info, the BSS
     // Parameters Change Count, and the Medium Synchronization Delay Information subfields
     // (Sec. 35.3.5.4 of 802.11be D2.0)
-    // TODO Add the MLD Capabilities and Operations and the EML Capabilities subfields
+    // TODO Add the MLD Capabilities and Operations subfield
     multiLinkElement.SetMldMacAddress(GetAddress());
+
+    if (m_emlsrManager) // EMLSR Manager is only installed if EMLSR is activated
+    {
+        multiLinkElement.SetEmlsrSupported(true);
+        TimeValue time;
+        m_emlsrManager->GetAttribute("EmlsrPaddingDelay", time);
+        multiLinkElement.SetEmlsrPaddingDelay(time.Get());
+        m_emlsrManager->GetAttribute("EmlsrTransitionDelay", time);
+        multiLinkElement.SetEmlsrTransitionDelay(time.Get());
+        // 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)
+    }
+
     // For each requested link in addition to the link on which the (Re)Association Request
     // frame is transmitted, the Link Info field of the Basic Multi-Link element carried
     // in the (Re)Association Request frame shall contain the corresponding Per-STA Profile
@@ -421,7 +479,7 @@
         GetWifiRemoteStationManager(linkId)->GetMldAddress(*link.bssid).has_value())
     {
         auto addMle = [&](auto&& frame) {
-            frame.SetMultiLinkElement(GetMultiLinkElement(isReassoc, linkId));
+            frame.template Get<MultiLinkElement>() = GetMultiLinkElement(isReassoc, linkId);
         };
         std::visit(addMle, frame);
     }
@@ -471,7 +529,6 @@
     {
     case ASSOCIATED:
         return;
-        break;
     case SCANNING:
         /* we have initiated active or passive scanning, continue to wait
            and gather beacons or probe responses until the scanning timeout
@@ -521,13 +578,13 @@
     }
     if (m_activeProbing)
     {
-        scanParams.type = WifiScanParams::ACTIVE;
+        scanParams.type = WifiScanType::ACTIVE;
         scanParams.probeDelay = MicroSeconds(m_probeDelay->GetValue());
         scanParams.minChannelTime = scanParams.maxChannelTime = m_probeRequestTimeout;
     }
     else
     {
-        scanParams.type = WifiScanParams::PASSIVE;
+        scanParams.type = WifiScanType::PASSIVE;
         scanParams.maxChannelTime = m_waitBeaconTimeout;
     }
 
@@ -670,6 +727,8 @@
 
     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())
     {
@@ -733,6 +792,12 @@
     return linkIds;
 }
 
+std::optional<uint8_t>
+StaWifiMac::GetApLinkId(uint8_t linkId) const
+{
+    return GetLink(linkId).apLinkId;
+}
+
 Mac48Address
 StaWifiMac::DoGetLocalAddress(const Mac48Address& remoteAddr) const
 {
@@ -879,6 +944,12 @@
             NotifyRxDrop(packet);
             return;
         }
+        if (!hdr->HasData())
+        {
+            NS_LOG_LOGIC("Received (QoS) Null Data frame: ignore");
+            NotifyRxDrop(packet);
+            return;
+        }
         if (hdr->IsQosData())
         {
             if (hdr->IsQosAmsdu())
@@ -892,7 +963,7 @@
                 ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
             }
         }
-        else if (hdr->HasData())
+        else
         {
             ForwardUp(packet, hdr->GetAddr3(), hdr->GetAddr1());
         }
@@ -921,12 +992,27 @@
         ReceiveAssocResp(mpdu, linkId);
         break;
 
+    case WIFI_MAC_MGT_ACTION:
+        if (auto [category, action] = WifiActionHeader::Peek(packet);
+            category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            // this is handled by the EMLSR Manager
+            break;
+        }
+
     default:
         // Invoke the receive handler of our parent class to deal with any
         // other frames. Specifically, this will handle Block Ack-related
         // Management Action frames.
         WifiMac::Receive(mpdu, linkId);
     }
+
+    if (m_emlsrManager)
+    {
+        m_emlsrManager->NotifyMgtFrameReceived(mpdu, linkId);
+    }
 }
 
 void
@@ -939,7 +1025,7 @@
     NS_LOG_DEBUG("Beacon received");
     MgtBeaconHeader beacon;
     mpdu->GetPacket()->PeekHeader(beacon);
-    const CapabilityInformation& capabilities = beacon.GetCapabilities();
+    const auto& capabilities = beacon.Capabilities();
     NS_ASSERT(capabilities.IsEss());
     bool goodBeacon;
     if (IsWaitAssocResp() || IsAssociated())
@@ -1028,6 +1114,7 @@
         return;
     }
 
+    std::optional<Mac48Address> apMldAddress;
     MgtAssocResponseHeader assocResp;
     mpdu->GetPacket()->PeekHeader(assocResp);
     if (m_assocRequestEvent.IsRunning())
@@ -1041,11 +1128,11 @@
         UpdateApInfo(assocResp, hdr.GetAddr2(), hdr.GetAddr3(), linkId);
         NS_ASSERT(GetLink(linkId).bssid.has_value() && *GetLink(linkId).bssid == hdr.GetAddr3());
         SetBssid(hdr.GetAddr3(), linkId);
-        if ((GetNLinks() > 1) && assocResp.GetMultiLinkElement().has_value())
+        if ((GetNLinks() > 1) && assocResp.Get<MultiLinkElement>().has_value())
         {
-            // this is an ML setup, trace the MLD address (only once)
-            m_assocLogger(*GetWifiRemoteStationManager(linkId)->GetMldAddress(hdr.GetAddr3()));
+            // this is an ML setup, trace the setup link
             m_setupCompleted(linkId, hdr.GetAddr3());
+            apMldAddress = GetWifiRemoteStationManager(linkId)->GetMldAddress(hdr.GetAddr3());
         }
         else
         {
@@ -1072,7 +1159,7 @@
         }
 
         // if a Multi-Link Element is present, check its content
-        if (const auto& mle = assocResp.GetMultiLinkElement(); mle.has_value())
+        if (const auto& mle = assocResp.Get<MultiLinkElement>())
         {
             NS_ABORT_MSG_IF(!GetLink(linkId).apLinkId.has_value(),
                             "The link on which the Association Response was received "
@@ -1122,13 +1209,9 @@
                     NS_LOG_DEBUG("Setup on link " << staLinkid << " completed");
                     UpdateApInfo(assoc, *bssid, *bssid, staLinkid);
                     SetBssid(*bssid, staLinkid);
-                    if (m_state != ASSOCIATED)
-                    {
-                        m_assocLogger(
-                            *GetWifiRemoteStationManager(staLinkid)->GetMldAddress(*bssid));
-                    }
                     m_setupCompleted(staLinkid, *bssid);
                     SetState(ASSOCIATED);
+                    apMldAddress = GetWifiRemoteStationManager(staLinkid)->GetMldAddress(*bssid);
                     if (!m_linkUp.IsNull())
                     {
                         m_linkUp();
@@ -1149,6 +1232,11 @@
                 GetLink(id).phy->SetOffMode();
             }
         }
+        if (apMldAddress)
+        {
+            // this is an ML setup, trace the MLD address of the AP (only once)
+            m_assocLogger(*apMldAddress);
+        }
     }
 
     if (m_state == WAIT_ASSOC_RESP)
@@ -1157,7 +1245,90 @@
         NS_LOG_DEBUG("association refused");
         SetState(REFUSED);
         StartScanning();
+        return;
     }
+
+    SetPmModeAfterAssociation(linkId);
+}
+
+void
+StaWifiMac::SetPmModeAfterAssociation(uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << linkId);
+
+    // STAs operating on setup links may need to transition to a new PM mode after the
+    // 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 */) {
+            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");
+
+            auto ackDuration =
+                WifiPhy::CalculateTxDuration(psduMap, txVector, GetLink(linkId).phy->GetPhyBand());
+
+            for (uint8_t id = 0; id < GetNLinks(); id++)
+            {
+                auto& link = GetLink(id);
+
+                if (!link.bssid)
+                {
+                    // link has not been setup
+                    continue;
+                }
+
+                if (id == linkId)
+                {
+                    /**
+                     * When a link becomes enabled for a non-AP STA that is affiliated with a
+                     * non-AP MLD after successful association with an AP MLD with (Re)Association
+                     * Request/Response  frames transmitted on that link [..], the power management
+                     * mode of the non-AP STA, immediately after the acknowledgement of the
+                     * (Re)Association Response frame [..], is active mode.
+                     * (Sec. 35.3.7.1.4 of 802.11be D3.0)
+                     */
+                    // if the user requested this link to be in powersave mode, we have to
+                    // switch PM mode
+                    if (link.pmMode == WIFI_PM_POWERSAVE)
+                    {
+                        Simulator::Schedule(ackDuration,
+                                            &StaWifiMac::SetPowerSaveMode,
+                                            this,
+                                            std::pair<bool, uint8_t>{true, id});
+                    }
+                    link.pmMode = WIFI_PM_ACTIVE;
+                }
+                else
+                {
+                    /**
+                     * When a link becomes enabled for a non-AP STA that is affiliated with a
+                     * non-AP MLD after successful association with an AP MLD with (Re)Association
+                     * Request/Response frames transmitted on another link [..], the power
+                     * management mode of the non-AP STA, immediately after the acknowledgement of
+                     * the (Re)Association Response frame [..], is power save mode, and its power
+                     * state is doze. (Sec. 35.3.7.1.4 of 802.11be D3.0)
+                     */
+                    // if the user requested this link to be in active mode, we have to
+                    // switch PM mode
+                    if (link.pmMode == WIFI_PM_ACTIVE)
+                    {
+                        Simulator::Schedule(ackDuration,
+                                            &StaWifiMac::SetPowerSaveMode,
+                                            this,
+                                            std::pair<bool, uint8_t>{false, id});
+                    }
+                    link.pmMode = WIFI_PM_POWERSAVE;
+                }
+            }
+        });
+
+    // connect the callback to the PHY TX begin trace to catch the Ack and disconnect
+    // after its transmission begins
+    auto phy = GetLink(linkId).phy;
+    phy->TraceConnectWithoutContext("PhyTxPsduBegin", cb);
+    Simulator::Schedule(phy->GetSifs() + NanoSeconds(1),
+                        [=]() { phy->TraceDisconnectWithoutContext("PhyTxPsduBegin", cb); });
 }
 
 bool
@@ -1169,7 +1340,9 @@
     // lambda to invoke on the current frame variant
     auto check = [&](auto&& mgtFrame) -> bool {
         // check supported rates
-        const SupportedRates& rates = mgtFrame.GetSupportedRates();
+        NS_ASSERT(mgtFrame.template Get<SupportedRates>());
+        const auto rates = AllSupportedRates{*mgtFrame.template Get<SupportedRates>(),
+                                             mgtFrame.template Get<ExtendedSupportedRatesIE>()};
         for (const auto& selector : GetWifiPhy(linkId)->GetBssMembershipSelectorList())
         {
             if (!rates.IsBssMembershipSelectorRate(selector))
@@ -1193,10 +1366,24 @@
 {
     NS_LOG_FUNCTION(this << frame.index() << apAddr << bssid << +linkId);
 
+    // ERP Information is not present in Association Response frames
+    const std::optional<ErpInformation>* erpInformation = nullptr;
+
+    if (const auto* beacon = std::get_if<MgtBeaconHeader>(&frame))
+    {
+        erpInformation = &beacon->Get<ErpInformation>();
+    }
+    else if (const auto* probe = std::get_if<MgtProbeResponseHeader>(&frame))
+    {
+        erpInformation = &probe->Get<ErpInformation>();
+    }
+
     // lambda processing Information Elements included in all frame types
     auto commonOps = [&](auto&& frame) {
-        const CapabilityInformation& capabilities = frame.GetCapabilities();
-        const SupportedRates& rates = frame.GetSupportedRates();
+        const auto& capabilities = frame.Capabilities();
+        NS_ASSERT(frame.template Get<SupportedRates>());
+        const auto rates = AllSupportedRates{*frame.template Get<SupportedRates>(),
+                                             frame.template Get<ExtendedSupportedRatesIE>()};
         for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
         {
             if (rates.IsSupportedRate(mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth())))
@@ -1210,11 +1397,10 @@
         }
 
         bool isShortPreambleEnabled = capabilities.IsShortPreamble();
-        if (const auto& erpInformation = frame.GetErpInformation();
-            erpInformation.has_value() && GetErpSupported(linkId))
+        if (erpInformation && erpInformation->has_value() && GetErpSupported(linkId))
         {
-            isShortPreambleEnabled &= !erpInformation->GetBarkerPreambleMode();
-            if (erpInformation->GetUseProtection() != 0)
+            isShortPreambleEnabled &= !(*erpInformation)->GetBarkerPreambleMode();
+            if ((*erpInformation)->GetUseProtection() != 0)
             {
                 GetWifiRemoteStationManager(linkId)->SetUseNonErpProtection(true);
             }
@@ -1243,32 +1429,36 @@
         }
         /* QoS station */
         bool qosSupported = false;
-        const auto& edcaParameters = frame.GetEdcaParameterSet();
+        const auto& edcaParameters = frame.template Get<EdcaParameterSet>();
         if (edcaParameters.has_value())
         {
             qosSupported = true;
             // The value of the TXOP Limit field is specified as an unsigned integer, with the least
             // significant octet transmitted first, in units of 32 Î¼s.
-            SetEdcaParameters(AC_BE,
-                              edcaParameters->GetBeCWmin(),
-                              edcaParameters->GetBeCWmax(),
-                              edcaParameters->GetBeAifsn(),
-                              32 * MicroSeconds(edcaParameters->GetBeTxopLimit()));
-            SetEdcaParameters(AC_BK,
-                              edcaParameters->GetBkCWmin(),
-                              edcaParameters->GetBkCWmax(),
-                              edcaParameters->GetBkAifsn(),
-                              32 * MicroSeconds(edcaParameters->GetBkTxopLimit()));
-            SetEdcaParameters(AC_VI,
-                              edcaParameters->GetViCWmin(),
-                              edcaParameters->GetViCWmax(),
-                              edcaParameters->GetViAifsn(),
-                              32 * MicroSeconds(edcaParameters->GetViTxopLimit()));
-            SetEdcaParameters(AC_VO,
-                              edcaParameters->GetVoCWmin(),
-                              edcaParameters->GetVoCWmax(),
-                              edcaParameters->GetVoAifsn(),
-                              32 * MicroSeconds(edcaParameters->GetVoTxopLimit()));
+            SetEdcaParameters({AC_BE,
+                               edcaParameters->GetBeCWmin(),
+                               edcaParameters->GetBeCWmax(),
+                               edcaParameters->GetBeAifsn(),
+                               32 * MicroSeconds(edcaParameters->GetBeTxopLimit())},
+                              linkId);
+            SetEdcaParameters({AC_BK,
+                               edcaParameters->GetBkCWmin(),
+                               edcaParameters->GetBkCWmax(),
+                               edcaParameters->GetBkAifsn(),
+                               32 * MicroSeconds(edcaParameters->GetBkTxopLimit())},
+                              linkId);
+            SetEdcaParameters({AC_VI,
+                               edcaParameters->GetViCWmin(),
+                               edcaParameters->GetViCWmax(),
+                               edcaParameters->GetViAifsn(),
+                               32 * MicroSeconds(edcaParameters->GetViTxopLimit())},
+                              linkId);
+            SetEdcaParameters({AC_VO,
+                               edcaParameters->GetVoCWmin(),
+                               edcaParameters->GetVoCWmax(),
+                               edcaParameters->GetVoAifsn(),
+                               32 * MicroSeconds(edcaParameters->GetVoTxopLimit())},
+                              linkId);
         }
         GetWifiRemoteStationManager(linkId)->SetQosSupport(apAddr, qosSupported);
 
@@ -1277,7 +1467,8 @@
             return;
         }
         /* HT station */
-        if (const auto& htCapabilities = frame.GetHtCapabilities(); htCapabilities.has_value())
+        if (const auto& htCapabilities = frame.template Get<HtCapabilities>();
+            htCapabilities.has_value())
         {
             if (!htCapabilities->IsSupportedMcs(0))
             {
@@ -1296,7 +1487,7 @@
         // the 2.4 GHz band do not support VHT
         if (GetVhtSupported(linkId))
         {
-            const auto& vhtCapabilities = frame.GetVhtCapabilities();
+            const auto& vhtCapabilities = frame.template Get<VhtCapabilities>();
             // we will always fill in RxHighestSupportedLgiDataRate field at TX, so this can be used
             // to check whether it supports VHT
             if (vhtCapabilities.has_value() &&
@@ -1320,7 +1511,7 @@
             return;
         }
         /* HE station */
-        const auto& heCapabilities = frame.GetHeCapabilities();
+        const auto& heCapabilities = frame.template Get<HeCapabilities>();
         if (heCapabilities.has_value() && heCapabilities->GetSupportedMcsAndNss() != 0)
         {
             GetWifiRemoteStationManager(linkId)->AddStationHeCapabilities(apAddr, *heCapabilities);
@@ -1331,36 +1522,41 @@
                     GetWifiRemoteStationManager(linkId)->AddSupportedMcs(apAddr, mcs);
                 }
             }
-            if (const auto& heOperation = frame.GetHeOperation(); heOperation.has_value())
+            if (const auto& heOperation = frame.template Get<HeOperation>();
+                heOperation.has_value())
             {
                 GetHeConfiguration()->SetAttribute("BssColor",
                                                    UintegerValue(heOperation->GetBssColor()));
             }
         }
 
-        const auto& muEdcaParameters = frame.GetMuEdcaParameterSet();
+        const auto& muEdcaParameters = frame.template Get<MuEdcaParameterSet>();
         if (muEdcaParameters.has_value())
         {
-            SetMuEdcaParameters(AC_BE,
-                                muEdcaParameters->GetMuCwMin(AC_BE),
-                                muEdcaParameters->GetMuCwMax(AC_BE),
-                                muEdcaParameters->GetMuAifsn(AC_BE),
-                                muEdcaParameters->GetMuEdcaTimer(AC_BE));
-            SetMuEdcaParameters(AC_BK,
-                                muEdcaParameters->GetMuCwMin(AC_BK),
-                                muEdcaParameters->GetMuCwMax(AC_BK),
-                                muEdcaParameters->GetMuAifsn(AC_BK),
-                                muEdcaParameters->GetMuEdcaTimer(AC_BK));
-            SetMuEdcaParameters(AC_VI,
-                                muEdcaParameters->GetMuCwMin(AC_VI),
-                                muEdcaParameters->GetMuCwMax(AC_VI),
-                                muEdcaParameters->GetMuAifsn(AC_VI),
-                                muEdcaParameters->GetMuEdcaTimer(AC_VI));
-            SetMuEdcaParameters(AC_VO,
-                                muEdcaParameters->GetMuCwMin(AC_VO),
-                                muEdcaParameters->GetMuCwMax(AC_VO),
-                                muEdcaParameters->GetMuAifsn(AC_VO),
-                                muEdcaParameters->GetMuEdcaTimer(AC_VO));
+            SetMuEdcaParameters({AC_BE,
+                                 muEdcaParameters->GetMuCwMin(AC_BE),
+                                 muEdcaParameters->GetMuCwMax(AC_BE),
+                                 muEdcaParameters->GetMuAifsn(AC_BE),
+                                 muEdcaParameters->GetMuEdcaTimer(AC_BE)},
+                                linkId);
+            SetMuEdcaParameters({AC_BK,
+                                 muEdcaParameters->GetMuCwMin(AC_BK),
+                                 muEdcaParameters->GetMuCwMax(AC_BK),
+                                 muEdcaParameters->GetMuAifsn(AC_BK),
+                                 muEdcaParameters->GetMuEdcaTimer(AC_BK)},
+                                linkId);
+            SetMuEdcaParameters({AC_VI,
+                                 muEdcaParameters->GetMuCwMin(AC_VI),
+                                 muEdcaParameters->GetMuCwMax(AC_VI),
+                                 muEdcaParameters->GetMuAifsn(AC_VI),
+                                 muEdcaParameters->GetMuEdcaTimer(AC_VI)},
+                                linkId);
+            SetMuEdcaParameters({AC_VO,
+                                 muEdcaParameters->GetMuCwMin(AC_VO),
+                                 muEdcaParameters->GetMuCwMax(AC_VO),
+                                 muEdcaParameters->GetMuAifsn(AC_VO),
+                                 muEdcaParameters->GetMuEdcaTimer(AC_VO)},
+                                linkId);
         }
 
         if (!GetEhtSupported())
@@ -1368,20 +1564,131 @@
             return;
         }
         /* EHT station */
-        const auto& ehtCapabilities = frame.GetEhtCapabilities();
+        const auto& ehtCapabilities = frame.template Get<EhtCapabilities>();
         // TODO: once we support non constant rate managers, we should add checks here whether EHT
         // is supported by the peer
         GetWifiRemoteStationManager(linkId)->AddStationEhtCapabilities(apAddr, *ehtCapabilities);
+
+        if (const auto& mle = frame.template Get<MultiLinkElement>();
+            mle && mle->HasEmlCapabilities() && m_emlsrManager)
+        {
+            m_emlsrManager->SetTransitionTimeout(mle->GetTransitionTimeout());
+        }
     };
 
     // process Information Elements included in the current frame variant
     std::visit(commonOps, frame);
 }
 
-SupportedRates
+void
+StaWifiMac::SetPowerSaveMode(const std::pair<bool, uint8_t>& enableLinkIdPair)
+{
+    const auto [enable, linkId] = enableLinkIdPair;
+    NS_LOG_FUNCTION(this << enable << linkId);
+
+    auto& link = GetLink(linkId);
+
+    if (!IsAssociated())
+    {
+        NS_LOG_DEBUG("Not associated yet, record the PM mode to switch to upon association");
+        link.pmMode = enable ? WIFI_PM_POWERSAVE : WIFI_PM_ACTIVE;
+        return;
+    }
+
+    if (!link.bssid)
+    {
+        NS_LOG_DEBUG("Link " << +linkId << " has not been setup, ignore request");
+        return;
+    }
+
+    if ((enable && link.pmMode == WIFI_PM_POWERSAVE) || (!enable && link.pmMode == WIFI_PM_ACTIVE))
+    {
+        NS_LOG_DEBUG("No PM mode change needed");
+        return;
+    }
+
+    link.pmMode = enable ? WIFI_PM_SWITCHING_TO_PS : WIFI_PM_SWITCHING_TO_ACTIVE;
+
+    // reschedule a call to this function to make sure that the PM mode switch
+    // is eventually completed
+    Simulator::Schedule(m_pmModeSwitchTimeout,
+                        &StaWifiMac::SetPowerSaveMode,
+                        this,
+                        enableLinkIdPair);
+
+    if (HasFramesToTransmit(linkId))
+    {
+        NS_LOG_DEBUG("Next transmitted frame will be sent with PM=" << enable);
+        return;
+    }
+
+    // No queued frames. Enqueue a Data Null frame to inform the AP of the PM mode change
+    WifiMacHeader hdr(WIFI_MAC_DATA_NULL);
+
+    hdr.SetAddr1(GetBssid(linkId));
+    hdr.SetAddr2(GetFrameExchangeManager(linkId)->GetAddress());
+    hdr.SetAddr3(GetBssid(linkId));
+    hdr.SetDsNotFrom();
+    hdr.SetDsTo();
+    enable ? hdr.SetPowerManagement() : hdr.SetNoPowerManagement();
+    if (GetQosSupported())
+    {
+        GetQosTxop(AC_BE)->Queue(Create<WifiMpdu>(Create<Packet>(), hdr));
+    }
+    else
+    {
+        m_txop->Queue(Create<WifiMpdu>(Create<Packet>(), hdr));
+    }
+}
+
+WifiPowerManagementMode
+StaWifiMac::GetPmMode(uint8_t linkId) const
+{
+    return GetLink(linkId).pmMode;
+}
+
+void
+StaWifiMac::TxOk(Ptr<const WifiMpdu> mpdu)
+{
+    NS_LOG_FUNCTION(this << *mpdu);
+
+    auto linkId = GetLinkIdByAddress(mpdu->GetHeader().GetAddr2());
+
+    if (!linkId)
+    {
+        // the given MPDU may be the original copy containing MLD addresses and not carrying
+        // a valid PM bit (which is set on the aliases).
+        auto linkIds = mpdu->GetInFlightLinkIds();
+        NS_ASSERT_MSG(!linkIds.empty(),
+                      "The TA of the acked MPDU (" << *mpdu
+                                                   << ") is not a link "
+                                                      "address and the MPDU is not inflight");
+        // in case the ack'ed MPDU is inflight on multiple links, we cannot really know if
+        // it was received by the AP on all links or only on some links. Hence, we only
+        // consider the first link ID in the set, given that in the most common case of MPDUs
+        // that cannot be sent concurrently on multiple links, there will be only one link ID
+        linkId = *linkIds.begin();
+        mpdu = GetTxopQueue(mpdu->GetQueueAc())->GetAlias(mpdu, *linkId);
+    }
+
+    auto& link = GetLink(*linkId);
+    const WifiMacHeader& hdr = mpdu->GetHeader();
+
+    // we received an acknowledgment while switching PM mode; the PM mode change is effective now
+    if (hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_PS)
+    {
+        link.pmMode = WIFI_PM_POWERSAVE;
+    }
+    else if (!hdr.IsPowerManagement() && link.pmMode == WIFI_PM_SWITCHING_TO_ACTIVE)
+    {
+        link.pmMode = WIFI_PM_ACTIVE;
+    }
+}
+
+AllSupportedRates
 StaWifiMac::GetSupportedRates(uint8_t linkId) const
 {
-    SupportedRates rates;
+    AllSupportedRates rates;
     for (const auto& mode : GetWifiPhy(linkId)->GetModeList())
     {
         uint64_t modeDataRate = mode.GetDataRate(GetWifiPhy(linkId)->GetChannelWidth());
@@ -1415,31 +1722,23 @@
 }
 
 void
-StaWifiMac::SetEdcaParameters(AcIndex ac,
-                              uint32_t cwMin,
-                              uint32_t cwMax,
-                              uint8_t aifsn,
-                              Time txopLimit)
-{
-    Ptr<QosTxop> edca = GetQosTxop(ac);
-    edca->SetMinCw(cwMin, SINGLE_LINK_OP_ID);
-    edca->SetMaxCw(cwMax, SINGLE_LINK_OP_ID);
-    edca->SetAifsn(aifsn, SINGLE_LINK_OP_ID);
-    edca->SetTxopLimit(txopLimit, SINGLE_LINK_OP_ID);
+StaWifiMac::SetEdcaParameters(const EdcaParams& params, uint8_t linkId)
+{
+    Ptr<QosTxop> edca = GetQosTxop(params.ac);
+    edca->SetMinCw(params.cwMin, linkId);
+    edca->SetMaxCw(params.cwMax, linkId);
+    edca->SetAifsn(params.aifsn, linkId);
+    edca->SetTxopLimit(params.txopLimit, linkId);
 }
 
 void
-StaWifiMac::SetMuEdcaParameters(AcIndex ac,
-                                uint16_t cwMin,
-                                uint16_t cwMax,
-                                uint8_t aifsn,
-                                Time muEdcaTimer)
-{
-    Ptr<QosTxop> edca = GetQosTxop(ac);
-    edca->SetMuCwMin(cwMin, SINGLE_LINK_OP_ID);
-    edca->SetMuCwMax(cwMax, SINGLE_LINK_OP_ID);
-    edca->SetMuAifsn(aifsn, SINGLE_LINK_OP_ID);
-    edca->SetMuEdcaTimer(muEdcaTimer, SINGLE_LINK_OP_ID);
+StaWifiMac::SetMuEdcaParameters(const MuEdcaParams& params, uint8_t linkId)
+{
+    Ptr<QosTxop> edca = GetQosTxop(params.ac);
+    edca->SetMuCwMin(params.cwMin, linkId);
+    edca->SetMuCwMax(params.cwMax, linkId);
+    edca->SetMuAifsn(params.aifsn, linkId);
+    edca->SetMuEdcaTimer(params.muEdcaTimer, linkId);
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/sta-wifi-mac.h ns-3.39/src/wifi/model/sta-wifi-mac.h
--- ns-3.38/src/wifi/model/sta-wifi-mac.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/sta-wifi-mac.h	2023-07-05 14:23:48.121428500 -0700
@@ -40,6 +40,18 @@
 class CapabilityInformation;
 class RandomVariableStream;
 class WifiAssocManager;
+class EmlsrManager;
+
+/**
+ * \ingroup wifi
+ *
+ * Scan type (active or passive)
+ */
+enum class WifiScanType : uint8_t
+{
+    ACTIVE = 0,
+    PASSIVE
+};
 
 /**
  * \ingroup wifi
@@ -63,12 +75,7 @@
     /// typedef for a list of channels
     using ChannelList = std::list<Channel>;
 
-    enum : uint8_t
-    {
-        ACTIVE = 0,
-        PASSIVE
-    } type; ///< indicates either active or passive scanning
-
+    WifiScanType type;                    ///< indicates either active or passive scanning
     Ssid ssid;                            ///< desired SSID or wildcard SSID
     std::vector<ChannelList> channelList; ///< list of channels to scan, for each link
     Time probeDelay;                      ///< delay prior to transmitting a Probe Request
@@ -79,6 +86,19 @@
 /**
  * \ingroup wifi
  *
+ * Enumeration for power management modes
+ */
+enum WifiPowerManagementMode : uint8_t
+{
+    WIFI_PM_ACTIVE = 0,
+    WIFI_PM_SWITCHING_TO_PS,
+    WIFI_PM_POWERSAVE,
+    WIFI_PM_SWITCHING_TO_ACTIVE
+};
+
+/**
+ * \ingroup wifi
+ *
  * The Wifi MAC high model for a non-AP STA in a BSS. The state
  * machine is as follows:
  *
@@ -149,7 +169,7 @@
         WifiScanParams::Channel m_channel; ///< The channel the management frame was received on
         uint8_t m_linkId;                  ///< ID of the link used to communicate with the AP
         /// list of (local link ID, AP link ID) pairs identifying the links to setup between MLDs
-        std::list<std::pair<std::uint8_t, uint8_t>> m_setupLinks;
+        std::list<std::pair<uint8_t, uint8_t>> m_setupLinks;
     };
 
     /**
@@ -185,10 +205,23 @@
     void SetAssocManager(Ptr<WifiAssocManager> assocManager);
 
     /**
-     * Forward a probe request packet to the DCF. The standard is not clear on the correct
-     * queue for management frames if QoS is supported. We always use the DCF.
+     * Set the EMLSR Manager.
+     *
+     * \param emlsrManager the EMLSR Manager
+     */
+    void SetEmlsrManager(Ptr<EmlsrManager> emlsrManager);
+
+    /**
+     * \return the EMLSR Manager
+     */
+    Ptr<EmlsrManager> GetEmlsrManager() const;
+
+    /**
+     * Enqueue a probe request packet for transmission on the given link.
+     *
+     * \param linkId the ID of the given link
      */
-    void SendProbeRequest();
+    void SendProbeRequest(uint8_t linkId);
 
     /**
      * This method is called after wait beacon timeout or wait probe request timeout has
@@ -214,12 +247,47 @@
     std::set<uint8_t> GetSetupLinkIds() const;
 
     /**
+     * \param linkId the IO of the given link
+     * \return the ID (as set by the AP) of the given link, if the given link has been setup
+     */
+    std::optional<uint8_t> GetApLinkId(uint8_t linkId) const;
+
+    /**
      * Return the association ID.
      *
      * \return the association ID
      */
     uint16_t GetAssociationId() const;
 
+    /**
+     * Enable or disable Power Save mode on the given link.
+     *
+     * \param enableLinkIdPair a pair indicating whether to enable or not power save mode on
+     *                         the link with the given ID
+     */
+    void SetPowerSaveMode(const std::pair<bool, uint8_t>& enableLinkIdPair);
+
+    /**
+     * \param linkId the ID of the given link
+     * \return the current Power Management mode of the STA operating on the given link
+     */
+    WifiPowerManagementMode GetPmMode(uint8_t linkId) const;
+
+    /**
+     * Set the Power Management mode of the setup links after association.
+     *
+     * \param linkId the ID of the link used to establish association
+     */
+    void SetPmModeAfterAssociation(uint8_t linkId);
+
+    /**
+     * Notify that the MPDU we sent was successfully received by the receiver
+     * (i.e. we received an Ack from the receiver).
+     *
+     * \param mpdu the MPDU that we successfully sent
+     */
+    void TxOk(Ptr<const WifiMpdu> mpdu);
+
     void NotifyChannelSwitching(uint8_t linkId) override;
 
     /**
@@ -251,6 +319,9 @@
         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 */
     };
 
     /**
@@ -413,7 +484,7 @@
      * \param linkId the ID of the link for which the request is made
      * \return SupportedRates all rates that we support
      */
-    SupportedRates GetSupportedRates(uint8_t linkId) const;
+    AllSupportedRates GetSupportedRates(uint8_t linkId) const;
     /**
      * Return the Multi-Link Element to include in the management frames transmitted
      * on the given link
@@ -429,34 +500,47 @@
      * \param value the new state
      */
     void SetState(MacState value);
+
     /**
-     * Set the EDCA parameters.
+     * EDCA Parameters
+     */
+    struct EdcaParams
+    {
+        AcIndex ac;     //!< the access category
+        uint32_t cwMin; //!< the minimum contention window size
+        uint32_t cwMax; //!< the maximum contention window size
+        uint8_t aifsn;  //!< the number of slots that make up an AIFS
+        Time txopLimit; //!< the TXOP limit
+    };
+
+    /**
+     * Set the EDCA parameters for the given link.
      *
-     * \param ac the access class
-     * \param cwMin the minimum contention window size
-     * \param cwMax the maximum contention window size
-     * \param aifsn the number of slots that make up an AIFS
-     * \param txopLimit the TXOP limit
-     */
-    void SetEdcaParameters(AcIndex ac,
-                           uint32_t cwMin,
-                           uint32_t cwMax,
-                           uint8_t aifsn,
-                           Time txopLimit);
-    /**
-     * Set the MU EDCA parameters.
-     *
-     * \param ac the Access Category
-     * \param cwMin the minimum contention window size
-     * \param cwMax the maximum contention window size
-     * \param aifsn the number of slots that make up an AIFS
-     * \param muEdcaTimer the MU EDCA timer
-     */
-    void SetMuEdcaParameters(AcIndex ac,
-                             uint16_t cwMin,
-                             uint16_t cwMax,
-                             uint8_t aifsn,
-                             Time muEdcaTimer);
+     * \param params the EDCA parameters
+     * \param linkId the ID of the given link
+     */
+    void SetEdcaParameters(const EdcaParams& params, uint8_t linkId);
+
+    /**
+     * MU EDCA Parameters
+     */
+    struct MuEdcaParams
+    {
+        AcIndex ac;       //!< the access category
+        uint32_t cwMin;   //!< the minimum contention window size
+        uint32_t cwMax;   //!< the maximum contention window size
+        uint8_t aifsn;    //!< the number of slots that make up an AIFS
+        Time muEdcaTimer; //!< the MU EDCA timer
+    };
+
+    /**
+     * Set the MU EDCA parameters for the given link.
+     *
+     * \param params the MU EDCA parameters
+     * \param linkId the ID of the given link
+     */
+    void SetMuEdcaParameters(const MuEdcaParams& params, uint8_t linkId);
+
     /**
      * Return the Capability information for the given link.
      *
@@ -485,6 +569,7 @@
     MacState m_state;                       ///< MAC state
     uint16_t m_aid;                         ///< Association AID
     Ptr<WifiAssocManager> m_assocManager;   ///< Association Manager
+    Ptr<EmlsrManager> m_emlsrManager;       ///< EMLSR Manager
     Time m_waitBeaconTimeout;               ///< wait beacon timeout
     Time m_probeRequestTimeout;             ///< probe request timeout
     Time m_assocRequestTimeout;             ///< association request timeout
@@ -493,6 +578,7 @@
     bool m_activeProbing;                   ///< active probing
     Ptr<RandomVariableStream> m_probeDelay; ///< RandomVariable used to randomize the time
                                             ///< of the first Probe Response on each channel
+    Time m_pmModeSwitchTimeout;             ///< PM mode switch timeout
 
     TracedCallback<Mac48Address> m_assocLogger;             ///< association logger
     TracedCallback<uint8_t, Mac48Address> m_setupCompleted; ///< link setup completed logger
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/supported-rates.cc ns-3.39/src/wifi/model/supported-rates.cc
--- ns-3.38/src/wifi/model/supported-rates.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/supported-rates.cc	2023-07-05 14:23:48.121428500 -0700
@@ -32,62 +32,81 @@
 #define BSS_MEMBERSHIP_SELECTOR_EHT_PHY 121 // TODO not defined yet as of 802.11be D1.4
 
 SupportedRates::SupportedRates()
-    : extended(this),
-      m_nRates(0)
 {
     NS_LOG_FUNCTION(this);
 }
 
-SupportedRates::SupportedRates(const SupportedRates& rates)
+void
+SupportedRates::Print(std::ostream& os) const
 {
-    NS_LOG_FUNCTION(this);
-    m_nRates = rates.m_nRates;
-    memcpy(m_rates, rates.m_rates, MAX_SUPPORTED_RATES);
-    // reset the back pointer to this object
-    extended = ExtendedSupportedRatesIE(this);
+    os << "rates=[";
+    for (std::size_t i = 0; i < m_rates.size(); i++)
+    {
+        if ((m_rates[i] & 0x80) > 0)
+        {
+            os << "*";
+        }
+        os << GetRate(i) / 1000000 << "mbs";
+        if (i < m_rates.size() - 1)
+        {
+            os << " ";
+        }
+    }
+    os << "]";
 }
 
-SupportedRates&
-SupportedRates::operator=(const SupportedRates& rates)
-{
-    this->m_nRates = rates.m_nRates;
-    memcpy(this->m_rates, rates.m_rates, MAX_SUPPORTED_RATES);
-    // reset the back pointer to this object
-    this->extended = ExtendedSupportedRatesIE(this);
-    return (*this);
+bool
+AllSupportedRates::IsBasicRate(uint64_t bs) const
+{
+    NS_LOG_FUNCTION(this << bs);
+    uint8_t rate = static_cast<uint8_t>(bs / 500000) | 0x80;
+    return std::find(rates.m_rates.cbegin(), rates.m_rates.cend(), rate) != rates.m_rates.cend() ||
+           (extendedRates &&
+            std::find(extendedRates->m_rates.cbegin(), extendedRates->m_rates.cend(), rate) !=
+                extendedRates->m_rates.cend());
 }
 
 void
-SupportedRates::AddSupportedRate(uint64_t bs)
+AllSupportedRates::AddSupportedRate(uint64_t bs)
 {
     NS_LOG_FUNCTION(this << bs);
     NS_ASSERT_MSG(IsBssMembershipSelectorRate(bs) == false, "Invalid rate");
-    NS_ASSERT(m_nRates < MAX_SUPPORTED_RATES);
     if (IsSupportedRate(bs))
     {
         return;
     }
-    m_rates[m_nRates] = static_cast<uint8_t>(bs / 500000);
-    m_nRates++;
-    NS_LOG_DEBUG("add rate=" << bs << ", n rates=" << +m_nRates);
+    if (rates.m_rates.size() < 8)
+    {
+        rates.m_rates.emplace_back(static_cast<uint8_t>(bs / 500000));
+    }
+    else
+    {
+        if (!extendedRates)
+        {
+            extendedRates.emplace();
+        }
+        extendedRates->m_rates.emplace_back(static_cast<uint8_t>(bs / 500000));
+    }
+    NS_LOG_DEBUG("add rate=" << bs << ", n rates=" << +GetNRates());
 }
 
 void
-SupportedRates::SetBasicRate(uint64_t bs)
+AllSupportedRates::SetBasicRate(uint64_t bs)
 {
     NS_LOG_FUNCTION(this << bs);
     NS_ASSERT_MSG(IsBssMembershipSelectorRate(bs) == false, "Invalid rate");
     uint8_t rate = static_cast<uint8_t>(bs / 500000);
-    for (uint8_t i = 0; i < m_nRates; i++)
+    for (uint8_t i = 0; i < GetNRates(); i++)
     {
-        if ((rate | 0x80) == m_rates[i])
+        auto& currRate = i < 8 ? rates.m_rates[i] : extendedRates->m_rates[i - 8];
+        if ((rate | 0x80) == currRate)
         {
             return;
         }
-        if (rate == m_rates[i])
+        if (rate == currRate)
         {
-            NS_LOG_DEBUG("set basic rate=" << bs << ", n rates=" << +m_nRates);
-            m_rates[i] |= 0x80;
+            NS_LOG_DEBUG("set basic rate=" << bs << ", n rates=" << +GetNRates());
+            currRate |= 0x80;
             return;
         }
     }
@@ -96,73 +115,84 @@
 }
 
 void
-SupportedRates::AddBssMembershipSelectorRate(uint64_t bs)
+AllSupportedRates::AddBssMembershipSelectorRate(uint64_t bs)
 {
     NS_LOG_FUNCTION(this << bs);
     NS_ASSERT_MSG(bs == BSS_MEMBERSHIP_SELECTOR_HT_PHY || bs == BSS_MEMBERSHIP_SELECTOR_VHT_PHY ||
                       bs == BSS_MEMBERSHIP_SELECTOR_HE_PHY || bs == BSS_MEMBERSHIP_SELECTOR_EHT_PHY,
                   "Value " << bs << " not a BSS Membership Selector");
     uint8_t rate = static_cast<uint8_t>(bs / 500000);
-    for (uint8_t i = 0; i < m_nRates; i++)
+    for (std::size_t i = 0; i < rates.m_rates.size(); i++)
     {
-        if (rate == m_rates[i])
+        if (rate == rates.m_rates[i])
         {
             return;
         }
     }
-    m_rates[m_nRates] = rate;
-    NS_LOG_DEBUG("add BSS membership selector rate " << bs << " as rate " << +rate);
-    m_nRates++;
-}
-
-bool
-SupportedRates::IsBasicRate(uint64_t bs) const
-{
-    NS_LOG_FUNCTION(this << bs);
-    uint8_t rate = static_cast<uint8_t>(bs / 500000) | 0x80;
-    for (uint8_t i = 0; i < m_nRates; i++)
+    if (extendedRates)
     {
-        if (rate == m_rates[i])
+        for (std::size_t i = 0; i < extendedRates->m_rates.size(); i++)
         {
-            return true;
+            if (rate == extendedRates->m_rates[i])
+            {
+                return;
+            }
         }
     }
-    return false;
+    if (rates.m_rates.size() < 8)
+    {
+        rates.m_rates.emplace_back(rate);
+    }
+    else
+    {
+        if (!extendedRates)
+        {
+            extendedRates.emplace();
+        }
+        extendedRates->m_rates.emplace_back(rate);
+    }
+    NS_LOG_DEBUG("add BSS membership selector rate " << bs << " as rate " << +rate);
 }
 
 bool
-SupportedRates::IsSupportedRate(uint64_t bs) const
+AllSupportedRates::IsSupportedRate(uint64_t bs) const
 {
     NS_LOG_FUNCTION(this << bs);
     uint8_t rate = static_cast<uint8_t>(bs / 500000);
-    for (uint8_t i = 0; i < m_nRates; i++)
+    for (std::size_t i = 0; i < rates.m_rates.size(); i++)
     {
-        if (rate == m_rates[i] || (rate | 0x80) == m_rates[i])
+        if (rate == rates.m_rates[i] || (rate | 0x80) == rates.m_rates[i])
         {
             return true;
         }
     }
+    if (extendedRates)
+    {
+        for (std::size_t i = 0; i < extendedRates->m_rates.size(); i++)
+        {
+            if (rate == extendedRates->m_rates[i] || (rate | 0x80) == extendedRates->m_rates[i])
+            {
+                return true;
+            }
+        }
+    }
     return false;
 }
 
 bool
-SupportedRates::IsBssMembershipSelectorRate(uint64_t bs) const
+AllSupportedRates::IsBssMembershipSelectorRate(uint64_t bs) const
 {
     NS_LOG_FUNCTION(this << bs);
-    if ((bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY ||
-        (bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_VHT_PHY ||
-        (bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HE_PHY ||
-        (bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_EHT_PHY)
-    {
-        return true;
-    }
-    return false;
+    return (bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY ||
+           (bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_VHT_PHY ||
+           (bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HE_PHY ||
+           (bs & 0x7f) == BSS_MEMBERSHIP_SELECTOR_EHT_PHY;
 }
 
 uint8_t
-SupportedRates::GetNRates() const
+AllSupportedRates::GetNRates() const
 {
-    return m_nRates;
+    return rates.m_rates.size() + (extendedRates ? extendedRates->m_rates.size() : 0);
 }
 
 uint32_t
@@ -180,37 +210,27 @@
 uint16_t
 SupportedRates::GetInformationFieldSize() const
 {
-    // The Supported Rates Information Element contains only the first 8
-    // supported rates - the remainder appear in the Extended Supported
-    // Rates Information Element.
-    return m_nRates > 8 ? 8 : m_nRates;
+    return m_rates.size();
 }
 
 void
 SupportedRates::SerializeInformationField(Buffer::Iterator start) const
 {
-    // The Supported Rates Information Element contains only the first 8
-    // supported rates - the remainder appear in the Extended Supported
-    // Rates Information Element.
-    start.Write(m_rates, m_nRates > 8 ? 8 : m_nRates);
+    for (const uint8_t rate : m_rates)
+    {
+        start.WriteU8(rate);
+    }
 }
 
 uint16_t
 SupportedRates::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
 {
     NS_ASSERT(length <= 8);
-    m_nRates = length;
-    start.Read(m_rates, m_nRates);
-    return m_nRates;
-}
-
-ExtendedSupportedRatesIE::ExtendedSupportedRatesIE()
-{
-}
-
-ExtendedSupportedRatesIE::ExtendedSupportedRatesIE(SupportedRates* sr)
-{
-    m_supportedRates = sr;
+    for (uint16_t i = 0; i < length; i++)
+    {
+        m_rates.push_back(start.ReadU8());
+    }
+    return length;
 }
 
 WifiInformationElementId
@@ -219,64 +239,4 @@
     return IE_EXTENDED_SUPPORTED_RATES;
 }
 
-void
-ExtendedSupportedRatesIE::SetSupportedRates(SupportedRates* sr)
-{
-    m_supportedRates = sr;
-}
-
-uint16_t
-ExtendedSupportedRatesIE::GetInformationFieldSize() const
-{
-    // If there are 8 or fewer rates then we don't need an Extended Supported
-    // Rates, so if this function is invoked in that case then it indicates a
-    // programming error. Hence we have an assertion on that condition.
-    NS_ASSERT(m_supportedRates->m_nRates > 8);
-
-    // The number of rates we have beyond the initial 8 is the size of
-    // the information field.
-    return (m_supportedRates->m_nRates - 8);
-}
-
-void
-ExtendedSupportedRatesIE::SerializeInformationField(Buffer::Iterator start) const
-{
-    // If there are 8 or fewer rates then there should be no Extended
-    // Supported Rates Information Element at all so being here would
-    // seemingly indicate a programming error.
-    NS_ASSERT(m_supportedRates->m_nRates > 8);
-    start.Write(m_supportedRates->m_rates + 8, m_supportedRates->m_nRates - 8);
-}
-
-uint16_t
-ExtendedSupportedRatesIE::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
-{
-    NS_ASSERT(length > 0);
-    NS_ASSERT(m_supportedRates->m_nRates + length <= SupportedRates::MAX_SUPPORTED_RATES);
-    start.Read(m_supportedRates->m_rates + m_supportedRates->m_nRates, length);
-    m_supportedRates->m_nRates += length;
-    return length;
-}
-
-std::ostream&
-operator<<(std::ostream& os, const SupportedRates& rates)
-{
-    os << "[";
-    for (uint8_t i = 0; i < rates.GetNRates(); i++)
-    {
-        uint32_t rate = rates.GetRate(i);
-        if (rates.IsBasicRate(rate))
-        {
-            os << "*";
-        }
-        os << rate / 1000000 << "mbs";
-        if (i < rates.GetNRates() - 1)
-        {
-            os << " ";
-        }
-    }
-    os << "]";
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/supported-rates.h ns-3.39/src/wifi/model/supported-rates.h
--- ns-3.38/src/wifi/model/supported-rates.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/supported-rates.h	2023-07-05 14:23:48.121428500 -0700
@@ -22,99 +22,70 @@
 
 #include "wifi-information-element.h"
 
+#include <optional>
+#include <vector>
+
 namespace ns3
 {
 
-class SupportedRates;
-
 /**
- * \brief The Extended Supported Rates Information Element
+ * \brief The Supported Rates Information Element
  * \ingroup wifi
  *
- * This class knows how to serialise and deserialise the Extended
- * Supported Rates Element that holds (non-HT) rates beyond the 8 that
- * the original Supported Rates element can carry.
+ * This class knows how to serialise and deserialise the Supported
+ * Rates Element that holds the first 8 (non-HT) supported rates.
  *
- * The \c SupportedRates class still records all the rates, and an
- * instance of \c ExtendedSupportedRatesIE lies within \c
- * SupportedRates.
+ * The \c ExtendedSupportedRatesIE class deals with rates beyond the first 8.
  */
-class ExtendedSupportedRatesIE : public WifiInformationElement
+class SupportedRates : public WifiInformationElement
 {
+    friend struct AllSupportedRates;
+
   public:
-    ExtendedSupportedRatesIE();
-    /**
-     * Create an extended supported rates information element
-     * from the given rates.
-     *
-     * \param rates the extended supported rates
-     */
-    ExtendedSupportedRatesIE(SupportedRates* rates);
+    SupportedRates();
 
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
+    void Print(std::ostream& os) const override;
 
     /**
-     * Set supported rates.
+     * Return the rate (converted back to raw value) at the given index.
      *
-     * \param rates the supported rates
+     * \param i the given index
+     * \return the rate in bps
      */
-    void SetSupportedRates(SupportedRates* rates);
+    uint32_t GetRate(uint8_t i) const;
 
-  private:
+  protected:
     uint16_t GetInformationFieldSize() const override;
     void SerializeInformationField(Buffer::Iterator start) const override;
     uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
 
-    /**
-     * This member points to the SupportedRates object that contains the
-     * actual rate details. This class is a friend of that, so we have
-     * access to all the private data we need.
-     */
-    SupportedRates* m_supportedRates;
+    std::vector<uint8_t> m_rates; //!< List of supported bit rates (divided by 500000)
 };
 
 /**
- * \brief The Supported Rates Information Element
+ * \brief The Extended Supported Rates Information Element
  * \ingroup wifi
  *
- * This class knows how to serialise and deserialise the Supported
- * Rates Element that holds the first 8 (non-HT) supported rates.
- *
- * The \c ExtendedSupportedRatesIE class (of which an instance exists
- * in objects of this class) deals with rates beyond the first 8.
+ * This class knows how to serialise and deserialise the Extended
+ * Supported Rates Element that holds (non-HT) rates beyond the 8 that
+ * the original Supported Rates element can carry.
  */
-class SupportedRates : public WifiInformationElement
+class ExtendedSupportedRatesIE : public SupportedRates
 {
   public:
-    SupportedRates();
-
-    /**
-     * Type conversion operator
-     *
-     * \param rates the reference const SupportedRates
-     */
-    SupportedRates(const SupportedRates& rates);
-
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
+};
 
-    /**
-     * Assignment operator
-     *
-     * \param rates the rates to assign
-     * \returns the assigned value
-     */
-    SupportedRates& operator=(const SupportedRates& rates);
-
-    /**
-     * This defines the maximum number of supported rates that a STA is
-     * allowed to have. Currently this number is set for IEEE 802.11b/g and SISO IEEE 802.11n
-     * stations which need 2 rates each from Clauses 15 and 18, and then 8
-     * from Clause 19.
-     */
-    static const uint8_t MAX_SUPPORTED_RATES = 32;
-
+/**
+ * \brief Struct containing all supported rates.
+ * \ingroup wifi
+ *
+ */
+struct AllSupportedRates
+{
     /**
      * Add the given rate to the supported rates.
      *
@@ -170,44 +141,11 @@
      * \return the number of supported rates
      */
     uint8_t GetNRates() const;
-    /**
-     * Return the rate at the given index.
-     * Return the rate (converted back to raw value) at the given index.
-     *
-     * \param i the given index
-     * \return the rate in bps
-     */
-    uint32_t GetRate(uint8_t i) const;
 
-    /**
-     * We support the Extended Supported Rates Information Element
-     * through the ExtendedSupportedRatesIE object which is declared
-     * above. We allow this class to be a friend so that it can access
-     * our private data detailing the rates, and create an instance as
-     * extended.
-     */
-    friend class ExtendedSupportedRatesIE;
-    std::optional<ExtendedSupportedRatesIE> extended; //!< extended supported rates info element
-
-  private:
-    uint16_t GetInformationFieldSize() const override;
-    void SerializeInformationField(Buffer::Iterator start) const override;
-    uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
-
-    uint8_t m_nRates;                     //!< Number of supported rates
-    uint8_t m_rates[MAX_SUPPORTED_RATES]; //!< List of supported bit rates (divided by 500000)
+    SupportedRates rates;                                  //!< supported rates
+    std::optional<ExtendedSupportedRatesIE> extendedRates; //!< supported extended rates
 };
 
-/**
- * Serialize SupportedRates to the given ostream.
- *
- * \param os output stream
- * \param rates the SupportedRates
- *
- * \return std::ostream
- */
-std::ostream& operator<<(std::ostream& os, const SupportedRates& rates);
-
 } // namespace ns3
 
 #endif /* SUPPORTED_RATES_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/txop.cc ns-3.39/src/wifi/model/txop.cc
--- ns-3.38/src/wifi/model/txop.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/txop.cc	2023-07-05 14:23:48.121428500 -0700
@@ -68,8 +68,7 @@
                 "The minimum values of the contention window for all the links",
                 TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
                 AttributeContainerValue<UintegerValue>(),
-                MakeAttributeContainerAccessor<UintegerValue, std::list>(&Txop::SetMinCws,
-                                                                         &Txop::GetMinCws),
+                MakeAttributeContainerAccessor<UintegerValue>(&Txop::SetMinCws, &Txop::GetMinCws),
                 MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint32_t>()))
             .AddAttribute("MaxCw",
                           "The maximum value of the contention window (just for the first link, "
@@ -84,8 +83,7 @@
                 "The maximum values of the contention window for all the links",
                 TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
                 AttributeContainerValue<UintegerValue>(),
-                MakeAttributeContainerAccessor<UintegerValue, std::list>(&Txop::SetMaxCws,
-                                                                         &Txop::GetMaxCws),
+                MakeAttributeContainerAccessor<UintegerValue>(&Txop::SetMaxCws, &Txop::GetMaxCws),
                 MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint32_t>()))
             .AddAttribute(
                 "Aifsn",
@@ -101,8 +99,7 @@
                 "The values of AIFSN for all the links",
                 TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
                 AttributeContainerValue<UintegerValue>(),
-                MakeAttributeContainerAccessor<UintegerValue, std::list>(&Txop::SetAifsns,
-                                                                         &Txop::GetAifsns),
+                MakeAttributeContainerAccessor<UintegerValue>(&Txop::SetAifsns, &Txop::GetAifsns),
                 MakeAttributeContainerChecker<UintegerValue>(MakeUintegerChecker<uint8_t>()))
             .AddAttribute("TxopLimit",
                           "The TXOP limit: the default value conforms to non-QoS "
@@ -112,14 +109,13 @@
                           MakeTimeAccessor((void(Txop::*)(Time)) & Txop::SetTxopLimit,
                                            (Time(Txop::*)() const) & Txop::GetTxopLimit),
                           MakeTimeChecker())
-            .AddAttribute(
-                "TxopLimits",
-                "The values of TXOP limit for all the links",
-                TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
-                AttributeContainerValue<TimeValue>(),
-                MakeAttributeContainerAccessor<TimeValue, std::list>(&Txop::SetTxopLimits,
-                                                                     &Txop::GetTxopLimits),
-                MakeAttributeContainerChecker<TimeValue>(MakeTimeChecker()))
+            .AddAttribute("TxopLimits",
+                          "The values of TXOP limit for all the links",
+                          TypeId::ATTR_GET | TypeId::ATTR_SET, // do not set at construction time
+                          AttributeContainerValue<TimeValue>(),
+                          MakeAttributeContainerAccessor<TimeValue>(&Txop::SetTxopLimits,
+                                                                    &Txop::GetTxopLimits),
+                          MakeAttributeContainerChecker<TimeValue>(MakeTimeChecker()))
             .AddAttribute("Queue",
                           "The WifiMacQueue object",
                           PointerValue(),
@@ -246,7 +242,7 @@
     auto& link = GetLink(linkId);
     bool changed = (link.cwMin != minCw);
     link.cwMin = minCw;
-    if (changed == true)
+    if (changed)
     {
         ResetCw(linkId);
     }
@@ -275,7 +271,7 @@
     auto& link = GetLink(linkId);
     bool changed = (link.cwMax != maxCw);
     link.cwMax = maxCw;
-    if (changed == true)
+    if (changed)
     {
         ResetCw(linkId);
     }
@@ -515,9 +511,7 @@
 Txop::Queue(Ptr<WifiMpdu> mpdu)
 {
     NS_LOG_FUNCTION(this << *mpdu);
-    const auto linkIds =
-        m_mac->GetMacQueueScheduler()->GetLinkIds(m_queue->GetAc(),
-                                                  WifiMacQueueContainer::GetQueueId(mpdu));
+    const auto linkIds = m_mac->GetMacQueueScheduler()->GetLinkIds(m_queue->GetAc(), mpdu);
     for (const auto linkId : linkIds)
     {
         if (m_mac->GetChannelAccessManager(linkId)->NeedBackoffUponAccess(this))
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/txop.h ns-3.39/src/wifi/model/txop.h
--- ns-3.38/src/wifi/model/txop.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/txop.h	2023-07-05 14:23:48.121428500 -0700
@@ -401,6 +401,13 @@
      */
     void StartBackoffNow(uint32_t nSlots, uint8_t linkId);
 
+    /**
+     * Check if the Txop has frames to transmit over the given link
+     * \param linkId the ID of the given link.
+     * \return true if the Txop has frames to transmit.
+     */
+    virtual bool HasFramesToTransmit(uint8_t linkId);
+
   protected:
     ///< ChannelAccessManager associated class
     friend class ChannelAccessManager;
@@ -417,12 +424,6 @@
     virtual void NotifyAccessRequested(uint8_t linkId);
 
     /**
-     * Check if the Txop has frames to transmit over the given link
-     * \param linkId the ID of the given link.
-     * \return true if the Txop has frames to transmit.
-     */
-    virtual bool HasFramesToTransmit(uint8_t linkId);
-    /**
      * Generate a new backoff for the given link now.
      *
      * \param linkId the ID of the given link
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/vht/vht-capabilities.cc ns-3.39/src/wifi/model/vht/vht-capabilities.cc
--- ns-3.38/src/wifi/model/vht/vht-capabilities.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/vht/vht-capabilities.cc	2023-07-05 14:23:48.121428500 -0700
@@ -63,6 +63,12 @@
     return IE_VHT_CAPABILITIES;
 }
 
+void
+VhtCapabilities::Print(std::ostream& os) const
+{
+    os << "VHT Capabilities=" << GetVhtCapabilitiesInfo() << "|" << GetSupportedMcsAndNssSet();
+}
+
 uint16_t
 VhtCapabilities::GetInformationFieldSize() const
 {
@@ -384,13 +390,4 @@
     return m_rxHighestSupportedLongGuardIntervalDataRate;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const VhtCapabilities& vhtCapabilities)
-{
-    os << vhtCapabilities.GetVhtCapabilitiesInfo() << "|"
-       << vhtCapabilities.GetSupportedMcsAndNssSet();
-
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/vht/vht-capabilities.h ns-3.39/src/wifi/model/vht/vht-capabilities.h
--- ns-3.38/src/wifi/model/vht/vht-capabilities.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/vht/vht-capabilities.h	2023-07-05 14:23:48.121428500 -0700
@@ -38,6 +38,7 @@
 
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Set the VHT Capabilities Info field in the VHT Capabilities information element.
@@ -241,16 +242,6 @@
                                                             ///< guard interval data rate
 };
 
-/**
- * output stream output operator
- *
- * \param os the output stream
- * \param vhtCapabilities the VHT capabilities
- *
- * \returns output stream
- */
-std::ostream& operator<<(std::ostream& os, const VhtCapabilities& vhtCapabilities);
-
 } // namespace ns3
 
 #endif /* VHT_CAPABILITY_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/vht/vht-operation.cc ns-3.39/src/wifi/model/vht/vht-operation.cc
--- ns-3.38/src/wifi/model/vht/vht-operation.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/vht/vht-operation.cc	2023-07-05 14:23:48.121428500 -0700
@@ -36,6 +36,13 @@
     return IE_VHT_OPERATION;
 }
 
+void
+VhtOperation::Print(std::ostream& os) const
+{
+    os << "VHT Operation=" << +GetChannelWidth() << "|" << +GetChannelCenterFrequencySegment0()
+       << "|" << +GetChannelCenterFrequencySegment1() << "|" << GetBasicVhtMcsAndNssSet();
+}
+
 uint16_t
 VhtOperation::GetInformationFieldSize() const
 {
@@ -129,14 +136,4 @@
     return length;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const VhtOperation& VhtOperation)
-{
-    os << +VhtOperation.GetChannelWidth() << "|"
-       << +VhtOperation.GetChannelCenterFrequencySegment0() << "|"
-       << +VhtOperation.GetChannelCenterFrequencySegment1() << "|"
-       << VhtOperation.GetBasicVhtMcsAndNssSet();
-    return os;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/vht/vht-operation.h ns-3.39/src/wifi/model/vht/vht-operation.h
--- ns-3.38/src/wifi/model/vht/vht-operation.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/vht/vht-operation.h	2023-07-05 14:23:48.121428500 -0700
@@ -39,6 +39,7 @@
 
     // Implementations of pure virtual methods of WifiInformationElement
     WifiInformationElementId ElementId() const override;
+    void Print(std::ostream& os) const override;
 
     /**
      * Set the Channel Width field in the VHT Operation information element.
@@ -115,16 +116,6 @@
     uint16_t m_basicVhtMcsAndNssSet; ///< basic VHT MCS NSS set
 };
 
-/**
- * output stream output operator
- *
- * \param os the output stream
- * \param VhtOperation the VHT operation
- *
- * \returns output stream
- */
-std::ostream& operator<<(std::ostream& os, const VhtOperation& VhtOperation);
-
 } // namespace ns3
 
 #endif /* VHT_OPERATION_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/vht/vht-phy.cc ns-3.39/src/wifi/model/vht/vht-phy.cc
--- ns-3.38/src/wifi/model/vht/vht-phy.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/vht/vht-phy.cc	2023-07-05 14:23:48.121428500 -0700
@@ -250,10 +250,8 @@
     NS_LOG_FUNCTION(this << psdus << txVector << ppduDuration);
     return Create<VhtPpdu>(psdus.begin()->second,
                            txVector,
-                           m_wifiPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(
-                               txVector.GetChannelWidth()),
+                           m_wifiPhy->GetOperatingChannel(),
                            ppduDuration,
-                           m_wifiPhy->GetPhyBand(),
                            ObtainNextUid(txVector));
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/vht/vht-ppdu.cc ns-3.39/src/wifi/model/vht/vht-ppdu.cc
--- ns-3.38/src/wifi/model/vht/vht-ppdu.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/vht/vht-ppdu.cc	2023-07-05 14:23:48.121428500 -0700
@@ -24,6 +24,7 @@
 #include "vht-phy.h"
 
 #include "ns3/log.h"
+#include "ns3/wifi-phy-operating-channel.h"
 #include "ns3/wifi-phy.h"
 #include "ns3/wifi-psdu.h"
 
@@ -34,18 +35,16 @@
 
 VhtPpdu::VhtPpdu(Ptr<const WifiPsdu> psdu,
                  const WifiTxVector& txVector,
-                 uint16_t txCenterFreq,
+                 const WifiPhyOperatingChannel& channel,
                  Time ppduDuration,
-                 WifiPhyBand band,
                  uint64_t uid)
     : OfdmPpdu(psdu,
                txVector,
-               txCenterFreq,
-               band,
+               channel,
                uid,
                false) // don't instantiate LSigHeader of OfdmPpdu
 {
-    NS_LOG_FUNCTION(this << psdu << txVector << txCenterFreq << ppduDuration << band << uid);
+    NS_LOG_FUNCTION(this << psdu << txVector << channel << ppduDuration << uid);
     SetPhyHeaders(txVector, ppduDuration);
 }
 
@@ -53,20 +52,8 @@
 VhtPpdu::SetPhyHeaders(const WifiTxVector& txVector, Time ppduDuration)
 {
     NS_LOG_FUNCTION(this << txVector << ppduDuration);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    LSigHeader lSig;
-    SetLSigHeader(lSig, ppduDuration);
-
-    VhtSigHeader vhtSig;
-    SetVhtSigHeader(vhtSig, txVector, ppduDuration);
-
-    m_phyHeaders->AddHeader(vhtSig);
-    m_phyHeaders->AddHeader(lSig);
-#else
     SetLSigHeader(m_lSig, ppduDuration);
     SetVhtSigHeader(m_vhtSig, txVector, ppduDuration);
-#endif
 }
 
 void
@@ -105,27 +92,7 @@
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(m_preamble);
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    auto phyHeaders = m_phyHeaders->Copy();
-
-    LSigHeader lSig;
-    if (phyHeaders->RemoveHeader(lSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing L-SIG header in VHT PPDU");
-    }
-
-    VhtSigHeader vhtSig;
-    if (phyHeaders->RemoveHeader(vhtSig) == 0)
-    {
-        NS_FATAL_ERROR("Missing VHT-SIG header in VHT PPDU");
-    }
-
-    SetTxVectorFromPhyHeaders(txVector, lSig, vhtSig);
-#else
     SetTxVectorFromPhyHeaders(txVector, m_lSig, m_vhtSig);
-#endif
-
     return txVector;
 }
 
@@ -144,32 +111,14 @@
 Time
 VhtPpdu::GetTxDuration() const
 {
-    Time ppduDuration = Seconds(0);
     const WifiTxVector& txVector = GetTxVector();
-
-    uint16_t length = 0;
-    bool sgi = false;
-    bool sgiDisambiguation = false;
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    auto phyHeaders = m_phyHeaders->Copy();
-
-    LSigHeader lSig;
-    phyHeaders->RemoveHeader(lSig);
-    VhtSigHeader vhtSig;
-    phyHeaders->RemoveHeader(vhtSig);
-
-    length = lSig.GetLength();
-    sgi = vhtSig.GetShortGuardInterval();
-    sgiDisambiguation = vhtSig.GetShortGuardIntervalDisambiguation();
-#else
-    length = m_lSig.GetLength();
-    sgi = m_vhtSig.GetShortGuardInterval();
-    sgiDisambiguation = m_vhtSig.GetShortGuardIntervalDisambiguation();
-#endif
-
-    Time tSymbol = NanoSeconds(3200 + txVector.GetGuardInterval());
-    Time preambleDuration = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
-    Time calculatedDuration = MicroSeconds(((ceil(static_cast<double>(length + 3) / 3)) * 4) + 20);
+    const auto length = m_lSig.GetLength();
+    const auto sgi = m_vhtSig.GetShortGuardInterval();
+    const auto sgiDisambiguation = m_vhtSig.GetShortGuardIntervalDisambiguation();
+    const auto tSymbol = NanoSeconds(3200 + txVector.GetGuardInterval());
+    const auto preambleDuration = WifiPhy::CalculatePhyPreambleAndHeaderDuration(txVector);
+    const auto calculatedDuration =
+        MicroSeconds(((ceil(static_cast<double>(length + 3) / 3)) * 4) + 20);
     uint32_t nSymbols =
         floor(static_cast<double>((calculatedDuration - preambleDuration).GetNanoSeconds()) /
               tSymbol.GetNanoSeconds());
@@ -177,8 +126,7 @@
     {
         nSymbols--;
     }
-    ppduDuration = preambleDuration + (nSymbols * tSymbol);
-    return ppduDuration;
+    return (preambleDuration + (nSymbols * tSymbol));
 }
 
 Ptr<WifiPpdu>
@@ -203,42 +151,6 @@
 {
 }
 
-TypeId
-VhtPpdu::VhtSigHeader::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::VhtSigHeader")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<VhtSigHeader>();
-    return tid;
-}
-
-TypeId
-VhtPpdu::VhtSigHeader::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-void
-VhtPpdu::VhtSigHeader::Print(std::ostream& os) const
-{
-    os << "SU_MCS=" << +m_suMcs << " CHANNEL_WIDTH=" << GetChannelWidth() << " SGI=" << +m_sgi
-       << " NSTS=" << +m_nsts << " MU=" << +m_mu;
-}
-
-uint32_t
-VhtPpdu::VhtSigHeader::GetSerializedSize() const
-{
-    uint32_t size = 0;
-    size += 3; // VHT-SIG-A1
-    size += 3; // VHT-SIG-A2
-    if (m_mu)
-    {
-        size += 4; // VHT-SIG-B
-    }
-    return size;
-}
-
 void
 VhtPpdu::VhtSigHeader::SetMuFlag(bool mu)
 {
@@ -337,57 +249,4 @@
     return m_suMcs;
 }
 
-void
-VhtPpdu::VhtSigHeader::Serialize(Buffer::Iterator start) const
-{
-    // VHT-SIG-A1
-    uint8_t byte = m_bw;
-    byte |= (0x01 << 2); // Set Reserved bit #2 to 1
-    start.WriteU8(byte);
-    uint16_t bytes = (m_nsts & 0x07) << 2;
-    bytes |= (0x01 << (23 - 8)); // Set Reserved bit #23 to 1
-    start.WriteU16(bytes);
-
-    // VHT-SIG-A2
-    byte = m_sgi & 0x01;
-    byte |= ((m_sgi_disambiguation & 0x01) << 1);
-    byte |= ((m_suMcs & 0x0f) << 4);
-    start.WriteU8(byte);
-    bytes = (0x01 << (9 - 8)); // Set Reserved bit #9 to 1
-    start.WriteU16(bytes);
-
-    if (m_mu)
-    {
-        // VHT-SIG-B
-        start.WriteU32(0);
-    }
-}
-
-uint32_t
-VhtPpdu::VhtSigHeader::Deserialize(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-
-    // VHT-SIG-A1
-    uint8_t byte = i.ReadU8();
-    m_bw = byte & 0x03;
-    uint16_t bytes = i.ReadU16();
-    m_nsts = ((bytes >> 2) & 0x07);
-
-    // VHT-SIG-A2
-    byte = i.ReadU8();
-    m_sgi = byte & 0x01;
-    m_sgi_disambiguation = ((byte >> 1) & 0x01);
-    m_suMcs = ((byte >> 4) & 0x0f);
-    i.ReadU16();
-
-    if (m_mu)
-    {
-        // VHT-SIG-B
-        i.ReadU32();
-    }
-
-    return i.GetDistanceFrom(start);
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/vht/vht-ppdu.h ns-3.39/src/wifi/model/vht/vht-ppdu.h
--- ns-3.38/src/wifi/model/vht/vht-ppdu.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/vht/vht-ppdu.h	2023-07-05 14:23:48.121428500 -0700
@@ -23,6 +23,7 @@
 #define VHT_PPDU_H
 
 #include "ns3/ofdm-ppdu.h"
+#include "ns3/wifi-phy-operating-channel.h"
 
 /**
  * \file
@@ -48,24 +49,12 @@
      * VHT PHY header (VHT-SIG-A1/A2/B).
      * See section 21.3.8 in IEEE 802.11-2016.
      */
-    class VhtSigHeader : public Header
+    class VhtSigHeader
     {
       public:
         VhtSigHeader();
 
         /**
-         * \brief Get the type ID.
-         * \return the object 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;
-
-        /**
          * Set the Multi-User (MU) flag.
          *
          * \param mu the MU flag
@@ -153,16 +142,14 @@
      *
      * \param psdu the PHY payload (PSDU)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param ppduDuration the transmission duration of this PPDU
-     * \param band the WifiPhyBand used for the transmission of this PPDU
      * \param uid the unique ID of this PPDU
      */
     VhtPpdu(Ptr<const WifiPsdu> psdu,
             const WifiTxVector& txVector,
-            uint16_t txCenterFreq,
+            const WifiPhyOperatingChannel& channel,
             Time ppduDuration,
-            WifiPhyBand band,
             uint64_t uid);
 
     Time GetTxDuration() const override;
@@ -210,10 +197,8 @@
                                    const LSigHeader& lSig,
                                    const VhtSigHeader& vhtSig) const;
 
-#ifndef NS3_BUILD_PROFILE_DEBUG
     VhtSigHeader m_vhtSig; //!< the VHT-SIG PHY header
-#endif
-}; // class VhtPpdu
+};                         // class VhtPpdu
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-acknowledgment.cc ns-3.39/src/wifi/model/wifi-acknowledgment.cc
--- ns-3.38/src/wifi/model/wifi-acknowledgment.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-acknowledgment.cc	2023-07-05 14:23:48.121428500 -0700
@@ -70,7 +70,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiNoAck::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiNoAck(*this));
+    return std::make_unique<WifiNoAck>(*this);
 }
 
 bool
@@ -78,11 +78,7 @@
                              uint8_t tid,
                              WifiMacHeader::QosAckPolicy ackPolicy) const
 {
-    if (ackPolicy == WifiMacHeader::NO_ACK || ackPolicy == WifiMacHeader::BLOCK_ACK)
-    {
-        return true;
-    }
-    return false;
+    return ackPolicy == WifiMacHeader::NO_ACK || ackPolicy == WifiMacHeader::BLOCK_ACK;
 }
 
 void
@@ -103,7 +99,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiNormalAck::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiNormalAck(*this));
+    return std::make_unique<WifiNormalAck>(*this);
 }
 
 bool
@@ -111,11 +107,7 @@
                                  uint8_t tid,
                                  WifiMacHeader::QosAckPolicy ackPolicy) const
 {
-    if (ackPolicy == WifiMacHeader::NORMAL_ACK)
-    {
-        return true;
-    }
-    return false;
+    return ackPolicy == WifiMacHeader::NORMAL_ACK;
 }
 
 void
@@ -136,7 +128,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiBlockAck::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiBlockAck(*this));
+    return std::make_unique<WifiBlockAck>(*this);
 }
 
 bool
@@ -144,11 +136,7 @@
                                 uint8_t tid,
                                 WifiMacHeader::QosAckPolicy ackPolicy) const
 {
-    if (ackPolicy == WifiMacHeader::NORMAL_ACK)
-    {
-        return true;
-    }
-    return false;
+    return ackPolicy == WifiMacHeader::NORMAL_ACK;
 }
 
 void
@@ -169,7 +157,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiBarBlockAck::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiBarBlockAck(*this));
+    return std::make_unique<WifiBarBlockAck>(*this);
 }
 
 bool
@@ -177,11 +165,7 @@
                                    uint8_t tid,
                                    WifiMacHeader::QosAckPolicy ackPolicy) const
 {
-    if (ackPolicy == WifiMacHeader::BLOCK_ACK)
-    {
-        return true;
-    }
-    return false;
+    return ackPolicy == WifiMacHeader::BLOCK_ACK;
 }
 
 void
@@ -202,7 +186,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiDlMuBarBaSequence::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiDlMuBarBaSequence(*this));
+    return std::make_unique<WifiDlMuBarBaSequence>(*this);
 }
 
 bool
@@ -228,12 +212,7 @@
         return false;
     }
 
-    if (ackPolicy == WifiMacHeader::BLOCK_ACK)
-    {
-        return true;
-    }
-
-    return false;
+    return ackPolicy == WifiMacHeader::BLOCK_ACK;
 }
 
 void
@@ -268,7 +247,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiDlMuTfMuBar::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiDlMuTfMuBar(*this));
+    return std::make_unique<WifiDlMuTfMuBar>(*this);
 }
 
 bool
@@ -277,12 +256,7 @@
                                    WifiMacHeader::QosAckPolicy ackPolicy) const
 {
     // the only admitted ack policy is Block Ack because stations need to wait for a MU-BAR
-    if (ackPolicy == WifiMacHeader::BLOCK_ACK)
-    {
-        return true;
-    }
-
-    return false;
+    return ackPolicy == WifiMacHeader::BLOCK_ACK;
 }
 
 void
@@ -309,7 +283,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiDlMuAggregateTf::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiDlMuAggregateTf(*this));
+    return std::make_unique<WifiDlMuAggregateTf>(*this);
 }
 
 bool
@@ -318,12 +292,7 @@
                                        WifiMacHeader::QosAckPolicy ackPolicy) const
 {
     // the only admitted ack policy is No explicit acknowledgment or TB PPDU Ack policy
-    if (ackPolicy == WifiMacHeader::NO_EXPLICIT_ACK)
-    {
-        return true;
-    }
-
-    return false;
+    return ackPolicy == WifiMacHeader::NO_EXPLICIT_ACK;
 }
 
 void
@@ -350,7 +319,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiUlMuMultiStaBa::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiUlMuMultiStaBa(*this));
+    return std::make_unique<WifiUlMuMultiStaBa>(*this);
 }
 
 bool
@@ -385,7 +354,7 @@
 std::unique_ptr<WifiAcknowledgment>
 WifiAckAfterTbPpdu::Copy() const
 {
-    return std::unique_ptr<WifiAcknowledgment>(new WifiAckAfterTbPpdu(*this));
+    return std::make_unique<WifiAckAfterTbPpdu>(*this);
 }
 
 bool
@@ -393,12 +362,7 @@
                                       uint8_t tid,
                                       WifiMacHeader::QosAckPolicy ackPolicy) const
 {
-    if (ackPolicy == WifiMacHeader::NORMAL_ACK)
-    {
-        return true;
-    }
-
-    return false;
+    return ackPolicy == WifiMacHeader::NORMAL_ACK;
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-assoc-manager.cc ns-3.39/src/wifi/model/wifi-assoc-manager.cc
--- ns-3.38/src/wifi/model/wifi-assoc-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-assoc-manager.cc	2023-07-05 14:23:48.121428500 -0700
@@ -117,13 +117,13 @@
         Ssid apSsid;
         if (auto beacon = std::get_if<MgtBeaconHeader>(&apInfo.m_frame); beacon)
         {
-            apSsid = beacon->GetSsid();
+            apSsid = beacon->Get<Ssid>().value();
         }
         else
         {
             auto probeResp = std::get_if<MgtProbeResponseHeader>(&apInfo.m_frame);
             NS_ASSERT(probeResp);
-            apSsid = probeResp->GetSsid();
+            apSsid = probeResp->Get<Ssid>().value();
         }
         if (!apSsid.IsEqual(m_scanParams.ssid))
         {
@@ -234,10 +234,10 @@
     m_mac->ScanningTimeout(std::move(bestAp));
 }
 
-std::list<std::pair<std::uint8_t, uint8_t>>&
+std::list<std::pair<uint8_t, uint8_t>>&
 WifiAssocManager::GetSetupLinks(const StaWifiMac::ApInfo& apInfo)
 {
-    return const_cast<std::list<std::pair<std::uint8_t, uint8_t>>&>(apInfo.m_setupLinks);
+    return const_cast<std::list<std::pair<uint8_t, uint8_t>>&>(apInfo.m_setupLinks);
 }
 
 bool
@@ -254,15 +254,15 @@
     // from Beacon or Probe Response
     if (auto beacon = std::get_if<MgtBeaconHeader>(&m_apList.begin()->m_frame); beacon)
     {
-        mle = beacon->GetMultiLinkElement();
-        rnr = beacon->GetReducedNeighborReport();
+        mle = beacon->Get<MultiLinkElement>();
+        rnr = beacon->Get<ReducedNeighborReport>();
     }
     else
     {
         auto probeResp = std::get_if<MgtProbeResponseHeader>(&m_apList.begin()->m_frame);
         NS_ASSERT(probeResp);
-        mle = probeResp->GetMultiLinkElement();
-        rnr = probeResp->GetReducedNeighborReport();
+        mle = probeResp->Get<MultiLinkElement>();
+        rnr = probeResp->Get<ReducedNeighborReport>();
     }
 
     if (!mle.has_value())
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-assoc-manager.h ns-3.39/src/wifi/model/wifi-assoc-manager.h
--- ns-3.38/src/wifi/model/wifi-assoc-manager.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-assoc-manager.h	2023-07-05 14:23:48.121428500 -0700
@@ -23,6 +23,7 @@
 #include "qos-utils.h"
 #include "sta-wifi-mac.h"
 
+#include <optional>
 #include <set>
 #include <unordered_map>
 
@@ -177,7 +178,7 @@
      * \param apInfo the info about the given AP
      * \return a reference to the list of the links to setup with the given AP
      */
-    std::list<std::pair<std::uint8_t, uint8_t>>& GetSetupLinks(const StaWifiMac::ApInfo& apInfo);
+    std::list<std::pair<uint8_t, uint8_t>>& GetSetupLinks(const StaWifiMac::ApInfo& apInfo);
 
     /**
      * \return the scanning parameters.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-bandwidth-filter.cc ns-3.39/src/wifi/model/wifi-bandwidth-filter.cc
--- ns-3.38/src/wifi/model/wifi-bandwidth-filter.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/wifi-bandwidth-filter.cc	2023-07-05 14:23:48.121428500 -0700
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2022 Universita' degli Studi di Napoli "Federico II"
+ * Copyright (c) 2022 University of Washington (port logic to WifiBandwidthFilter)
+ *
+ * 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 "wifi-bandwidth-filter.h"
+
+#include "spectrum-wifi-phy.h"
+#include "wifi-psdu.h"
+#include "wifi-spectrum-phy-interface.h"
+#include "wifi-spectrum-signal-parameters.h"
+
+#include <ns3/boolean.h>
+#include <ns3/spectrum-transmit-filter.h>
+
+namespace ns3
+{
+
+NS_LOG_COMPONENT_DEFINE("WifiBandwidthFilter");
+
+NS_OBJECT_ENSURE_REGISTERED(WifiBandwidthFilter);
+
+WifiBandwidthFilter::WifiBandwidthFilter()
+{
+    NS_LOG_FUNCTION(this);
+}
+
+TypeId
+WifiBandwidthFilter::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::WifiBandwidthFilter")
+                            .SetParent<SpectrumTransmitFilter>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<WifiBandwidthFilter>();
+    return tid;
+}
+
+bool
+WifiBandwidthFilter::DoFilter(Ptr<const SpectrumSignalParameters> params,
+                              Ptr<const SpectrumPhy> receiverPhy)
+{
+    NS_LOG_FUNCTION(this << params);
+
+    auto wifiRxParams = DynamicCast<const WifiSpectrumSignalParameters>(params);
+    if (!wifiRxParams)
+    {
+        NS_LOG_DEBUG("Received a non Wi-Fi signal: do not filter");
+        return false;
+    }
+
+    // WifiSpectrumPhyInterface (passed in) can be used to fetch SpectrumWifiPhy
+
+    auto interface = DynamicCast<const WifiSpectrumPhyInterface>(receiverPhy);
+    auto wifiPhy = interface->GetSpectrumWifiPhy();
+
+    NS_ASSERT_MSG(wifiPhy, "WifiPhy should be valid if WifiSpectrumSignalParameters was found");
+
+    BooleanValue trackSignalsInactiveInterfaces;
+    wifiPhy->GetAttribute("TrackSignalsFromInactiveInterfaces", trackSignalsInactiveInterfaces);
+
+    NS_ASSERT_MSG(trackSignalsInactiveInterfaces.Get() ||
+                      (interface == wifiPhy->GetCurrentInterface()),
+                  "DoFilter should not be called for an inactive interface if "
+                  "SpectrumWifiPhy::TrackSignalsFromInactiveInterfaces attribute is not enabled");
+
+    NS_ASSERT((interface != wifiPhy->GetCurrentInterface()) ||
+              (wifiPhy->GetOperatingChannel().GetFrequency() == interface->GetCenterFrequency()));
+    NS_ASSERT((interface != wifiPhy->GetCurrentInterface()) ||
+              (wifiPhy->GetOperatingChannel().GetWidth() == interface->GetChannelWidth()));
+
+    // The signal power is spread over a frequency interval that includes a guard
+    // band on the left and a guard band on the right of the nominal TX band
+
+    const auto rxCenterFreq = wifiRxParams->ppdu->GetTxCenterFreq();
+    const auto rxWidth = wifiRxParams->ppdu->GetTxVector().GetChannelWidth();
+    const auto guardBandwidth = wifiPhy->GetGuardBandwidth(rxWidth);
+    const auto operatingFrequency = interface->GetCenterFrequency();
+    const auto operatingChannelWidth = interface->GetChannelWidth();
+
+    const auto rxMinFreq = rxCenterFreq - rxWidth / 2 - guardBandwidth;
+    const auto rxMaxFreq = rxCenterFreq + rxWidth / 2 + guardBandwidth;
+
+    const auto channelMinFreq = operatingFrequency - operatingChannelWidth / 2;
+    const auto channelMaxFreq = operatingFrequency + operatingChannelWidth / 2;
+
+    /**
+     * The PPDU can be ignored if the two bands do not overlap.
+     *
+     * First non-overlapping case:
+     *
+     *                                        â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”�
+     *                                PPDU    â”‚  Guard  â”‚ Nominal â”‚  Guard  â”‚
+     *                                        â”‚  Band   â”‚   Band  â”‚  Band   â”‚
+     *                                        â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
+     *                                    rxMinFreq                     rxMaxFreq
+     *
+     * channelMinFreq                channelMaxFreq
+     *         â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”�
+     *         â”‚         Operating            â”‚
+     *         â”‚           Channel            â”‚
+     *         â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
+     *
+     * Second non-overlapping case:
+     *
+     *         â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”�
+     * PPDU    â”‚  Guard  â”‚ Nominal â”‚  Guard  â”‚
+     *         â”‚  Band   â”‚   Band  â”‚  Band   â”‚
+     *         â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
+     *     rxMinFreq                     rxMaxFreq
+     *
+     *                               channelMinFreq                channelMaxFreq
+     *                                       â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”�
+     *                                       â”‚         Operating            â”‚
+     *                                       â”‚           Channel            â”‚
+     *                                       â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
+     */
+    auto filter = (rxMinFreq >= channelMaxFreq || rxMaxFreq <= channelMinFreq);
+    NS_LOG_DEBUG("Returning " << filter);
+    return filter;
+}
+
+} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-bandwidth-filter.h ns-3.39/src/wifi/model/wifi-bandwidth-filter.h
--- ns-3.38/src/wifi/model/wifi-bandwidth-filter.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/wifi-bandwidth-filter.h	2023-07-05 14:23:48.121428500 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022 University of Washington
+ *
+ * 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 WIFI_BANDWIDTH_FILTER_H
+#define WIFI_BANDWIDTH_FILTER_H
+
+#include <ns3/spectrum-transmit-filter.h>
+
+namespace ns3
+{
+
+class WifiBandwidthFilter : public SpectrumTransmitFilter
+{
+  public:
+    WifiBandwidthFilter();
+
+    /**
+     * \brief Get the type ID.
+     * \return the object TypeId
+     */
+    static TypeId GetTypeId();
+
+    /**
+     * \brief Ignore the signal being received if it is a Wi-Fi PPDU whose TX band
+     *        (including guard bands) does not overlap the current operating channel.
+     *
+     * \param params the parameters of the signals being received
+     * \param receiverPhy the SpectrumPhy of the receiver
+     * \return whether the signal being received should be ignored
+     */
+
+    bool DoFilter(Ptr<const SpectrumSignalParameters> params,
+                  Ptr<const SpectrumPhy> receiverPhy) override;
+};
+
+} // namespace ns3
+
+#endif
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-default-ack-manager.cc ns-3.39/src/wifi/model/wifi-default-ack-manager.cc
--- ns-3.38/src/wifi/model/wifi-default-ack-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-default-ack-manager.cc	2023-07-05 14:23:48.121428500 -0700
@@ -154,20 +154,16 @@
     //   a case, a Block Ack is not going to be requested anytime soon)
     // * this is the initial frame of a transmission opportunity and it is not
     //   protected by RTS/CTS (see Annex G.3 of IEEE 802.11-2016)
-    if (m_baThreshold > 0 &&
+    return !(
+        m_baThreshold > 0 &&
         GetMaxDistFromStartingSeq(mpdu, txParams) <
             m_baThreshold * edca->GetBaBufferSize(receiver, tid) &&
-        (edca->GetWifiMacQueue()->GetNPackets({WIFI_QOSDATA_UNICAST_QUEUE, receiver, tid}) -
+        (edca->GetWifiMacQueue()->GetNPackets({WIFI_QOSDATA_QUEUE, WIFI_UNICAST, receiver, tid}) -
              edca->GetBaManager()->GetNBufferedPackets(receiver, tid) >
          1) &&
         !(edca->GetTxopLimit(m_linkId).IsStrictlyPositive() &&
           edca->GetRemainingTxop(m_linkId) == edca->GetTxopLimit(m_linkId) &&
-          !(txParams.m_protection && txParams.m_protection->method == WifiProtection::RTS_CTS)))
-    {
-        return false;
-    }
-
-    return true;
+          !(txParams.m_protection && txParams.m_protection->method == WifiProtection::RTS_CTS)));
 }
 
 bool
@@ -238,20 +234,19 @@
         if (hdr.IsQosData() && !hdr.HasData())
         {
             // QoS Null frame
-            WifiNoAck* acknowledgment = nullptr;
+            std::unique_ptr<WifiAcknowledgment> acknowledgment;
 
             if (txParams.m_acknowledgment)
             {
                 NS_ASSERT(txParams.m_acknowledgment->method == WifiAcknowledgment::NONE);
-                acknowledgment = static_cast<WifiNoAck*>(txParams.m_acknowledgment.get());
-                acknowledgment = new WifiNoAck(*acknowledgment);
+                acknowledgment = txParams.m_acknowledgment->Copy();
             }
             else
             {
-                acknowledgment = new WifiNoAck;
+                acknowledgment = std::make_unique<WifiNoAck>();
             }
             acknowledgment->SetQosAckPolicy(receiver, hdr.GetQosTid(), WifiMacHeader::NO_ACK);
-            return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+            return acknowledgment;
         }
 
         if (txParams.m_acknowledgment)
@@ -260,12 +255,12 @@
             return nullptr;
         }
 
-        WifiAckAfterTbPpdu* acknowledgment = new WifiAckAfterTbPpdu;
+        auto acknowledgment = std::make_unique<WifiAckAfterTbPpdu>();
         if (hdr.IsQosData())
         {
             acknowledgment->SetQosAckPolicy(receiver, hdr.GetQosTid(), WifiMacHeader::NORMAL_ACK);
         }
-        return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+        return acknowledgment;
     }
 
     // if this is a Trigger Frame, call a separate method
@@ -285,12 +280,12 @@
     if (receiver.IsGroup())
     {
         NS_ABORT_MSG_IF(txParams.GetSize(receiver) > 0, "Unicast frames only can be aggregated");
-        WifiNoAck* acknowledgment = new WifiNoAck;
+        auto acknowledgment = std::make_unique<WifiNoAck>();
         if (hdr.IsQosData())
         {
             acknowledgment->SetQosAckPolicy(receiver, hdr.GetQosTid(), WifiMacHeader::NO_ACK);
         }
-        return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+        return acknowledgment;
     }
 
     if ((!hdr.IsQosData() ||
@@ -299,14 +294,14 @@
     {
         NS_LOG_DEBUG(
             "Non-QoS data frame or Block Ack agreement not established, request Normal Ack");
-        WifiNormalAck* acknowledgment = new WifiNormalAck;
+        auto acknowledgment = std::make_unique<WifiNormalAck>();
         acknowledgment->ackTxVector =
             GetWifiRemoteStationManager()->GetAckTxVector(receiver, txParams.m_txVector);
         if (hdr.IsQosData())
         {
             acknowledgment->SetQosAckPolicy(receiver, hdr.GetQosTid(), WifiMacHeader::NORMAL_ACK);
         }
-        return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+        return acknowledgment;
     }
 
     // we get here if mpdu is a QoS data frame related to an established Block Ack agreement
@@ -321,12 +316,12 @@
             return nullptr;
         }
 
-        WifiNoAck* acknowledgment = new WifiNoAck;
+        auto acknowledgment = std::make_unique<WifiNoAck>();
         if (hdr.IsQosData())
         {
             acknowledgment->SetQosAckPolicy(receiver, hdr.GetQosTid(), WifiMacHeader::BLOCK_ACK);
         }
-        return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+        return acknowledgment;
     }
 
     // we get here if a response is needed
@@ -334,11 +329,11 @@
     if (!hdr.IsBlockAckReq() && txParams.GetSize(receiver) == 0 && !ExistInflightOnSameLink(mpdu))
     {
         NS_LOG_DEBUG("Sending a single MPDU, no previous frame to ack: request Normal Ack");
-        WifiNormalAck* acknowledgment = new WifiNormalAck;
+        auto acknowledgment = std::make_unique<WifiNormalAck>();
         acknowledgment->ackTxVector =
             GetWifiRemoteStationManager()->GetAckTxVector(receiver, txParams.m_txVector);
         acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::NORMAL_ACK);
-        return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+        return acknowledgment;
     }
 
     // we get here if multiple MPDUs are being/have been sent
@@ -349,24 +344,24 @@
         // normal ack as response
         NS_LOG_DEBUG("Request to schedule a Block Ack Request");
 
-        WifiBarBlockAck* acknowledgment = new WifiBarBlockAck;
+        auto acknowledgment = std::make_unique<WifiBarBlockAck>();
         acknowledgment->blockAckReqTxVector =
             GetWifiRemoteStationManager()->GetBlockAckTxVector(receiver, txParams.m_txVector);
         acknowledgment->blockAckTxVector = acknowledgment->blockAckReqTxVector;
         acknowledgment->barType = m_mac->GetBarTypeAsOriginator(receiver, tid);
         acknowledgment->baType = m_mac->GetBaTypeAsOriginator(receiver, tid);
         acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::BLOCK_ACK);
-        return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+        return acknowledgment;
     }
 
     NS_LOG_DEBUG(
         "A-MPDU using Implicit Block Ack Request policy or BlockAckReq, request Block Ack");
-    WifiBlockAck* acknowledgment = new WifiBlockAck;
+    auto acknowledgment = std::make_unique<WifiBlockAck>();
     acknowledgment->blockAckTxVector =
         GetWifiRemoteStationManager()->GetBlockAckTxVector(receiver, txParams.m_txVector);
     acknowledgment->baType = m_mac->GetBaTypeAsOriginator(receiver, tid);
     acknowledgment->SetQosAckPolicy(receiver, tid, WifiMacHeader::NORMAL_ACK);
-    return std::unique_ptr<WifiAcknowledgment>(acknowledgment);
+    return acknowledgment;
 }
 
 std::unique_ptr<WifiAcknowledgment>
@@ -698,13 +693,13 @@
     if (trigger.IsBasic())
     {
         // the only supported ack method for now is through a multi-STA BlockAck frame
-        WifiUlMuMultiStaBa* acknowledgment = new WifiUlMuMultiStaBa;
+        auto acknowledgment = std::make_unique<WifiUlMuMultiStaBa>();
 
         for (const auto& userInfo : trigger)
         {
             uint16_t aid12 = userInfo.GetAid12();
 
-            if (aid12 == 2046)
+            if (aid12 == NO_USER_STA_ID)
             {
                 NS_LOG_INFO("Unallocated RU");
                 continue;
@@ -738,11 +733,11 @@
         acknowledgment->multiStaBaTxVector = GetWifiRemoteStationManager()->GetBlockAckTxVector(
             apMac->GetStaList(m_linkId).find(staId)->second,
             acknowledgment->tbPpduTxVector);
-        return std::unique_ptr<WifiUlMuMultiStaBa>(acknowledgment);
+        return acknowledgment;
     }
     else if (trigger.IsBsrp())
     {
-        return std::unique_ptr<WifiAcknowledgment>(new WifiNoAck);
+        return std::make_unique<WifiNoAck>();
     }
 
     return nullptr;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-default-assoc-manager.cc ns-3.39/src/wifi/model/wifi-default-assoc-manager.cc
--- ns-3.38/src/wifi/model/wifi-default-assoc-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-default-assoc-manager.cc	2023-07-05 14:23:48.121428500 -0700
@@ -94,9 +94,15 @@
     m_probeRequestEvent.Cancel();
     m_waitBeaconEvent.Cancel();
 
-    if (GetScanParams().type == WifiScanParams::ACTIVE)
+    if (GetScanParams().type == WifiScanType::ACTIVE)
     {
-        Simulator::Schedule(GetScanParams().probeDelay, &StaWifiMac::SendProbeRequest, m_mac);
+        for (uint8_t linkId = 0; linkId < m_mac->GetNLinks(); linkId++)
+        {
+            Simulator::Schedule(GetScanParams().probeDelay,
+                                &StaWifiMac::SendProbeRequest,
+                                m_mac,
+                                linkId);
+        }
         m_probeRequestEvent =
             Simulator::Schedule(GetScanParams().probeDelay + GetScanParams().maxChannelTime,
                                 &WifiDefaultAssocManager::EndScanning,
@@ -180,10 +186,7 @@
             }
 
             bool needChannelSwitch = false;
-            if (const auto& channel = phy->GetOperatingChannel();
-                channel.GetNumber() != apChannel.GetNumber() ||
-                channel.GetWidth() != apChannel.GetWidth() ||
-                channel.GetPhyBand() != apChannel.GetPhyBand())
+            if (phy->GetOperatingChannel() != apChannel)
             {
                 needChannelSwitch = true;
             }
@@ -218,11 +221,7 @@
                 }
                 // switch this link to using the channel used by a reported AP
                 // TODO check if the STA only supports a narrower channel width
-                NS_LOG_DEBUG("Switch link "
-                             << +linkId << " to using channel " << +apChannel.GetNumber()
-                             << " in band " << apChannel.GetPhyBand() << " frequency "
-                             << apChannel.GetFrequency() << "MHz width " << apChannel.GetWidth()
-                             << "MHz");
+                NS_LOG_DEBUG("Switch link " << +linkId << " to using " << apChannel);
                 WifiPhy::ChannelTuple chTuple{apChannel.GetNumber(),
                                               apChannel.GetWidth(),
                                               apChannel.GetPhyBand(),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-default-protection-manager.cc ns-3.39/src/wifi/model/wifi-default-protection-manager.cc
--- ns-3.38/src/wifi/model/wifi-default-protection-manager.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-default-protection-manager.cc	2023-07-05 14:23:48.121428500 -0700
@@ -20,6 +20,7 @@
 #include "wifi-default-protection-manager.h"
 
 #include "ap-wifi-mac.h"
+#include "frame-exchange-manager.h"
 #include "wifi-mpdu.h"
 #include "wifi-tx-parameters.h"
 
@@ -67,14 +68,22 @@
 {
     NS_LOG_FUNCTION(this << *mpdu << &txParams);
 
-    // For a DL MU PPDU containing more than one PSDU, call a separate method.
+    // Call a separate method that handles MU-RTS/CTS protection in case of DL MU PPDU containing
+    // more than one PSDU or in case the MPDU being added is addressed to an EMLSR client or in
+    // case the protection method is already MU-RTS/CTS.
     // A DL MU PPDU contains more than one PSDU if either the TX params' PSDU info map
     // contains more than one entry or it contains one entry but the MPDU being added is
     // addressed to a different receiver (hence generating a new entry if the MPDU is added)
-    if (const auto& psduInfoMap = txParams.GetPsduInfoMap();
+    const auto& psduInfoMap = txParams.GetPsduInfoMap();
+    auto dlMuPpdu =
         txParams.m_txVector.IsDlMu() &&
         (psduInfoMap.size() > 1 ||
-         (psduInfoMap.size() == 1 && psduInfoMap.begin()->first != mpdu->GetHeader().GetAddr1())))
+         (psduInfoMap.size() == 1 && psduInfoMap.begin()->first != mpdu->GetHeader().GetAddr1()));
+    auto isEmlsrDestination =
+        GetWifiRemoteStationManager()->GetEmlsrEnabled(mpdu->GetHeader().GetAddr1());
+
+    if (dlMuPpdu || isEmlsrDestination ||
+        (txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS))
     {
         return TryAddMpduToMuPpdu(mpdu, txParams);
     }
@@ -87,7 +96,7 @@
             NS_ASSERT(txParams.m_protection->method == WifiProtection::NONE);
             return nullptr;
         }
-        return std::unique_ptr<WifiProtection>(new WifiNoProtection);
+        return std::make_unique<WifiNoProtection>();
     }
 
     // if this is a Trigger Frame, call a separate method
@@ -170,30 +179,36 @@
     // a non-initial fragment does not need to be protected, unless it is being retransmitted
     if (hdr.GetFragmentNumber() > 0 && !hdr.IsRetry())
     {
-        return std::unique_ptr<WifiProtection>(new WifiNoProtection());
+        return std::make_unique<WifiNoProtection>();
+    }
+
+    // no need to use protection if destination already received an RTS in this TXOP
+    if (m_mac->GetFrameExchangeManager(m_linkId)->GetProtectedStas().count(hdr.GetAddr1()) == 1)
+    {
+        return std::make_unique<WifiNoProtection>();
     }
 
     // check if RTS/CTS is needed
     if (GetWifiRemoteStationManager()->NeedRts(hdr, size))
     {
-        WifiRtsCtsProtection* protection = new WifiRtsCtsProtection;
+        auto protection = std::make_unique<WifiRtsCtsProtection>();
         protection->rtsTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(hdr.GetAddr1());
         protection->ctsTxVector =
             GetWifiRemoteStationManager()->GetCtsTxVector(hdr.GetAddr1(),
                                                           protection->rtsTxVector.GetMode());
-        return std::unique_ptr<WifiProtection>(protection);
+        return protection;
     }
 
     // check if CTS-to-Self is needed
     if (GetWifiRemoteStationManager()->GetUseNonErpProtection() &&
         GetWifiRemoteStationManager()->NeedCtsToSelf(txVector))
     {
-        WifiCtsToSelfProtection* protection = new WifiCtsToSelfProtection;
+        auto protection = std::make_unique<WifiCtsToSelfProtection>();
         protection->ctsTxVector = GetWifiRemoteStationManager()->GetCtsToSelfTxVector();
-        return std::unique_ptr<WifiProtection>(protection);
+        return protection;
     }
 
-    return std::unique_ptr<WifiProtection>(new WifiNoProtection());
+    return std::make_unique<WifiNoProtection>();
 }
 
 std::unique_ptr<WifiProtection>
@@ -201,16 +216,31 @@
                                                  const WifiTxParameters& txParams)
 {
     NS_LOG_FUNCTION(this << *mpdu << &txParams);
-    NS_ASSERT(txParams.m_txVector.IsDlMu());
 
-    if (!m_sendMuRts)
+    auto receiver = mpdu->GetHeader().GetAddr1();
+    const auto& psduInfoMap = txParams.GetPsduInfoMap();
+    auto dlMuPpdu = txParams.m_txVector.IsDlMu() &&
+                    (psduInfoMap.size() > 1 ||
+                     (psduInfoMap.size() == 1 && psduInfoMap.begin()->first != receiver));
+    auto isEmlsrDestination = GetWifiRemoteStationManager()->GetEmlsrEnabled(receiver);
+    NS_ASSERT(
+        dlMuPpdu || isEmlsrDestination ||
+        (txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS));
+
+    auto isProtected =
+        m_mac->GetFrameExchangeManager(m_linkId)->GetProtectedStas().count(receiver) == 1;
+    bool needMuRts =
+        (txParams.m_protection && txParams.m_protection->method == WifiProtection::MU_RTS_CTS) ||
+        (dlMuPpdu && m_sendMuRts && !isProtected) || (isEmlsrDestination && !isProtected);
+
+    if (!needMuRts)
     {
-        // No protection because sending MU-RTS is disabled
+        // No protection needed
         if (txParams.m_protection && txParams.m_protection->method == WifiProtection::NONE)
         {
             return nullptr;
         }
-        return std::unique_ptr<WifiProtection>(new WifiNoProtection());
+        return std::make_unique<WifiNoProtection>();
     }
 
     WifiMuRtsCtsProtection* protection = nullptr;
@@ -219,14 +249,15 @@
         protection = static_cast<WifiMuRtsCtsProtection*>(txParams.m_protection.get());
     }
 
-    auto receiver = mpdu->GetHeader().GetAddr1();
-
     if (txParams.GetPsduInfo(receiver) == nullptr)
     {
         // we get here if this is the first MPDU for this receiver.
         NS_ABORT_MSG_IF(m_mac->GetTypeOfStation() != AP, "HE APs only can send DL MU PPDUs");
         auto apMac = StaticCast<ApWifiMac>(m_mac);
-        auto txWidth = txParams.m_txVector.GetChannelWidth();
+        auto modClass = txParams.m_txVector.GetModulationClass();
+        auto txWidth = modClass == WIFI_MOD_CLASS_DSSS || modClass == WIFI_MOD_CLASS_HR_DSSS
+                           ? 20
+                           : txParams.m_txVector.GetChannelWidth();
 
         if (protection != nullptr)
         {
@@ -247,15 +278,12 @@
             protection->muRts.SetType(TriggerFrameType::MU_RTS_TRIGGER);
             protection->muRts.SetUlBandwidth(txWidth);
 
-            NS_ASSERT_MSG(txParams.GetPsduInfoMap().size() == 1,
-                          "There should be one PSDU in the DL MU PPDU when creating a new "
-                          "WifiMuRtsCtsProtection object");
-
-            // this is the first MPDU for the second receiver added to the DL MU PPDU.
-            // Add a User Info field for the first receiver
-            AddUserInfoToMuRts(protection->muRts,
-                               txWidth,
-                               txParams.GetPsduInfoMap().cbegin()->first);
+            // Add a User Info field for each of the receivers already in the TX params
+            for (const auto& [address, info] : txParams.GetPsduInfoMap())
+            {
+                NS_ASSERT_MSG(address != receiver, "This must be the first MPDU for " << receiver);
+                AddUserInfoToMuRts(protection->muRts, txWidth, address);
+            }
 
             // compute the TXVECTOR to use to send the MU-RTS Trigger Frame
             protection->muRtsTxVector = GetWifiRemoteStationManager()->GetRtsTxVector(receiver);
@@ -263,6 +291,20 @@
             // 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)
             protection->muRtsTxVector.SetChannelWidth(txWidth);
+            // OFDM is needed to transmit the PPDU over a bandwidth that is a multiple of 20 MHz
+            const auto modulation = protection->muRtsTxVector.GetModulationClass();
+            if (modulation == WIFI_MOD_CLASS_DSSS || modulation == WIFI_MOD_CLASS_HR_DSSS)
+            {
+                protection->muRtsTxVector.SetMode(ErpOfdmPhy::GetErpOfdmRate6Mbps());
+            }
+        }
+
+        // The initial Control frame of frame exchanges shall be sent in the non-HT PPDU or
+        // non-HT duplicate PPDU format using a rate of 6 Mb/s, 12 Mb/s, or 24 Mb/s.
+        // (Sec. 35.3.17 of 802.11be D3.0)
+        if (isEmlsrDestination && !isProtected)
+        {
+            GetWifiRemoteStationManager()->AdjustTxVectorForIcf(protection->muRtsTxVector);
         }
 
         // Add a User Info field for the new receiver
@@ -287,18 +329,12 @@
     NS_LOG_FUNCTION(this << *mpdu << &txParams);
     NS_ASSERT(mpdu->GetHeader().IsTrigger());
 
-    if (!m_sendMuRts)
-    {
-        // No protection because sending MU-RTS is disabled
-        return std::unique_ptr<WifiProtection>(new WifiNoProtection());
-    }
-
     CtrlTriggerHeader trigger;
     mpdu->GetPacket()->PeekHeader(trigger);
     NS_ASSERT(trigger.GetNUserInfoFields() > 0);
     auto txWidth = trigger.GetUlBandwidth();
 
-    WifiMuRtsCtsProtection* protection = new WifiMuRtsCtsProtection;
+    auto protection = std::make_unique<WifiMuRtsCtsProtection>();
     // initialize the MU-RTS Trigger Frame
     // The UL Length, GI And HE-LTF Type, MU-MIMO HE-LTF Mode, Number Of HE-LTF Symbols,
     // UL STBC, LDPC Extra Symbol Segment, AP TX Power, Pre-FEC Padding Factor,
@@ -311,6 +347,9 @@
     const auto& staList = StaticCast<ApWifiMac>(m_mac)->GetStaList(m_linkId);
     std::remove_reference_t<decltype(staList)>::const_iterator staIt;
 
+    bool allProtected = true;
+    bool isUnprotectedEmlsrDst = false;
+
     for (const auto& userInfo : trigger)
     {
         // Add a User Info field to the MU-RTS for this solicited station
@@ -319,6 +358,21 @@
         staIt = staList.find(userInfo.GetAid12());
         NS_ASSERT(staIt != staList.cend());
         AddUserInfoToMuRts(protection->muRts, txWidth, staIt->second);
+        bool isProtected =
+            m_mac->GetFrameExchangeManager(m_linkId)->GetProtectedStas().count(staIt->second) == 1;
+        allProtected = allProtected && isProtected;
+
+        isUnprotectedEmlsrDst =
+            isUnprotectedEmlsrDst ||
+            (!isProtected && GetWifiRemoteStationManager()->GetEmlsrEnabled(staIt->second));
+    }
+
+    bool needMuRts = (m_sendMuRts && !allProtected) || isUnprotectedEmlsrDst;
+
+    if (!needMuRts)
+    {
+        // No protection needed
+        return std::make_unique<WifiNoProtection>();
     }
 
     // compute the TXVECTOR to use to send the MU-RTS Trigger Frame
@@ -334,8 +388,15 @@
     {
         protection->muRtsTxVector.SetMode(ErpOfdmPhy::GetErpOfdmRate6Mbps());
     }
+    // The initial Control frame of frame exchanges shall be sent in the non-HT PPDU or
+    // non-HT duplicate PPDU format using a rate of 6 Mb/s, 12 Mb/s, or 24 Mb/s.
+    // (Sec. 35.3.17 of 802.11be D3.0)
+    if (isUnprotectedEmlsrDst)
+    {
+        GetWifiRemoteStationManager()->AdjustTxVectorForIcf(protection->muRtsTxVector);
+    }
 
-    return std::unique_ptr<WifiMuRtsCtsProtection>(protection);
+    return protection;
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-information-element.cc ns-3.39/src/wifi/model/wifi-information-element.cc
--- ns-3.38/src/wifi/model/wifi-information-element.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-information-element.cc	2023-07-05 14:23:48.121428500 -0700
@@ -238,27 +238,34 @@
         return false;
     }
 
-    if (GetInformationFieldSize() != a.GetInformationFieldSize())
+    if (ElementIdExt() != a.ElementIdExt())
     {
         return false;
     }
 
-    if (ElementIdExt() != a.ElementIdExt())
+    uint32_t ieSize = GetSerializedSize();
+
+    if (ieSize != a.GetSerializedSize())
     {
         return false;
     }
 
-    uint32_t ieSize = GetInformationFieldSize();
-
     Buffer myIe;
     Buffer hisIe;
     myIe.AddAtEnd(ieSize);
     hisIe.AddAtEnd(ieSize);
 
-    SerializeInformationField(myIe.Begin());
-    a.SerializeInformationField(hisIe.Begin());
+    Serialize(myIe.Begin());
+    a.Serialize(hisIe.Begin());
 
     return (memcmp(myIe.PeekData(), hisIe.PeekData(), ieSize) == 0);
 }
 
+std::ostream&
+operator<<(std::ostream& os, const WifiInformationElement& element)
+{
+    element.Print(os);
+    return os;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-information-element.h ns-3.39/src/wifi/model/wifi-information-element.h
--- ns-3.38/src/wifi/model/wifi-information-element.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-information-element.h	2023-07-05 14:23:48.121428500 -0700
@@ -199,7 +199,7 @@
 #define IE_CLUSTER_REPORT ((WifiInformationElementId)166)
 #define IE_RELAY_CAPABILITIES ((WifiInformationElementId)167)
 #define IE_RELAY_TRANSFER_PARAMETER_SET ((WifiInformationElementId)168)
-#define IE_BEAMLINK_MAINENANCE ((WifiInformationElementId)169)
+#define IE_BEAMLINK_MAINTENANCE ((WifiInformationElementId)169)
 // 170 to 171 are reserved
 #define IE_DMG_LINK_ADAPTATION_ACKNOWLEDGMENT ((WifiInformationElementId)172)
 // 173 is reserved
@@ -233,6 +233,8 @@
 #define IE_EXT_UORA_PARAMETER_SET ((WifiInformationElementId)37)
 #define IE_EXT_MU_EDCA_PARAMETER_SET ((WifiInformationElementId)38)
 
+#define IE_EXT_NON_INHERITANCE ((WifiInformationElementId)56)
+
 #define IE_EXT_EHT_OPERATION ((WifiInformationElementId)106)
 #define IE_EXT_MULTI_LINK_ELEMENT ((WifiInformationElementId)107)
 #define IE_EXT_EHT_CAPABILITIES ((WifiInformationElementId)108)
@@ -306,25 +308,17 @@
      */
     Buffer::Iterator Deserialize(Buffer::Iterator i);
     /**
-     * Deserialize an entire IE (which may possibly be fragmented into multiple
-     * elements) that is optionally present. The iterator passed in
+     * Deserialize entire IE (which may possibly be fragmented into multiple
+     * elements) if it is present. The iterator passed in
      * must be pointing at the Element ID of an information element. If
-     * the Element ID is not the requested one, the same iterator will
-     * be returned. Otherwise, an iterator pointing to the octet after
-     * the end of the information element is returned.
-     *
-     * \tparam IE \deduced Information Element type
-     * \tparam Args \deduced type of the arguments to forward to the constructor of the IE
-     * \param[out] optElem an object that contains the information element, if present
-     * \param i an iterator which points to where the IE should be read
-     * \param args arguments to forward to the constructor of the IE.
-     * \return the input iterator, if the requested IE is not present, or an iterator
-     *         pointing to the octet after the end of the information element, otherwise
-     */
-    template <typename IE, typename... Args>
-    static Buffer::Iterator DeserializeIfPresent(std::optional<IE>& optElem,
-                                                 Buffer::Iterator i,
-                                                 Args&&... args);
+     * the Element ID is not the one that the given class is interested
+     * in then it will return the same iterator.
+     *
+     * \param i an iterator which points to where the IE should be read.
+     *
+     * \return an iterator
+     */
+    Buffer::Iterator DeserializeIfPresent(Buffer::Iterator i);
     /**
      * Get the size of the serialized IE including Element ID and
      * length fields (for every element this IE is possibly fragmented into).
@@ -366,18 +360,6 @@
 
   private:
     /**
-     * Deserialize entire IE (which may possibly be fragmented into multiple
-     * elements) if it is present. The iterator passed in
-     * must be pointing at the Element ID of an information element. If
-     * the Element ID is not the one that the given class is interested
-     * in then it will return the same iterator.
-     *
-     * \param i an iterator which points to where the IE should be read.
-     *
-     * \return an iterator
-     */
-    Buffer::Iterator DeserializeIfPresent(Buffer::Iterator i);
-    /**
      * Serialize an IE that needs to be fragmented.
      *
      * \param i an iterator which points to where the IE should be written.
@@ -423,35 +405,14 @@
     virtual uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) = 0;
 };
 
-} // namespace ns3
-
-/***************************************************************
- *  Implementation of the templates declared above.
- ***************************************************************/
-
-namespace ns3
-{
-
-template <typename IE, typename... Args>
-Buffer::Iterator
-WifiInformationElement::DeserializeIfPresent(std::optional<IE>& optElem,
-                                             Buffer::Iterator i,
-                                             Args&&... args)
-{
-    optElem = std::nullopt;
-    IE elem(std::forward<Args>(args)...);
-
-    Buffer::Iterator start = i;
-    i = elem.DeserializeIfPresent(i);
-
-    if (i.GetDistanceFrom(start) != 0)
-    {
-        // the element is present and has been deserialized
-        optElem = elem;
-    }
-
-    return i;
-}
+/**
+ * \brief Stream insertion operator.
+ *
+ * \param os the output stream
+ * \param element the Information Element
+ * \returns a reference to the stream
+ */
+std::ostream& operator<<(std::ostream& os, const WifiInformationElement& element);
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-information-element-vector.cc ns-3.39/src/wifi/model/wifi-information-element-vector.cc
--- ns-3.38/src/wifi/model/wifi-information-element-vector.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-information-element-vector.cc	1969-12-31 16:00:00.000000000 -0800
@@ -1,210 +0,0 @@
-/*
- * Copyright (c) 2009 IITP RAS
- *
- * 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: Pavel Boyko <boyko@iitp.ru>
- */
-
-#include "wifi-information-element-vector.h"
-
-namespace ns3
-{
-
-NS_OBJECT_ENSURE_REGISTERED(WifiInformationElementVector);
-
-WifiInformationElementVector::WifiInformationElementVector()
-    : m_maxSize(1500)
-{
-}
-
-WifiInformationElementVector::~WifiInformationElementVector()
-{
-    for (IE_VECTOR::iterator i = m_elements.begin(); i != m_elements.end(); i++)
-    {
-        *i = nullptr;
-    }
-    m_elements.clear();
-}
-
-TypeId
-WifiInformationElementVector::GetTypeId()
-{
-    static TypeId tid = TypeId("ns3::WifiInformationElementVector")
-                            .SetParent<Header>()
-                            .SetGroupName("Wifi")
-                            .AddConstructor<WifiInformationElementVector>();
-    return tid;
-}
-
-TypeId
-WifiInformationElementVector::GetInstanceTypeId() const
-{
-    return GetTypeId();
-}
-
-uint32_t
-WifiInformationElementVector::GetSerializedSize() const
-{
-    return GetSize();
-}
-
-void
-WifiInformationElementVector::Serialize(Buffer::Iterator start) const
-{
-    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
-    {
-        start = (*i)->Serialize(start);
-    }
-}
-
-uint32_t
-WifiInformationElementVector::Deserialize(Buffer::Iterator start)
-{
-    NS_FATAL_ERROR("This variant should not be called on a variable-sized header");
-    return 0;
-}
-
-uint32_t
-WifiInformationElementVector::Deserialize(Buffer::Iterator start, Buffer::Iterator end)
-{
-    uint32_t size = start.GetDistanceFrom(end);
-    uint32_t remaining = size;
-    while (remaining > 0)
-    {
-        uint32_t deserialized = DeserializeSingleIe(start);
-        start.Next(deserialized);
-        NS_ASSERT(deserialized <= remaining);
-        remaining -= deserialized;
-    }
-    NS_ASSERT_MSG(remaining == 0, "Error in deserialization");
-    return size;
-}
-
-uint32_t
-WifiInformationElementVector::DeserializeSingleIe(Buffer::Iterator start)
-{
-    Buffer::Iterator i = start;
-    uint8_t id = i.ReadU8();
-    // unused: uint8_t length = i.ReadU8 ();
-    // but need side effects of read:
-    i.ReadU8();
-    Ptr<WifiInformationElement> newElement;
-    switch (id)
-    {
-    case 0: // eliminate compiler warning
-    default:
-        NS_FATAL_ERROR("Information element " << +id << " is not implemented");
-        return 0;
-    }
-    /*  unreachable:  b/c switch is guaranteed to return from this function
-    if (GetSize () + length > m_maxSize)
-      {
-        NS_FATAL_ERROR ("Check max size for information element!");
-      }
-    newElement->DeserializeInformationField (i, length);
-    i.Next (length);
-    m_elements.push_back (newElement);
-    return i.GetDistanceFrom (start);
-    */
-}
-
-void
-WifiInformationElementVector::Print(std::ostream& os) const
-{
-    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
-    {
-        os << "(";
-        (*i)->Print(os);
-        os << ")";
-    }
-}
-
-WifiInformationElementVector::Iterator
-WifiInformationElementVector::Begin()
-{
-    return m_elements.begin();
-}
-
-WifiInformationElementVector::Iterator
-WifiInformationElementVector::End()
-{
-    return m_elements.end();
-}
-
-bool
-WifiInformationElementVector::AddInformationElement(Ptr<WifiInformationElement> element)
-{
-    if (element->GetSerializedSize() + GetSize() > m_maxSize)
-    {
-        return false;
-    }
-    m_elements.push_back(element);
-    return true;
-}
-
-Ptr<WifiInformationElement>
-WifiInformationElementVector::FindFirst(WifiInformationElementId id) const
-{
-    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
-    {
-        if ((*i)->ElementId() == id)
-        {
-            return (*i);
-        }
-    }
-    return nullptr;
-}
-
-uint32_t
-WifiInformationElementVector::GetSize() const
-{
-    uint32_t size = 0;
-    for (IE_VECTOR::const_iterator i = m_elements.begin(); i != m_elements.end(); i++)
-    {
-        size += (*i)->GetSerializedSize();
-    }
-    return size;
-}
-
-bool
-WifiInformationElementVector::operator==(const WifiInformationElementVector& a) const
-{
-    if (m_elements.size() != a.m_elements.size())
-    {
-        NS_ASSERT(false);
-        return false;
-    }
-    // In principle we could bypass some of the faffing about (and speed
-    // the comparison) by simply serialising each IE vector into a
-    // buffer and memcmp'ing the two.
-    //
-    // I'm leaving it like this, however, so that there is the option of
-    // having individual Information Elements implement slightly more
-    // flexible equality operators.
-    WifiInformationElementVector::IE_VECTOR::const_iterator j = a.m_elements.begin();
-    for (WifiInformationElementVector::IE_VECTOR::const_iterator i = m_elements.begin();
-         i != m_elements.end();
-         i++, j++)
-    {
-        if (!(*(*i) == *(*j)))
-        {
-            return false;
-        }
-    }
-
-    return true;
-}
-
-} // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-information-element-vector.h ns-3.39/src/wifi/model/wifi-information-element-vector.h
--- ns-3.38/src/wifi/model/wifi-information-element-vector.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-information-element-vector.h	1969-12-31 16:00:00.000000000 -0800
@@ -1,135 +0,0 @@
-/*
- * Copyright (c) 2009 IITP RAS
- *
- * 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: Kirill Andreev <andreev@iitp.ru>
- *          Pavel Boyko <boyko.iitp.ru>
- */
-
-#ifndef WIFI_INFORMATION_ELEMENT_VECTOR_H
-#define WIFI_INFORMATION_ELEMENT_VECTOR_H
-
-#include "wifi-information-element.h"
-
-namespace ns3
-{
-
-/**
- * \brief Information element vector
- * \ingroup wifi
- *
- * Implements a vector of WifiInformationElements.
- * Information elements typically come in groups, and the
- * WifiInformationElementVector class provides a representation of a
- * series of IEs, and the facility for serialisation to and
- * deserialisation from the over-the-air format.
- */
-class WifiInformationElementVector : public Header
-{
-  public:
-    WifiInformationElementVector();
-    ~WifiInformationElementVector() override;
-
-    /**
-     * \brief Get the type ID.
-     * \return the object TypeId
-     */
-    static TypeId GetTypeId();
-
-    TypeId GetInstanceTypeId() const override;
-    uint32_t GetSerializedSize() const override;
-    void Serialize(Buffer::Iterator start) const override;
-    /**
-     * \attention This variant should not be used but is implemented due to
-     * backward compatibility reasons
-     *
-     * \param start buffer location to start deserializing from
-     * \return number of bytes deserialized
-     */
-    uint32_t Deserialize(Buffer::Iterator start) override;
-    /**
-     * Deserialize a number of WifiInformationElements
-     *
-     * The size of this Header should equal start.GetDistanceFrom (end).
-     *
-     * \param start starting buffer location
-     * \param end ending buffer location
-     * \return number of bytes deserialized
-     */
-    uint32_t Deserialize(Buffer::Iterator start, Buffer::Iterator end) override;
-    void Print(std::ostream& os) const override;
-
-    /**
-     * \brief Needed when you try to deserialize a lonely IE inside other header
-     *
-     * \param start is the start of the buffer
-     *
-     * \return deserialized bytes
-     */
-    virtual uint32_t DeserializeSingleIe(Buffer::Iterator start);
-    /// As soon as this is a vector, we define an Iterator
-    typedef std::vector<Ptr<WifiInformationElement>>::iterator Iterator;
-    /**
-     * Returns Begin of the vector
-     * \returns the begin of the vector
-     */
-    Iterator Begin();
-    /**
-     * Returns End of the vector
-     * \returns the end of the vector
-     */
-    Iterator End();
-    /**
-     * add an IE, if maxSize has exceeded, returns false
-     *
-     * \param element wifi information element to add
-     * \returns true is added
-     */
-    bool AddInformationElement(Ptr<WifiInformationElement> element);
-    /**
-     * vector of pointers to information elements is the body of IeVector
-     *
-     * \param id the element id to find
-     * \returns the information element
-     */
-    Ptr<WifiInformationElement> FindFirst(WifiInformationElementId id) const;
-
-    /**
-     * Check if the given WifiInformationElementVectors are equivalent.
-     *
-     * \param a another WifiInformationElementVector
-     *
-     * \return true if the given WifiInformationElementVectors are equivalent,
-     *         false otherwise
-     */
-    virtual bool operator==(const WifiInformationElementVector& a) const;
-
-  protected:
-    /**
-     * typedef for a vector of WifiInformationElements.
-     */
-    typedef std::vector<Ptr<WifiInformationElement>> IE_VECTOR;
-    /**
-     * Current number of bytes
-     * \returns the number of bytes
-     */
-    uint32_t GetSize() const;
-    IE_VECTOR m_elements; //!< Information element vector
-    uint16_t m_maxSize;   //!< Size in bytes (actually, max packet length)
-};
-
-} // namespace ns3
-
-#endif /* WIFI_INFORMATION_ELEMENT_VECTOR_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac.cc ns-3.39/src/wifi/model/wifi-mac.cc
--- ns-3.38/src/wifi/model/wifi-mac.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-mac.cc	2023-07-05 14:23:48.121428500 -0700
@@ -26,7 +26,6 @@
 #include "mgt-headers.h"
 #include "qos-txop.h"
 #include "ssid.h"
-#include "wifi-mac-queue-scheduler.h"
 #include "wifi-mac-queue.h"
 #include "wifi-net-device.h"
 
@@ -363,6 +362,14 @@
     {
         it->second->Initialize();
     }
+
+    for (auto& link : m_links)
+    {
+        if (auto cam = link->channelAccessManager)
+        {
+            cam->Initialize();
+        }
+    }
 }
 
 void
@@ -539,6 +546,23 @@
     return (txop ? txop->GetWifiMacQueue() : nullptr);
 }
 
+bool
+WifiMac::HasFramesToTransmit(uint8_t linkId)
+{
+    if (m_txop && m_txop->HasFramesToTransmit(linkId))
+    {
+        return true;
+    }
+    for (const auto& [aci, qosTxop] : m_edca)
+    {
+        if (qosTxop->HasFramesToTransmit(linkId))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 void
 WifiMac::SetMacQueueScheduler(Ptr<WifiMacQueueScheduler> scheduler)
 {
@@ -1086,6 +1110,98 @@
 }
 
 void
+WifiMac::BlockUnicastTxOnLinks(WifiQueueBlockedReason reason,
+                               const Mac48Address& address,
+                               const std::set<uint8_t>& linkIds)
+{
+    NS_LOG_FUNCTION(this << reason << address);
+    NS_ASSERT(m_scheduler);
+
+    for (const auto linkId : linkIds)
+    {
+        auto& link = GetLink(linkId);
+        auto linkAddr = link.stationManager->GetAffiliatedStaAddress(address).value_or(address);
+
+        if (link.stationManager->GetMldAddress(address) == address && linkAddr == address)
+        {
+            NS_LOG_DEBUG("Link " << +linkId << " has not been setup with the MLD, skip");
+            continue;
+        }
+
+        for (const auto [acIndex, ac] : wifiAcList)
+        {
+            // block queues storing QoS data frames and control frames that use MLD addresses
+            m_scheduler->BlockQueues(reason,
+                                     acIndex,
+                                     {WIFI_QOSDATA_QUEUE, WIFI_CTL_QUEUE},
+                                     address,
+                                     GetAddress(),
+                                     {ac.GetLowTid(), ac.GetHighTid()},
+                                     {linkId});
+            // block queues storing management and control frames that use link addresses
+            m_scheduler->BlockQueues(reason,
+                                     acIndex,
+                                     {WIFI_MGT_QUEUE, WIFI_CTL_QUEUE},
+                                     linkAddr,
+                                     link.feManager->GetAddress(),
+                                     {},
+                                     {linkId});
+        }
+    }
+}
+
+void
+WifiMac::UnblockUnicastTxOnLinks(WifiQueueBlockedReason reason,
+                                 const Mac48Address& address,
+                                 const std::set<uint8_t>& linkIds)
+{
+    NS_LOG_FUNCTION(this << reason << address);
+    NS_ASSERT(m_scheduler);
+
+    for (const auto linkId : linkIds)
+    {
+        auto& link = GetLink(linkId);
+        auto linkAddr = link.stationManager->GetAffiliatedStaAddress(address).value_or(address);
+
+        if (link.stationManager->GetMldAddress(address) == address && linkAddr == address)
+        {
+            NS_LOG_DEBUG("Link " << +linkId << " has not been setup with the MLD, skip");
+            continue;
+        }
+
+        for (const auto [acIndex, ac] : wifiAcList)
+        {
+            // unblock queues storing QoS data frames and control frames that use MLD addresses
+            m_scheduler->UnblockQueues(reason,
+                                       acIndex,
+                                       {WIFI_QOSDATA_QUEUE, WIFI_CTL_QUEUE},
+                                       address,
+                                       GetAddress(),
+                                       {ac.GetLowTid(), ac.GetHighTid()},
+                                       {linkId});
+            // unblock queues storing management and control frames that use link addresses
+            m_scheduler->UnblockQueues(reason,
+                                       acIndex,
+                                       {WIFI_MGT_QUEUE, WIFI_CTL_QUEUE},
+                                       linkAddr,
+                                       link.feManager->GetAddress(),
+                                       {},
+                                       {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);
+                }
+            });
+        }
+    }
+}
+
+void
 WifiMac::Enqueue(Ptr<Packet> packet, Mac48Address to, Mac48Address from)
 {
     // We expect WifiMac subclasses which do support forwarding (e.g.,
@@ -1124,6 +1240,12 @@
     {
         return;
     }
+
+    // Nothing to do with (QoS) Null Data frames
+    if (hdr->IsData() && !hdr->HasData())
+    {
+        return;
+    }
 
     if (hdr->IsMgt() && hdr->IsAction())
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac.h ns-3.39/src/wifi/model/wifi-mac.h
--- ns-3.38/src/wifi/model/wifi-mac.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-mac.h	2023-07-05 14:23:48.121428500 -0700
@@ -22,6 +22,7 @@
 
 #include "qos-utils.h"
 #include "ssid.h"
+#include "wifi-mac-queue-scheduler.h"
 #include "wifi-remote-station-manager.h"
 #include "wifi-standards.h"
 
@@ -50,12 +51,12 @@
 class FrameExchangeManager;
 class ChannelAccessManager;
 class ExtendedCapabilities;
-class WifiMacQueueScheduler;
 class OriginatorBlockAckAgreement;
 class RecipientBlockAckAgreement;
 
 /**
- * Enumeration for type of station
+ * \ingroup wifi
+ * Enumeration for type of WiFi station
  */
 enum TypeOfStation
 {
@@ -68,7 +69,6 @@
 
 /**
  * \ingroup wifi
- * \enum WifiMacDropReason
  * \brief The reason why an MPDU was dropped
  */
 enum WifiMacDropReason : uint8_t
@@ -203,6 +203,13 @@
     virtual Ptr<WifiMacQueue> GetTxopQueue(AcIndex ac) const;
 
     /**
+     * Check if the MAC has frames to transmit over the given link
+     * \param linkId the ID of the given link.
+     * \return whether the MAC has frames to transmit.
+     */
+    virtual bool HasFramesToTransmit(uint8_t linkId);
+
+    /**
      * Set the wifi MAC queue scheduler
      *
      * \param scheduler the wifi MAC queue scheduler
@@ -274,6 +281,32 @@
     void SetBssid(Mac48Address bssid, uint8_t linkId);
 
     /**
+     * Block the transmission on the given links of all unicast frames addressed to
+     * the station with the given address for the given reason. The given MAC address
+     * must be the MLD address in case the addressed device is multi-link.
+     *
+     * \param reason the reason for blocking transmissions
+     * \param address the MAC address of the given device
+     * \param linkIds the IDs of the links to block
+     */
+    void BlockUnicastTxOnLinks(WifiQueueBlockedReason reason,
+                               const Mac48Address& address,
+                               const std::set<uint8_t>& linkIds);
+
+    /**
+     * Unblock the transmission on the given links of all unicast frames addressed to
+     * the station with the given address for the given reason. The given MAC address
+     * must be the MLD address in case the addressed device is multi-link.
+     *
+     * \param reason the reason for unblocking transmissions
+     * \param address the MAC address of the given device
+     * \param linkIds the IDs of the links to unblock
+     */
+    void UnblockUnicastTxOnLinks(WifiQueueBlockedReason reason,
+                                 const Mac48Address& address,
+                                 const std::set<uint8_t>& linkIds);
+
+    /**
      * Return true if packets can be forwarded to the given destination,
      * false otherwise.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-header.cc ns-3.39/src/wifi/model/wifi-mac-header.cc
--- ns-3.38/src/wifi/model/wifi-mac-header.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-header.cc	2023-07-05 14:23:48.121428500 -0700
@@ -49,6 +49,7 @@
     SUBTYPE_CTL_CTLWRAPPER = 7,
     SUBTYPE_CTL_BACKREQ = 8,
     SUBTYPE_CTL_BACKRESP = 9,
+    SUBTYPE_CTL_PSPOLL = 10,
     SUBTYPE_CTL_RTS = 11,
     SUBTYPE_CTL_CTS = 12,
     SUBTYPE_CTL_ACK = 13,
@@ -59,6 +60,7 @@
 WifiMacHeader::WifiMacHeader()
     : m_ctrlMoreFrag(0),
       m_ctrlRetry(0),
+      m_ctrlPowerManagement(0),
       m_ctrlMoreData(0),
       m_ctrlWep(0),
       m_ctrlOrder(0),
@@ -150,6 +152,10 @@
         m_ctrlType = TYPE_CTL;
         m_ctrlSubtype = SUBTYPE_CTL_BACKRESP;
         break;
+    case WIFI_MAC_CTL_PSPOLL:
+        m_ctrlType = TYPE_CTL;
+        m_ctrlSubtype = SUBTYPE_CTL_PSPOLL;
+        break;
     case WIFI_MAC_CTL_RTS:
         m_ctrlType = TYPE_CTL;
         m_ctrlSubtype = SUBTYPE_CTL_RTS;
@@ -369,6 +375,18 @@
 }
 
 void
+WifiMacHeader::SetPowerManagement()
+{
+    m_ctrlPowerManagement = 1;
+}
+
+void
+WifiMacHeader::SetNoPowerManagement()
+{
+    m_ctrlPowerManagement = 0;
+}
+
+void
 WifiMacHeader::SetQosEosp()
 {
     m_qosEosp = 1;
@@ -510,6 +528,8 @@
             return WIFI_MAC_CTL_BACKREQ;
         case SUBTYPE_CTL_BACKRESP:
             return WIFI_MAC_CTL_BACKRESP;
+        case SUBTYPE_CTL_PSPOLL:
+            return WIFI_MAC_CTL_PSPOLL;
         case SUBTYPE_CTL_RTS:
             return WIFI_MAC_CTL_RTS;
         case SUBTYPE_CTL_CTS:
@@ -644,7 +664,6 @@
         return true;
     default:
         return false;
-        break;
     }
 }
 
@@ -680,6 +699,12 @@
 }
 
 bool
+WifiMacHeader::IsPsPoll() const
+{
+    return (GetType() == WIFI_MAC_CTL_PSPOLL);
+}
+
+bool
 WifiMacHeader::IsAck() const
 {
     return (GetType() == WIFI_MAC_CTL_ACK);
@@ -830,6 +855,12 @@
 }
 
 bool
+WifiMacHeader::IsPowerManagement() const
+{
+    return (m_ctrlPowerManagement == 1);
+}
+
+bool
 WifiMacHeader::IsQosBlockAck() const
 {
     NS_ASSERT(IsQosData());
@@ -914,6 +945,7 @@
     val |= (m_ctrlFromDs << 9) & (0x1 << 9);
     val |= (m_ctrlMoreFrag << 10) & (0x1 << 10);
     val |= (m_ctrlRetry << 11) & (0x1 << 11);
+    val |= (m_ctrlPowerManagement << 12) & (0x1 << 12);
     val |= (m_ctrlMoreData << 13) & (0x1 << 13);
     val |= (m_ctrlWep << 14) & (0x1 << 14);
     val |= (m_ctrlOrder << 15) & (0x1 << 15);
@@ -941,6 +973,7 @@
     m_ctrlFromDs = (ctrl >> 9) & 0x01;
     m_ctrlMoreFrag = (ctrl >> 10) & 0x01;
     m_ctrlRetry = (ctrl >> 11) & 0x01;
+    m_ctrlPowerManagement = (ctrl >> 12) & 0x01;
     m_ctrlMoreData = (ctrl >> 13) & 0x01;
     m_ctrlWep = (ctrl >> 14) & 0x01;
     m_ctrlOrder = (ctrl >> 15) & 0x01;
@@ -975,6 +1008,7 @@
     case TYPE_CTL:
         switch (m_ctrlSubtype)
         {
+        case SUBTYPE_CTL_PSPOLL:
         case SUBTYPE_CTL_RTS:
         case SUBTYPE_CTL_BACKREQ:
         case SUBTYPE_CTL_BACKRESP:
@@ -1010,55 +1044,55 @@
 const char*
 WifiMacHeader::GetTypeString() const
 {
-#define FOO(x)                                                                                     \
+#define CASE_WIFI_MAC_TYPE(x)                                                                      \
     case WIFI_MAC_##x:                                                                             \
-        return #x;                                                                                 \
-        break;
+        return #x;
 
     switch (GetType())
     {
-        FOO(CTL_RTS);
-        FOO(CTL_CTS);
-        FOO(CTL_ACK);
-        FOO(CTL_BACKREQ);
-        FOO(CTL_BACKRESP);
-        FOO(CTL_END);
-        FOO(CTL_END_ACK);
-        FOO(CTL_TRIGGER);
-
-        FOO(MGT_BEACON);
-        FOO(MGT_ASSOCIATION_REQUEST);
-        FOO(MGT_ASSOCIATION_RESPONSE);
-        FOO(MGT_DISASSOCIATION);
-        FOO(MGT_REASSOCIATION_REQUEST);
-        FOO(MGT_REASSOCIATION_RESPONSE);
-        FOO(MGT_PROBE_REQUEST);
-        FOO(MGT_PROBE_RESPONSE);
-        FOO(MGT_AUTHENTICATION);
-        FOO(MGT_DEAUTHENTICATION);
-        FOO(MGT_ACTION);
-        FOO(MGT_ACTION_NO_ACK);
-        FOO(MGT_MULTIHOP_ACTION);
-
-        FOO(DATA);
-        FOO(DATA_CFACK);
-        FOO(DATA_CFPOLL);
-        FOO(DATA_CFACK_CFPOLL);
-        FOO(DATA_NULL);
-        FOO(DATA_NULL_CFACK);
-        FOO(DATA_NULL_CFPOLL);
-        FOO(DATA_NULL_CFACK_CFPOLL);
-        FOO(QOSDATA);
-        FOO(QOSDATA_CFACK);
-        FOO(QOSDATA_CFPOLL);
-        FOO(QOSDATA_CFACK_CFPOLL);
-        FOO(QOSDATA_NULL);
-        FOO(QOSDATA_NULL_CFPOLL);
-        FOO(QOSDATA_NULL_CFACK_CFPOLL);
+        CASE_WIFI_MAC_TYPE(CTL_RTS);
+        CASE_WIFI_MAC_TYPE(CTL_CTS);
+        CASE_WIFI_MAC_TYPE(CTL_ACK);
+        CASE_WIFI_MAC_TYPE(CTL_BACKREQ);
+        CASE_WIFI_MAC_TYPE(CTL_BACKRESP);
+        CASE_WIFI_MAC_TYPE(CTL_END);
+        CASE_WIFI_MAC_TYPE(CTL_END_ACK);
+        CASE_WIFI_MAC_TYPE(CTL_PSPOLL);
+        CASE_WIFI_MAC_TYPE(CTL_TRIGGER);
+
+        CASE_WIFI_MAC_TYPE(MGT_BEACON);
+        CASE_WIFI_MAC_TYPE(MGT_ASSOCIATION_REQUEST);
+        CASE_WIFI_MAC_TYPE(MGT_ASSOCIATION_RESPONSE);
+        CASE_WIFI_MAC_TYPE(MGT_DISASSOCIATION);
+        CASE_WIFI_MAC_TYPE(MGT_REASSOCIATION_REQUEST);
+        CASE_WIFI_MAC_TYPE(MGT_REASSOCIATION_RESPONSE);
+        CASE_WIFI_MAC_TYPE(MGT_PROBE_REQUEST);
+        CASE_WIFI_MAC_TYPE(MGT_PROBE_RESPONSE);
+        CASE_WIFI_MAC_TYPE(MGT_AUTHENTICATION);
+        CASE_WIFI_MAC_TYPE(MGT_DEAUTHENTICATION);
+        CASE_WIFI_MAC_TYPE(MGT_ACTION);
+        CASE_WIFI_MAC_TYPE(MGT_ACTION_NO_ACK);
+        CASE_WIFI_MAC_TYPE(MGT_MULTIHOP_ACTION);
+
+        CASE_WIFI_MAC_TYPE(DATA);
+        CASE_WIFI_MAC_TYPE(DATA_CFACK);
+        CASE_WIFI_MAC_TYPE(DATA_CFPOLL);
+        CASE_WIFI_MAC_TYPE(DATA_CFACK_CFPOLL);
+        CASE_WIFI_MAC_TYPE(DATA_NULL);
+        CASE_WIFI_MAC_TYPE(DATA_NULL_CFACK);
+        CASE_WIFI_MAC_TYPE(DATA_NULL_CFPOLL);
+        CASE_WIFI_MAC_TYPE(DATA_NULL_CFACK_CFPOLL);
+        CASE_WIFI_MAC_TYPE(QOSDATA);
+        CASE_WIFI_MAC_TYPE(QOSDATA_CFACK);
+        CASE_WIFI_MAC_TYPE(QOSDATA_CFPOLL);
+        CASE_WIFI_MAC_TYPE(QOSDATA_CFACK_CFPOLL);
+        CASE_WIFI_MAC_TYPE(QOSDATA_NULL);
+        CASE_WIFI_MAC_TYPE(QOSDATA_NULL_CFPOLL);
+        CASE_WIFI_MAC_TYPE(QOSDATA_NULL_CFACK_CFPOLL);
     default:
         return "ERROR";
     }
-#undef FOO
+#undef CASE_WIFI_MAC_TYPE
 #ifndef _WIN32
     // needed to make gcc 4.0.1 ppc darwin happy.
     return "BIG_ERROR";
@@ -1084,9 +1118,12 @@
 void
 WifiMacHeader::PrintFrameControl(std::ostream& os) const
 {
-    os << "ToDS=" << std::hex << (int)m_ctrlToDs << ", FromDS=" << std::hex << (int)m_ctrlFromDs
-       << ", MoreFrag=" << std::hex << (int)m_ctrlMoreFrag << ", Retry=" << std::hex
-       << (int)m_ctrlRetry << ", MoreData=" << std::hex << (int)m_ctrlMoreData << std::dec;
+    os << "ToDS=" << std::hex << static_cast<int>(m_ctrlToDs)
+       << ", FromDS=" << static_cast<int>(m_ctrlFromDs)
+       << ", MoreFrag=" << static_cast<int>(m_ctrlMoreFrag)
+       << ", Retry=" << static_cast<int>(m_ctrlRetry)
+       << ", PowerManagement=" << static_cast<int>(m_ctrlPowerManagement)
+       << ", MoreData=" << static_cast<int>(m_ctrlMoreData) << std::dec;
 }
 
 void
@@ -1095,6 +1132,10 @@
     os << GetTypeString() << " ";
     switch (GetType())
     {
+    case WIFI_MAC_CTL_PSPOLL:
+        os << "Duration/ID=" << std::hex << m_duration << std::dec << ", BSSID(RA)=" << m_addr1
+           << ", TA=" << m_addr2;
+        break;
     case WIFI_MAC_CTL_RTS:
     case WIFI_MAC_CTL_TRIGGER:
         os << "Duration/ID=" << m_duration << "us"
@@ -1208,6 +1249,7 @@
     case TYPE_CTL:
         switch (m_ctrlSubtype)
         {
+        case SUBTYPE_CTL_PSPOLL:
         case SUBTYPE_CTL_RTS:
         case SUBTYPE_CTL_TRIGGER:
         case SUBTYPE_CTL_BACKREQ:
@@ -1264,6 +1306,7 @@
     case TYPE_CTL:
         switch (m_ctrlSubtype)
         {
+        case SUBTYPE_CTL_PSPOLL:
         case SUBTYPE_CTL_RTS:
         case SUBTYPE_CTL_TRIGGER:
         case SUBTYPE_CTL_BACKREQ:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-header.h ns-3.39/src/wifi/model/wifi-mac-header.h
--- ns-3.38/src/wifi/model/wifi-mac-header.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-header.h	2023-07-05 14:23:48.121428500 -0700
@@ -37,6 +37,7 @@
 {
     WIFI_MAC_CTL_TRIGGER = 0,
     WIFI_MAC_CTL_CTLWRAPPER,
+    WIFI_MAC_CTL_PSPOLL,
     WIFI_MAC_CTL_RTS,
     WIFI_MAC_CTL_CTS,
     WIFI_MAC_CTL_ACK,
@@ -236,6 +237,14 @@
      */
     void SetNoRetry();
     /**
+     * Set the Power Management bit in the Frame Control field.
+     */
+    void SetPowerManagement();
+    /**
+     * Un-set the Power Management bit in the Frame Control field.
+     */
+    void SetNoPowerManagement();
+    /**
      * Set the TID for the QoS header.
      *
      * \param tid the TID for the QoS header
@@ -317,9 +326,9 @@
      */
     Mac48Address GetAddr4() const;
     /**
-     * Return the type (enum WifiMacType)
+     * Return the type (WifiMacType)
      *
-     * \return the type (enum WifiMacType)
+     * \return the type (WifiMacType)
      */
     WifiMacType GetType() const;
     /**
@@ -383,6 +392,12 @@
      */
     bool IsCfEnd() const;
     /**
+     * Return true if the header is a PS-POLL header.
+     *
+     * \return true if the header is a PS-POLL header, false otherwise
+     */
+    bool IsPsPoll() const;
+    /**
      * Return true if the header is a RTS header.
      *
      * \return true if the header is a RTS header, false otherwise
@@ -534,6 +549,12 @@
      */
     bool IsRetry() const;
     /**
+     * Return if the Power Management bit is set.
+     *
+     * \return true if the Power Management bit is set, false otherwise
+     */
+    bool IsPowerManagement() const;
+    /**
      * Return if the More Data bit is set.
      *
      * \return true if the More Data bit is set, false otherwise
@@ -653,14 +674,15 @@
      */
     void PrintFrameControl(std::ostream& os) const;
 
-    uint8_t m_ctrlType;     ///< control type
-    uint8_t m_ctrlSubtype;  ///< control subtype
-    uint8_t m_ctrlToDs;     ///< control to DS
-    uint8_t m_ctrlFromDs;   ///< control from DS
-    uint8_t m_ctrlMoreFrag; ///< control more fragments
-    uint8_t m_ctrlRetry;    ///< control retry
-    uint8_t m_ctrlMoreData; ///< control more data
-    uint8_t m_ctrlWep;      ///< control WEP
+    uint8_t m_ctrlType;            ///< control type
+    uint8_t m_ctrlSubtype;         ///< control subtype
+    uint8_t m_ctrlToDs;            ///< control to DS
+    uint8_t m_ctrlFromDs;          ///< control from DS
+    uint8_t m_ctrlMoreFrag;        ///< control more fragments
+    uint8_t m_ctrlRetry;           ///< control retry
+    uint8_t m_ctrlPowerManagement; ///< control power management
+    uint8_t m_ctrlMoreData;        ///< control more data
+    uint8_t m_ctrlWep;             ///< control WEP
     uint8_t m_ctrlOrder;  ///< control order (set to 1 for QoS Data and Management frames to signify
                           ///< that HT/VHT/HE control field is present, knowing that the latter are
                           ///< not implemented yet)
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-queue.cc ns-3.39/src/wifi/model/wifi-mac-queue.cc
--- ns-3.38/src/wifi/model/wifi-mac-queue.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-queue.cc	2023-07-05 14:23:48.121428500 -0700
@@ -22,12 +22,12 @@
 
 #include "wifi-mac-queue.h"
 
-#include "qos-blocked-destinations.h"
 #include "wifi-mac-queue-scheduler.h"
 
 #include "ns3/simulator.h"
 
 #include <functional>
+#include <optional>
 
 namespace ns3
 {
@@ -100,6 +100,21 @@
     return GetIt(mpdu)->mpdu;
 }
 
+Ptr<WifiMpdu>
+WifiMacQueue::GetAlias(Ptr<const WifiMpdu> mpdu, uint8_t linkId)
+{
+    if (!mpdu->IsQueued())
+    {
+        return nullptr;
+    }
+    if (auto aliasIt = GetIt(mpdu)->inflights.find(linkId);
+        aliasIt != GetIt(mpdu)->inflights.cend())
+    {
+        return aliasIt->second;
+    }
+    return nullptr;
+}
+
 void
 WifiMacQueue::ExtractExpiredMpdus(const WifiContainerQueueId& queueId) const
 {
@@ -303,7 +318,7 @@
 {
     NS_LOG_FUNCTION(this << +tid << dest << item);
     NS_ABORT_IF(dest.IsGroup());
-    WifiContainerQueueId queueId(WIFI_QOSDATA_UNICAST_QUEUE, dest, tid);
+    WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, dest, tid);
     return PeekByQueueId(queueId, item);
 }
 
@@ -331,18 +346,13 @@
 }
 
 Ptr<WifiMpdu>
-WifiMacQueue::PeekFirstAvailable(uint8_t linkId,
-                                 const Ptr<QosBlockedDestinations> blockedPackets,
-                                 Ptr<const WifiMpdu> item) const
+WifiMacQueue::PeekFirstAvailable(uint8_t linkId, Ptr<const WifiMpdu> item) const
 {
     NS_LOG_FUNCTION(this << +linkId << item);
     NS_ASSERT(!item || item->IsQueued());
 
     if (item)
     {
-        NS_ASSERT(!item->GetHeader().IsQosData() || !blockedPackets ||
-                  !blockedPackets->IsBlocked(item->GetHeader().GetAddr1(),
-                                             item->GetHeader().GetQosTid()));
         // check if there are other MPDUs in the same container queue as item
         auto mpdu = PeekByQueueId(WifiMacQueueContainer::GetQueueId(item), item);
 
@@ -363,19 +373,6 @@
         queueId = m_scheduler->GetNext(m_ac, linkId);
     }
 
-    NS_ASSERT(!queueId || std::get<0>(*queueId) != WIFI_QOSDATA_UNICAST_QUEUE ||
-              std::get<2>(*queueId));
-
-    while (queueId.has_value() && blockedPackets &&
-           std::get<0>(queueId.value()) == WIFI_QOSDATA_UNICAST_QUEUE &&
-           blockedPackets->IsBlocked(std::get<1>(queueId.value()), *std::get<2>(queueId.value())))
-    {
-        queueId = m_scheduler->GetNext(m_ac, linkId, queueId.value());
-
-        NS_ASSERT(!queueId || std::get<0>(*queueId) != WIFI_QOSDATA_UNICAST_QUEUE ||
-                  std::get<2>(*queueId));
-    }
-
     if (!queueId.has_value())
     {
         NS_LOG_DEBUG("The queue is empty");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-queue-container.cc ns-3.39/src/wifi/model/wifi-mac-queue-container.cc
--- ns-3.38/src/wifi/model/wifi-mac-queue-container.cc	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-queue-container.cc	2023-07-05 14:23:48.121428500 -0700
@@ -79,23 +79,22 @@
 {
     const WifiMacHeader& hdr = mpdu->GetHeader();
 
+    auto addrType = hdr.GetAddr1().IsGroup() ? WIFI_BROADCAST : WIFI_UNICAST;
+    auto address = hdr.GetAddr1().IsGroup() ? hdr.GetAddr2() : hdr.GetAddr1();
+
     if (hdr.IsCtl())
     {
-        return {WIFI_CTL_QUEUE, hdr.GetAddr2(), std::nullopt};
+        return {WIFI_CTL_QUEUE, addrType, address, std::nullopt};
     }
     if (hdr.IsMgt())
     {
-        return {WIFI_MGT_QUEUE, hdr.GetAddr2(), std::nullopt};
+        return {WIFI_MGT_QUEUE, addrType, address, std::nullopt};
     }
     if (hdr.IsQosData())
     {
-        if (hdr.GetAddr1().IsGroup())
-        {
-            return {WIFI_QOSDATA_BROADCAST_QUEUE, hdr.GetAddr2(), hdr.GetQosTid()};
-        }
-        return {WIFI_QOSDATA_UNICAST_QUEUE, hdr.GetAddr1(), hdr.GetQosTid()};
+        return {WIFI_QOSDATA_QUEUE, addrType, address, hdr.GetQosTid()};
     }
-    return {WIFI_DATA_QUEUE, hdr.GetAddr1(), std::nullopt};
+    return {WIFI_DATA_QUEUE, addrType, address, std::nullopt};
 }
 
 const WifiMacQueueContainer::ContainerQueue&
@@ -207,7 +206,7 @@
 std::size_t
 std::hash<ns3::WifiContainerQueueId>::operator()(ns3::WifiContainerQueueId queueId) const
 {
-    auto [type, address, tid] = queueId;
+    auto [type, addrType, address, tid] = queueId;
     const std::size_t size = tid.has_value() ? 8 : 7;
 
     uint8_t buffer[size];
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-queue-container.h ns-3.39/src/wifi/model/wifi-mac-queue-container.h
--- ns-3.38/src/wifi/model/wifi-mac-queue-container.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-queue-container.h	2023-07-05 14:23:48.121428500 -0700
@@ -37,37 +37,46 @@
 {
     WIFI_CTL_QUEUE = 0,
     WIFI_MGT_QUEUE = 1,
-    WIFI_QOSDATA_UNICAST_QUEUE = 2,
-    WIFI_QOSDATA_BROADCAST_QUEUE = 3,
-    WIFI_DATA_QUEUE = 4
+    WIFI_QOSDATA_QUEUE = 2,
+    WIFI_DATA_QUEUE = 3
+};
+
+/// enumeration of frame directions
+enum WifiReceiverAddressType : uint8_t
+{
+    WIFI_UNICAST = 0,
+    WIFI_BROADCAST
 };
 
 /**
- * Tuple (queue type, Address, TID) identifying a container queue.
+ * Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
  *
  * \note that Address has a different meaning depending on container queue type:
- * - if container queue type is WIFI_CTL_QUEUE, Address is the Transmitter Address
- * (TA) of the frames stored in the queue. We have distinct control queues
- * depending on TA to distinguish among control frames that need to be sent
- * over different links by 11be MLDs. MLD address as TA indicates that the frames
- * can be sent on any link. TID is ignored.
- * - if container queue type is WIFI_MGT_QUEUE, Address is the Transmitter Address
- * (TA) of the frames stored in the queue. We have distinct management queues
- * depending on TA to distinguish among management frames that need to be sent
- * over different links by 11be MLDs. TID is ignored.
- * - if container queue type is WIFI_QOSDATA_UNICAST_QUEUE, Address is the Receiver
- * Address (RA) of the frames stored in the queue.
- * - if container queue type is WIFI_QOSDATA_BROADCAST_QUEUE, Address is the
- * Transmitter Address (TA) of the frames stored in the queue. We have distinct
- * broadcast QoS queues depending on TA to distinguish among broadcast QoS Data
- * frames that need to be sent over different links by 11be MLDs.
- * - if container queue type is WIFI_DATA_QUEUE, Address is the Receiver Address
- * (RA) of the frames stored in the queue. We do not need to consider the
- * Transmitter Address (TA) because 11be stations are QoS stations and hence do
- * not send non-QoS Data frames. TID is ignored.
+ *
+ * - for container queue types holding unicast frames, Address is the Receiver Address (RA)
+ *   of the frames stored in the queue. For 11be MLDs, it is expected that:
+ *   + the RA of unicast management frames are link addresses (indicating the link on which
+ *     they must be sent)
+ *   + the RA of unicast QoS data frames are MLD addresses (indicating that they can be sent
+ *     on any link)
+ *   + if the RA of a unicast control frame is a link address, that control frame can only be
+ *     sent on the corresponding link; if the RA is an MLD address, that control frame can be
+ *     sent on any link
+ *
+ * - for container queue types holding broadcast frames, Address is the Transmitter Address (TA)
+ *   of the frames stored in the queue. For 11be MLDs, it is expected that:
+ *   + the TA of broadcast management frames are link addresses (indicating the link on which
+ *     they must be sent)
+ *   + the TA of broadcast QoS data frames are MLD addresses (indicating that they can be sent
+ *     on any link)
+ *   + if the TA of a broadcast control frame is a link address, that control frame can only be
+ *     sent on the corresponding link; if the TA is an MLD address, that control frame can be
+ *     sent on any link
+ *
+ * The TID is only specified for container queue types holding QoS data frames.
  */
-using WifiContainerQueueId =
-    std::tuple<WifiContainerQueueType, Mac48Address, std::optional<uint8_t>>;
+using WifiContainerQueueId = std::
+    tuple<WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional<uint8_t>>;
 
 } // namespace ns3
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-queue.h ns-3.39/src/wifi/model/wifi-mac-queue.h
--- ns-3.38/src/wifi/model/wifi-mac-queue.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-queue.h	2023-07-05 14:23:48.121428500 -0700
@@ -35,7 +35,6 @@
 namespace ns3
 {
 
-class QosBlockedDestinations;
 class WifiMacQueueScheduler;
 
 // The following explicit template instantiation declaration prevents modules
@@ -190,15 +189,11 @@
      * The packet is not removed from queue.
      *
      * \param linkId the ID of the given link
-     * \param blockedPackets the destination address & TID pairs that are waiting for a
-     * BlockAck response
      * \param item the item after which the search starts from
      *
      * \return the peeked packet or nullptr if no packet was found
      */
-    Ptr<WifiMpdu> PeekFirstAvailable(uint8_t linkId,
-                                     const Ptr<QosBlockedDestinations> blockedPackets = nullptr,
-                                     Ptr<const WifiMpdu> item = nullptr) const;
+    Ptr<WifiMpdu> PeekFirstAvailable(uint8_t linkId, Ptr<const WifiMpdu> item = nullptr) const;
     /**
      * Remove the packet in the front of the queue.
      *
@@ -290,6 +285,14 @@
      */
     Ptr<WifiMpdu> GetOriginal(Ptr<WifiMpdu> mpdu);
 
+    /**
+     * \param mpdu the given MPDU
+     * \param linkId the ID of the given link
+     * \return the alias of the given MPDU that is inflight on the given link, if any, or
+     *         a null pointer, otherwise
+     */
+    Ptr<WifiMpdu> GetAlias(Ptr<const WifiMpdu> mpdu, uint8_t linkId);
+
   protected:
     using Queue<WifiMpdu, WifiMacQueueContainer>::GetContainer;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-queue-scheduler.h ns-3.39/src/wifi/model/wifi-mac-queue-scheduler.h
--- ns-3.38/src/wifi/model/wifi-mac-queue-scheduler.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-queue-scheduler.h	2023-07-05 14:23:48.121428500 -0700
@@ -25,6 +25,7 @@
 
 #include "ns3/object.h"
 
+#include <bitset>
 #include <optional>
 
 namespace ns3
@@ -36,6 +37,48 @@
 /**
  * \ingroup wifi
  *
+ * Enumeration of the reasons to block container queues.
+ */
+enum class WifiQueueBlockedReason : uint8_t
+{
+    WAITING_ADDBA_RESP = 0,
+    POWER_SAVE_MODE,
+    USING_OTHER_EMLSR_LINK,
+    WAITING_EMLSR_TRANSITION_DELAY,
+    REASONS_COUNT
+};
+
+/**
+ * \brief Stream insertion operator.
+ *
+ * \param os the stream
+ * \param reason the reason to block container queues
+ * \returns a reference to the stream
+ */
+inline std::ostream&
+operator<<(std::ostream& os, WifiQueueBlockedReason reason)
+{
+    switch (reason)
+    {
+    case WifiQueueBlockedReason::WAITING_ADDBA_RESP:
+        return (os << "WAITING_ADDBA_RESP");
+    case WifiQueueBlockedReason::POWER_SAVE_MODE:
+        return (os << "POWER_SAVE_MODE");
+    case WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK:
+        return (os << "USING_OTHER_EMLSR_LINK");
+    case WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY:
+        return (os << "WAITING_EMLSR_TRANSITION_DELAY");
+    case WifiQueueBlockedReason::REASONS_COUNT:
+        return (os << "REASONS_COUNT");
+    default:
+        NS_ABORT_MSG("Unknown queue blocked reason");
+        return (os << "unknown");
+    }
+}
+
+/**
+ * \ingroup wifi
+ *
  * WifiMacQueueScheduler is an abstract base class defining the public interface
  * for a wifi MAC queue scheduler.
  */
@@ -81,25 +124,74 @@
         const WifiContainerQueueId& prevQueueId) = 0;
 
     /**
-     * Get the list of the IDs of the links the given container queue (belonging to
-     * the given Access Category) is associated with.
+     * Get the list of the IDs of the links the given MPDU (belonging to the given
+     * Access Category) can be sent over.
      *
      * \param ac the given Access Category
-     * \param queueId the given container queue
-     * \return the list of the IDs of the links the given container queue is associated with
+     * \param mpdu the given MPDU
+     * \return the list of the IDs of the links the given MPDU can be sent over
      */
-    virtual std::list<uint8_t> GetLinkIds(AcIndex ac, const WifiContainerQueueId& queueId) = 0;
+    virtual std::list<uint8_t> GetLinkIds(AcIndex ac, Ptr<const WifiMpdu> mpdu) = 0;
+
     /**
-     * Set the list of the IDs of the links the given container queue (belonging to
-     * the given Access Category) is associated with.
+     * Block the given set of links for the container queues of the given types and
+     * Access Category that hold frames having the given Receiver Address (RA),
+     * Transmitter Address (TA) and TID (if needed) for the given reason, such that
+     * frames in these queues are not transmitted on the given set of links.
      *
+     * \param reason the reason for blocking the queues
      * \param ac the given Access Category
-     * \param queueId the given container queue
-     * \param linkIds the list of the IDs of the links the given container queue is associated with
-     */
-    virtual void SetLinkIds(AcIndex ac,
-                            const WifiContainerQueueId& queueId,
-                            const std::list<uint8_t>& linkIds) = 0;
+     * \param types the types of the queues to block
+     * \param rxAddress the Receiver Address (RA) of the frames
+     * \param txAddress the Transmitter Address (TA) of the frames
+     * \param tids the TIDs optionally identifying the queues to block
+     * \param linkIds set of links to block (empty to block all setup links)
+     */
+    virtual void BlockQueues(WifiQueueBlockedReason reason,
+                             AcIndex ac,
+                             const std::list<WifiContainerQueueType>& types,
+                             const Mac48Address& rxAddress,
+                             const Mac48Address& txAddress,
+                             const std::set<uint8_t>& tids = {},
+                             const std::set<uint8_t>& linkIds = {}) = 0;
+    /**
+     * Unblock the given set of links for the container queues of the given types and
+     * Access Category that hold frames having the given Receiver Address (RA),
+     * Transmitter Address (TA) and TID (if needed) for the given reason, such that
+     * frames in these queues can be transmitted on the given set of links.
+     *
+     * \param reason the reason for unblocking the queues
+     * \param ac the given Access Category
+     * \param types the types of the queues to unblock
+     * \param rxAddress the Receiver Address (RA) of the frames
+     * \param txAddress the Transmitter Address (TA) of the frames
+     * \param tids the TIDs optionally identifying the queues to unblock
+     * \param linkIds set of links to unblock (empty to unblock all setup links)
+     */
+    virtual void UnblockQueues(WifiQueueBlockedReason reason,
+                               AcIndex ac,
+                               const std::list<WifiContainerQueueType>& types,
+                               const Mac48Address& rxAddress,
+                               const Mac48Address& txAddress,
+                               const std::set<uint8_t>& tids = {},
+                               const std::set<uint8_t>& linkIds = {}) = 0;
+
+    /// Bitset identifying the reasons to block individual links for a container queue
+    using Mask = std::bitset<static_cast<std::size_t>(WifiQueueBlockedReason::REASONS_COUNT)>;
+
+    /**
+     * Get the mask associated with the given container queue indicating whether the given link
+     * is blocked and for which reason, provided that the given container queue exists and has
+     * a mask for the given link.
+     *
+     * \param ac the given Access Category
+     * \param queueId the ID of the given container queue
+     * \param linkId the ID of the given link
+     * \return the mask associated with the given container queue for the given link
+     */
+    virtual std::optional<Mask> GetQueueLinkMask(AcIndex ac,
+                                                 const WifiContainerQueueId& queueId,
+                                                 uint8_t linkId) = 0;
 
     /**
      * Check whether an MPDU has to be dropped before enqueuing the given MPDU.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mac-queue-scheduler-impl.h ns-3.39/src/wifi/model/wifi-mac-queue-scheduler-impl.h
--- ns-3.38/src/wifi/model/wifi-mac-queue-scheduler-impl.h	2023-03-16 20:54:23.864869400 -0700
+++ ns-3.39/src/wifi/model/wifi-mac-queue-scheduler-impl.h	2023-07-05 14:23:48.121428500 -0700
@@ -73,11 +73,27 @@
                                                 uint8_t linkId,
                                                 const WifiContainerQueueId& prevQueueId) final;
     /** \copydoc ns3::WifiMacQueueScheduler::GetLinkIds */
-    std::list<uint8_t> GetLinkIds(AcIndex ac, const WifiContainerQueueId& queueId) final;
-    /** \copydoc ns3::WifiMacQueueScheduler::SetLinkIds */
-    void SetLinkIds(AcIndex ac,
-                    const WifiContainerQueueId& queueId,
-                    const std::list<uint8_t>& linkIds) final;
+    std::list<uint8_t> GetLinkIds(AcIndex ac, Ptr<const WifiMpdu> mpdu) final;
+    /** \copydoc ns3::WifiMacQueueScheduler::BlockQueues */
+    void BlockQueues(WifiQueueBlockedReason reason,
+                     AcIndex ac,
+                     const std::list<WifiContainerQueueType>& types,
+                     const Mac48Address& rxAddress,
+                     const Mac48Address& txAddress,
+                     const std::set<uint8_t>& tids,
+                     const std::set<uint8_t>& linkIds) final;
+    /** \copydoc ns3::WifiMacQueueScheduler::UnblockQueues */
+    void UnblockQueues(WifiQueueBlockedReason reason,
+                       AcIndex ac,
+                       const std::list<WifiContainerQueueType>& types,
+                       const Mac48Address& rxAddress,
+                       const Mac48Address& txAddress,
+                       const std::set<uint8_t>& tids,
+                       const std::set<uint8_t>& linkIds) final;
+    /** \copydoc ns3::WifiMacQueueScheduler::GetQueueLinkMask */
+    std::optional<Mask> GetQueueLinkMask(AcIndex ac,
+                                         const WifiContainerQueueId& queueId,
+                                         uint8_t linkId) final;
     /** \copydoc ns3::WifiMacQueueScheduler::HasToDropBeforeEnqueue */
     Ptr<WifiMpdu> HasToDropBeforeEnqueue(AcIndex ac, Ptr<WifiMpdu> mpdu) final;
     /** \copydoc ns3::WifiMacQueueScheduler::NotifyEnqueue */
@@ -130,11 +146,12 @@
     struct QueueInfo
     {
         std::optional<typename SortedQueues::iterator>
-            priorityIt;             /**< iterator pointing to the entry
-                                         for this queue in the sorted list */
-        std::list<uint8_t> linkIds; /**< IDs of the links over which packets contained in this
-                                         queue can be sent over. Empty means that packets in this
-                                         queue can be sent over any link */
+            priorityIt;                  /**< iterator pointing to the entry
+                                              for this queue in the sorted list */
+        std::map<uint8_t, Mask> linkIds; /**< Maps ID of each link on which packets contained
+                                              in this queue can be sent to a bitset indicating
+                                              whether the link is blocked (at least one bit is
+                                              non-zero) and for which reason */
     };
 
     /**
@@ -167,16 +184,17 @@
 
   private:
     /**
-     * Add the information associated with the given container queue (if not already
-     * present) to the map corresponding to the given Access Category and Initialize
-     * the list of the IDs of the links over which packets contained in the given
-     * container queue can be sent over.
+     * If no information for the container queue used to store the given MPDU of the given
+     * Access Category is present in the queue info map, add the information for such a
+     * container queue and initialize the list of the IDs of the links over which packets
+     * contained in that container queue can be sent.
      *
      * \param ac the given Access Category
-     * \param queueId the ID of the given container queue
-     * \return an iterator to the information associated with the given container queue
+     * \param mpdu the given MPDU
+     * \return an iterator to the information associated with the container queue used to
+     *         store the given MPDU of the given Access Category
      */
-    typename QueueInfoMap::iterator InitQueueInfo(AcIndex ac, const WifiContainerQueueId& queueId);
+    typename QueueInfoMap::iterator InitQueueInfo(AcIndex ac, Ptr<const WifiMpdu> mpdu);
 
     /**
      * Get the next queue to serve. The search starts from the given one. The returned
@@ -228,6 +246,29 @@
      */
     virtual void DoNotifyRemove(AcIndex ac, const std::list<Ptr<WifiMpdu>>& mpdus) = 0;
 
+    /**
+     * Block or unblock the given set of links for the container queues of the given types and
+     * Access Category that hold frames having the given Receiver Address (RA),
+     * Transmitter Address (TA) and TID (if needed) for the given reason.
+     *
+     * \param block true to block the queues, false to unblock
+     * \param reason the reason for blocking the queues
+     * \param ac the given Access Category
+     * \param types the types of the queues to block
+     * \param rxAddress the Receiver Address (RA) of the frames
+     * \param txAddress the Transmitter Address (TA) of the frames
+     * \param tids the TIDs optionally identifying the queues to block
+     * \param linkIds set of links to block (empty to block all setup links)
+     */
+    void DoBlockQueues(bool block,
+                       WifiQueueBlockedReason reason,
+                       AcIndex ac,
+                       const std::list<WifiContainerQueueType>& types,
+                       const Mac48Address& rxAddress,
+                       const Mac48Address& txAddress,
+                       const std::set<uint8_t>& tids,
+                       const std::set<uint8_t>& linkIds);
+
     std::vector<PerAcInfo> m_perAcInfo{AC_UNDEF}; //!< vector of per-AC information
     NS_LOG_TEMPLATE_DECLARE;                      //!< the log component
 };
@@ -293,32 +334,67 @@
 
 template <class Priority, class Compare>
 typename WifiMacQueueSchedulerImpl<Priority, Compare>::QueueInfoMap::iterator
-WifiMacQueueSchedulerImpl<Priority, Compare>::InitQueueInfo(AcIndex ac,
-                                                            const WifiContainerQueueId& queueId)
+WifiMacQueueSchedulerImpl<Priority, Compare>::InitQueueInfo(AcIndex ac, Ptr<const WifiMpdu> mpdu)
 {
-    NS_LOG_FUNCTION(this);
+    NS_LOG_FUNCTION(this << ac << *mpdu);
 
+    auto queueId = WifiMacQueueContainer::GetQueueId(mpdu);
     // insert queueId in the queue info map if not present yet
     auto [queueInfoIt, ret] = m_perAcInfo[ac].queueInfoMap.insert({queueId, QueueInfo()});
 
-    if (ret)
+    // Initialize/update the set of link IDs depending on the container queue type
+    if (GetMac() && GetMac()->GetNLinks() > 1 &&
+        mpdu->GetHeader().GetAddr2() == GetMac()->GetAddress())
     {
-        // The given queueid has just been inserted in the queue info map.
-        // Initialize the set of link IDs depending on the container queue type
-        auto queueType = std::get<WifiContainerQueueType>(queueId);
-        auto address = std::get<Mac48Address>(queueId);
-
-        if (queueType == WIFI_MGT_QUEUE ||
-            (queueType == WIFI_CTL_QUEUE && GetMac() && address != GetMac()->GetAddress()) ||
-            queueType == WIFI_QOSDATA_BROADCAST_QUEUE)
+        // this is an MLD and the TA field of the frame contains the MLD address,
+        // which means that the frame can be sent on multiple links
+        const auto rxAddr = mpdu->GetHeader().GetAddr1();
+
+        // this assert checks that the RA field also contain an MLD address, unless
+        // it contains the broadcast address
+        NS_ASSERT_MSG(rxAddr.IsGroup() || GetMac()->GetMldAddress(rxAddr),
+                      "Address 1 (" << rxAddr << ") is not an MLD address");
+
+        // this assert checks that association (ML setup) has been established
+        // between sender and receiver (unless the receiver is the broadcast address)
+        NS_ASSERT_MSG(GetMac()->CanForwardPacketsTo(rxAddr), "Cannot forward frame to " << rxAddr);
+
+        // we have to include all the links in case of broadcast frame (we are an AP)
+        // and the links that have been setup with the receiver in case of unicast frame
+        for (uint8_t linkId = 0; linkId < GetMac()->GetNLinks(); linkId++)
         {
-            // these queue types are associated with just one link
-            NS_ASSERT(GetMac());
-            auto linkId = GetMac()->GetLinkIdByAddress(address);
-            NS_ASSERT(linkId.has_value());
-            queueInfoIt->second.linkIds = {*linkId};
+            if (rxAddr.IsGroup() ||
+                GetMac()->GetWifiRemoteStationManager(linkId)->GetAffiliatedStaAddress(rxAddr))
+            {
+                // the mask is not modified if linkId is already in the map
+                queueInfoIt->second.linkIds.emplace(linkId, Mask{});
+            }
+            else
+            {
+                // this link is no (longer) setup
+                queueInfoIt->second.linkIds.erase(linkId);
+            }
         }
     }
+    else
+    {
+        // the TA field of the frame contains a link address, which means that the
+        // frame can only be sent on the corresponding link
+        auto linkId = GetMac() ? GetMac()->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2())
+                               : SINGLE_LINK_OP_ID; // make unit test happy
+        NS_ASSERT(linkId.has_value());
+        auto& linkIdsMap = queueInfoIt->second.linkIds;
+        NS_ASSERT_MSG(linkIdsMap.size() <= 1,
+                      "At most one link can be associated with this container queue");
+        // set the link map to contain one entry corresponding to the computed link ID;
+        // unless the link map already contained such an entry (in which case the mask
+        // is preserved)
+        if (linkIdsMap.empty() || linkIdsMap.cbegin()->first != *linkId)
+        {
+            linkIdsMap = {{*linkId, Mask{}}};
+        }
+    }
+
     return queueInfoIt;
 }
 
@@ -334,8 +410,9 @@
     NS_ABORT_MSG_IF(GetWifiMacQueue(ac)->GetNBytes(queueId) == 0,
                     "Cannot set the priority of an empty queue");
 
-    // insert queueId in the queue info map if not present yet
-    auto queueInfoIt = InitQueueInfo(ac, queueId);
+    auto queueInfoIt = m_perAcInfo[ac].queueInfoMap.find(queueId);
+    NS_ASSERT_MSG(queueInfoIt != m_perAcInfo[ac].queueInfoMap.end(),
+                  "No queue info for the given container queue");
     typename SortedQueues::iterator sortedQueuesIt;
 
     if (queueInfoIt->second.priorityIt.has_value())
@@ -363,31 +440,129 @@
 
 template <class Priority, class Compare>
 std::list<uint8_t>
-WifiMacQueueSchedulerImpl<Priority, Compare>::GetLinkIds(AcIndex ac,
-                                                         const WifiContainerQueueId& queueId)
+WifiMacQueueSchedulerImpl<Priority, Compare>::GetLinkIds(AcIndex ac, Ptr<const WifiMpdu> mpdu)
 {
-    auto queueInfoIt = InitQueueInfo(ac, queueId);
+    auto queueInfoIt = InitQueueInfo(ac, mpdu);
+    std::list<uint8_t> linkIds;
 
-    if (queueInfoIt->second.linkIds.empty())
+    // include only links that are not blocked in the returned list
+    for (const auto [linkId, mask] : queueInfoIt->second.linkIds)
     {
-        // return the IDs of all available links
-        NS_ASSERT(GetMac() != nullptr);
-        std::list<uint8_t> linkIds(GetMac()->GetNLinks());
-        std::iota(linkIds.begin(), linkIds.end(), 0);
-        return linkIds;
+        if (mask.none())
+        {
+            linkIds.emplace_back(linkId);
+        }
     }
-    return queueInfoIt->second.linkIds;
+
+    return linkIds;
 }
 
 template <class Priority, class Compare>
 void
-WifiMacQueueSchedulerImpl<Priority, Compare>::SetLinkIds(AcIndex ac,
-                                                         const WifiContainerQueueId& queueId,
-                                                         const std::list<uint8_t>& linkIds)
+WifiMacQueueSchedulerImpl<Priority, Compare>::DoBlockQueues(
+    bool block,
+    WifiQueueBlockedReason reason,
+    AcIndex ac,
+    const std::list<WifiContainerQueueType>& types,
+    const Mac48Address& rxAddress,
+    const Mac48Address& txAddress,
+    const std::set<uint8_t>& tids,
+    const std::set<uint8_t>& linkIds)
 {
-    NS_LOG_FUNCTION(this << +ac);
-    auto [queueInfoIt, ret] = m_perAcInfo[ac].queueInfoMap.insert({queueId, QueueInfo()});
-    queueInfoIt->second.linkIds = linkIds;
+    NS_LOG_FUNCTION(this << block << reason << ac << rxAddress << txAddress);
+    std::list<WifiMacHeader> headers;
+
+    for (const auto queueType : types)
+    {
+        switch (queueType)
+        {
+        case WIFI_CTL_QUEUE:
+            headers.emplace_back(WIFI_MAC_CTL_BACKREQ);
+            break;
+        case WIFI_MGT_QUEUE:
+            headers.emplace_back(WIFI_MAC_MGT_ACTION);
+            break;
+        case WIFI_QOSDATA_QUEUE:
+            NS_ASSERT_MSG(!tids.empty(),
+                          "TID must be specified for queues containing QoS data frames");
+            for (const auto tid : tids)
+            {
+                headers.emplace_back(WIFI_MAC_QOSDATA);
+                headers.back().SetQosTid(tid);
+            }
+            break;
+        case WIFI_DATA_QUEUE:
+            headers.emplace_back(WIFI_MAC_DATA);
+            break;
+        }
+    }
+    for (auto& hdr : headers)
+    {
+        hdr.SetAddr1(rxAddress);
+        hdr.SetAddr2(txAddress);
+
+        auto queueInfoIt = InitQueueInfo(ac, Create<WifiMpdu>(Create<Packet>(), hdr));
+        for (auto& [linkId, mask] : queueInfoIt->second.linkIds)
+        {
+            if (linkIds.empty() || linkIds.count(linkId) > 0)
+            {
+                mask.set(static_cast<std::size_t>(reason), block);
+            }
+        }
+    }
+}
+
+template <class Priority, class Compare>
+void
+WifiMacQueueSchedulerImpl<Priority, Compare>::BlockQueues(
+    WifiQueueBlockedReason reason,
+    AcIndex ac,
+    const std::list<WifiContainerQueueType>& types,
+    const Mac48Address& rxAddress,
+    const Mac48Address& txAddress,
+    const std::set<uint8_t>& tids,
+    const std::set<uint8_t>& linkIds)
+{
+    DoBlockQueues(true, reason, ac, types, rxAddress, txAddress, tids, linkIds);
+}
+
+template <class Priority, class Compare>
+void
+WifiMacQueueSchedulerImpl<Priority, Compare>::UnblockQueues(
+    WifiQueueBlockedReason reason,
+    AcIndex ac,
+    const std::list<WifiContainerQueueType>& types,
+    const Mac48Address& rxAddress,
+    const Mac48Address& txAddress,
+    const std::set<uint8_t>& tids,
+    const std::set<uint8_t>& linkIds)
+{
+    DoBlockQueues(false, reason, ac, types, rxAddress, txAddress, tids, linkIds);
+}
+
+template <class Priority, class Compare>
+std::optional<WifiMacQueueScheduler::Mask>
+WifiMacQueueSchedulerImpl<Priority, Compare>::GetQueueLinkMask(AcIndex ac,
+                                                               const WifiContainerQueueId& queueId,
+                                                               uint8_t linkId)
+{
+    NS_LOG_FUNCTION(this << +ac << +linkId);
+
+    const auto queueInfoIt = m_perAcInfo[ac].queueInfoMap.find(queueId);
+
+    if (queueInfoIt == m_perAcInfo[ac].queueInfoMap.cend())
+    {
+        // the given container queue does not exist
+        return std::nullopt;
+    }
+
+    const auto& linkIds = queueInfoIt->second.linkIds;
+    if (const auto linkIt = linkIds.find(linkId); linkIt != linkIds.cend())
+    {
+        return linkIt->second;
+    }
+
+    return std::nullopt;
 }
 
 template <class Priority, class Compare>
@@ -431,7 +606,8 @@
         const auto& queueInfoPair = sortedQueuesIt->second.get();
         const auto& linkIds = queueInfoPair.second.linkIds;
 
-        if (linkIds.empty() || std::find(linkIds.begin(), linkIds.end(), linkId) != linkIds.end())
+        if (const auto linkIt = linkIds.find(linkId);
+            linkIt != linkIds.cend() && linkIt->second.none())
         {
             // Packets in this queue can be sent over the link we got channel access on.
             // Now remove packets with expired lifetime from this queue.
@@ -482,12 +658,12 @@
     NS_LOG_FUNCTION(this << +ac << *mpdu);
     NS_ASSERT(static_cast<uint8_t>(ac) < AC_UNDEF);
 
+    // add information for the queue storing the MPDU to the queue info map, if not present yet
+    auto queueInfoIt = InitQueueInfo(ac, mpdu);
+
     DoNotifyEnqueue(ac, mpdu);
 
-    if (auto queueInfoIt =
-            m_perAcInfo[ac].queueInfoMap.find(WifiMacQueueContainer::GetQueueId(mpdu));
-        queueInfoIt == m_perAcInfo[ac].queueInfoMap.end() ||
-        !queueInfoIt->second.priorityIt.has_value())
+    if (!queueInfoIt->second.priorityIt.has_value())
     {
         NS_ABORT_MSG(
             "No info for the queue the MPDU was stored into (forgot to call SetPriority()?)");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mgt-header.h ns-3.39/src/wifi/model/wifi-mgt-header.h
--- ns-3.38/src/wifi/model/wifi-mgt-header.h	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/model/wifi-mgt-header.h	2023-07-05 14:23:48.121428500 -0700
@@ -0,0 +1,992 @@
+/*
+ * 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_MGT_HEADER_H
+#define WIFI_MGT_HEADER_H
+
+#include "non-inheritance.h"
+
+#include "ns3/eht-capabilities.h"
+#include "ns3/header.h"
+#include "ns3/multi-link-element.h"
+#include "ns3/supported-rates.h"
+
+#include <algorithm>
+#include <iterator>
+#include <numeric>
+#include <optional>
+#include <utility>
+#include <vector>
+
+namespace ns3
+{
+
+namespace internal
+{
+
+/**
+ * \ingroup object
+ * \tparam T \explicit An Information Element type
+ *
+ * Provides the type used to store Information Elements in the tuple held by WifiMgtHeader:
+ * - a mandatory Information Element of type T is stored as std::optional\<T\>
+ * - an optional Information Element of type T is stored as std::optional\<T\>
+ * - an Information Element of type T that can appear 0 or more times is stored as std::vector\<T\>
+ */
+template <class T>
+struct GetStoredIe
+{
+    /// typedef for the resulting optional type
+    typedef std::optional<T> type;
+};
+
+/** \copydoc GetStoredIe */
+template <class T>
+struct GetStoredIe<std::optional<T>>
+{
+    /// typedef for the resulting optional type
+    typedef std::optional<T> type;
+};
+
+/** \copydoc GetStoredIe */
+template <class T>
+struct GetStoredIe<std::vector<T>>
+{
+    /// typedef for the resulting optional type
+    typedef std::vector<T> type;
+};
+
+/** \copydoc GetStoredIe */
+template <class T>
+using GetStoredIeT = typename GetStoredIe<T>::type;
+
+} // namespace internal
+
+/**
+ * \ingroup wifi
+ * Implement the header for management frames.
+ * \tparam Derived \explicit the type of derived management frame
+ * \tparam Tuple \explicit A tuple of the types of Information Elements included in the mgt frame
+ */
+template <typename Derived, typename Tuple>
+class WifiMgtHeader;
+
+/**
+ * \ingroup wifi
+ * Base class for implementing management frame headers. This class adopts the CRTP idiom,
+ * mainly to allow subclasses to specialize the method used to initialize Information
+ * Elements before deserialization (<i>InitForDeserialization</i>).
+ *
+ * The sorted list of Information Elements that can be included in the management frame implemented
+ * as a subclass of this class is provided as the template parameter pack. Specifically:
+ * - the type of a mandatory Information Element IE is IE
+ * - the type of an optional Information Element IE is std::optional\<IE\>
+ * - the type of an Information Element IE that can appear zero or more times is std::vector\<IE\>
+ *
+ * \tparam Derived \explicit the type of derived management frame
+ * \tparam Elems \explicit sorted list of Information Elements that can be included in mgt frame
+ */
+template <typename Derived, typename... Elems>
+class WifiMgtHeader<Derived, std::tuple<Elems...>> : public Header
+{
+  public:
+    /**
+     * Access a (mandatory or optional) Information Element.
+     *
+     * \tparam T \explicit the type of the Information Element to return
+     * \return a reference to the Information Element of the given type
+     */
+    template <typename T,
+              std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int> = 0>
+    std::optional<T>& Get();
+
+    /**
+     * Access a (mandatory or optional) Information Element.
+     *
+     * \tparam T \explicit the type of the Information Element to return
+     * \return a const reference to the Information Element of the given type
+     */
+    template <typename T,
+              std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int> = 0>
+    const std::optional<T>& Get() const;
+
+    /**
+     * Access an Information Element that can be present zero or more times.
+     *
+     * \tparam T \explicit the type of the Information Element to return
+     * \return a reference to the Information Element of the given type
+     */
+    template <typename T,
+              std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int> = 0>
+    std::vector<T>& Get();
+
+    /**
+     * Access an Information Element that can be present zero or more times.
+     *
+     * \tparam T \explicit the type of the Information Element to return
+     * \return a reference to the Information Element of the given type
+     */
+    template <typename T,
+              std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int> = 0>
+    const std::vector<T>& Get() const;
+
+    void Print(std::ostream& os) const final;
+    uint32_t GetSerializedSize() const final;
+    void Serialize(Buffer::Iterator start) const final;
+    uint32_t Deserialize(Buffer::Iterator start) final;
+
+  protected:
+    /**
+     * \tparam IE \deduced the type of the Information Element to initialize for deserialization
+     * \param optElem the object to initialize for deserializing the information element into
+     *
+     * The Information Element object is constructed by calling the object's default constructor.
+     */
+    template <typename IE>
+    void InitForDeserialization(std::optional<IE>& optElem);
+
+    /**
+     * \param optElem the EhtCapabilities object to initialize for deserializing the
+     *                information element into
+     */
+    void InitForDeserialization(std::optional<EhtCapabilities>& optElem);
+
+    /** \copydoc ns3::Header::Print */
+    void PrintImpl(std::ostream& os) const;
+    /** \copydoc ns3::Header::GetSerializedSize */
+    uint32_t GetSerializedSizeImpl() const;
+    /** \copydoc ns3::Header::Serialize */
+    void SerializeImpl(Buffer::Iterator start) const;
+    /** \copydoc ns3::Header::Deserialize */
+    uint32_t DeserializeImpl(Buffer::Iterator start);
+
+    /**
+     * \tparam T \deduced the type of the Information Element
+     * \param elem the optional Information Element
+     * \param start the buffer iterator pointing to where deserialization starts
+     * \return an iterator pointing to where deserialization terminated
+     */
+    template <typename T>
+    Buffer::Iterator DoDeserialize(std::optional<T>& elem, Buffer::Iterator start);
+
+    /**
+     * \tparam T \deduced the type of the Information Elements
+     * \param elems a vector of Information Elements
+     * \param start the buffer iterator pointing to where deserialization starts
+     * \return an iterator pointing to where deserialization terminated
+     */
+    template <typename T>
+    Buffer::Iterator DoDeserialize(std::vector<T>& elems, Buffer::Iterator start);
+
+    /// type of the Information Elements contained by this frame
+    using Elements = std::tuple<internal::GetStoredIeT<Elems>...>;
+
+    Elements m_elements; //!< Information Elements contained by this frame
+};
+
+/**
+ * \ingroup wifi
+ *  Inspect a type to deduce whether it is an Information Element that can be included in a
+ *  Per-STA Profile subelement of a Multi-Link Element.
+ *  \tparam T \explicit The type to inspect.
+ */
+template <class T>
+struct CanBeInPerStaProfile : std::true_type
+{
+};
+
+/** \copydoc CanBeInPerStaProfile */
+template <class T>
+inline constexpr bool CanBeInPerStaProfileV = CanBeInPerStaProfile<T>::value;
+
+/**
+ * \ingroup wifi
+ * Implement the header for management frames that can be included in a Per-STA Profile
+ * subelement of a Multi-Link Element.
+ * \tparam Derived \explicit the type of derived management frame
+ * \tparam Tuple \explicit A tuple of the types of Information Elements included in the mgt frame
+ */
+template <typename Derived, typename Tuple>
+class MgtHeaderInPerStaProfile;
+
+/**
+ * \ingroup wifi
+ *
+ * Add methods needed to serialize/deserialize a management header into a Per-STA Profile
+ * subelement of a Multi-Link Element.
+ *
+ * \tparam Derived \explicit the type of derived management frame
+ * \tparam Elems \explicit sorted list of Information Elements that can be included in mgt frame
+ */
+template <typename Derived, typename... Elems>
+class MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>
+    : public WifiMgtHeader<Derived, std::tuple<Elems...>>
+{
+  public:
+    /**
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes that are needed to serialize this header into a Per-STA Profile
+     *         subelement of the Multi-Link Element
+     */
+    uint32_t GetSerializedSizeInPerStaProfile(const Derived& frame) const;
+
+    /**
+     * Serialize this header into a Per-STA Profile subelement of a Multi-Link Element
+     *
+     * \param start an iterator which points to where the header should be written
+     * \param frame the frame containing the Multi-Link Element
+     */
+    void SerializeInPerStaProfile(Buffer::Iterator start, const Derived& frame) const;
+
+    /**
+     * Deserialize this header from a Per-STA Profile subelement of a Multi-Link Element.
+     *
+     * \param start an iterator which points to where the header should be read from
+     * \param length the expected number of bytes to read
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes read
+     */
+    uint32_t DeserializeFromPerStaProfile(Buffer::Iterator start,
+                                          uint16_t length,
+                                          const Derived& frame);
+
+    /**
+     * Copy Information Elements inherited from the management frame containing the Multi-Link
+     * Element into this header (which is stored in a Per-STA Profile subelement). This method
+     * shall be invoked when the deserialization has been completed (i.e., the Non-Inheritance
+     * element, if present, has been deserialized).
+     *
+     * \param frame the frame containing the Multi-Link Element
+     */
+    void CopyIesFromContainingFrame(const Derived& frame);
+
+  protected:
+    using WifiMgtHeader<Derived, std::tuple<Elems...>>::InitForDeserialization;
+
+    /**
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes that are needed to serialize this header into a Per-STA Profile
+     *         subelement of the Multi-Link Element
+     */
+    uint32_t GetSerializedSizeInPerStaProfileImpl(const Derived& frame) const;
+
+    /**
+     * Serialize this header into a Per-STA Profile subelement of a Multi-Link Element
+     *
+     * \param start an iterator which points to where the header should be written
+     * \param frame the frame containing the Multi-Link Element
+     */
+    void SerializeInPerStaProfileImpl(Buffer::Iterator start, const Derived& frame) const;
+
+    /**
+     * Deserialize this header from a Per-STA Profile subelement of a Multi-Link Element.
+     *
+     * \param start an iterator which points to where the header should be read from
+     * \param length the expected number of bytes to read
+     * \param frame the frame containing the Multi-Link Element
+     * \return the number of bytes read
+     */
+    uint32_t DeserializeFromPerStaProfileImpl(Buffer::Iterator start,
+                                              uint16_t length,
+                                              const Derived& frame);
+
+    /**
+     * Pass a pointer to this frame to the Multi-Link Element (if any) included in this frame.
+     */
+    void SetMleContainingFrame() const;
+
+    /**
+     * \param optElem the MultiLinkElement object to initialize for deserializing the
+     *                information element into
+     */
+    void InitForDeserialization(std::optional<MultiLinkElement>& optElem);
+
+  private:
+    using WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize;
+    using WifiMgtHeader<Derived, std::tuple<Elems...>>::m_elements;
+
+    std::optional<NonInheritance> m_nonInheritance; /**< the Non-Inheritance IE possibly appended
+                                                         to the Per-STA Profile subelement */
+};
+
+/**
+ * Implementation of the templates declared above.
+ */
+
+template <typename Derived, typename... Elems>
+template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int>>
+std::optional<T>&
+WifiMgtHeader<Derived, std::tuple<Elems...>>::Get()
+{
+    return std::get<std::optional<T>>(m_elements);
+}
+
+template <typename Derived, typename... Elems>
+template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 0, int>>
+const std::optional<T>&
+WifiMgtHeader<Derived, std::tuple<Elems...>>::Get() const
+{
+    return std::get<std::optional<T>>(m_elements);
+}
+
+template <typename Derived, typename... Elems>
+template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int>>
+std::vector<T>&
+WifiMgtHeader<Derived, std::tuple<Elems...>>::Get()
+{
+    return std::get<std::vector<T>>(m_elements);
+}
+
+template <typename Derived, typename... Elems>
+template <typename T, std::enable_if_t<(std::is_same_v<std::vector<T>, Elems> + ...) == 1, int>>
+const std::vector<T>&
+WifiMgtHeader<Derived, std::tuple<Elems...>>::Get() const
+{
+    return std::get<std::vector<T>>(m_elements);
+}
+
+template <typename Derived, typename... Elems>
+template <typename IE>
+void
+WifiMgtHeader<Derived, std::tuple<Elems...>>::InitForDeserialization(std::optional<IE>& optElem)
+{
+    optElem.emplace();
+}
+
+template <typename Derived, typename... Elems>
+void
+WifiMgtHeader<Derived, std::tuple<Elems...>>::InitForDeserialization(
+    std::optional<EhtCapabilities>& optElem)
+{
+    NS_ASSERT(Get<SupportedRates>());
+    auto rates = AllSupportedRates{*Get<SupportedRates>(), std::nullopt};
+    const bool is2_4Ghz = rates.IsSupportedRate(
+        1000000 /* 1 Mbit/s */); // TODO: use presence of VHT capabilities IE and HE 6 GHz Band
+                                 // Capabilities IE once the later is implemented
+    auto& heCapabilities = Get<HeCapabilities>();
+    if (heCapabilities)
+    {
+        optElem.emplace(is2_4Ghz, heCapabilities.value());
+    }
+    else
+    {
+        optElem.emplace();
+    }
+}
+
+template <typename Derived, typename... Elems>
+void
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::InitForDeserialization(
+    std::optional<MultiLinkElement>& optElem)
+{
+    optElem.emplace(*static_cast<const Derived*>(this));
+}
+
+namespace internal
+{
+
+/**
+ * \tparam T \deduced the type of the Information Element
+ * \param elem the optional Information Element
+ * \return the serialized size of the Information Element, if present, or 0, otherwise
+ */
+template <typename T>
+uint16_t
+DoGetSerializedSize(const std::optional<T>& elem)
+{
+    return elem.has_value() ? elem->GetSerializedSize() : 0;
+}
+
+/**
+ * \tparam T \deduced the type of the Information Elements
+ * \param elems a vector of Information Elements
+ * \return the serialized size of the Information Elements
+ */
+template <typename T>
+uint16_t
+DoGetSerializedSize(const std::vector<T>& elems)
+{
+    return std::accumulate(elems.cbegin(), elems.cend(), 0, [](uint16_t a, const auto& b) {
+        return b.GetSerializedSize() + a;
+    });
+}
+
+} // namespace internal
+
+template <typename Derived, typename... Elems>
+uint32_t
+WifiMgtHeader<Derived, std::tuple<Elems...>>::GetSerializedSize() const
+{
+    return static_cast<const Derived*>(this)->GetSerializedSizeImpl();
+}
+
+template <typename Derived, typename... Elems>
+uint32_t
+WifiMgtHeader<Derived, std::tuple<Elems...>>::GetSerializedSizeImpl() const
+{
+    return std::apply([&](auto&... elems) { return (internal::DoGetSerializedSize(elems) + ...); },
+                      m_elements);
+}
+
+namespace internal
+{
+
+/**
+ * \tparam T \deduced the type of the Information Element
+ * \param elem the optional Information Element
+ * \param start the buffer iterator pointing to where serialization starts
+ * \return an iterator pointing to where serialization terminated
+ */
+template <typename T>
+Buffer::Iterator
+DoSerialize(const std::optional<T>& elem, Buffer::Iterator start)
+{
+    return elem.has_value() ? elem->Serialize(start) : start;
+}
+
+/**
+ * \tparam T \deduced the type of the Information Elements
+ * \param elems a vector of Information Elements
+ * \param start the buffer iterator pointing to where serialization starts
+ * \return an iterator pointing to where serialization terminated
+ */
+template <typename T>
+Buffer::Iterator
+DoSerialize(const std::vector<T>& elems, Buffer::Iterator start)
+{
+    return std::accumulate(elems.cbegin(),
+                           elems.cend(),
+                           start,
+                           [](Buffer::Iterator i, const auto& a) { return a.Serialize(i); });
+}
+
+} // namespace internal
+
+template <typename Derived, typename... Elems>
+void
+WifiMgtHeader<Derived, std::tuple<Elems...>>::Serialize(Buffer::Iterator start) const
+{
+    static_cast<const Derived*>(this)->SerializeImpl(start);
+}
+
+template <typename Derived, typename... Elems>
+void
+WifiMgtHeader<Derived, std::tuple<Elems...>>::SerializeImpl(Buffer::Iterator start) const
+{
+    auto i = start;
+    std::apply([&](auto&... elems) { ((i = internal::DoSerialize(elems, i)), ...); }, m_elements);
+}
+
+template <typename Derived, typename... Elems>
+template <typename T>
+Buffer::Iterator
+WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize(std::optional<T>& elem,
+                                                            Buffer::Iterator start)
+{
+    auto i = start;
+    static_cast<Derived*>(this)->InitForDeserialization(elem);
+    i = elem->DeserializeIfPresent(i);
+    if (i.GetDistanceFrom(start) == 0)
+    {
+        elem.reset(); // the element is not present
+    }
+    return i;
+}
+
+template <typename Derived, typename... Elems>
+template <typename T>
+Buffer::Iterator
+WifiMgtHeader<Derived, std::tuple<Elems...>>::DoDeserialize(std::vector<T>& elems,
+                                                            Buffer::Iterator start)
+{
+    auto i = start;
+    do
+    {
+        auto tmp = i;
+        std::optional<T> item;
+        static_cast<Derived*>(this)->InitForDeserialization(item);
+        i = item->DeserializeIfPresent(i);
+        if (i.GetDistanceFrom(tmp) == 0)
+        {
+            break;
+        }
+        elems.push_back(std::move(*item));
+    } while (true);
+    return i;
+}
+
+template <typename Derived, typename... Elems>
+uint32_t
+WifiMgtHeader<Derived, std::tuple<Elems...>>::Deserialize(Buffer::Iterator start)
+{
+    return static_cast<Derived*>(this)->DeserializeImpl(start);
+}
+
+template <typename Derived, typename... Elems>
+uint32_t
+WifiMgtHeader<Derived, std::tuple<Elems...>>::DeserializeImpl(Buffer::Iterator start)
+{
+    auto i = start;
+
+    std::apply(
+        // auto cannot be used until gcc 10.4 due to gcc bug 97938
+        // (see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97938)
+        [&](internal::GetStoredIeT<Elems>&... elems) {
+            (
+                [&] {
+                    if constexpr (std::is_same_v<std::remove_reference_t<decltype(elems)>, Elems>)
+                    {
+                        // optional IE or IE that can be present 0 or more times
+                        i = DoDeserialize(elems, i);
+                    }
+                    else
+                    {
+                        // mandatory IE
+                        static_cast<Derived*>(this)->InitForDeserialization(elems);
+                        i = elems->Deserialize(i);
+                    }
+                }(),
+                ...);
+        },
+        m_elements);
+
+    return i.GetDistanceFrom(start);
+}
+
+namespace internal
+{
+
+/**
+ * \tparam T \deduced the type of the Information Element
+ * \param elem the optional Information Element
+ * \param os the output stream
+ */
+template <typename T>
+void
+DoPrint(const std::optional<T>& elem, std::ostream& os)
+{
+    if (elem.has_value())
+    {
+        os << *elem << " , ";
+    }
+}
+
+/**
+ * \tparam T \deduced the type of the Information Elements
+ * \param elems a vector of Information Elements
+ * \param os the output stream
+ */
+template <typename T>
+void
+DoPrint(const std::vector<T>& elems, std::ostream& os)
+{
+    std::copy(elems.cbegin(), elems.cend(), std::ostream_iterator<T>(os, " , "));
+}
+
+} // namespace internal
+
+template <typename Derived, typename... Elems>
+void
+WifiMgtHeader<Derived, std::tuple<Elems...>>::Print(std::ostream& os) const
+{
+    static_cast<const Derived*>(this)->PrintImpl(os);
+}
+
+template <typename Derived, typename... Elems>
+void
+WifiMgtHeader<Derived, std::tuple<Elems...>>::PrintImpl(std::ostream& os) const
+{
+    std::apply([&](auto&... elems) { ((internal::DoPrint(elems, os)), ...); }, m_elements);
+}
+
+namespace internal
+{
+
+/**
+ * \tparam T \deduced the type of the given Information Element
+ * \tparam Derived \deduced the type of the containing management frame
+ * \param elem the given Information Element
+ * \param frame the containing management frame
+ * \return whether the given Information Element shall be serialized in a Per-STA Profile
+ *         subelement of the Multi-Link Element included in the containing management frame
+ */
+template <typename T, typename Derived>
+bool
+MustBeSerializedInPerStaProfile(const std::optional<T>& elem, const Derived& frame)
+{
+    if (!CanBeInPerStaProfileV<T>)
+    {
+        return false;
+    }
+
+    if (auto& outsideIe = frame.template Get<T>();
+        outsideIe.has_value() && elem.has_value() && !(outsideIe.value() == elem.value()))
+    {
+        // the IE is present both outside the Multi-Link Element and in the Per-STA Profile,
+        // but they are different, hence the IE must be serialized in the Per-STA Profile
+        return true;
+    }
+
+    if (!frame.template Get<T>().has_value() && elem.has_value())
+    {
+        // the IE is not present outside the Multi-Link Element and is present in the Per-STA
+        // Profile, hence the IE must be serialized in the Per-STA Profile
+        return true;
+    }
+
+    return false;
+}
+
+/**
+ * \tparam T \deduced the type of the given vector of Information Elements
+ * \tparam Derived \deduced the type of the containing management frame
+ * \param elems the given vector of Information Elements
+ * \param frame the containing management frame
+ * \return whether the given Information Elements shall be serialized in a Per-STA Profile
+ *         subelement of the Multi-Link Element included in the containing management frame
+ */
+template <typename T, typename Derived>
+bool
+MustBeSerializedInPerStaProfile(const std::vector<T>& elems, const Derived& frame)
+{
+    if (!CanBeInPerStaProfileV<T>)
+    {
+        return false;
+    }
+
+    if (auto& outsideIe = frame.template Get<T>();
+        !outsideIe.empty() && !elems.empty() && !(outsideIe == elems))
+    {
+        // the IEs are present both outside the Multi-Link Element and in the Per-STA Profile,
+        // but they are different, hence the IEs must be serialized in the Per-STA Profile
+        return true;
+    }
+
+    if (frame.template Get<T>().empty() && !elems.empty())
+    {
+        // the IEs are not present outside the Multi-Link Element and is present in the Per-STA
+        // Profile, hence the IEs must be serialized in the Per-STA Profile
+        return true;
+    }
+
+    return false;
+}
+
+/**
+ * \tparam T \deduced the type of the given Information Element
+ * \tparam Derived \deduced the type of the containing management frame
+ * \param elem the given Information Element
+ * \param frame the containing management frame
+ * \return a pair (Element ID, Element ID Extension) if the given Information Element shall be
+ *         listed in the Non-Inheritance IE of the Per-STA Profile subelement of the Multi-Link
+ *         Element included in the containing management frame
+ */
+template <typename T, typename Derived>
+std::optional<std::pair<uint8_t, uint8_t>>
+MustBeListedInNonInheritance(const std::optional<T>& elem, const Derived& frame)
+{
+    if (auto& outsideIe = frame.template Get<T>();
+        CanBeInPerStaProfileV<T> && outsideIe.has_value() && !elem.has_value())
+    {
+        return {{outsideIe->ElementId(), outsideIe->ElementIdExt()}};
+    }
+    return std::nullopt;
+}
+
+/**
+ * \tparam T \deduced the type of the given vector of Information Elements
+ * \tparam Derived \deduced the type of the containing management frame
+ * \param elems the given Information Elements
+ * \param frame the containing management frame
+ * \return a pair (Element ID, Element ID Extension) if the given Information Element shall be
+ *         listed in the Non-Inheritance IE of the Per-STA Profile subelement of the Multi-Link
+ *         Element included in the containing management frame
+ */
+template <typename T, typename Derived>
+std::optional<std::pair<uint8_t, uint8_t>>
+MustBeListedInNonInheritance(const std::vector<T>& elems, const Derived& frame)
+{
+    if (auto& outsideIe = frame.template Get<T>();
+        CanBeInPerStaProfileV<T> && !outsideIe.empty() && elems.empty())
+    {
+        return {{outsideIe.front().ElementId(), outsideIe.front().ElementIdExt()}};
+    }
+    return std::nullopt;
+}
+
+} // namespace internal
+
+template <typename Derived, typename... Elems>
+uint32_t
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::GetSerializedSizeInPerStaProfile(
+    const Derived& frame) const
+{
+    return static_cast<const Derived*>(this)->GetSerializedSizeInPerStaProfileImpl(frame);
+}
+
+template <typename Derived, typename... Elems>
+uint32_t
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::GetSerializedSizeInPerStaProfileImpl(
+    const Derived& frame) const
+{
+    uint32_t size = 0;
+    std::optional<NonInheritance> nonInheritance;
+
+    std::apply(
+        [&](auto&... elems) {
+            (
+                [&] {
+                    if (internal::MustBeSerializedInPerStaProfile(elems, frame))
+                    {
+                        size += internal::DoGetSerializedSize(elems);
+                    }
+                    else if (auto idPair = internal::MustBeListedInNonInheritance(elems, frame))
+                    {
+                        if (!nonInheritance)
+                        {
+                            nonInheritance.emplace();
+                        }
+                        nonInheritance->Add(idPair->first, idPair->second);
+                    }
+                }(),
+                ...);
+        },
+        m_elements);
+
+    if (nonInheritance)
+    {
+        size += nonInheritance->GetSerializedSize();
+    }
+    return size;
+}
+
+template <typename Derived, typename... Elems>
+void
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SerializeInPerStaProfile(
+    Buffer::Iterator start,
+    const Derived& frame) const
+{
+    static_cast<const Derived*>(this)->SerializeInPerStaProfileImpl(start, frame);
+}
+
+template <typename Derived, typename... Elems>
+void
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SerializeInPerStaProfileImpl(
+    Buffer::Iterator start,
+    const Derived& frame) const
+{
+    auto i = start;
+    std::optional<NonInheritance> nonInheritance;
+
+    std::apply(
+        [&](auto&... elems) {
+            (
+                [&] {
+                    if (internal::MustBeSerializedInPerStaProfile(elems, frame))
+                    {
+                        i = internal::DoSerialize(elems, i);
+                    }
+                    else if (auto idPair = internal::MustBeListedInNonInheritance(elems, frame))
+                    {
+                        if (!nonInheritance)
+                        {
+                            nonInheritance.emplace();
+                        }
+                        nonInheritance->Add(idPair->first, idPair->second);
+                    }
+                }(),
+                ...);
+        },
+        m_elements);
+
+    if (nonInheritance)
+    {
+        nonInheritance->Serialize(i);
+    }
+}
+
+namespace internal
+{
+
+/**
+ * \tparam T \deduced the type of the given Information Element
+ * \tparam Derived \deduced the type of the containing management frame
+ * \param elem the given Information Element
+ * \param frame the containing management frame
+ *
+ * Copy the given Information Element from the containing frame to the Per-STA Profile subelement
+ * of the Multi-Link Element, if the Information Element has been inherited (i.e., it is present
+ * outside the Multi-Link Element and not present in the Per-STA Profile subelement)
+ */
+template <typename T, typename Derived>
+void
+DoCopyIeFromContainingFrame(std::optional<T>& elem, const Derived& frame)
+{
+    if (auto& outsideIe = frame.template Get<T>();
+        CanBeInPerStaProfileV<T> && outsideIe.has_value() && !elem.has_value())
+    {
+        elem = outsideIe.value();
+    }
+}
+
+/**
+ * \tparam T \deduced the type of the given vector of Information Elements
+ * \tparam Derived \deduced the type of the containing management frame
+ * \param elems the given vector of Information Elements
+ * \param frame the containing management frame
+ *
+ * Copy the given Information Element from the containing frame to the Per-STA Profile subelement
+ * of the Multi-Link Element, if the Information Element has been inherited (i.e., it is present
+ * outside the Multi-Link Element and not present in the Per-STA Profile subelement)
+ */
+template <typename T, typename Derived>
+void
+DoCopyIeFromContainingFrame(std::vector<T>& elems, const Derived& frame)
+{
+    if (auto& outsideIe = frame.template Get<T>();
+        CanBeInPerStaProfileV<T> && !outsideIe.empty() && elems.empty())
+    {
+        elems = outsideIe;
+    }
+}
+
+} // namespace internal
+
+template <typename Derived, typename... Elems>
+uint32_t
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::DeserializeFromPerStaProfile(
+    Buffer::Iterator start,
+    uint16_t length,
+    const Derived& frame)
+{
+    return static_cast<Derived*>(this)->DeserializeFromPerStaProfileImpl(start, length, frame);
+}
+
+template <typename Derived, typename... Elems>
+uint32_t
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::DeserializeFromPerStaProfileImpl(
+    Buffer::Iterator start,
+    uint16_t length,
+    const Derived& frame)
+{
+    auto i = start;
+
+    // deserialize the IEs in the Per-STA Profile subelement
+    std::apply(
+        [&](auto&... elems) {
+            (
+                [&] {
+                    if (i.GetDistanceFrom(start) < length)
+                    {
+                        i = static_cast<Derived*>(this)->DoDeserialize(elems, i);
+                        internal::DoCopyIeFromContainingFrame(elems, frame);
+                    }
+                }(),
+                ...);
+        },
+        m_elements);
+
+    // deserialize the Non-Inheritance IE, if present
+    m_nonInheritance.reset();
+    i = DoDeserialize(m_nonInheritance, i);
+
+    auto distance = i.GetDistanceFrom(start);
+    NS_ASSERT_MSG(distance == length,
+                  "Bytes read (" << distance << ") not matching expected number (" << length
+                                 << ")");
+    return distance;
+}
+
+namespace internal
+{
+
+/**
+ * \tparam T \deduced the type of the given Information Element
+ * \param elem the given Information Element
+ * \param nonInheritance the Non-Inheritance information element
+ *
+ * Remove the given Information Element from this header, if it is present and is listed in
+ * the given Non-Inheritance element.
+ */
+template <typename T>
+void
+RemoveIfNotInherited(std::optional<T>& elem, const NonInheritance& nonInheritance)
+{
+    if (elem.has_value() && nonInheritance.IsPresent(elem->ElementId(), elem->ElementIdExt()))
+    {
+        elem.reset();
+    }
+}
+
+/**
+ * \tparam T \deduced the type of the given vector of Information Elements
+ * \param elem the given Information Elements
+ * \param nonInheritance the Non-Inheritance information element
+ *
+ * Remove the given Information Elements from this header, if they are present and are listed in
+ * the given Non-Inheritance element.
+ */
+template <typename T>
+void
+RemoveIfNotInherited(std::vector<T>& elem, const NonInheritance& nonInheritance)
+{
+    if (!elem.empty() &&
+        nonInheritance.IsPresent(elem.front().ElementId(), elem.front().ElementIdExt()))
+    {
+        elem.clear();
+    }
+}
+
+} // namespace internal
+
+template <typename Derived, typename... Elems>
+void
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::CopyIesFromContainingFrame(
+    const Derived& frame)
+{
+    // copy inherited Information Elements that appear in the containing frame after the
+    // MLE (those appearing before have been copied by DeserializeFromPerStaProfileImpl)
+    std::apply(
+        [&](auto&... elems) { ((internal::DoCopyIeFromContainingFrame(elems, frame)), ...); },
+        m_elements);
+
+    // we have possibly deserialized a Non-Inheritance element; remove IEs listed therein
+    if (m_nonInheritance)
+    {
+        std::apply(
+            [&](auto&... elems) {
+                ((internal::RemoveIfNotInherited(elems, *m_nonInheritance)), ...);
+            },
+            m_elements);
+    }
+}
+
+template <typename Derived, typename... Elems>
+void
+MgtHeaderInPerStaProfile<Derived, std::tuple<Elems...>>::SetMleContainingFrame() const
+{
+    if (auto& mle = WifiMgtHeader<Derived, std::tuple<Elems...>>::template Get<MultiLinkElement>())
+    {
+        mle->m_containingFrame = *static_cast<const Derived*>(this);
+    }
+}
+
+} // namespace ns3
+
+#endif /* WIFI_MGT_HEADER_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mode.h ns-3.39/src/wifi/model/wifi-mode.h
--- ns-3.38/src/wifi/model/wifi-mode.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-mode.h	2023-07-05 14:23:48.121428500 -0700
@@ -31,7 +31,8 @@
 namespace ns3
 {
 
-#define SU_STA_ID 65535
+/// STA_ID to identify a single user (SU)
+static constexpr uint16_t SU_STA_ID = 65535;
 
 class WifiTxVector;
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mpdu.cc ns-3.39/src/wifi/model/wifi-mpdu.cc
--- ns-3.38/src/wifi/model/wifi-mpdu.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-mpdu.cc	2023-07-05 14:23:48.121428500 -0700
@@ -28,18 +28,18 @@
 
 #include "ns3/log.h"
 #include "ns3/packet.h"
-#include "ns3/simulator.h"
 
 namespace ns3
 {
 
 NS_LOG_COMPONENT_DEFINE("WifiMpdu");
 
-WifiMpdu::WifiMpdu(Ptr<const Packet> p, const WifiMacHeader& header)
+WifiMpdu::WifiMpdu(Ptr<const Packet> p, const WifiMacHeader& header, Time stamp)
     : m_header(header)
 {
     auto& original = std::get<OriginalInfo>(m_instanceInfo);
     original.m_packet = p;
+    original.m_timestamp = stamp;
 
     if (header.IsQosData() && header.IsQosAmsdu())
     {
@@ -115,6 +115,17 @@
     return GetOriginalInfo().m_packet;
 }
 
+Time
+WifiMpdu::GetTimestamp() const
+{
+    if (auto original = std::get_if<ORIGINAL>(&m_instanceInfo))
+    {
+        return original->m_timestamp;
+    }
+    const auto& origInstanceInfo = std::get<Ptr<WifiMpdu>>(m_instanceInfo)->m_instanceInfo;
+    return std::get<OriginalInfo>(origInstanceInfo).m_timestamp;
+}
+
 const WifiMacHeader&
 WifiMpdu::GetHeader() const
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-mpdu.h ns-3.39/src/wifi/model/wifi-mpdu.h
--- ns-3.38/src/wifi/model/wifi-mpdu.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-mpdu.h	2023-07-05 14:23:48.121428500 -0700
@@ -28,6 +28,7 @@
 #include "wifi-mac-queue-elem.h"
 
 #include "ns3/packet.h"
+#include "ns3/simulator.h"
 
 #include <list>
 #include <optional>
@@ -64,8 +65,9 @@
      * \brief Create a Wifi MAC queue item containing a packet and a Wifi MAC header.
      * \param p the const packet included in the created item.
      * \param header the Wifi MAC header included in the created item.
+     * \param stamp the timestamp to associate with the MPDU
      */
-    WifiMpdu(Ptr<const Packet> p, const WifiMacHeader& header);
+    WifiMpdu(Ptr<const Packet> p, const WifiMacHeader& header, Time stamp = Simulator::Now());
 
     virtual ~WifiMpdu();
 
@@ -183,6 +185,10 @@
      */
     AcIndex GetQueueAc() const;
     /**
+     * \return the time this MPDU was constructed
+     */
+    Time GetTimestamp() const;
+    /**
      * \return the expiry time of this MPDU
      */
     Time GetExpiryTime() const;
@@ -278,6 +284,7 @@
     struct OriginalInfo
     {
         Ptr<const Packet> m_packet;        //!< MSDU or A-MSDU contained in this queue item
+        Time m_timestamp;                  //!< construction time
         DeaggregatedMsdus m_msduList;      //!< list of aggregated MSDUs included in this MPDU
         std::optional<Iterator> m_queueIt; //!< Queue iterator pointing to this MPDU, if queued
         bool m_seqNoAssigned;              //!< whether a sequence number has been assigned
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-phy.cc ns-3.39/src/wifi/model/wifi-phy.cc
--- ns-3.38/src/wifi/model/wifi-phy.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-phy.cc	2023-07-05 14:23:48.125428400 -0700
@@ -67,6 +67,7 @@
             .SetGroupName("Wifi")
             .AddAttribute("Channel",
                           "The channel attached to this PHY",
+                          TypeId::ATTR_GET,
                           PointerValue(),
                           MakePointerAccessor(&WifiPhy::GetChannel),
                           MakePointerChecker<Channel>())
@@ -88,7 +89,7 @@
                 "number uniquely identify a frequency channel for the given standard and band.",
                 StringValue("{0, 0, BAND_UNSPECIFIED, 0}"),
                 MakeTupleAccessor<UintegerValue, UintegerValue, EnumValue, UintegerValue>(
-                    &WifiPhy::SetOperatingChannel),
+                    (void(WifiPhy::*)(const ChannelTuple&)) & WifiPhy::SetOperatingChannel),
                 MakeTupleChecker<UintegerValue, UintegerValue, EnumValue, UintegerValue>(
                     MakeUintegerChecker<uint8_t>(0, 233),
                     MakeUintegerChecker<uint16_t>(0, 160),
@@ -103,17 +104,20 @@
                     MakeUintegerChecker<uint8_t>(0, 7)))
             .AddAttribute("Frequency",
                           "The center frequency (MHz) of the current operating channel.",
+                          TypeId::ATTR_GET,
                           UintegerValue(0),
                           MakeUintegerAccessor(&WifiPhy::GetFrequency),
                           MakeUintegerChecker<uint16_t>())
             .AddAttribute("ChannelNumber",
                           "The channel number of the current operating channel.",
+                          TypeId::ATTR_GET,
                           UintegerValue(0),
                           MakeUintegerAccessor(&WifiPhy::GetChannelNumber),
                           MakeUintegerChecker<uint8_t>(0, 233))
             .AddAttribute(
                 "ChannelWidth",
                 "The width in MHz of the current operating channel (5, 10, 20, 22, 40, 80 or 160).",
+                TypeId::ATTR_GET,
                 UintegerValue(0),
                 MakeUintegerAccessor(&WifiPhy::GetChannelWidth),
                 MakeUintegerChecker<uint16_t>(5, 160))
@@ -247,6 +251,11 @@
                           PointerValue(),
                           MakePointerAccessor(&WifiPhy::m_postReceptionErrorModel),
                           MakePointerChecker<ErrorModel>())
+            .AddAttribute("InterferenceHelper",
+                          "Ptr to an object that implements the interference helper",
+                          PointerValue(),
+                          MakePointerAccessor(&WifiPhy::m_interference),
+                          MakePointerChecker<InterferenceHelper>())
             .AddAttribute("Sifs",
                           "The duration of the Short Interframe Space. "
                           "NOTE that the default value is overwritten by the value defined "
@@ -1059,6 +1068,17 @@
 }
 
 void
+WifiPhy::SetOperatingChannel(const WifiPhyOperatingChannel& channel)
+{
+    NS_LOG_FUNCTION(this << channel);
+    WifiPhy::ChannelTuple tuple(channel.GetNumber(),
+                                channel.GetWidth(),
+                                channel.GetPhyBand(),
+                                channel.GetPrimaryChannelIndex(20));
+    SetOperatingChannel(tuple);
+}
+
+void
 WifiPhy::SetOperatingChannel(const ChannelTuple& channelTuple)
 {
     // the generic operator<< for tuples does not give a pretty result
@@ -1089,7 +1109,8 @@
     if (delay.IsStrictlyPositive())
     {
         // switching channel has been postponed
-        Simulator::Schedule(delay, &WifiPhy::SetOperatingChannel, this, channelTuple);
+        void (WifiPhy::*fp)(const ChannelTuple&) = &WifiPhy::SetOperatingChannel;
+        Simulator::Schedule(delay, fp, this, channelTuple);
         return;
     }
 
@@ -1118,11 +1139,7 @@
     {
     case WifiPhyState::RX:
         NS_LOG_DEBUG("drop packet because of channel switching while reception");
-        m_endPhyRxEvent.Cancel();
-        for (auto& phyEntity : m_phyEntities)
-        {
-            phyEntity.second->CancelAllEvents();
-        }
+        AbortCurrentReception(CHANNEL_SWITCHING);
         break;
     case WifiPhyState::TX:
         NS_LOG_DEBUG("channel switching postponed until end of current transmission");
@@ -1154,8 +1171,8 @@
     NS_LOG_FUNCTION(this);
 
     // Update unspecified parameters with default values
-    if (auto& [number, width, band, primary20] = m_channelSettings; true)
     {
+        auto& [number, width, band, primary20] = m_channelSettings;
         if (band == static_cast<int>(WIFI_PHY_BAND_UNSPECIFIED))
         {
             band = static_cast<int>(GetDefaultPhyBand(m_standard));
@@ -1218,7 +1235,6 @@
     {
         // notify channel switching
         m_state->SwitchToChannelSwitching(GetChannelSwitchDelay());
-        m_interference->EraseEvents();
         /*
          * Needed here to be able to correctly sensed the medium for the first
          * time after the switching. The actual switching is not performed until
@@ -1835,17 +1851,6 @@
     }
 }
 
-WifiSpectrumBand
-WifiPhy::ConvertHeRuSubcarriers(uint16_t bandWidth,
-                                uint16_t guardBandwidth,
-                                HeRu::SubcarrierRange range,
-                                uint8_t bandIndex) const
-{
-    NS_ASSERT_MSG(false, "802.11ax can only be used with SpectrumWifiPhy");
-    WifiSpectrumBand convertedSubcarriers;
-    return convertedSubcarriers;
-}
-
 void
 WifiPhy::EndReceiveInterBss()
 {
@@ -2083,7 +2088,7 @@
         {
             m_endPhyRxEvent.Cancel();
         }
-        m_interference->NotifyRxEnd(Simulator::Now());
+        m_interference->NotifyRxEnd(Simulator::Now(), GetCurrentFrequencyRange());
         if (!m_currentEvent)
         {
             return;
@@ -2171,15 +2176,6 @@
     return GetPhyEntityForPpdu(ppdu)->GetAddressedPsduInPpdu(ppdu);
 }
 
-WifiSpectrumBand
-WifiPhy::GetBand(uint16_t /*bandWidth*/, uint8_t /*bandIndex*/)
-{
-    WifiSpectrumBand band;
-    band.first = 0;
-    band.second = 0;
-    return band;
-}
-
 int64_t
 WifiPhy::AssignStreams(int64_t stream)
 {
@@ -2204,4 +2200,38 @@
     return m_operatingChannel.GetPrimaryChannelNumber(primaryChannelWidth, m_standard);
 }
 
+uint32_t
+WifiPhy::GetSubcarrierSpacing() const
+{
+    uint32_t subcarrierSpacing = 0;
+    switch (GetStandard())
+    {
+    case WIFI_STANDARD_80211a:
+    case WIFI_STANDARD_80211g:
+    case WIFI_STANDARD_80211b:
+    case WIFI_STANDARD_80211n:
+    case WIFI_STANDARD_80211ac:
+        subcarrierSpacing = 312500;
+        break;
+    case WIFI_STANDARD_80211p:
+        if (GetChannelWidth() == 5)
+        {
+            subcarrierSpacing = 78125;
+        }
+        else
+        {
+            subcarrierSpacing = 156250;
+        }
+        break;
+    case WIFI_STANDARD_80211ax:
+    case WIFI_STANDARD_80211be:
+        subcarrierSpacing = 78125;
+        break;
+    default:
+        NS_FATAL_ERROR("Standard unknown: " << GetStandard());
+        break;
+    }
+    return subcarrierSpacing;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-phy-common.h ns-3.39/src/wifi/model/wifi-phy-common.h
--- ns-3.38/src/wifi/model/wifi-phy-common.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-phy-common.h	2023-07-05 14:23:48.125428400 -0700
@@ -26,6 +26,7 @@
 
 #include "ns3/fatal-error.h"
 #include "ns3/ptr.h"
+#include "ns3/wifi-spectrum-value-helper.h"
 
 #include <ostream>
 
@@ -48,28 +49,114 @@
 class Time;
 
 /**
+ * typedef for a pair of start and stop frequencies in Hz to represent a band
+ */
+using WifiSpectrumBandFrequencies = std::pair<uint64_t, uint64_t>;
+
+/// WifiSpectrumBandInfo structure containing info about a spectrum band
+struct WifiSpectrumBandInfo
+{
+    WifiSpectrumBandIndices indices;         //!< the start and stop indices of the band
+    WifiSpectrumBandFrequencies frequencies; //!< the start and stop frequencies of the band
+};
+
+/**
+ * \ingroup wifi
+ * Compare two bands.
+ *
+ * \param lhs the band on the left of operator<
+ * \param rhs the band on the right of operator<
+ * \return true if the start/stop frequencies of left are lower than the start/stop frequencies of
+ * right, false otherwise
+ */
+inline bool
+operator<(const WifiSpectrumBandInfo& lhs, const WifiSpectrumBandInfo& rhs)
+{
+    return lhs.frequencies < rhs.frequencies;
+}
+
+/**
+ * \brief Stream insertion operator.
+ *
+ * \param os the stream
+ * \param band the band
+ * \returns a reference to the stream
+ */
+inline std::ostream&
+operator<<(std::ostream& os, const WifiSpectrumBandInfo& band)
+{
+    os << "indices: [" << band.indices.first << "-" << band.indices.second << "], frequencies: ["
+       << band.frequencies.first << "Hz-" << band.frequencies.second << "Hz]";
+    return os;
+}
+
+/**
  * These constants define the various convolutional coding rates
  * used for the OFDM transmission modes in the IEEE 802.11
  * standard. DSSS (for example) rates which do not have an explicit
  * coding stage in their generation should have this parameter set to
  * WIFI_CODE_RATE_UNDEFINED.
- * \note This typedef and constants could be converted to an enum or scoped
- * enum if pybindgen is upgraded to support Callback<WifiCodeRate>
  */
-typedef uint16_t WifiCodeRate;
-const uint16_t WIFI_CODE_RATE_UNDEFINED = 0; //!< undefined coding rate
-const uint16_t WIFI_CODE_RATE_1_2 = 1;       //!< 1/2 coding rate
-const uint16_t WIFI_CODE_RATE_2_3 = 2;       //!< 2/3 coding rate
-const uint16_t WIFI_CODE_RATE_3_4 = 3;       //!< 3/4 coding rate
-const uint16_t WIFI_CODE_RATE_5_6 = 4;       //!< 5/6 coding rate
-const uint16_t WIFI_CODE_RATE_5_8 = 5;       //!< 5/8 coding rate
-const uint16_t WIFI_CODE_RATE_13_16 = 6;     //!< 13/16 coding rate
-const uint16_t WIFI_CODE_RATE_1_4 = 7;       //!< 1/4 coding rate
-const uint16_t WIFI_CODE_RATE_13_28 = 8;     //!< 13/28 coding rate
-const uint16_t WIFI_CODE_RATE_13_21 = 9;     //!< 13/21 coding rate
-const uint16_t WIFI_CODE_RATE_52_63 = 10;    //!< 52/63 coding rate
-const uint16_t WIFI_CODE_RATE_13_14 = 11;    //!< 13/14 coding rate
-const uint16_t WIFI_CODE_RATE_7_8 = 12;      //!< 7/8 coding rate
+enum WifiCodeRate : uint16_t
+{
+    WIFI_CODE_RATE_UNDEFINED, //!< undefined coding rate
+    WIFI_CODE_RATE_1_2,       //!< 1/2 coding rate
+    WIFI_CODE_RATE_2_3,       //!< 2/3 coding rate
+    WIFI_CODE_RATE_3_4,       //!< 3/4 coding rate
+    WIFI_CODE_RATE_5_6,       //!< 5/6 coding rate
+    WIFI_CODE_RATE_5_8,       //!< 5/8 coding rate
+    WIFI_CODE_RATE_13_16,     //!< 13/16 coding rate
+    WIFI_CODE_RATE_1_4,       //!< 1/4 coding rate
+    WIFI_CODE_RATE_13_28,     //!< 13/28 coding rate
+    WIFI_CODE_RATE_13_21,     //!< 13/21 coding rate
+    WIFI_CODE_RATE_52_63,     //!< 52/63 coding rate
+    WIFI_CODE_RATE_13_14,     //!< 13/14 coding rate
+    WIFI_CODE_RATE_7_8,       //!< 7/8 coding rate
+};
+
+/**
+ * \brief Stream insertion operator.
+ *
+ * \param os the stream
+ * \param codeRate the code rate
+ * \returns a reference to the stream
+ */
+inline std::ostream&
+operator<<(std::ostream& os, const WifiCodeRate& codeRate)
+{
+    switch (codeRate)
+    {
+    case WIFI_CODE_RATE_UNDEFINED:
+        return (os << "Code rate undefined");
+    case WIFI_CODE_RATE_1_2:
+        return (os << "Code rate 1/2");
+    case WIFI_CODE_RATE_2_3:
+        return (os << "Code rate 2/3");
+    case WIFI_CODE_RATE_3_4:
+        return (os << "Code rate 3/4");
+    case WIFI_CODE_RATE_5_6:
+        return (os << "Code rate 5/6");
+    case WIFI_CODE_RATE_5_8:
+        return (os << "Code rate 5/8");
+    case WIFI_CODE_RATE_13_16:
+        return (os << "Code rate 13/16");
+    case WIFI_CODE_RATE_1_4:
+        return (os << "Code rate 1/4");
+    case WIFI_CODE_RATE_13_28:
+        return (os << "Code rate 13/28");
+    case WIFI_CODE_RATE_13_21:
+        return (os << "Code rate 13/21");
+    case WIFI_CODE_RATE_52_63:
+        return (os << "Code rate 52/63");
+    case WIFI_CODE_RATE_13_14:
+        return (os << "Code rate 13/14");
+    case WIFI_CODE_RATE_7_8:
+        return (os << "Code rate 7/8");
+    default:
+        NS_FATAL_ERROR("Unknown code rate");
+        return (os << "Unknown");
+    }
+}
 
 /**
  * \ingroup wifi
@@ -332,7 +419,7 @@
     PREAMBLE_DETECTION_PACKET_SWITCH,
     FRAME_CAPTURE_PACKET_SWITCH,
     OBSS_PD_CCA_RESET,
-    HE_TB_PPDU_TOO_LATE,
+    PPDU_TOO_LATE,
     FILTERED,
     DMG_HEADER_FAILURE,
     DMG_ALLOCATION_ENDED
@@ -388,8 +475,8 @@
         return (os << "FRAME_CAPTURE_PACKET_SWITCH");
     case OBSS_PD_CCA_RESET:
         return (os << "OBSS_PD_CCA_RESET");
-    case HE_TB_PPDU_TOO_LATE:
-        return (os << "HE_TB_PPDU_TOO_LATE");
+    case PPDU_TOO_LATE:
+        return (os << "PPDU_TOO_LATE");
     case FILTERED:
         return (os << "FILTERED");
     case DMG_HEADER_FAILURE:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-phy.h ns-3.39/src/wifi/model/wifi-phy.h
--- ns-3.38/src/wifi/model/wifi-phy.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-phy.h	2023-07-05 14:23:48.125428400 -0700
@@ -501,6 +501,8 @@
 
     /**
      * Configure the PHY-level parameters for different Wi-Fi standard.
+     * Note that, in case a Spectrum PHY is used, this method must be called after adding
+     * a spectrum channel covering the operating channel bandwidth.
      *
      * \param standard the Wi-Fi standard
      */
@@ -834,7 +836,7 @@
      *
      * \param device the device this PHY is associated with
      */
-    void SetDevice(const Ptr<WifiNetDevice> device);
+    virtual void SetDevice(const Ptr<WifiNetDevice> device);
     /**
      * Return the device this PHY is associated with
      *
@@ -876,10 +878,28 @@
      * Otherwise, set the operating channel based on the given channel settings and
      * call ConfigureStandard if the PHY band has changed.
      *
+     * Note that, in case a Spectrum PHY is used, a spectrum channel covering the
+     * operating channel bandwidth must have been already added when actually setting
+     * the operating channel.
+     *
      * \param channelTuple the given channel settings
      */
     void SetOperatingChannel(const ChannelTuple& channelTuple);
     /**
+     * If the standard for this object has not been set yet, store the channel settings
+     * corresponding to the given operating channel. Otherwise, check if a channel switch
+     * can be performed now. If not, schedule another call to this method when channel switch
+     * can be performed. Otherwise, set the given operating channel and call ConfigureStandard
+     * if the PHY band has changed.
+     *
+     * Note that, in case a Spectrum PHY is used, a spectrum channel covering the
+     * operating channel bandwidth must have been already added when actually setting
+     * the operating channel.
+     *
+     * \param channel the given operating channel
+     */
+    void SetOperatingChannel(const WifiPhyOperatingChannel& channel);
+    /**
      * Configure whether it is prohibited to change PHY band after initialization.
      *
      * \param enable true to prohibit changing PHY band after initialization,
@@ -1034,20 +1054,13 @@
     void NotifyChannelAccessRequested();
 
     /**
-     * \param bandWidth the width (MHz) of the band used for the OFDMA transmission. Must be
-     *                  a multiple of 20 MHz
-     * \param guardBandwidth width of the guard band (MHz)
-     * \param range the subcarrier range of the HE RU
-     * \param bandIndex the index (starting at 0) of the band within the operating channel
-     * \return the converted subcarriers
-     *
-     * This is a helper function to convert HE RU subcarriers, which are relative to the center
-     * frequency subcarrier, to the indexes used by the Spectrum model.
-     */
-    virtual WifiSpectrumBand ConvertHeRuSubcarriers(uint16_t bandWidth,
-                                                    uint16_t guardBandwidth,
-                                                    HeRu::SubcarrierRange range,
-                                                    uint8_t bandIndex = 0) const;
+     * This is a helper function to convert start and stop indices to start and stop frequencies.
+     *
+     * \param indices the start/stop indices to convert
+     * \return the converted frequencies
+     */
+    virtual WifiSpectrumBandFrequencies ConvertIndicesToFrequencies(
+        const WifiSpectrumBandIndices& indices) const = 0;
 
     /**
      * Add the PHY entity to the map of __implemented__ PHY entities for the
@@ -1142,14 +1155,26 @@
     uint8_t GetPrimaryChannelNumber(uint16_t primaryChannelWidth) const;
 
     /**
-     * Get the start band index and the stop band index for a given band
+     * Get the info of a given band
      *
      * \param bandWidth the width of the band to be returned (MHz)
      * \param bandIndex the index of the band to be returned
      *
-     * \return a pair of start and stop indexes that defines the band
+     * \return the info that defines the band
+     */
+    virtual WifiSpectrumBandInfo GetBand(uint16_t bandWidth, uint8_t bandIndex = 0) = 0;
+
+    /**
+     * Get the frequency range of the current RF interface.
+     *
+     * \return the frequency range of the current RF interface
+     */
+    virtual FrequencyRange GetCurrentFrequencyRange() const = 0;
+
+    /**
+     * \return the subcarrier spacing corresponding to the configure standard (Hz)
      */
-    virtual WifiSpectrumBand GetBand(uint16_t bandWidth, uint8_t bandIndex = 0);
+    uint32_t GetSubcarrierSpacing() const;
 
   protected:
     void DoInitialize() override;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-phy-operating-channel.cc ns-3.39/src/wifi/model/wifi-phy-operating-channel.cc
--- ns-3.38/src/wifi/model/wifi-phy-operating-channel.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-phy-operating-channel.cc	2023-07-05 14:23:48.125428400 -0700
@@ -695,4 +695,31 @@
     return indices;
 }
 
+bool
+WifiPhyOperatingChannel::operator==(const WifiPhyOperatingChannel& other) const
+{
+    return m_channelIt == other.m_channelIt;
+}
+
+bool
+WifiPhyOperatingChannel::operator!=(const WifiPhyOperatingChannel& other) const
+{
+    return !(*this == other);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const WifiPhyOperatingChannel& channel)
+{
+    if (channel.IsSet())
+    {
+        os << "channel " << +channel.GetNumber() << " in band " << channel.GetPhyBand()
+           << " frequency " << channel.GetFrequency() << " width " << channel.GetWidth();
+    }
+    else
+    {
+        os << "channel not set";
+    }
+    return os;
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-phy-operating-channel.h ns-3.39/src/wifi/model/wifi-phy-operating-channel.h
--- ns-3.38/src/wifi/model/wifi-phy-operating-channel.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-phy-operating-channel.h	2023-07-05 14:23:48.125428400 -0700
@@ -64,6 +64,27 @@
 
     virtual ~WifiPhyOperatingChannel();
 
+    /**
+     * Check if the given WifiPhyOperatingChannel is equivalent.
+     * Note that the primary20 channels are not compared.
+     *
+     * \param other another WifiPhyOperatingChannel
+     *
+     * \return true if the given WifiPhyOperatingChannel is equivalent,
+     *         false otherwise
+     */
+    bool operator==(const WifiPhyOperatingChannel& other) const;
+
+    /**
+     * Check if the given WifiPhyOperatingChannel is different.
+     *
+     * \param other another WifiPhyOperatingChannel
+     *
+     * \return true if the given WifiPhyOperatingChannel is different,
+     *         false otherwise
+     */
+    bool operator!=(const WifiPhyOperatingChannel& other) const;
+
     static const std::set<FrequencyChannelInfo>
         m_frequencyChannels; //!< Available frequency channels
 
@@ -281,6 +302,15 @@
                                     subchannel with the lowest center frequency) */
 };
 
+/**
+ * \brief Stream insertion operator.
+ *
+ * \param os the stream
+ * \param channel the operating channel
+ * \returns a reference to the stream
+ */
+std::ostream& operator<<(std::ostream& os, const WifiPhyOperatingChannel& channel);
+
 } // namespace ns3
 
 #endif /* WIFI_PHY_OPERATING_CHANNEL_H */
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-ppdu.cc ns-3.39/src/wifi/model/wifi-ppdu.cc
--- ns-3.38/src/wifi/model/wifi-ppdu.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-ppdu.cc	2023-07-05 14:23:48.125428400 -0700
@@ -22,7 +22,7 @@
 #include "wifi-psdu.h"
 
 #include "ns3/log.h"
-#include "ns3/packet.h"
+#include "ns3/wifi-phy-operating-channel.h"
 
 namespace ns3
 {
@@ -31,41 +31,41 @@
 
 WifiPpdu::WifiPpdu(Ptr<const WifiPsdu> psdu,
                    const WifiTxVector& txVector,
-                   uint16_t txCenterFreq,
+                   const WifiPhyOperatingChannel& channel,
                    uint64_t uid /* = UINT64_MAX */)
     : m_preamble(txVector.GetPreambleType()),
       m_modulation(txVector.IsValid() ? txVector.GetModulationClass() : WIFI_MOD_CLASS_UNKNOWN),
-      m_txCenterFreq(txCenterFreq),
+      m_txCenterFreq(channel.IsSet()
+                         ? channel.GetPrimaryChannelCenterFrequency(txVector.GetChannelWidth())
+                         : 0),
       m_uid(uid),
       m_txVector(txVector),
-#ifdef NS3_BUILD_PROFILE_DEBUG
-      m_phyHeaders(Create<Packet>()),
-#endif
+      m_operatingChannel(channel),
       m_truncatedTx(false),
       m_txPowerLevel(txVector.GetTxPowerLevel())
 {
-    NS_LOG_FUNCTION(this << *psdu << txVector << txCenterFreq << uid);
+    NS_LOG_FUNCTION(this << *psdu << txVector << channel << uid);
     m_psdus.insert(std::make_pair(SU_STA_ID, psdu));
 }
 
 WifiPpdu::WifiPpdu(const WifiConstPsduMap& psdus,
                    const WifiTxVector& txVector,
-                   uint16_t txCenterFreq,
+                   const WifiPhyOperatingChannel& channel,
                    uint64_t uid)
     : m_preamble(txVector.GetPreambleType()),
       m_modulation(txVector.IsValid() ? txVector.GetMode(psdus.begin()->first).GetModulationClass()
                                       : WIFI_MOD_CLASS_UNKNOWN),
-      m_txCenterFreq(txCenterFreq),
+      m_txCenterFreq(channel.IsSet()
+                         ? channel.GetPrimaryChannelCenterFrequency(txVector.GetChannelWidth())
+                         : 0),
       m_uid(uid),
       m_txVector(txVector),
-#ifdef NS3_BUILD_PROFILE_DEBUG
-      m_phyHeaders(Create<Packet>()),
-#endif
+      m_operatingChannel(channel),
       m_truncatedTx(false),
       m_txPowerLevel(txVector.GetTxPowerLevel()),
       m_txAntennas(txVector.GetNTx())
 {
-    NS_LOG_FUNCTION(this << psdus << txVector << txCenterFreq << uid);
+    NS_LOG_FUNCTION(this << psdus << txVector << channel << uid);
     m_psdus = psdus;
 }
 
@@ -186,11 +186,7 @@
      *                   â”‚           Channel            â”‚
      *                   â””â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”˜
      */
-    if (minTxFreq >= maxFreq || maxTxFreq <= minFreq)
-    {
-        return false;
-    }
-    return true;
+    return minTxFreq < maxFreq && maxTxFreq > minFreq;
 }
 
 uint64_t
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-ppdu.h ns-3.39/src/wifi/model/wifi-ppdu.h
--- ns-3.38/src/wifi/model/wifi-ppdu.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-ppdu.h	2023-07-05 14:23:48.125428400 -0700
@@ -40,6 +40,7 @@
 
 class Packet;
 class WifiPsdu;
+class WifiPhyOperatingChannel;
 
 /**
  * Map of const PSDUs indexed by STA-ID
@@ -60,24 +61,24 @@
      *
      * \param psdu the PHY payload (PSDU)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param uid the unique ID of this PPDU
      */
     WifiPpdu(Ptr<const WifiPsdu> psdu,
              const WifiTxVector& txVector,
-             uint16_t txCenterFreq,
+             const WifiPhyOperatingChannel& channel,
              uint64_t uid = UINT64_MAX);
     /**
      * Create a PPDU storing a map of PSDUs.
      *
      * \param psdus the PHY payloads (PSDUs)
      * \param txVector the TXVECTOR that was used for this PPDU
-     * \param txCenterFreq the center frequency (MHz) that was used for this PPDU
+     * \param channel the operating channel of the PHY used to transmit this PPDU
      * \param uid the unique ID of this PPDU
      */
     WifiPpdu(const WifiConstPsduMap& psdus,
              const WifiTxVector& txVector,
-             uint16_t txCenterFreq,
+             const WifiPhyOperatingChannel& channel,
              uint64_t uid);
     /**
      * Destructor for WifiPpdu.
@@ -206,10 +207,7 @@
     mutable std::optional<WifiTxVector>
         m_txVector; //!< the TXVECTOR at TX PHY or the reconstructed TXVECTOR at RX PHY (or
                     //!< std::nullopt if TXVECTOR has not been reconstructed yet)
-
-#ifdef NS3_BUILD_PROFILE_DEBUG
-    Ptr<Packet> m_phyHeaders; //!< the PHY headers contained in this PPDU
-#endif
+    const WifiPhyOperatingChannel& m_operatingChannel; //!< the operating channel of the PHY
 
   private:
     /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-protection.cc ns-3.39/src/wifi/model/wifi-protection.cc
--- ns-3.38/src/wifi/model/wifi-protection.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-protection.cc	2023-07-05 14:23:48.125428400 -0700
@@ -51,7 +51,7 @@
 std::unique_ptr<WifiProtection>
 WifiNoProtection::Copy() const
 {
-    return std::unique_ptr<WifiProtection>(new WifiNoProtection(*this));
+    return std::make_unique<WifiNoProtection>(*this);
 }
 
 void
@@ -72,7 +72,7 @@
 std::unique_ptr<WifiProtection>
 WifiRtsCtsProtection::Copy() const
 {
-    return std::unique_ptr<WifiProtection>(new WifiRtsCtsProtection(*this));
+    return std::make_unique<WifiRtsCtsProtection>(*this);
 }
 
 void
@@ -93,7 +93,7 @@
 std::unique_ptr<WifiProtection>
 WifiCtsToSelfProtection::Copy() const
 {
-    return std::unique_ptr<WifiProtection>(new WifiCtsToSelfProtection(*this));
+    return std::make_unique<WifiCtsToSelfProtection>(*this);
 }
 
 void
@@ -114,7 +114,7 @@
 std::unique_ptr<WifiProtection>
 WifiMuRtsCtsProtection::Copy() const
 {
-    return std::unique_ptr<WifiProtection>(new WifiMuRtsCtsProtection(*this));
+    return std::make_unique<WifiMuRtsCtsProtection>(*this);
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-radio-energy-model.h ns-3.39/src/wifi/model/wifi-radio-energy-model.h
--- ns-3.38/src/wifi/model/wifi-radio-energy-model.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-radio-energy-model.h	2023-07-05 14:23:48.125428400 -0700
@@ -170,7 +170,7 @@
     ~WifiRadioEnergyModel() override;
 
     /**
-     * \brief Sets pointer to EnergySouce installed on node.
+     * \brief Sets pointer to EnergySource installed on node.
      *
      * \param source Pointer to EnergySource installed on node.
      *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-remote-station-manager.cc ns-3.39/src/wifi/model/wifi-remote-station-manager.cc
--- ns-3.38/src/wifi/model/wifi-remote-station-manager.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-remote-station-manager.cc	2023-07-05 14:23:48.125428400 -0700
@@ -30,6 +30,7 @@
 #include "ns3/boolean.h"
 #include "ns3/eht-configuration.h"
 #include "ns3/enum.h"
+#include "ns3/erp-ofdm-phy.h"
 #include "ns3/he-configuration.h"
 #include "ns3/ht-configuration.h"
 #include "ns3/ht-phy.h"
@@ -552,6 +553,18 @@
     return staId;
 }
 
+bool
+WifiRemoteStationManager::IsInPsMode(const Mac48Address& address) const
+{
+    return LookupState(address)->m_isInPsMode;
+}
+
+void
+WifiRemoteStationManager::SetPsMode(const Mac48Address& address, bool isInPsMode)
+{
+    LookupState(address)->m_isInPsMode = isInPsMode;
+}
+
 void
 WifiRemoteStationManager::SetMldAddress(const Mac48Address& address, const Mac48Address& mldAddress)
 {
@@ -734,6 +747,39 @@
     return v;
 }
 
+void
+WifiRemoteStationManager::AdjustTxVectorForIcf(WifiTxVector& txVector) const
+{
+    NS_LOG_FUNCTION(this << txVector);
+
+    auto txMode = txVector.GetMode();
+    if (txMode.GetModulationClass() >= WIFI_MOD_CLASS_HT)
+    {
+        auto rate = txMode.GetDataRate(txVector);
+        if (rate >= 24e6)
+        {
+            rate = 24e6;
+        }
+        else if (rate >= 12e6)
+        {
+            rate = 12e6;
+        }
+        else
+        {
+            rate = 6e6;
+        }
+        txVector.SetPreambleType(WIFI_PREAMBLE_LONG);
+        if (m_wifiPhy->GetPhyBand() == WIFI_PHY_BAND_2_4GHZ)
+        {
+            txVector.SetMode(ErpOfdmPhy::GetErpOfdmRate(rate));
+        }
+        else
+        {
+            txVector.SetMode(OfdmPhy::GetOfdmRate(rate));
+        }
+    }
+}
+
 WifiTxVector
 WifiRemoteStationManager::GetAckTxVector(Mac48Address to, const WifiTxVector& dataTxVector) const
 {
@@ -1376,11 +1422,14 @@
     state->m_vhtCapabilities = nullptr;
     state->m_heCapabilities = nullptr;
     state->m_ehtCapabilities = nullptr;
+    state->m_emlCapabilities = nullptr;
+    state->m_emlsrEnabled = false;
     state->m_channelWidth = m_wifiPhy->GetChannelWidth();
     state->m_guardInterval = GetGuardInterval();
     state->m_ness = 0;
     state->m_aggregation = false;
     state->m_qosSupported = false;
+    state->m_isInPsMode = false;
     const_cast<WifiRemoteStationManager*>(this)->m_states.insert({address, state});
     NS_LOG_DEBUG("WifiRemoteStationManager::LookupState returning new state");
     return state;
@@ -1419,6 +1468,13 @@
 }
 
 void
+WifiRemoteStationManager::SetEmlsrEnabled(const Mac48Address& from, bool emlsrEnabled)
+{
+    NS_LOG_FUNCTION(this << from << emlsrEnabled);
+    LookupState(from)->m_emlsrEnabled = emlsrEnabled;
+}
+
+void
 WifiRemoteStationManager::AddStationHtCapabilities(Mac48Address from, HtCapabilities htCapabilities)
 {
     // Used by all stations to record HT capabilities of remote stations
@@ -1543,6 +1599,15 @@
     SetQosSupport(from, true);
 }
 
+void
+WifiRemoteStationManager::AddStationEmlCapabilities(
+    Mac48Address from,
+    const std::shared_ptr<CommonInfoBasicMle::EmlCapabilities>& emlCapabilities)
+{
+    NS_LOG_FUNCTION(this << from);
+    LookupState(from)->m_emlCapabilities = emlCapabilities;
+}
+
 Ptr<const HtCapabilities>
 WifiRemoteStationManager::GetStationHtCapabilities(Mac48Address from)
 {
@@ -1567,6 +1632,12 @@
     return LookupState(from)->m_ehtCapabilities;
 }
 
+std::shared_ptr<CommonInfoBasicMle::EmlCapabilities>
+WifiRemoteStationManager::GetStationEmlCapabilities(const Mac48Address& from)
+{
+    return LookupState(from)->m_emlCapabilities;
+}
+
 bool
 WifiRemoteStationManager::GetLdpcSupported(Mac48Address address) const
 {
@@ -1940,6 +2011,19 @@
     return (bool)(station->m_state->m_ehtCapabilities);
 }
 
+bool
+WifiRemoteStationManager::GetEmlsrSupported(const WifiRemoteStation* station) const
+{
+    auto emlCapabilities = station->m_state->m_emlCapabilities;
+    return emlCapabilities && emlCapabilities->emlsrSupport == 1;
+}
+
+bool
+WifiRemoteStationManager::GetEmlsrEnabled(const WifiRemoteStation* station) const
+{
+    return station->m_state->m_emlsrEnabled;
+}
+
 uint8_t
 WifiRemoteStationManager::GetNMcsSupported(const WifiRemoteStation* station) const
 {
@@ -2041,6 +2125,23 @@
     return (bool)(LookupState(address)->m_ehtCapabilities);
 }
 
+bool
+WifiRemoteStationManager::GetEmlsrSupported(const Mac48Address& address) const
+{
+    auto emlCapabilities = LookupState(address)->m_emlCapabilities;
+    return emlCapabilities && emlCapabilities->emlsrSupport == 1;
+}
+
+bool
+WifiRemoteStationManager::GetEmlsrEnabled(const Mac48Address& address) const
+{
+    if (auto stateIt = m_states.find(address); stateIt != m_states.cend())
+    {
+        return stateIt->second->m_emlsrEnabled;
+    }
+    return false;
+}
+
 void
 WifiRemoteStationManager::SetDefaultTxPowerLevel(uint8_t txPower)
 {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-remote-station-manager.h ns-3.39/src/wifi/model/wifi-remote-station-manager.h
--- ns-3.38/src/wifi/model/wifi-remote-station-manager.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-remote-station-manager.h	2023-07-05 14:23:48.125428400 -0700
@@ -30,6 +30,7 @@
 #include "ns3/he-capabilities.h"
 #include "ns3/ht-capabilities.h"
 #include "ns3/mac48-address.h"
+#include "ns3/multi-link-element.h"
 #include "ns3/object.h"
 #include "ns3/traced-callback.h"
 #include "ns3/vht-capabilities.h"
@@ -113,6 +114,9 @@
     Ptr<const VhtCapabilities> m_vhtCapabilities; //!< remote station VHT capabilities
     Ptr<const HeCapabilities> m_heCapabilities;   //!< remote station HE capabilities
     Ptr<const EhtCapabilities> m_ehtCapabilities; //!< remote station EHT capabilities
+    /// remote station EML capabilities
+    std::shared_ptr<CommonInfoBasicMle::EmlCapabilities> m_emlCapabilities;
+    bool m_emlsrEnabled; //!< whether EMLSR mode is enabled on this link
 
     uint16_t m_channelWidth;  //!< Channel width (in MHz) supported by the remote station
     uint16_t m_guardInterval; //!< HE Guard interval duration (in nanoseconds) supported by the
@@ -122,6 +126,7 @@
     bool m_shortPreamble;     //!< Flag if short PHY preamble is supported by the remote station
     bool m_shortSlotTime;     //!< Flag if short ERP slot time is supported by the remote station
     bool m_qosSupported;      //!< Flag if QoS is supported by the station
+    bool m_isInPsMode;        //!< Flag if the STA is currently in PS mode
 };
 
 /**
@@ -232,6 +237,11 @@
      */
     void SetQosSupport(Mac48Address from, bool qosSupported);
     /**
+     * \param from the address of the station being recorded
+     * \param emlsrEnabled whether EMLSR mode is enabled for the station on this link
+     */
+    void SetEmlsrEnabled(const Mac48Address& from, bool emlsrEnabled);
+    /**
      * Records HT capabilities of the remote station.
      *
      * \param from the address of the station being recorded
@@ -260,6 +270,15 @@
      */
     void AddStationEhtCapabilities(Mac48Address from, EhtCapabilities ehtCapabilities);
     /**
+     * Records EML capabilities of the remote station.
+     *
+     * \param from the address of the station being recorded
+     * \param emlCapabilities the EML capabilities of the station
+     */
+    void AddStationEmlCapabilities(
+        Mac48Address from,
+        const std::shared_ptr<CommonInfoBasicMle::EmlCapabilities>& emlCapabilities);
+    /**
      * Return the HT capabilities sent by the remote station.
      *
      * \param from the address of the remote station
@@ -288,6 +307,12 @@
      */
     Ptr<const EhtCapabilities> GetStationEhtCapabilities(Mac48Address from);
     /**
+     * \param from the (MLD or link) address of the remote non-AP MLD
+     * \return the EML Capabilities advertised by the remote non-AP MLD
+     */
+    std::shared_ptr<CommonInfoBasicMle::EmlCapabilities> GetStationEmlCapabilities(
+        const Mac48Address& from);
+    /**
      * Return whether the device has HT capability support enabled.
      *
      * \return true if HT capability support is enabled, false otherwise
@@ -609,6 +634,16 @@
      *         false otherwise
      */
     bool GetEhtSupported(Mac48Address address) const;
+    /**
+     * \param address the (MLD or link) address of the non-AP MLD
+     * \return whether the non-AP MLD supports EMLSR
+     */
+    bool GetEmlsrSupported(const Mac48Address& address) const;
+    /**
+     * \param address the (MLD or link) address of the non-AP MLD
+     * \return whether EMLSR mode is enabled for the non-AP MLD on this link
+     */
+    bool GetEmlsrEnabled(const Mac48Address& address) const;
 
     /**
      * Return a mode for non-unicast packets.
@@ -734,6 +769,22 @@
     void RecordAssocRefused(Mac48Address address);
 
     /**
+     * Return whether the STA is currently in Power Save mode.
+     *
+     * \param address the address of the station
+     *
+     * \return true if the station is in Power Save mode, false otherwise
+     */
+    bool IsInPsMode(const Mac48Address& address) const;
+    /**
+     * Register whether the STA is in Power Save mode or not.
+     *
+     * \param address the address of the station
+     * \param isInPsMode whether the STA is in PS mode or not
+     */
+    void SetPsMode(const Mac48Address& address, bool isInPsMode);
+
+    /**
      * Set the address of the MLD the given station is affiliated with.
      *
      * \param address the MAC address of the remote station
@@ -792,6 +843,13 @@
      */
     WifiTxVector GetCtsToSelfTxVector();
     /**
+     * Adjust the TXVECTOR for an initial Control frame to ensure that the modulation class
+     * is non-HT and the rate is 6 Mbps, 12 Mbps or 24 Mbps.
+     *
+     * \param txVector the TXVECTOR to adjust
+     */
+    void AdjustTxVectorForIcf(WifiTxVector& txVector) const;
+    /**
      * Return a TXVECTOR for the Ack frame given the destination and the mode of the Data
      * used by the sender.
      *
@@ -1097,6 +1155,16 @@
      */
     bool GetEhtSupported(const WifiRemoteStation* station) const;
     /**
+     * \param station the station of a non-AP MLD
+     * \return whether the non-AP MLD supports EMLSR
+     */
+    bool GetEmlsrSupported(const WifiRemoteStation* station) const;
+    /**
+     * \param station the station of a non-AP MLD
+     * \return whether EMLSR mode is enabled for the non-AP MLD on this link
+     */
+    bool GetEmlsrEnabled(const WifiRemoteStation* station) const;
+    /**
      * Return the WifiMode supported by the specified station at the specified index.
      *
      * \param station the station being queried
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-spectrum-phy-interface.cc ns-3.39/src/wifi/model/wifi-spectrum-phy-interface.cc
--- ns-3.38/src/wifi/model/wifi-spectrum-phy-interface.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-spectrum-phy-interface.cc	2023-07-05 14:23:48.125428400 -0700
@@ -19,11 +19,10 @@
 
 #include "wifi-spectrum-phy-interface.h"
 
-#include "spectrum-wifi-phy.h"
-
 #include "ns3/log.h"
 #include "ns3/mobility-model.h"
 #include "ns3/net-device.h"
+#include "ns3/spectrum-channel.h"
 #include "ns3/spectrum-value.h"
 
 NS_LOG_COMPONENT_DEFINE("WifiSpectrumPhyInterface");
@@ -41,18 +40,26 @@
     return tid;
 }
 
-WifiSpectrumPhyInterface::WifiSpectrumPhyInterface()
+WifiSpectrumPhyInterface::WifiSpectrumPhyInterface(FrequencyRange freqRange)
+    : m_frequencyRange{freqRange},
+      m_centerFrequency{0},
+      m_channelWidth{0},
+      m_bands{},
+      m_ruBands{}
 {
-    NS_LOG_FUNCTION(this);
+    NS_LOG_FUNCTION(this << freqRange);
 }
 
 void
 WifiSpectrumPhyInterface::DoDispose()
 {
     NS_LOG_FUNCTION(this);
+    m_rxSpectrumModel = nullptr;
     m_spectrumWifiPhy = nullptr;
     m_netDevice = nullptr;
     m_channel = nullptr;
+    m_bands.clear();
+    m_ruBands.clear();
 }
 
 void
@@ -61,6 +68,12 @@
     m_spectrumWifiPhy = spectrumWifiPhy;
 }
 
+Ptr<const SpectrumWifiPhy>
+WifiSpectrumPhyInterface::GetSpectrumWifiPhy() const
+{
+    return m_spectrumWifiPhy;
+}
+
 Ptr<NetDevice>
 WifiSpectrumPhyInterface::GetDevice() const
 {
@@ -89,13 +102,35 @@
 WifiSpectrumPhyInterface::SetChannel(const Ptr<SpectrumChannel> c)
 {
     NS_LOG_FUNCTION(this << c);
+    NS_ASSERT_MSG(!m_rxSpectrumModel, "Spectrum channel shall be set before RX spectrum model");
     m_channel = c;
 }
 
+void
+WifiSpectrumPhyInterface::SetRxSpectrumModel(uint32_t centerFrequency,
+                                             uint16_t channelWidth,
+                                             uint32_t bandBandwidth,
+                                             uint16_t guardBandwidth)
+{
+    NS_LOG_FUNCTION(this << centerFrequency << channelWidth << bandBandwidth << guardBandwidth);
+    m_centerFrequency = centerFrequency;
+    m_channelWidth = channelWidth;
+    m_rxSpectrumModel = WifiSpectrumValueHelper::GetSpectrumModel(centerFrequency,
+                                                                  channelWidth,
+                                                                  bandBandwidth,
+                                                                  guardBandwidth);
+}
+
+Ptr<SpectrumChannel>
+WifiSpectrumPhyInterface::GetChannel() const
+{
+    return m_channel;
+}
+
 Ptr<const SpectrumModel>
 WifiSpectrumPhyInterface::GetRxSpectrumModel() const
 {
-    return m_spectrumWifiPhy->GetRxSpectrumModel();
+    return m_rxSpectrumModel;
 }
 
 Ptr<Object>
@@ -104,10 +139,60 @@
     return m_spectrumWifiPhy->GetAntenna();
 }
 
+const FrequencyRange&
+WifiSpectrumPhyInterface::GetFrequencyRange() const
+{
+    return m_frequencyRange;
+}
+
+uint16_t
+WifiSpectrumPhyInterface::GetCenterFrequency() const
+{
+    return m_centerFrequency;
+}
+
+uint16_t
+WifiSpectrumPhyInterface::GetChannelWidth() const
+{
+    return m_channelWidth;
+}
+
+void
+WifiSpectrumPhyInterface::SetBands(WifiSpectrumPhyInterface::WifiSpectrumBands&& bands)
+{
+    m_bands = std::move(bands);
+}
+
+const WifiSpectrumPhyInterface::WifiSpectrumBands&
+WifiSpectrumPhyInterface::GetBands() const
+{
+    return m_bands;
+}
+
+void
+WifiSpectrumPhyInterface::SetRuBands(HePhy::RuBands&& ruBands)
+{
+    m_ruBands = std::move(ruBands);
+}
+
+const HePhy::RuBands&
+WifiSpectrumPhyInterface::GetRuBands() const
+{
+    return m_ruBands;
+}
+
 void
 WifiSpectrumPhyInterface::StartRx(Ptr<SpectrumSignalParameters> params)
 {
-    m_spectrumWifiPhy->StartRx(params);
+    m_spectrumWifiPhy->StartRx(params, this);
+}
+
+void
+WifiSpectrumPhyInterface::StartTx(Ptr<SpectrumSignalParameters> params)
+{
+    params->txPhy = Ptr<SpectrumPhy>(this);
+    params->txAntenna = m_spectrumWifiPhy->GetAntenna();
+    m_channel->StartTx(params);
 }
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-spectrum-phy-interface.h ns-3.39/src/wifi/model/wifi-spectrum-phy-interface.h
--- ns-3.38/src/wifi/model/wifi-spectrum-phy-interface.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-spectrum-phy-interface.h	2023-07-05 14:23:48.125428400 -0700
@@ -20,6 +20,9 @@
 #ifndef WIFI_SPECTRUM_PHY_INTERFACE_H
 #define WIFI_SPECTRUM_PHY_INTERFACE_H
 
+#include "spectrum-wifi-phy.h"
+
+#include "ns3/he-phy.h"
 #include "ns3/spectrum-phy.h"
 
 namespace ns3
@@ -46,13 +49,24 @@
      * \return the object TypeId
      */
     static TypeId GetTypeId();
-    WifiSpectrumPhyInterface();
+    /**
+     * Constructor
+     *
+     * \param freqRange the frequency range covered by the interface
+     */
+    WifiSpectrumPhyInterface(FrequencyRange freqRange);
     /**
      * Connect SpectrumWifiPhy object
      * \param phy SpectrumWifiPhy object to be connected to this object
      */
     void SetSpectrumWifiPhy(const Ptr<SpectrumWifiPhy> phy);
 
+    /**
+     * Get SpectrumWifiPhy object
+     * \return Pointer to SpectrumWifiPhy object
+     */
+    Ptr<const SpectrumWifiPhy> GetSpectrumWifiPhy() const;
+
     Ptr<NetDevice> GetDevice() const override;
     void SetDevice(const Ptr<NetDevice> d) override;
     void SetMobility(const Ptr<MobilityModel> m) override;
@@ -62,12 +76,99 @@
     Ptr<Object> GetAntenna() const override;
     void StartRx(Ptr<SpectrumSignalParameters> params) override;
 
+    /**
+     * Get the spectrum channel this interface is attached to
+     *
+     * \return the spectrum channel this interface is attached to
+     */
+    Ptr<SpectrumChannel> GetChannel() const;
+
+    /**
+     * Get the frequency range covered by the spectrum channel this interface is attached to
+     *
+     * \return the frequency range covered by the spectrum channel this interface is attached to
+     */
+    const FrequencyRange& GetFrequencyRange() const;
+
+    /**
+     * Get the center frequency in MHz of the the spectrum channel this interface is attached to
+     *
+     * \return the center frequency in MHz of the the spectrum channel this interface is attached to
+     * to
+     */
+    uint16_t GetCenterFrequency() const;
+
+    /**
+     * Get the channel width in MHz covered by the spectrum channel this interface is attached to
+     *
+     * \return the channel width in MHz covered by the spectrum channel this interface is attached
+     * to to
+     */
+    uint16_t GetChannelWidth() const;
+
+    /**
+     * Start transmission over the spectrum channel
+     *
+     * \param params the parameters of the signal to transmit
+     */
+    void StartTx(Ptr<SpectrumSignalParameters> params);
+
+    /**
+     * Set the RX spectrum model
+     *
+     * \param centerFrequency the center frequency in MHz
+     * \param channelWidth the channel width in MHz
+     * \param bandBandwidth the width of each band in Hz
+     * \param guardBandwidth the width of the guard band in MHz
+     */
+    void SetRxSpectrumModel(uint32_t centerFrequency,
+                            uint16_t channelWidth,
+                            uint32_t bandBandwidth,
+                            uint16_t guardBandwidth);
+
+    /// vector of spectrum bands handled by this interface
+    using WifiSpectrumBands = std::vector<WifiSpectrumBandInfo>;
+
+    /**
+     * Set the vector of spectrum bands handled by this interface
+     *
+     * \param bands vector of spectrum bands
+     */
+    void SetBands(WifiSpectrumBands&& bands);
+    /**
+     * Get the vector of spectrum bands handled by this interface
+     *
+     * \return the vector of spectrum bands
+     */
+    const WifiSpectrumBands& GetBands() const;
+
+    /**
+     * Set the HE RU spectrum bands handled by this interface (if any)
+     *
+     * \param ruBands the HE RU spectrum bands
+     */
+    void SetRuBands(HePhy::RuBands&& ruBands);
+    /**
+     * Get the HE RU spectrum bands handled by this interface
+     *
+     * \return the HE RU spectrum bands
+     */
+    const HePhy::RuBands& GetRuBands() const;
+
   private:
     void DoDispose() override;
 
-    Ptr<SpectrumWifiPhy> m_spectrumWifiPhy; ///< spectrum PHY
-    Ptr<NetDevice> m_netDevice;             ///< the device
-    Ptr<SpectrumChannel> m_channel;         ///< spectrum channel
+    FrequencyRange m_frequencyRange;            ///< frequency range
+    Ptr<SpectrumWifiPhy> m_spectrumWifiPhy;     ///< spectrum PHY
+    Ptr<NetDevice> m_netDevice;                 ///< the device
+    Ptr<SpectrumChannel> m_channel;             ///< spectrum channel
+    uint16_t m_centerFrequency;                 ///< center frequency in MHz
+    uint16_t m_channelWidth;                    ///< channel width in MHz
+    Ptr<const SpectrumModel> m_rxSpectrumModel; ///< receive spectrum model
+
+    WifiSpectrumBands
+        m_bands; /**< Store all the distinct spectrum bands associated with every channels widths */
+    HePhy::RuBands m_ruBands; /**< Store all the distinct spectrum bands associated with every RU */
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-tx-timer.cc ns-3.39/src/wifi/model/wifi-tx-timer.cc
--- ns-3.38/src/wifi/model/wifi-tx-timer.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-tx-timer.cc	2023-07-05 14:23:48.125428400 -0700
@@ -94,29 +94,27 @@
 std::string
 WifiTxTimer::GetReasonString(Reason reason) const
 {
-#define FOO(x)                                                                                     \
+#define CASE_REASON(x)                                                                             \
     case WAIT_##x:                                                                                 \
-        return #x;                                                                                 \
-        break;
+        return #x;
 
     switch (reason)
     {
     case NOT_RUNNING:
         return "NOT_RUNNING";
-        break;
-        FOO(CTS);
-        FOO(NORMAL_ACK);
-        FOO(BLOCK_ACK);
-        FOO(CTS_AFTER_MU_RTS);
-        FOO(NORMAL_ACK_AFTER_DL_MU_PPDU);
-        FOO(BLOCK_ACKS_IN_TB_PPDU);
-        FOO(TB_PPDU_AFTER_BASIC_TF);
-        FOO(QOS_NULL_AFTER_BSRP_TF);
-        FOO(BLOCK_ACK_AFTER_TB_PPDU);
+        CASE_REASON(CTS);
+        CASE_REASON(NORMAL_ACK);
+        CASE_REASON(BLOCK_ACK);
+        CASE_REASON(CTS_AFTER_MU_RTS);
+        CASE_REASON(NORMAL_ACK_AFTER_DL_MU_PPDU);
+        CASE_REASON(BLOCK_ACKS_IN_TB_PPDU);
+        CASE_REASON(TB_PPDU_AFTER_BASIC_TF);
+        CASE_REASON(QOS_NULL_AFTER_BSRP_TF);
+        CASE_REASON(BLOCK_ACK_AFTER_TB_PPDU);
     default:
         NS_ABORT_MSG("Unknown reason");
     }
-#undef FOO
+#undef CASE_REASON
 }
 
 bool
@@ -136,7 +134,7 @@
 Time
 WifiTxTimer::GetDelayLeft() const
 {
-    return Simulator::GetDelayLeft(m_timeoutEvent);
+    return m_end - Simulator::Now();
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-tx-vector.cc ns-3.39/src/wifi/model/wifi-tx-vector.cc
--- ns-3.38/src/wifi/model/wifi-tx-vector.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-tx-vector.cc	2023-07-05 14:23:48.125428400 -0700
@@ -48,6 +48,7 @@
       m_modeInitialized(false),
       m_inactiveSubchannels(),
       m_ruAllocation(),
+      m_center26ToneRuIndication(std::nullopt),
       m_ehtPpduType(1) // SU transmission by default
 {
 }
@@ -83,6 +84,7 @@
       m_modeInitialized(true),
       m_inactiveSubchannels(),
       m_ruAllocation(),
+      m_center26ToneRuIndication(std::nullopt),
       m_ehtPpduType(1) // SU transmission by default
 {
 }
@@ -106,6 +108,7 @@
       m_inactiveSubchannels(txVector.m_inactiveSubchannels),
       m_sigBMcs(txVector.m_sigBMcs),
       m_ruAllocation(txVector.m_ruAllocation),
+      m_center26ToneRuIndication(txVector.m_center26ToneRuIndication),
       m_ehtPpduType(txVector.m_ehtPpduType)
 {
     m_muUserInfos.clear();
@@ -393,21 +396,21 @@
 }
 
 void
-WifiTxVector::SetRuAllocation(const RuAllocation& ruAlloc)
+WifiTxVector::SetRuAllocation(const RuAllocation& ruAlloc, uint8_t p20Index)
 {
     if (ns3::IsDlMu(m_preamble) && !m_muUserInfos.empty())
     {
-        NS_ASSERT(ruAlloc == DeriveRuAllocation());
+        NS_ASSERT(ruAlloc == DeriveRuAllocation(p20Index));
     }
     m_ruAllocation = ruAlloc;
 }
 
 const RuAllocation&
-WifiTxVector::GetRuAllocation() const
+WifiTxVector::GetRuAllocation(uint8_t p20Index) const
 {
     if (ns3::IsDlMu(m_preamble) && m_ruAllocation.empty())
     {
-        m_ruAllocation = DeriveRuAllocation();
+        m_ruAllocation = DeriveRuAllocation(p20Index);
     }
     return m_ruAllocation;
 }
@@ -479,6 +482,12 @@
     return ns3::IsUlMu(m_preamble);
 }
 
+bool
+WifiTxVector::IsAllocated(uint16_t staId) const
+{
+    return m_muUserInfos.count(staId) > 0;
+}
+
 HeRu::RuSpec
 WifiTxVector::GetRu(uint16_t staId) const
 {
@@ -527,54 +536,6 @@
     return m_muUserInfos;
 }
 
-std::pair<std::size_t, std::size_t>
-WifiTxVector::GetNumRusPerHeSigBContentChannel() const
-{
-    if (m_preamble == WIFI_PREAMBLE_EHT_MU && m_ehtPpduType == 1)
-    {
-        return {1, 0};
-    }
-
-    // MU-MIMO is not handled for now, i.e. one station per RU
-    auto ruAllocation = GetRuAllocation();
-    NS_ASSERT_MSG(!ruAllocation.empty(), "RU allocation is not set");
-    if (ruAllocation.size() != m_channelWidth / 20)
-    {
-        ruAllocation = DeriveRuAllocation();
-    }
-    NS_ASSERT_MSG(ruAllocation.size() == m_channelWidth / 20,
-                  "RU allocation is not consistent with packet bandwidth");
-
-    std::pair<std::size_t /* number of RUs in content channel 1 */,
-              std::size_t /* number of RUs in content channel 2 */>
-        chSize{0, 0};
-
-    switch (GetChannelWidth())
-    {
-    case 40:
-        chSize.second += HeRu::GetRuSpecs(ruAllocation[1]).size();
-        [[fallthrough]];
-    case 20:
-        chSize.first += HeRu::GetRuSpecs(ruAllocation[0]).size();
-        break;
-    default:
-        for (auto n = 0; n < m_channelWidth / 20;)
-        {
-            chSize.first += HeRu::GetRuSpecs(ruAllocation[n]).size();
-            chSize.second += HeRu::GetRuSpecs(ruAllocation[n + 1]).size();
-            if (ruAllocation[n] >= 208)
-            {
-                // 996 tone RU occupies 80 MHz
-                n += 4;
-                continue;
-            }
-            n += 2;
-        }
-        break;
-    }
-    return chSize;
-}
-
 void
 WifiTxVector::SetInactiveSubchannels(const std::vector<bool>& inactiveSubchannels)
 {
@@ -596,6 +557,30 @@
     return m_inactiveSubchannels;
 }
 
+void
+WifiTxVector::SetCenter26ToneRuIndication(Center26ToneRuIndication center26ToneRuIndication)
+{
+    if (IsDlMu())
+    {
+        NS_ASSERT(center26ToneRuIndication == DeriveCenter26ToneRuIndication());
+    }
+    m_center26ToneRuIndication = center26ToneRuIndication;
+}
+
+std::optional<Center26ToneRuIndication>
+WifiTxVector::GetCenter26ToneRuIndication() const
+{
+    if (!IsDlMu() || (m_channelWidth < 80))
+    {
+        return std::nullopt;
+    }
+    if (!m_center26ToneRuIndication.has_value())
+    {
+        m_center26ToneRuIndication.emplace(DeriveCenter26ToneRuIndication());
+    }
+    return m_center26ToneRuIndication;
+}
+
 std::ostream&
 operator<<(std::ostream& os, const WifiTxVector& v)
 {
@@ -662,63 +647,33 @@
     return !(*this == other);
 }
 
-ContentChannelAllocation
-WifiTxVector::GetContentChannelAllocation() const
+WifiTxVector::UserInfoMapOrderedByRus
+WifiTxVector::GetUserInfoMapOrderedByRus(uint8_t p20Index) const
 {
-    ContentChannelAllocation channelAlloc{{}};
-
-    if (m_channelWidth > 20)
-    {
-        channelAlloc.emplace_back();
-    }
-
-    for (const auto& [staId, userInfo] : m_muUserInfos)
-    {
-        auto ruType = userInfo.ru.GetRuType();
-        auto ruIdx = userInfo.ru.GetIndex();
-
-        if ((ruType == HeRu::RU_484_TONE) || (ruType == HeRu::RU_996_TONE))
-        {
-            channelAlloc[0].push_back(staId);
-            channelAlloc[1].push_back(staId);
-            continue;
-        }
-
-        size_t numRus{1};
-        if (ruType < HeRu::RU_242_TONE)
-        {
-            numRus = HeRu::m_heRuSubcarrierGroups.at({20, ruType}).size();
-        }
-
-        if (((ruIdx - 1) / numRus) % 2 == 0)
-        {
-            channelAlloc[0].push_back(staId);
-        }
-        else
-        {
-            channelAlloc[1].push_back(staId);
-        }
-    }
-
-    return channelAlloc;
+    auto heRuComparator = HeRu::RuSpecCompare(m_channelWidth, p20Index);
+    UserInfoMapOrderedByRus orderedMap{heRuComparator};
+    std::transform(
+        m_muUserInfos.cbegin(),
+        m_muUserInfos.cend(),
+        std::inserter(orderedMap, orderedMap.end()),
+        [](auto&& userInfo) { return std::make_pair(userInfo.second.ru, userInfo.first); });
+    return orderedMap;
 }
 
 RuAllocation
-WifiTxVector::DeriveRuAllocation() const
+WifiTxVector::DeriveRuAllocation(uint8_t p20Index) const
 {
-    std::all_of(m_muUserInfos.cbegin(), m_muUserInfos.cend(), [&](const auto& userInfo) {
-        return userInfo.second.ru.GetRuType() == m_muUserInfos.cbegin()->second.ru.GetRuType();
-    });
     RuAllocation ruAllocations(m_channelWidth / 20, HeRu::EMPTY_242_TONE_RU);
     std::vector<HeRu::RuType> ruTypes{};
     ruTypes.resize(ruAllocations.size());
-    for (auto it = m_muUserInfos.begin(); it != m_muUserInfos.end(); ++it)
+    const auto& orderedMap = GetUserInfoMapOrderedByRus(p20Index);
+    for (const auto& [ru, staId] : orderedMap)
     {
-        const auto ruType = it->second.ru.GetRuType();
+        const auto ruType = ru.GetRuType();
         const auto ruBw = HeRu::GetBandwidth(ruType);
-        const auto isPrimary80MHz = it->second.ru.GetPrimary80MHz();
+        const auto isPrimary80MHz = ru.GetPrimary80MHz();
         const auto rusPerSubchannel = HeRu::GetRusOfType(ruBw > 20 ? ruBw : 20, ruType);
-        auto ruIndex = it->second.ru.GetIndex();
+        auto ruIndex = ru.GetIndex();
         if ((m_channelWidth >= 80) && (ruIndex > 19))
         {
             // take into account the center 26-tone RU in the primary 80 MHz
@@ -770,4 +725,21 @@
     return ruAllocations;
 }
 
+Center26ToneRuIndication
+WifiTxVector::DeriveCenter26ToneRuIndication() const
+{
+    uint8_t center26ToneRuIndication{0};
+    for (const auto& userInfo : m_muUserInfos)
+    {
+        if ((userInfo.second.ru.GetRuType() == HeRu::RU_26_TONE) &&
+            (userInfo.second.ru.GetIndex() == 19))
+        {
+            center26ToneRuIndication |= (userInfo.second.ru.GetPrimary80MHz())
+                                            ? CENTER_26_TONE_RU_LOW_80_MHZ_ALLOCATED
+                                            : CENTER_26_TONE_RU_HIGH_80_MHZ_ALLOCATED;
+        }
+    }
+    return static_cast<Center26ToneRuIndication>(center26ToneRuIndication);
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/wifi-tx-vector.h ns-3.39/src/wifi/model/wifi-tx-vector.h
--- ns-3.38/src/wifi/model/wifi-tx-vector.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/wifi-tx-vector.h	2023-07-05 14:23:48.125428400 -0700
@@ -27,11 +27,15 @@
 #include "ns3/he-ru.h"
 
 #include <list>
+#include <optional>
 #include <vector>
 
 namespace ns3
 {
 
+/// STA_ID for a RU that is intended for no user (Section 26.11.1 802.11ax-2021)
+static constexpr uint16_t NO_USER_STA_ID = 2046;
+
 /// HE MU specific user transmission parameters.
 struct HeMuUserInfo
 {
@@ -55,19 +59,23 @@
     bool operator!=(const HeMuUserInfo& other) const;
 };
 
-/// A vector of subcarrier group
-using SubcarrierGroups = std::vector<HeRu::SubcarrierGroup>;
-
-/// Maximum number of HE-SIG-B content channels
-constexpr size_t WIFI_MAX_NUM_HE_SIGB_CONTENT_CHANNELS = 2;
-
-/// HE SIG-B Content Channels STA ID Allocation
-using ContentChannelAllocation = std::vector<std::vector<uint16_t>>;
-
 /// 8 bit RU_ALLOCATION per 20 MHz
 using RuAllocation = std::vector<uint8_t>;
 
 /**
+ * \ingroup wifi
+ * Enum for the different values for CENTER_26_TONE_RU
+ */
+enum Center26ToneRuIndication : uint8_t
+{
+    CENTER_26_TONE_RU_UNALLOCATED = 0,
+    CENTER_26_TONE_RU_LOW_80_MHZ_ALLOCATED,          /* also used if BW == 80 MHz */
+    CENTER_26_TONE_RU_HIGH_80_MHZ_ALLOCATED,         /* unused if BW < 160 MHz */
+    CENTER_26_TONE_RU_LOW_AND_HIGH_80_MHZ_ALLOCATED, /* unused if BW < 160 MHz */
+    CENTER_26_TONE_RU_INDICATION_MAX                 /* last value */
+};
+
+/**
  * This class mimics the TXVECTOR which is to be
  * passed to the PHY in order to define the parameters which are to be
  * used for a transmission. See IEEE 802.11-2016 16.2.5 "Transmit PHY",
@@ -363,6 +371,12 @@
      */
     bool IsUlMu() const;
     /**
+     * Check if STA ID is allocated
+     * \param staId STA ID
+     * \return true if allocated, false otherwise
+     */
+    bool IsAllocated(uint16_t staId) const;
+    /**
      * Get the RU specification for the STA-ID.
      * This is applicable only for MU.
      *
@@ -408,15 +422,17 @@
      * \return a reference to the map of HE MU user-specific information indexed by STA-ID
      */
     HeMuUserInfoMap& GetHeMuUserInfoMap();
+
+    /// map of specific user info parameters ordered per increasing frequency RUs
+    using UserInfoMapOrderedByRus = std::map<HeRu::RuSpec, uint16_t, HeRu::RuSpecCompare>;
+
     /**
-     * Get the number of RUs per HE-SIG-B content channel.
-     * This is applicable only for MU. MU-MIMO (i.e. multiple stations
-     * per RU) is not supported yet.
-     * See section 27.3.10.8.3 of IEEE 802.11ax draft 4.0.
+     * Get the map of specific user info parameters ordered per increasing frequency RUs.
      *
-     * \return a pair containing the number of RUs in each HE-SIG-B content channel (resp. 1 and 2)
+     * \param p20Index the index of the primary20 channel
+     * \return the map of specific user info parameters ordered per increasing frequency RUs
      */
-    std::pair<std::size_t, std::size_t> GetNumRusPerHeSigBContentChannel() const;
+    UserInfoMapOrderedByRus GetUserInfoMapOrderedByRus(uint8_t p20Index) const;
 
     /**
      * Set the 20 MHz subchannels that are punctured.
@@ -448,23 +464,32 @@
     WifiMode GetSigBMode() const;
 
     /**
-     * Set RU Allocation of SIG-B common field
+     * Set RU_ALLOCATION field
      * \param ruAlloc 8 bit RU_ALLOCATION per 20 MHz
+     * \param p20Index the index of the primary20 channel
      */
-    void SetRuAllocation(const RuAllocation& ruAlloc);
+    void SetRuAllocation(const RuAllocation& ruAlloc, uint8_t p20Index);
 
     /**
-     * Get RU Allocation of SIG-B
+     * Get RU_ALLOCATION field
      * \return 8 bit RU_ALLOCATION per 20 MHz
+     * \param p20Index the index of the primary20 channel
+     */
+    const RuAllocation& GetRuAllocation(uint8_t p20Index) const;
+
+    /**
+     * Set CENTER_26_TONE_RU field
+     * \param center26ToneRuIndication the CENTER_26_TONE_RU field
      */
-    const RuAllocation& GetRuAllocation() const;
+    void SetCenter26ToneRuIndication(Center26ToneRuIndication center26ToneRuIndication);
 
     /**
-     * Get the HE SIG-B content channel STA ID allocation
-     * IEEE 802.11ax-2021 27.3.11.8.2 HE-SIG-B content channels
-     * \return content channel allocation
+     * Get CENTER_26_TONE_RU field
+     * This field is present if format is HE_MU and
+     * when channel width is set to 80 MHz or larger.
+     * \return the CENTER_26_TONE_RU field if present
      */
-    ContentChannelAllocation GetContentChannelAllocation() const;
+    std::optional<Center26ToneRuIndication> GetCenter26ToneRuIndication() const;
 
     /**
      * Set the EHT_PPDU_TYPE parameter
@@ -479,12 +504,24 @@
 
   private:
     /**
-     * Derive the RU allocation from the TXVECTOR for which its RU allocation has not been set yet.
-     * This is valid only for allocations of RUs of the same size.
+     * Derive the RU_ALLOCATION field from the TXVECTOR
+     * for which its RU_ALLOCATION field has not been set yet,
+     * based on the content of per-user information.
+     * This is valid only for allocations of RUs of the same size per 20 MHz subchannel.
      *
-     * \return the RU allocation
+     * \param p20Index the index of the primary20 channel
+     * \return 8 bit RU_ALLOCATION per 20 MHz
      */
-    RuAllocation DeriveRuAllocation() const;
+    RuAllocation DeriveRuAllocation(uint8_t p20Index) const;
+
+    /**
+     * Derive the CENTER_26_TONE_RU field from the TXVECTOR
+     * for which its CENTER_26_TONE_RU has not been set yet,
+     * based on the content of per-user information.
+     *
+     * \return the CENTER_26_TONE_RU field
+     */
+    Center26ToneRuIndication DeriveCenter26ToneRuIndication() const;
 
     WifiMode m_mode;          /**< The DATARATE parameter in Table 15-4.
                               It is the value that will be passed
@@ -519,7 +556,13 @@
 
     mutable RuAllocation m_ruAllocation; /**< RU allocations that are going to be carried
                                               in SIG-B common field per Table 27-1 IEEE */
-    uint8_t m_ehtPpduType;               /**< EHT_PPDU_TYPE per Table 36-1 IEEE 802.11be D2.3 */
+
+    mutable std::optional<Center26ToneRuIndication>
+        m_center26ToneRuIndication; /**< CENTER_26_TONE_RU field when format is HE_MU and
+                                         when channel width is set to 80 MHz or larger  (Table 27-1
+                                       802.11ax-2021)*/
+
+    uint8_t m_ehtPpduType; /**< EHT_PPDU_TYPE per Table 36-1 IEEE 802.11be D2.3 */
 };
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/yans-error-rate-model.cc ns-3.39/src/wifi/model/yans-error-rate-model.cc
--- ns-3.38/src/wifi/model/yans-error-rate-model.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/yans-error-rate-model.cc	2023-07-05 14:23:48.125428400 -0700
@@ -206,7 +206,7 @@
     if (mode.GetModulationClass() >= WIFI_MOD_CLASS_ERP_OFDM)
     {
         uint64_t phyRate;
-        if ((txVector.IsMu() && (staId == SU_STA_ID)) || (mode != txVector.GetMode()))
+        if ((txVector.IsMu() && (staId == SU_STA_ID)) || (mode != txVector.GetMode(staId)))
         {
             phyRate = mode.GetPhyRate(txVector.GetChannelWidth() >= 40
                                           ? 20
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/yans-wifi-channel.cc ns-3.39/src/wifi/model/yans-wifi-channel.cc
--- ns-3.38/src/wifi/model/yans-wifi-channel.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/yans-wifi-channel.cc	2023-07-05 14:23:48.125428400 -0700
@@ -145,7 +145,7 @@
     }
     RxPowerWattPerChannelBand rxPowerW;
     rxPowerW.insert(
-        {std::make_pair(0, 0), (DbmToW(rxPowerDbm + phy->GetRxGain()))}); // dummy band for YANS
+        {{{0, 0}, {0, 0}}, (DbmToW(rxPowerDbm + phy->GetRxGain()))}); // dummy band for YANS
     phy->StartReceivePreamble(ppdu, rxPowerW, ppdu->GetTxDuration());
 }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/yans-wifi-phy.cc ns-3.39/src/wifi/model/yans-wifi-phy.cc
--- ns-3.38/src/wifi/model/yans-wifi-phy.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/yans-wifi-phy.cc	2023-07-05 14:23:48.125428400 -0700
@@ -53,10 +53,7 @@
 {
     WifiPhy::SetInterferenceHelper(helper);
     // add dummy band for Yans
-    WifiSpectrumBand band;
-    band.first = 0;
-    band.second = 0;
-    m_interference->AddBand(band);
+    m_interference->AddBand({{0, 0}, {0, 0}});
 }
 
 YansWifiPhy::~YansWifiPhy()
@@ -109,4 +106,22 @@
     return std::make_tuple(0.0, 0.0, 0.0);
 }
 
+WifiSpectrumBandInfo
+YansWifiPhy::GetBand(uint16_t /*bandWidth*/, uint8_t /*bandIndex*/)
+{
+    return {{0, 0}, {0, 0}};
+}
+
+FrequencyRange
+YansWifiPhy::GetCurrentFrequencyRange() const
+{
+    return WHOLE_WIFI_SPECTRUM;
+}
+
+WifiSpectrumBandFrequencies
+YansWifiPhy::ConvertIndicesToFrequencies(const WifiSpectrumBandIndices& /*indices*/) const
+{
+    return {0, 0};
+}
+
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/model/yans-wifi-phy.h ns-3.39/src/wifi/model/yans-wifi-phy.h
--- ns-3.38/src/wifi/model/yans-wifi-phy.h	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/model/yans-wifi-phy.h	2023-07-05 14:23:48.125428400 -0700
@@ -61,6 +61,10 @@
     Ptr<Channel> GetChannel() const override;
     uint16_t GetGuardBandwidth(uint16_t currentChannelWidth) const override;
     std::tuple<double, double, double> GetTxMaskRejectionParams() const override;
+    WifiSpectrumBandInfo GetBand(uint16_t bandWidth, uint8_t bandIndex = 0) override;
+    FrequencyRange GetCurrentFrequencyRange() const override;
+    WifiSpectrumBandFrequencies ConvertIndicesToFrequencies(
+        const WifiSpectrumBandIndices& indices) const override;
 
     /**
      * Set the YansWifiChannel this YansWifiPhy is to be connected to.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/80211b.c ns-3.39/src/wifi/test/80211b.c
--- ns-3.38/src/wifi/test/80211b.c	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/test/80211b.c	2023-07-05 14:23:48.125428400 -0700
@@ -1,4 +1,3 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
  * Copyright (c) 2010 The Boeing Company
  *
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/channel-access-manager-test.cc ns-3.39/src/wifi/test/channel-access-manager-test.cc
--- ns-3.38/src/wifi/test/channel-access-manager-test.cc	2023-03-16 20:54:23.868869300 -0700
+++ ns-3.39/src/wifi/test/channel-access-manager-test.cc	2023-07-05 14:23:48.125428400 -0700
@@ -20,6 +20,8 @@
 #include "ns3/adhoc-wifi-mac.h"
 #include "ns3/channel-access-manager.h"
 #include "ns3/frame-exchange-manager.h"
+#include "ns3/interference-helper.h"
+#include "ns3/multi-model-spectrum-channel.h"
 #include "ns3/qos-txop.h"
 #include "ns3/simulator.h"
 #include "ns3/spectrum-wifi-phy.h"
@@ -84,7 +86,8 @@
         uint64_t at;     //!< at
         uint32_t nSlots; //!< number of slots
     };
-    typedef std::list<struct ExpectedBackoff> ExpectedBackoffs; //!< expected backoffs typedef
+
+    typedef std::list<ExpectedBackoff> ExpectedBackoffs; //!< expected backoffs typedef
 
     ExpectedBackoffs m_expectedInternalCollision; //!< expected backoff due to an internal collision
     ExpectedBackoffs m_expectedBackoff; //!< expected backoff (not due to an internal collision)
@@ -409,7 +412,7 @@
 
     Ptr<FrameExchangeManagerStub<TxopType>> m_feManager;  //!< the Frame Exchange Manager stubbed
     Ptr<ChannelAccessManagerStub> m_ChannelAccessManager; //!< the channel access manager
-    Ptr<WifiPhy> m_phy;                                   //!< the PHY object
+    Ptr<SpectrumWifiPhy> m_phy;                           //!< the PHY object
     TxopTests m_txop;                                     //!< the vector of Txop test instances
     uint32_t m_ackTimeoutValue;                           //!< the Ack timeout value
 };
@@ -620,6 +623,8 @@
     // SetupPhyListener(), requires an attached PHY to determine the channel types
     // to initialize
     m_phy = CreateObject<SpectrumWifiPhy>();
+    m_phy->SetInterferenceHelper(CreateObject<InterferenceHelper>());
+    m_phy->AddChannel(CreateObject<MultiModelSpectrumChannel>());
     m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{0, chWidth, WIFI_PHY_BAND_UNSPECIFIED, 0});
     m_phy->ConfigureStandard(WIFI_STANDARD_80211ac); // required to use 160 MHz channels
     m_ChannelAccessManager->SetupPhyListener(m_phy);
@@ -1342,7 +1347,7 @@
     void RunOne(uint16_t chWidth, WifiChannelListType busyChannel);
 
     Ptr<ChannelAccessManager> m_cam; //!< channel access manager
-    Ptr<WifiPhy> m_phy;              //!< PHY object
+    Ptr<SpectrumWifiPhy> m_phy;      //!< PHY object
 };
 
 LargestIdlePrimaryChannelTest::LargestIdlePrimaryChannelTest()
@@ -1436,9 +1441,9 @@
 
     // 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.
-    Time checkTime4 = checkTime3;
+    const Time& checkTime4 = checkTime3;
     Simulator::Schedule(checkTime4 - start, [=]() {
-        Time interval4 = ccaBusyRxInterval;
+        const Time& interval4 = ccaBusyRxInterval;
         Time end4 = checkTime4 - rxDuration;
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval4, end4),
                               chWidth,
@@ -1461,7 +1466,7 @@
     });
 
     // After RX end, no channel is idle if the interval overlaps the RX period
-    Time checkTime6 = checkTime5;
+    const Time& checkTime6 = checkTime5;
     Simulator::Schedule(checkTime6 - start, [=]() {
         Time interval6 = interval5 + rxDuration / 2;
         NS_TEST_EXPECT_MSG_EQ(m_cam->GetLargestIdlePrimaryChannel(interval6, checkTime6),
@@ -1495,6 +1500,8 @@
                 }
                 // create a new PHY operating on a channel of the current width
                 m_phy = CreateObject<SpectrumWifiPhy>();
+                m_phy->SetInterferenceHelper(CreateObject<InterferenceHelper>());
+                m_phy->AddChannel(CreateObject<MultiModelSpectrumChannel>());
                 m_phy->SetOperatingChannel(
                     WifiPhy::ChannelTuple{0, chWidth, WIFI_PHY_BAND_5GHZ, 0});
                 m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/examples-to-run.py ns-3.39/src/wifi/test/examples-to-run.py
--- ns-3.38/src/wifi/test/examples-to-run.py	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/examples-to-run.py	2023-07-05 14:23:48.125428400 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/inter-bss-test-suite.cc ns-3.39/src/wifi/test/inter-bss-test-suite.cc
--- ns-3.38/src/wifi/test/inter-bss-test-suite.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/inter-bss-test-suite.cc	2023-07-05 14:23:48.125428400 -0700
@@ -77,7 +77,12 @@
 class TestInterBssConstantObssPdAlgo : public TestCase
 {
   public:
-    TestInterBssConstantObssPdAlgo();
+    /**
+     * Constructor
+     *
+     * \param standard The standard to use for the test
+     */
+    TestInterBssConstantObssPdAlgo(WifiStandard standard);
     ~TestInterBssConstantObssPdAlgo() override;
 
     void DoRun() override;
@@ -205,9 +210,11 @@
     uint8_t m_bssColor1; ///< color for BSS 1
     uint8_t m_bssColor2; ///< color for BSS 2
     uint8_t m_bssColor3; ///< color for BSS 3
+
+    WifiStandard m_standard; ///< the standard to use for the test
 };
 
-TestInterBssConstantObssPdAlgo::TestInterBssConstantObssPdAlgo()
+TestInterBssConstantObssPdAlgo::TestInterBssConstantObssPdAlgo(WifiStandard standard)
     : TestCase("InterBssConstantObssPd"),
       m_numSta1PacketsSent(0),
       m_numSta2PacketsSent(0),
@@ -226,7 +233,8 @@
       m_expectedTxPowerDbm(15),
       m_bssColor1(1),
       m_bssColor2(2),
-      m_bssColor3(3)
+      m_bssColor3(3),
+      m_standard(standard)
 {
 }
 
@@ -850,7 +858,7 @@
     phy.Set("ChannelSettings", StringValue("{36, 20, BAND_5GHZ, 0}"));
 
     WifiHelper wifi;
-    wifi.SetStandard(WIFI_STANDARD_80211ax);
+    wifi.SetStandard(m_standard);
     wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
                                  "DataMode",
                                  StringValue("HeMcs5"),
@@ -991,7 +999,8 @@
 InterBssTestSuite::InterBssTestSuite()
     : TestSuite("wifi-inter-bss", UNIT)
 {
-    AddTestCase(new TestInterBssConstantObssPdAlgo, TestCase::QUICK);
+    AddTestCase(new TestInterBssConstantObssPdAlgo(WIFI_STANDARD_80211ax), TestCase::QUICK);
+    AddTestCase(new TestInterBssConstantObssPdAlgo(WIFI_STANDARD_80211be), TestCase::QUICK);
 }
 
 // Do not forget to allocate an instance of this TestSuite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/spectrum-wifi-phy-test.cc ns-3.39/src/wifi/test/spectrum-wifi-phy-test.cc
--- ns-3.38/src/wifi/test/spectrum-wifi-phy-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/spectrum-wifi-phy-test.cc	2023-07-05 14:23:48.125428400 -0700
@@ -15,6 +15,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "ns3/boolean.h"
 #include "ns3/constant-position-mobility-model.h"
 #include "ns3/he-phy.h" //includes OFDM PHY
 #include "ns3/interference-helper.h"
@@ -22,6 +23,7 @@
 #include "ns3/multi-model-spectrum-channel.h"
 #include "ns3/nist-error-rate-model.h"
 #include "ns3/ofdm-ppdu.h"
+#include "ns3/pointer.h"
 #include "ns3/spectrum-wifi-helper.h"
 #include "ns3/spectrum-wifi-phy.h"
 #include "ns3/test.h"
@@ -29,25 +31,80 @@
 #include "ns3/wifi-net-device.h"
 #include "ns3/wifi-phy-listener.h"
 #include "ns3/wifi-psdu.h"
+#include "ns3/wifi-spectrum-phy-interface.h"
 #include "ns3/wifi-spectrum-signal-parameters.h"
 #include "ns3/wifi-spectrum-value-helper.h"
 #include "ns3/wifi-utils.h"
 
+#include <memory>
+#include <tuple>
+#include <vector>
+
 using namespace ns3;
 
-NS_LOG_COMPONENT_DEFINE("SpectrumWifiPhyBasicTest");
+NS_LOG_COMPONENT_DEFINE("SpectrumWifiPhyTest");
 
 static const uint8_t CHANNEL_NUMBER = 36;
-static const uint32_t FREQUENCY = 5180;   // MHz
 static const uint16_t CHANNEL_WIDTH = 20; // MHz
 static const uint16_t GUARD_WIDTH =
     CHANNEL_WIDTH; // MHz (expanded to channel width to model spectrum mask)
 
+/**
+ * Extended SpectrumWifiPhy class for the purpose of the tests.
+ */
 class ExtSpectrumWifiPhy : public SpectrumWifiPhy
 {
   public:
-    using SpectrumWifiPhy::GetBand;
     using SpectrumWifiPhy::SpectrumWifiPhy;
+    using WifiPhy::GetBand;
+
+    /**
+     * Get the spectrum PHY interfaces
+     *
+     * \return the spectrum PHY interfaces
+     */
+    const std::map<FrequencyRange, Ptr<WifiSpectrumPhyInterface>>& GetSpectrumPhyInterfaces() const
+    {
+        return m_spectrumPhyInterfaces;
+    }
+
+    /**
+     * \return the current spectrum PHY interface
+     */
+    Ptr<WifiSpectrumPhyInterface> GetCurrentSpectrumPhyInterface() const
+    {
+        return m_currentSpectrumPhyInterface;
+    }
+
+    /**
+     * Get the info of a given band for a given spectrum PHY interface
+     *
+     * \param bandWidth the width of the band to be returned (MHz)
+     * \param bandIndex the index of the band to be returned
+     * \param freqRange the frequency range identifying the spectrum PHY interface
+     * \param channelWidth the channel width currently used by the spectrum PHY interface
+     *
+     * \return the info that defines the band
+     */
+    WifiSpectrumBandInfo GetBandForInterface(uint16_t bandWidth,
+                                             uint8_t bandIndex,
+                                             const FrequencyRange& freqRange,
+                                             uint16_t channelWidth)
+    {
+        auto subcarrierSpacing = GetSubcarrierSpacing();
+        auto numBandsInChannel = static_cast<size_t>(channelWidth * 1e6 / subcarrierSpacing);
+        auto numBandsInBand = static_cast<size_t>(bandWidth * 1e6 / subcarrierSpacing);
+        auto rxSpectrumModel = m_spectrumPhyInterfaces.at(freqRange)->GetRxSpectrumModel();
+        size_t totalNumBands = rxSpectrumModel->GetNumBands();
+        auto startIndex = ((totalNumBands - numBandsInChannel) / 2) + (bandIndex * numBandsInBand);
+        auto stopIndex = startIndex + numBandsInBand - 1;
+        auto startGuardBand = rxSpectrumModel->Begin();
+        auto startChannel = startGuardBand + startIndex;
+        auto endChannel = startGuardBand + stopIndex + 1;
+        auto lowFreq = static_cast<uint64_t>(startChannel->fc);
+        auto highFreq = static_cast<uint64_t>(endChannel->fc);
+        return {{startIndex, stopIndex}, {lowFreq, highFreq}};
+    }
 };
 
 /**
@@ -75,9 +132,11 @@
     /**
      * Make signal function
      * \param txPowerWatts the transmit power in watts
+     * \param channel the operating channel of the PHY used for the transmission
      * \returns Ptr<SpectrumSignalParameters>
      */
-    Ptr<SpectrumSignalParameters> MakeSignal(double txPowerWatts);
+    Ptr<SpectrumSignalParameters> MakeSignal(double txPowerWatts,
+                                             const WifiPhyOperatingChannel& channel);
     /**
      * Send signal function
      * \param txPowerWatts the transmit power in watts
@@ -121,10 +180,17 @@
 
 // Make a Wi-Fi signal to inject directly to the StartRx() method
 Ptr<SpectrumSignalParameters>
-SpectrumWifiPhyBasicTest::MakeSignal(double txPowerWatts)
+SpectrumWifiPhyBasicTest::MakeSignal(double txPowerWatts, const WifiPhyOperatingChannel& channel)
 {
-    WifiTxVector txVector =
-        WifiTxVector(OfdmPhy::GetOfdmRate6Mbps(), 0, WIFI_PREAMBLE_LONG, 800, 1, 1, 0, 20, false);
+    WifiTxVector txVector = WifiTxVector(OfdmPhy::GetOfdmRate6Mbps(),
+                                         0,
+                                         WIFI_PREAMBLE_LONG,
+                                         800,
+                                         1,
+                                         1,
+                                         0,
+                                         CHANNEL_WIDTH,
+                                         false);
 
     Ptr<Packet> pkt = Create<Packet>(1000);
     WifiMacHeader hdr;
@@ -135,13 +201,13 @@
     Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
     Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
 
-    Ptr<WifiPpdu> ppdu = Create<OfdmPpdu>(psdu, txVector, FREQUENCY, WIFI_PHY_BAND_5GHZ, m_uid++);
+    Ptr<WifiPpdu> ppdu = Create<OfdmPpdu>(psdu, txVector, channel, m_uid++);
 
-    Ptr<SpectrumValue> txPowerSpectrum =
-        WifiSpectrumValueHelper::CreateOfdmTxPowerSpectralDensity(FREQUENCY,
-                                                                  CHANNEL_WIDTH,
-                                                                  txPowerWatts,
-                                                                  GUARD_WIDTH);
+    Ptr<SpectrumValue> txPowerSpectrum = WifiSpectrumValueHelper::CreateOfdmTxPowerSpectralDensity(
+        channel.GetPrimaryChannelCenterFrequency(CHANNEL_WIDTH),
+        CHANNEL_WIDTH,
+        txPowerWatts,
+        GUARD_WIDTH);
     Ptr<WifiSpectrumSignalParameters> txParams = Create<WifiSpectrumSignalParameters>();
     txParams->psd = txPowerSpectrum;
     txParams->txPhy = nullptr;
@@ -156,7 +222,7 @@
 void
 SpectrumWifiPhyBasicTest::SendSignal(double txPowerWatts)
 {
-    m_phy->StartRx(MakeSignal(txPowerWatts));
+    m_phy->StartRx(MakeSignal(txPowerWatts, m_phy->GetOperatingChannel()), nullptr);
 }
 
 void
@@ -185,17 +251,24 @@
 void
 SpectrumWifiPhyBasicTest::DoSetup()
 {
+    Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    Ptr<Node> node = CreateObject<Node>();
+    Ptr<WifiNetDevice> dev = CreateObject<WifiNetDevice>();
     m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211n);
     Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
     m_phy->SetInterferenceHelper(interferenceHelper);
     Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
     m_phy->SetErrorRateModel(error);
+    m_phy->SetDevice(dev);
+    m_phy->AddChannel(spectrumChannel);
+    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
+    m_phy->ConfigureStandard(WIFI_STANDARD_80211n);
     m_phy->SetReceiveOkCallback(
         MakeCallback(&SpectrumWifiPhyBasicTest::SpectrumWifiPhyRxSuccess, this));
     m_phy->SetReceiveErrorCallback(
         MakeCallback(&SpectrumWifiPhyBasicTest::SpectrumWifiPhyRxFailure, this));
+    dev->SetPhy(m_phy);
+    node->AddDevice(dev);
 }
 
 void
@@ -241,19 +314,9 @@
   public:
     /**
      * Create a test PhyListener
-     *
      */
-    TestPhyListener()
-        : m_notifyRxStart(0),
-          m_notifyRxEndOk(0),
-          m_notifyRxEndError(0),
-          m_notifyMaybeCcaBusyStart(0)
-    {
-    }
-
-    ~TestPhyListener() override
-    {
-    }
+    TestPhyListener() = default;
+    ~TestPhyListener() override = default;
 
     void NotifyRxStart(Time duration) override
     {
@@ -283,7 +346,15 @@
                             const std::vector<Time>& /*per20MhzDurations*/) override
     {
         NS_LOG_FUNCTION(this << duration << channelType);
-        ++m_notifyMaybeCcaBusyStart;
+        if (duration.IsStrictlyPositive())
+        {
+            ++m_notifyMaybeCcaBusyStart;
+            if (!m_ccaBusyStart.IsStrictlyPositive())
+            {
+                m_ccaBusyStart = Simulator::Now();
+            }
+            m_ccaBusyEnd = std::max(m_ccaBusyEnd, Simulator::Now() + duration);
+        }
     }
 
     void NotifySwitchingStart(Time duration) override
@@ -306,10 +377,26 @@
     {
     }
 
-    uint32_t m_notifyRxStart;           ///< notify receive start
-    uint32_t m_notifyRxEndOk;           ///< notify receive end OK
-    uint32_t m_notifyRxEndError;        ///< notify receive end error
-    uint32_t m_notifyMaybeCcaBusyStart; ///< notify maybe CCA busy start
+    /**
+     * Reset function
+     */
+    void Reset()
+    {
+        NS_LOG_FUNCTION(this);
+        m_notifyRxStart = 0;
+        m_notifyRxEndOk = 0;
+        m_notifyRxEndError = 0;
+        m_notifyMaybeCcaBusyStart = 0;
+        m_ccaBusyStart = Seconds(0);
+        m_ccaBusyEnd = Seconds(0);
+    }
+
+    uint32_t m_notifyRxStart{0};           ///< notify receive start
+    uint32_t m_notifyRxEndOk{0};           ///< notify receive end OK
+    uint32_t m_notifyRxEndError{0};        ///< notify receive end error
+    uint32_t m_notifyMaybeCcaBusyStart{0}; ///< notify maybe CCA busy start
+    Time m_ccaBusyStart{0};                ///< CCA_BUSY start time
+    Time m_ccaBusyEnd{0};                  ///< CCA_BUSY end time
 };
 
 /**
@@ -413,7 +500,7 @@
     uint16_t m_txChannelWidth; ///< TX channel width (MHz)
     uint16_t m_rxChannelWidth; ///< RX channel width (MHz)
 
-    std::set<WifiSpectrumBand> m_ruBands; ///< spectrum bands associated to all the RUs
+    std::set<WifiSpectrumBandIndices> m_ruBands; ///< spectrum bands associated to all the RUs
 };
 
 SpectrumWifiPhyFilterTest::SpectrumWifiPhyFilterTest()
@@ -462,8 +549,8 @@
 {
     for (const auto& pair : rxPowersW)
     {
-        NS_LOG_INFO("band: (" << pair.first.first << ";" << pair.first.second << ") -> powerW="
-                              << pair.second << " (" << WToDbm(pair.second) << " dBm)");
+        NS_LOG_INFO("band: (" << pair.first << ") -> powerW=" << pair.second << " ("
+                              << WToDbm(pair.second) << " dBm)");
     }
 
     size_t numBands = rxPowersW.size();
@@ -478,7 +565,7 @@
                           "Total number of bands handled by the receiver is incorrect");
 
     uint16_t channelWidth = std::min(m_txChannelWidth, m_rxChannelWidth);
-    WifiSpectrumBand band = m_rxPhy->GetBand(channelWidth, 0);
+    auto band = m_rxPhy->GetBand(channelWidth, 0);
     auto it = rxPowersW.find(band);
     NS_LOG_INFO("powerW total band: " << it->second << " (" << WToDbm(it->second) << " dBm)");
     int totalRxPower = static_cast<int>(WToDbm(it->second) + 0.5);
@@ -516,8 +603,8 @@
 void
 SpectrumWifiPhyFilterTest::DoSetup()
 {
-    // WifiHelper::EnableLogComponents ();
-    // LogComponentEnable ("SpectrumWifiPhyBasicTest", LOG_LEVEL_ALL);
+    // WifiHelper::EnableLogComponents();
+    // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
 
     Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
     Ptr<FriisPropagationLossModel> lossModel = CreateObject<FriisPropagationLossModel>();
@@ -530,14 +617,13 @@
     Ptr<Node> txNode = CreateObject<Node>();
     Ptr<WifiNetDevice> txDev = CreateObject<WifiNetDevice>();
     m_txPhy = CreateObject<ExtSpectrumWifiPhy>();
-    m_txPhy->CreateWifiSpectrumPhyInterface(txDev);
-    m_txPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> txInterferenceHelper = CreateObject<InterferenceHelper>();
     m_txPhy->SetInterferenceHelper(txInterferenceHelper);
     Ptr<ErrorRateModel> txErrorModel = CreateObject<NistErrorRateModel>();
     m_txPhy->SetErrorRateModel(txErrorModel);
     m_txPhy->SetDevice(txDev);
-    m_txPhy->SetChannel(spectrumChannel);
+    m_txPhy->AddChannel(spectrumChannel);
+    m_txPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<ConstantPositionMobilityModel> apMobility = CreateObject<ConstantPositionMobilityModel>();
     m_txPhy->SetMobility(apMobility);
     txDev->SetPhy(m_txPhy);
@@ -547,13 +633,12 @@
     Ptr<Node> rxNode = CreateObject<Node>();
     Ptr<WifiNetDevice> rxDev = CreateObject<WifiNetDevice>();
     m_rxPhy = CreateObject<ExtSpectrumWifiPhy>();
-    m_rxPhy->CreateWifiSpectrumPhyInterface(rxDev);
-    m_rxPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> rxInterferenceHelper = CreateObject<InterferenceHelper>();
     m_rxPhy->SetInterferenceHelper(rxInterferenceHelper);
     Ptr<ErrorRateModel> rxErrorModel = CreateObject<NistErrorRateModel>();
     m_rxPhy->SetErrorRateModel(rxErrorModel);
-    m_rxPhy->SetChannel(spectrumChannel);
+    m_rxPhy->AddChannel(spectrumChannel);
+    m_rxPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<ConstantPositionMobilityModel> sta1Mobility = CreateObject<ConstantPositionMobilityModel>();
     m_rxPhy->SetMobility(sta1Mobility);
     rxDev->SetPhy(m_rxPhy);
@@ -635,14 +720,17 @@
                 HeRu::RuType ruType = static_cast<HeRu::RuType>(type);
                 for (std::size_t index = 1; index <= HeRu::GetNRus(bw, ruType); index++)
                 {
-                    HeRu::SubcarrierGroup group = HeRu::GetSubcarrierGroup(bw, ruType, index);
-                    HeRu::SubcarrierRange range =
-                        std::make_pair(group.front().first, group.back().second);
-                    WifiSpectrumBand band = m_rxPhy->ConvertHeRuSubcarriers(
-                        bw,
-                        m_rxPhy->GetGuardBandwidth(m_rxChannelWidth),
-                        range,
-                        i);
+                    HeRu::SubcarrierGroup subcarrierGroup =
+                        HeRu::GetSubcarrierGroup(bw, ruType, index);
+                    HeRu::SubcarrierRange subcarrierRange =
+                        std::make_pair(subcarrierGroup.front().first,
+                                       subcarrierGroup.back().second);
+                    const auto band =
+                        HePhy::ConvertHeRuSubcarriers(bw,
+                                                      m_rxPhy->GetGuardBandwidth(m_rxChannelWidth),
+                                                      m_rxPhy->GetSubcarrierSpacing(),
+                                                      subcarrierRange,
+                                                      i);
                     m_ruBands.insert(band);
                 }
             }
@@ -728,6 +816,686 @@
  * \ingroup wifi-test
  * \ingroup tests
  *
+ * \brief Spectrum Wifi Phy Multiple Spectrum Test
+ *
+ * This test is testing the ability to plug multiple spectrum channels to the spectrum wifi PHY.
+ * It considers 4 TX-RX PHY pairs that are independent from each others and are plugged to different
+ * spectrum channels that are covering different frequency range. Each RX PHY is also attached to
+ * each of the other 3 spectrum channels it can switch to.
+ *
+ * In the first scenario, we consider the default case where each TX-RX PHY pairs are operating on
+ * different frequency ranges and hence using independent spectrum channels. We validate that no
+ * packets is received from other TX PHYs attached to different spectrum channels and we also verify
+ * the amount of connected PHYs to each spectrum channel is exactly 2. The test also makes sure each
+ * PHY has only one active spectrum channel and that the active one is operating at the expected
+ * frequency range.
+ *
+ * In the second scenario, we consecutively switch the channel of all RX PHYs to the one of each TX
+ * PHY. We validate that packets are received by all PHYs and we also verify the amount of connected
+ * PHYs to each spectrum channels is either 5 (1 TX PHY and 4 RX PHYs) or 1 (the TX PHY left alone).
+ */
+class SpectrumWifiPhyMultipleInterfacesTest : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param trackSignalsInactiveInterfaces flag to indicate whether signals coming from inactive
+     * spectrum PHY interfaces shall be tracked during the test
+     */
+    SpectrumWifiPhyMultipleInterfacesTest(bool trackSignalsInactiveInterfaces);
+
+  private:
+    void DoSetup() override;
+    void DoTeardown() override;
+    void DoRun() override;
+
+    /**
+     * Switch channel function
+     *
+     * \param index the index to identify the RX PHY
+     * \param band the PHY band to use
+     * \param channelNumber number the channel number to use
+     * \param channelWidth the channel width to use
+     */
+    void SwitchChannel(std::size_t index,
+                       WifiPhyBand band,
+                       uint8_t channelNumber,
+                       uint16_t channelWidth);
+
+    /**
+     * Send PPDU function
+     *
+     * \param phy the PHY to transmit the signal
+     * \param txPowerDbm the power in dBm to transmit the signal (this is also the received power
+     * since we do not have propagation loss to simplify)
+     */
+    void SendPpdu(Ptr<SpectrumWifiPhy> phy, double txPowerDbm);
+
+    /**
+     * Callback triggered when a packet is received by a PHY
+     * \param index the index to identify the RX PHY
+     * \param packet the received packet
+     * \param rxPowersW the received power per channel band in watts
+     */
+    void RxCallback(std::size_t index,
+                    Ptr<const Packet> packet,
+                    RxPowerWattPerChannelBand rxPowersW);
+
+    /**
+     * Schedule now to check the interferences
+     * \param phy the PHY for which the check has to be executed
+     * \param freqRange the frequency range for which the check has to be executed
+     * \param channelWidth the channel width for which the check has to be executed
+     * \param interferencesExpected flag whether interferences are expected to have been tracked
+     */
+    void CheckInterferences(Ptr<ExtSpectrumWifiPhy> phy,
+                            const FrequencyRange& freqRange,
+                            uint16_t channelWidth,
+                            bool interferencesExpected);
+
+    /**
+     * Check the interferences
+     * \param phy the PHY for which the check has to be executed
+     * \param freqRange the frequency range for which the check has to be executed
+     * \param channelWidth the channel width for which the check has to be executed
+     * \param interferencesExpected flag whether interferences are expected to have been tracked
+     */
+    void DoCheckInterferences(Ptr<ExtSpectrumWifiPhy> phy,
+                              const FrequencyRange& freqRange,
+                              uint16_t channelWidth,
+                              bool interferencesExpected);
+
+    /**
+     * Verify results
+     *
+     * \param index the index to identify the RX PHY to check
+     * \param expectedNumRx the expected number of RX events for that PHY
+     * \param expectedFrequencyRangeActiveRfInterface the expected frequency range (in MHz) of the
+     * active RF interface
+     * \param expectedConnectedPhysPerChannel the expected
+     * number of PHYs attached for each spectrum channel
+     */
+    void CheckResults(std::size_t index,
+                      uint32_t expectedNumRx,
+                      FrequencyRange expectedFrequencyRangeActiveRfInterface,
+                      const std::vector<std::size_t>& expectedConnectedPhysPerChannel);
+
+    /**
+     * Verify CCA indication reported by a given PHY
+     *
+     * \param index the index to identify the RX PHY to check
+     * \param expectedCcaBusyIndication flag to indicate whether a CCA BUSY notification is expected
+     * \param switchingDelay delay between the TX has started and the time RX switched to the TX
+     * channel
+     */
+    void CheckCcaIndication(std::size_t index, bool expectedCcaBusyIndication, Time switchingDelay);
+
+    /**
+     * Reset function
+     */
+    void Reset();
+
+    /// typedef for spectrum channel infos
+    using SpectrumChannelInfos = std::tuple<Ptr<MultiModelSpectrumChannel>, uint16_t, uint16_t>;
+
+    bool
+        m_trackSignalsInactiveInterfaces; //!< flag to indicate whether signals coming from inactive
+                                          //!< spectrum PHY interfaces are tracked during the test
+
+    std::vector<SpectrumChannelInfos> m_spectrumChannelInfos;    //!< Spectrum channels infos
+    std::vector<Ptr<ExtSpectrumWifiPhy>> m_txPhys{};             //!< TX PHYs
+    std::vector<Ptr<ExtSpectrumWifiPhy>> m_rxPhys{};             //!< RX PHYs
+    std::vector<std::unique_ptr<TestPhyListener>> m_listeners{}; //!< listeners
+
+    std::vector<uint32_t> m_counts{0}; //!< count number of packets received by PHYs
+
+    Time m_lastTxStart{0}; //!< hold the time at which the last transmission started
+    Time m_lastTxEnd{0};   //!< hold the time at which the last transmission ended
+};
+
+SpectrumWifiPhyMultipleInterfacesTest::SpectrumWifiPhyMultipleInterfacesTest(
+    bool trackSignalsInactiveInterfaces)
+    : TestCase{"SpectrumWifiPhy test operation with multiple RF interfaces"},
+      m_trackSignalsInactiveInterfaces{trackSignalsInactiveInterfaces}
+{
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::SwitchChannel(std::size_t index,
+                                                     WifiPhyBand band,
+                                                     uint8_t channelNumber,
+                                                     uint16_t channelWidth)
+{
+    NS_LOG_FUNCTION(this << index << band << +channelNumber << channelWidth);
+    auto& listener = m_listeners.at(index);
+    listener->m_notifyMaybeCcaBusyStart = 0;
+    listener->m_ccaBusyStart = Seconds(0);
+    listener->m_ccaBusyEnd = Seconds(0);
+    auto phy = m_rxPhys.at(index);
+    phy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNumber, channelWidth, band, 0});
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::SendPpdu(Ptr<SpectrumWifiPhy> phy, double txPowerDbm)
+{
+    NS_LOG_FUNCTION(this << phy << txPowerDbm << phy->GetCurrentFrequencyRange()
+                         << phy->GetChannelWidth() << phy->GetChannelNumber());
+
+    WifiTxVector txVector =
+        WifiTxVector(HePhy::GetHeMcs0(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, 20, false, false);
+    Ptr<Packet> pkt = Create<Packet>(1000);
+    WifiMacHeader hdr;
+    hdr.SetType(WIFI_MAC_QOSDATA);
+    hdr.SetQosTid(0);
+    hdr.SetAddr1(Mac48Address("00:00:00:00:00:01"));
+    hdr.SetSequenceNumber(1);
+    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
+
+    m_lastTxStart = Simulator::Now();
+    m_lastTxEnd = m_lastTxStart + WifiPhy::CalculateTxDuration({std::make_pair(SU_STA_ID, psdu)},
+                                                               txVector,
+                                                               phy->GetPhyBand());
+    phy->SetTxPowerStart(txPowerDbm);
+    phy->SetTxPowerEnd(txPowerDbm);
+    phy->Send(WifiConstPsduMap({std::make_pair(SU_STA_ID, psdu)}), txVector);
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::RxCallback(std::size_t index,
+                                                  Ptr<const Packet> /*packet*/,
+                                                  RxPowerWattPerChannelBand /*rxPowersW*/)
+{
+    auto phy = m_rxPhys.at(index);
+    NS_LOG_FUNCTION(this << index << phy->GetCurrentFrequencyRange() << phy->GetChannelWidth()
+                         << phy->GetChannelNumber());
+    m_counts.at(index)++;
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::CheckInterferences(Ptr<ExtSpectrumWifiPhy> phy,
+                                                          const FrequencyRange& freqRange,
+                                                          uint16_t channelWidth,
+                                                          bool interferencesExpected)
+{
+    if ((!m_trackSignalsInactiveInterfaces) && (phy->GetCurrentFrequencyRange() != freqRange))
+    {
+        // ignore since no bands for that range exists in interference helper in that case
+        return;
+    }
+    // This is needed to make sure PHY state will be checked as the last event if a state change
+    // occurred at the exact same time as the check
+    Simulator::ScheduleNow(&SpectrumWifiPhyMultipleInterfacesTest::DoCheckInterferences,
+                           this,
+                           phy,
+                           freqRange,
+                           channelWidth,
+                           interferencesExpected);
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::DoCheckInterferences(Ptr<ExtSpectrumWifiPhy> phy,
+                                                            const FrequencyRange& freqRange,
+                                                            uint16_t channelWidth,
+                                                            bool interferencesExpected)
+{
+    NS_LOG_FUNCTION(this << phy << freqRange << interferencesExpected);
+    PointerValue ptr;
+    phy->GetAttribute("InterferenceHelper", ptr);
+    auto interferenceHelper = DynamicCast<InterferenceHelper>(ptr.Get<InterferenceHelper>());
+    NS_ASSERT(interferenceHelper);
+    const auto band = phy->GetBandForInterface(channelWidth, 0, freqRange, channelWidth);
+    const auto energyDuration = interferenceHelper->GetEnergyDuration(0, band);
+    NS_TEST_ASSERT_MSG_EQ(energyDuration.IsStrictlyPositive(),
+                          interferencesExpected,
+                          "Incorrect interferences detection");
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::CheckResults(
+    std::size_t index,
+    uint32_t expectedNumRx,
+    FrequencyRange expectedFrequencyRangeActiveRfInterface,
+    const std::vector<std::size_t>& expectedConnectedPhysPerChannel)
+{
+    NS_LOG_FUNCTION(this << index << expectedNumRx << expectedFrequencyRangeActiveRfInterface);
+    const auto phy = m_rxPhys.at(index);
+    std::size_t numActiveInterfaces = 0;
+    for (const auto& [freqRange, interface] : phy->GetSpectrumPhyInterfaces())
+    {
+        const auto expectedActive = (freqRange == expectedFrequencyRangeActiveRfInterface);
+        const auto isActive = (interface == phy->GetCurrentSpectrumPhyInterface());
+        NS_TEST_ASSERT_MSG_EQ(isActive, expectedActive, "Incorrect active interface");
+        if (isActive)
+        {
+            numActiveInterfaces++;
+        }
+    }
+    NS_TEST_ASSERT_MSG_EQ(numActiveInterfaces, 1, "There should always be one active interface");
+    NS_ASSERT(expectedConnectedPhysPerChannel.size() == m_spectrumChannelInfos.size());
+    for (std::size_t i = 0; i < m_spectrumChannelInfos.size(); ++i)
+    {
+        const auto spectrumChannel = std::get<0>(m_spectrumChannelInfos.at(i));
+        NS_TEST_ASSERT_MSG_EQ(spectrumChannel->GetNDevices(),
+                              expectedConnectedPhysPerChannel.at(i),
+                              "Incorrect number of PHYs attached to the spectrum channel");
+    }
+    NS_TEST_ASSERT_MSG_EQ(m_counts.at(index),
+                          expectedNumRx,
+                          "Didn't receive right number of packets");
+    NS_TEST_ASSERT_MSG_EQ(m_listeners.at(index)->m_notifyRxStart,
+                          expectedNumRx,
+                          "Didn't receive NotifyRxStart");
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::CheckCcaIndication(std::size_t index,
+                                                          bool expectedCcaBusyIndication,
+                                                          Time switchingDelay)
+{
+    const auto expectedCcaBusyStart =
+        expectedCcaBusyIndication ? m_lastTxStart + switchingDelay : Seconds(0);
+    const auto expectedCcaBusyEnd = expectedCcaBusyIndication ? m_lastTxEnd : Seconds(0);
+    NS_LOG_FUNCTION(this << index << expectedCcaBusyIndication << expectedCcaBusyStart
+                         << expectedCcaBusyEnd);
+    auto& listener = m_listeners.at(index);
+    const auto ccaBusyIndication = (listener->m_notifyMaybeCcaBusyStart > 0);
+    const auto ccaBusyStart = listener->m_ccaBusyStart;
+    const auto ccaBusyEnd = listener->m_ccaBusyEnd;
+    NS_TEST_ASSERT_MSG_EQ(ccaBusyIndication,
+                          expectedCcaBusyIndication,
+                          "CCA busy indication check failed");
+    NS_TEST_ASSERT_MSG_EQ(ccaBusyStart, expectedCcaBusyStart, "CCA busy start mismatch");
+    NS_TEST_ASSERT_MSG_EQ(ccaBusyEnd, expectedCcaBusyEnd, "CCA busy end mismatch");
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::Reset()
+{
+    NS_LOG_FUNCTION(this);
+    for (auto& count : m_counts)
+    {
+        count = 0;
+    }
+    for (auto& listener : m_listeners)
+    {
+        listener->Reset();
+    }
+    // restore all RX PHYs to initial channels
+    for (std::size_t rxPhyIndex = 0; rxPhyIndex < m_rxPhys.size(); ++rxPhyIndex)
+    {
+        auto txPhy = m_txPhys.at(rxPhyIndex);
+        SwitchChannel(rxPhyIndex,
+                      txPhy->GetPhyBand(),
+                      txPhy->GetChannelNumber(),
+                      txPhy->GetChannelWidth());
+    }
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::DoSetup()
+{
+    NS_LOG_FUNCTION(this);
+
+    // WifiHelper::EnableLogComponents();
+    // LogComponentEnable("SpectrumWifiPhyTest", LOG_LEVEL_ALL);
+
+    const std::vector<std::pair<WifiPhyBand, uint8_t>> channels{{WIFI_PHY_BAND_2_4GHZ, 2},
+                                                                {WIFI_PHY_BAND_5GHZ, 42},
+                                                                {WIFI_PHY_BAND_5GHZ, 114},
+                                                                {WIFI_PHY_BAND_6GHZ, 215}};
+
+    // create spectrum channels
+    for (std::size_t i = 0; i < channels.size(); ++i)
+    {
+        auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+        [[maybe_unused]] const auto [channel, frequency, channelWidth, type, band] =
+            (*WifiPhyOperatingChannel::FindFirst(channels.at(i).second,
+                                                 0,
+                                                 0,
+                                                 WIFI_STANDARD_80211ax,
+                                                 channels.at(i).first));
+
+        m_spectrumChannelInfos.emplace_back(spectrumChannel, frequency, channelWidth);
+    }
+
+    // create PHYs and add all channels to each of them
+    for (std::size_t i = 0; i < channels.size(); ++i)
+    {
+        auto txNode = CreateObject<Node>();
+        auto txDev = CreateObject<WifiNetDevice>();
+        auto txPhy = CreateObject<ExtSpectrumWifiPhy>();
+        txPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
+        txPhy->SetAttribute("TrackSignalsFromInactiveInterfaces",
+                            BooleanValue(m_trackSignalsInactiveInterfaces));
+        auto txInterferenceHelper = CreateObject<InterferenceHelper>();
+        txPhy->SetInterferenceHelper(txInterferenceHelper);
+        auto txError = CreateObject<NistErrorRateModel>();
+        txPhy->SetErrorRateModel(txError);
+        txPhy->SetDevice(txDev);
+        for (const auto& [spectrumChannel, freq, width] : m_spectrumChannelInfos)
+        {
+            const uint16_t minFreq = freq - width / 2;
+            const uint16_t maxFreq = freq + width / 2;
+            txPhy->AddChannel(spectrumChannel, {minFreq, maxFreq});
+        }
+        txPhy->SetOperatingChannel(
+            WifiPhy::ChannelTuple{channels.at(i).second, 0, channels.at(i).first, 0});
+        txPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
+        txDev->SetPhy(txPhy);
+        txNode->AddDevice(txDev);
+
+        m_txPhys.push_back(txPhy);
+
+        auto rxNode = CreateObject<Node>();
+        auto rxDev = CreateObject<WifiNetDevice>();
+        auto rxPhy = CreateObject<ExtSpectrumWifiPhy>();
+        rxPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
+        rxPhy->SetAttribute("TrackSignalsFromInactiveInterfaces",
+                            BooleanValue(m_trackSignalsInactiveInterfaces));
+        auto rxInterferenceHelper = CreateObject<InterferenceHelper>();
+        rxPhy->SetInterferenceHelper(rxInterferenceHelper);
+        auto rxError = CreateObject<NistErrorRateModel>();
+        rxPhy->SetErrorRateModel(rxError);
+        rxPhy->SetDevice(rxDev);
+        for (const auto& [spectrumChannel, freq, width] : m_spectrumChannelInfos)
+        {
+            const uint16_t minFreq = freq - width / 2;
+            const uint16_t maxFreq = freq + width / 2;
+            rxPhy->AddChannel(spectrumChannel, {minFreq, maxFreq});
+        }
+        rxPhy->SetOperatingChannel(
+            WifiPhy::ChannelTuple{channels.at(i).second, 0, channels.at(i).first, 0});
+        rxPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
+        rxDev->SetPhy(rxPhy);
+        rxNode->AddDevice(rxDev);
+
+        const auto index = m_rxPhys.size();
+        rxPhy->TraceConnectWithoutContext(
+            "PhyRxBegin",
+            MakeCallback(&SpectrumWifiPhyMultipleInterfacesTest::RxCallback, this).Bind(index));
+
+        auto listener = std::make_unique<TestPhyListener>();
+        rxPhy->RegisterListener(listener.get());
+        m_listeners.push_back(std::move(listener));
+
+        m_rxPhys.push_back(rxPhy);
+        m_counts.push_back(0);
+    }
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::DoTeardown()
+{
+    NS_LOG_FUNCTION(this);
+    for (auto& phy : m_txPhys)
+    {
+        phy->Dispose();
+        phy = nullptr;
+    }
+    for (auto& phy : m_rxPhys)
+    {
+        phy->Dispose();
+        phy = nullptr;
+    }
+    Simulator::Destroy();
+}
+
+void
+SpectrumWifiPhyMultipleInterfacesTest::DoRun()
+{
+    NS_LOG_FUNCTION(this);
+
+    const auto ccaEdThresholdDbm = -62.0; ///< CCA-ED threshold in dBm
+    const auto txAfterChannelSwitchDelay =
+        Seconds(0.25); ///< delay in seconds between channel switch is triggered and a transmission
+                       ///< gets started
+    const auto checkResultsDelay =
+        Seconds(0.5); ///< delay in seconds between start of test and moment results are verified
+    const auto flushResultsDelay =
+        Seconds(0.9); ///< delay in seconds between start of test and moment results are flushed
+    const auto txOngoingAfterTxStartedDelay =
+        MicroSeconds(50); ///< delay in microseconds between a transmission has started and a point
+                          ///< in time the transmission is ongoing
+
+    Time delay{0};
+    // make sure all interfaces got active once for all RX PHYs to have them monitored
+    for (std::size_t rxPhyIndex = 0; rxPhyIndex < m_rxPhys.size(); ++rxPhyIndex)
+    {
+        for (std::size_t txPhyIndex = 0; txPhyIndex < m_txPhys.size(); ++txPhyIndex)
+        {
+            if (txPhyIndex == rxPhyIndex)
+            {
+                // handled at end
+                continue;
+            }
+            auto txPhy = m_txPhys.at(txPhyIndex);
+            Simulator::Schedule(delay + Seconds((rxPhyIndex + rxPhyIndex) * 0.1),
+                                &SpectrumWifiPhyMultipleInterfacesTest::SwitchChannel,
+                                this,
+                                rxPhyIndex,
+                                txPhy->GetPhyBand(),
+                                txPhy->GetChannelNumber(),
+                                txPhy->GetChannelWidth());
+        }
+        Simulator::Schedule(delay + flushResultsDelay,
+                            &SpectrumWifiPhyMultipleInterfacesTest::Reset,
+                            this);
+    }
+
+    // default channels active for all PHYs: each PHY only receives from its associated TX
+    std::vector<std::size_t> expectedConnectedPhysPerChannel =
+        m_trackSignalsInactiveInterfaces ? std::vector<std::size_t>{5, 5, 5, 5}
+                                         : // all RX PHYs keep all channels active when tracking
+                                           // interferences on inactive interfaces
+            std::vector<std::size_t>{2, 2, 2, 2}; // default channels active for all PHYs: each PHY
+                                                  // only receives from its associated TX
+    for (std::size_t i = 0; i < 4; ++i)
+    {
+        auto txPpduPhy = m_txPhys.at(i);
+        delay += Seconds(1);
+        Simulator::Schedule(delay,
+                            &SpectrumWifiPhyMultipleInterfacesTest::SendPpdu,
+                            this,
+                            txPpduPhy,
+                            0);
+        for (std::size_t j = 0; j < 4; ++j)
+        {
+            auto txPhy = m_txPhys.at(j);
+            auto rxPhy = m_rxPhys.at(j);
+            const uint16_t minExpectedFreq = txPhy->GetFrequency() - (txPhy->GetChannelWidth() / 2);
+            const uint16_t maxExpectedFreq = txPhy->GetFrequency() + (txPhy->GetChannelWidth() / 2);
+            const FrequencyRange expectedFreqRange = {minExpectedFreq, maxExpectedFreq};
+            Simulator::Schedule(delay + txOngoingAfterTxStartedDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::CheckInterferences,
+                                this,
+                                rxPhy,
+                                txPpduPhy->GetCurrentFrequencyRange(),
+                                txPpduPhy->GetChannelWidth(),
+                                true);
+            Simulator::Schedule(delay + checkResultsDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::CheckResults,
+                                this,
+                                j,
+                                (i == j) ? 1 : 0,
+                                expectedFreqRange,
+                                expectedConnectedPhysPerChannel);
+            Simulator::Schedule(delay + flushResultsDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::Reset,
+                                this);
+        }
+    }
+
+    // same channel active for all PHYs: all PHYs receive from TX
+    for (std::size_t i = 0; i < 4; ++i)
+    {
+        delay += Seconds(1);
+        auto txPpduPhy = m_txPhys.at(i);
+        Simulator::Schedule(delay + txAfterChannelSwitchDelay,
+                            &SpectrumWifiPhyMultipleInterfacesTest::SendPpdu,
+                            this,
+                            txPpduPhy,
+                            0);
+        const uint16_t minExpectedFreq =
+            txPpduPhy->GetFrequency() - (txPpduPhy->GetChannelWidth() / 2);
+        const uint16_t maxExpectedFreq =
+            txPpduPhy->GetFrequency() + (txPpduPhy->GetChannelWidth() / 2);
+        const FrequencyRange expectedFreqRange = {minExpectedFreq, maxExpectedFreq};
+        for (std::size_t j = 0; j < 4; ++j)
+        {
+            if (!m_trackSignalsInactiveInterfaces)
+            {
+                for (std::size_t k = 0; k < expectedConnectedPhysPerChannel.size(); ++k)
+                {
+                    expectedConnectedPhysPerChannel.at(k) = (k == i) ? 5 : 1;
+                }
+            }
+            auto rxPhy = m_rxPhys.at(j);
+            Simulator::Schedule(delay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::SwitchChannel,
+                                this,
+                                j,
+                                txPpduPhy->GetPhyBand(),
+                                txPpduPhy->GetChannelNumber(),
+                                txPpduPhy->GetChannelWidth());
+            Simulator::Schedule(delay + txAfterChannelSwitchDelay + txOngoingAfterTxStartedDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::CheckInterferences,
+                                this,
+                                rxPhy,
+                                txPpduPhy->GetCurrentFrequencyRange(),
+                                txPpduPhy->GetChannelWidth(),
+                                true);
+            Simulator::Schedule(delay + checkResultsDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::CheckResults,
+                                this,
+                                j,
+                                1,
+                                expectedFreqRange,
+                                expectedConnectedPhysPerChannel);
+            Simulator::Schedule(delay + flushResultsDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::Reset,
+                                this);
+        }
+    }
+
+    // Switch all PHYs to channel 36: all PHYs switch to the second spectrum channel
+    // since second spectrum channel is 42 (80 MHz) and hence covers channel 36 (20 MHz)
+    const auto secondSpectrumChannelIndex = 1;
+    auto channel36TxPhy = m_txPhys.at(secondSpectrumChannelIndex);
+    const uint16_t minExpectedFreq =
+        channel36TxPhy->GetFrequency() - (channel36TxPhy->GetChannelWidth() / 2);
+    const uint16_t maxExpectedFreq =
+        channel36TxPhy->GetFrequency() + (channel36TxPhy->GetChannelWidth() / 2);
+    FrequencyRange expectedFreqRange = {minExpectedFreq, maxExpectedFreq};
+    for (std::size_t i = 0; i < 4; ++i)
+    {
+        delay += Seconds(1);
+        auto txPpduPhy = m_txPhys.at(i);
+        Simulator::Schedule(delay + txAfterChannelSwitchDelay,
+                            &SpectrumWifiPhyMultipleInterfacesTest::SendPpdu,
+                            this,
+                            txPpduPhy,
+                            0);
+        for (std::size_t j = 0; j < 4; ++j)
+        {
+            if (!m_trackSignalsInactiveInterfaces)
+            {
+                for (std::size_t k = 0; k < expectedConnectedPhysPerChannel.size(); ++k)
+                {
+                    expectedConnectedPhysPerChannel.at(k) =
+                        (k == secondSpectrumChannelIndex) ? 5 : 1;
+                }
+            }
+            Simulator::Schedule(delay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::SwitchChannel,
+                                this,
+                                j,
+                                WIFI_PHY_BAND_5GHZ,
+                                CHANNEL_NUMBER,
+                                CHANNEL_WIDTH);
+            Simulator::Schedule(delay + checkResultsDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::CheckResults,
+                                this,
+                                j,
+                                (i == secondSpectrumChannelIndex) ? 1 : 0,
+                                expectedFreqRange,
+                                expectedConnectedPhysPerChannel);
+            Simulator::Schedule(delay + flushResultsDelay,
+                                &SpectrumWifiPhyMultipleInterfacesTest::Reset,
+                                this);
+        }
+    }
+
+    // verify CCA indication when switching to a channel with an ongoing transmission
+    for (const auto txPowerDbm : {-60.0 /* above CCA-ED */, -70.0 /* below CCA-ED */})
+    {
+        for (std::size_t i = 0; i < 4; ++i)
+        {
+            for (std::size_t j = 0; j < 4; ++j)
+            {
+                auto txPpduPhy = m_txPhys.at(i);
+                const auto startChannel =
+                    WifiPhyOperatingChannel::FindFirst(txPpduPhy->GetPrimaryChannelNumber(20),
+                                                       0,
+                                                       20,
+                                                       WIFI_STANDARD_80211ax,
+                                                       txPpduPhy->GetPhyBand());
+                for (uint16_t bw = txPpduPhy->GetChannelWidth(); bw >= 20; bw /= 2)
+                {
+                    [[maybe_unused]] const auto [channel, frequency, channelWidth, type, band] =
+                        (*WifiPhyOperatingChannel::FindFirst(0,
+                                                             0,
+                                                             bw,
+                                                             WIFI_STANDARD_80211ax,
+                                                             txPpduPhy->GetPhyBand(),
+                                                             startChannel));
+                    delay += Seconds(1);
+                    Simulator::Schedule(delay,
+                                        &SpectrumWifiPhyMultipleInterfacesTest::SendPpdu,
+                                        this,
+                                        txPpduPhy,
+                                        txPowerDbm);
+                    Simulator::Schedule(delay + txOngoingAfterTxStartedDelay,
+                                        &SpectrumWifiPhyMultipleInterfacesTest::SwitchChannel,
+                                        this,
+                                        j,
+                                        band,
+                                        channel,
+                                        channelWidth);
+                    for (std::size_t k = 0; k < 4; ++k)
+                    {
+                        Simulator::Schedule(delay + flushResultsDelay,
+                                            &SpectrumWifiPhyMultipleInterfacesTest::Reset,
+                                            this);
+                        if ((i != j) && (k == i))
+                        {
+                            continue;
+                        }
+                        const auto expectCcaBusyIndication =
+                            (k == i) ? (txPowerDbm >= ccaEdThresholdDbm)
+                                     : (m_trackSignalsInactiveInterfaces
+                                            ? ((txPowerDbm >= ccaEdThresholdDbm) ? (j == k) : false)
+                                            : false);
+                        Simulator::Schedule(
+                            delay + checkResultsDelay,
+                            &SpectrumWifiPhyMultipleInterfacesTest::CheckCcaIndication,
+                            this,
+                            k,
+                            expectCcaBusyIndication,
+                            txOngoingAfterTxStartedDelay);
+                    }
+                }
+            }
+        }
+    }
+
+    Simulator::Run();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
  * \brief Spectrum Wifi Phy Test Suite
  */
 class SpectrumWifiPhyTestSuite : public TestSuite
@@ -742,6 +1510,8 @@
     AddTestCase(new SpectrumWifiPhyBasicTest, TestCase::QUICK);
     AddTestCase(new SpectrumWifiPhyListenerTest, TestCase::QUICK);
     AddTestCase(new SpectrumWifiPhyFilterTest, TestCase::QUICK);
+    AddTestCase(new SpectrumWifiPhyMultipleInterfacesTest(false), TestCase::QUICK);
+    AddTestCase(new SpectrumWifiPhyMultipleInterfacesTest(true), TestCase::QUICK);
 }
 
 static SpectrumWifiPhyTestSuite spectrumWifiPhyTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/tx-duration-test.cc ns-3.39/src/wifi/test/tx-duration-test.cc
--- ns-3.38/src/wifi/test/tx-duration-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/tx-duration-test.cc	2023-07-05 14:23:48.125428400 -0700
@@ -19,7 +19,8 @@
  */
 
 #include "ns3/dsss-phy.h"
-#include "ns3/eht-phy.h" //includes OFDM, HT, VHT and HE
+#include "ns3/eht-phy.h"  //includes OFDM, HT, VHT and HE
+#include "ns3/eht-ppdu.h" //includes OFDM, HT, VHT and HE
 #include "ns3/erp-ofdm-phy.h"
 #include "ns3/he-ru.h"
 #include "ns3/log.h"
@@ -287,7 +288,7 @@
         staIds.push_back(staId++);
     }
     txVector.SetSigBMode(VhtPhy::GetVhtMcs0());
-    txVector.SetRuAllocation({192, 192});
+    txVector.SetRuAllocation({192, 192}, 0);
 
     Ptr<YansWifiPhy> phy = CreateObject<YansWifiPhy>();
     std::list<WifiPhyBand> testedBands{
@@ -303,7 +304,7 @@
                 MicroSeconds(6); // 2.4 GHz band should be at the end of the bands to test
         }
         Time calculatedDuration = NanoSeconds(0);
-        uint32_t longuestSize = 0;
+        uint32_t longestSize = 0;
         auto iterStaId = staIds.begin();
         for (auto& size : sizes)
         {
@@ -313,7 +314,7 @@
             {
                 calculatedDuration = ppduDurationForSta;
                 staId = *iterStaId;
-                longuestSize = size;
+                longestSize = size;
             }
             ++iterStaId;
         }
@@ -322,7 +323,7 @@
         if (calculatedDuration != knownDuration ||
             calculatedDuration != calculatedDurationUsingList)
         {
-            std::cerr << "size=" << longuestSize << " band=" << testedBand << " staId=" << staId
+            std::cerr << "size=" << longestSize << " band=" << testedBand << " staId=" << staId
                       << " nss=" << +txVector.GetNss(staId) << " mode=" << txVector.GetMode(staId)
                       << " channelWidth=" << channelWidth << " guardInterval=" << guardInterval
                       << " datarate="
@@ -1302,47 +1303,76 @@
 class HeSigBDurationTest : public TestCase
 {
   public:
-    HeSigBDurationTest();
-    ~HeSigBDurationTest() override;
-    void DoRun() override;
+    /**
+     * Constructor
+     *
+     * \param userInfos the HE MU specific per-user information to use for the test
+     * \param sigBMode the mode to transmit HE-SIG-B for the test
+     * \param channelWidth the channel width in MHz to select for the test
+     * \param expectedRuAllocation the expected RU_ALLOCATION
+     * \param expectedNumUsersPerCc the expected number of users per content channel
+     * \param expectedSigBDuration the expected duration of the HE-SIG-B header
+     */
+    HeSigBDurationTest(const std::list<HeMuUserInfo>& userInfos,
+                       const WifiMode& sigBMode,
+                       uint16_t channelWidth,
+                       const RuAllocation& expectedRuAllocation,
+                       const std::pair<std::size_t, std::size_t>& expectedNumUsersPerCc,
+                       Time expectedSigBDuration);
 
   private:
+    void DoRun() override;
+
     /**
-     * Build a TXVECTOR for HE MU with the given bandwidth and user information.
-     *
-     * \param bw the channel width of the PPDU in MHz
-     * \param userInfos the list of HE MU specific user transmission parameters
+     * Build a TXVECTOR for HE MU.
      *
      * \return the configured HE MU TXVECTOR
      */
-    static WifiTxVector BuildTxVector(uint16_t bw, std::list<HeMuUserInfo> userInfos);
-};
+    WifiTxVector BuildTxVector() const;
 
-HeSigBDurationTest::HeSigBDurationTest()
-    : TestCase("Check HE-SIG-B duration computation")
-{
-}
+    std::list<HeMuUserInfo> m_userInfos; ///< HE MU specific per-user information
+    WifiMode m_sigBMode;                 ///< Mode used to transmit HE-SIG-B
+    uint16_t m_channelWidth;             ///< Channel width in MHz
+    RuAllocation m_expectedRuAllocation; ///< Expected RU_ALLOCATION
+    std::pair<std::size_t, std::size_t>
+        m_expectedNumUsersPerCc; ///< Expected number of users per content channel
+    Time m_expectedSigBDuration; ///< Expected duration of the HE-SIG-B header
+};
 
-HeSigBDurationTest::~HeSigBDurationTest()
+HeSigBDurationTest::HeSigBDurationTest(
+    const std::list<HeMuUserInfo>& userInfos,
+    const WifiMode& sigBMode,
+    uint16_t channelWidth,
+    const RuAllocation& expectedRuAllocation,
+    const std::pair<std::size_t, std::size_t>& expectedNumUsersPerCc,
+    Time expectedSigBDuration)
+    : TestCase{"Check HE-SIG-B duration computation"},
+      m_userInfos{userInfos},
+      m_sigBMode{sigBMode},
+      m_channelWidth{channelWidth},
+      m_expectedRuAllocation{expectedRuAllocation},
+      m_expectedNumUsersPerCc{expectedNumUsersPerCc},
+      m_expectedSigBDuration{expectedSigBDuration}
 {
 }
 
 WifiTxVector
-HeSigBDurationTest::BuildTxVector(uint16_t bw, std::list<HeMuUserInfo> userInfos)
+HeSigBDurationTest::BuildTxVector() const
 {
     WifiTxVector txVector;
     txVector.SetPreambleType(WIFI_PREAMBLE_HE_MU);
-    txVector.SetChannelWidth(bw);
+    txVector.SetChannelWidth(m_channelWidth);
     txVector.SetGuardInterval(3200);
     txVector.SetStbc(0);
     txVector.SetNess(0);
     std::list<uint16_t> staIds;
     uint16_t staId = 1;
-    for (const auto& userInfo : userInfos)
+    for (const auto& userInfo : m_userInfos)
     {
         txVector.SetHeMuUserInfo(staId, userInfo);
         staIds.push_back(staId++);
     }
+    txVector.SetSigBMode(m_sigBMode);
     return txVector;
 }
 
@@ -1350,107 +1380,40 @@
 HeSigBDurationTest::DoRun()
 {
     const auto& hePhy = WifiPhy::GetStaticPhyEntity(WIFI_MOD_CLASS_HE);
+    const auto& txVector = BuildTxVector();
 
-    // 20 MHz band
-    std::list<HeMuUserInfo> userInfos;
-    userInfos.push_back({{HeRu::RU_106_TONE, 1, true}, 11, 1});
-    userInfos.push_back({{HeRu::RU_106_TONE, 2, true}, 10, 4});
-    WifiTxVector txVector = BuildTxVector(20, userInfos);
-    txVector.SetSigBMode(VhtPhy::GetVhtMcs5());
-    txVector.SetRuAllocation({96});
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          VhtPhy::GetVhtMcs5(),
-                          "HE-SIG-B should be sent at MCS 5");
-    std::pair<std::size_t, std::size_t> numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel();
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first,
-                          2,
-                          "Both users should be on HE-SIG-B content channel 1");
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.second,
-                          0,
-                          "Both users should be on HE-SIG-B content channel 2");
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetDuration(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          MicroSeconds(4),
-                          "HE-SIG-B should only last one OFDM symbol");
-
-    // 40 MHz band, even number of users per HE-SIG-B content channel
-    userInfos.push_back({{HeRu::RU_52_TONE, 5, true}, 4, 1});
-    userInfos.push_back({{HeRu::RU_52_TONE, 6, true}, 6, 2});
-    userInfos.push_back({{HeRu::RU_52_TONE, 7, true}, 5, 3});
-    userInfos.push_back({{HeRu::RU_52_TONE, 8, true}, 6, 2});
-    txVector = BuildTxVector(40, userInfos);
-    txVector.SetSigBMode(VhtPhy::GetVhtMcs4());
-    txVector.SetRuAllocation({96, 112});
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          VhtPhy::GetVhtMcs4(),
-                          "HE-SIG-B should be sent at MCS 4");
-    numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel();
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first,
-                          2,
-                          "Two users should be on HE-SIG-B content channel 1");
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.second,
-                          4,
-                          "Four users should be on HE-SIG-B content channel 2");
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetDuration(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          MicroSeconds(4),
-                          "HE-SIG-B should only last one OFDM symbol");
-
-    // 40 MHz band, odd number of users per HE-SIG-B content channel
-    userInfos.push_back({{HeRu::RU_26_TONE, 14, true}, 3, 1});
-    txVector = BuildTxVector(40, userInfos);
-    txVector.SetSigBMode(VhtPhy::GetVhtMcs3());
-    txVector.SetRuAllocation({96, 15});
+    // Verify mode for HE-SIG-B field
     NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          VhtPhy::GetVhtMcs3(),
-                          "HE-SIG-B should be sent at MCS 3");
-    numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel();
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first,
-                          2,
-                          "Two users should be on HE-SIG-B content channel 1");
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.second,
-                          5,
-                          "Five users should be on HE-SIG-B content channel 2");
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetDuration(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          MicroSeconds(8),
-                          "HE-SIG-B should last two OFDM symbols");
+                          m_sigBMode,
+                          "Incorrect mode used to send HE-SIG-B");
 
-    // 80 MHz band
-    userInfos.push_back({{HeRu::RU_242_TONE, 3, true}, 1, 1});
-    userInfos.push_back({{HeRu::RU_242_TONE, 4, true}, 4, 1});
-    txVector = BuildTxVector(80, userInfos);
-    txVector.SetSigBMode(VhtPhy::GetVhtMcs1());
-    txVector.SetRuAllocation({96, 15, 192, 192});
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          VhtPhy::GetVhtMcs1(),
-                          "HE-SIG-B should be sent at MCS 1");
-    numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel();
+    // Verify RU_ALLOCATION in TXVECTOR
+    NS_TEST_EXPECT_MSG_EQ((txVector.GetRuAllocation(0) == m_expectedRuAllocation),
+                          true,
+                          "Incorrect RU_ALLOCATION");
+
+    // Verify number of users for content channels 1 and 2
+    const auto& numUsersPerCc =
+        HePpdu::GetNumRusPerHeSigBContentChannel(txVector.GetChannelWidth(),
+                                                 txVector.GetRuAllocation(0));
+    const auto contentChannels = HePpdu::GetHeSigBContentChannels(txVector, 0);
     NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first,
-                          3,
-                          "Three users should be on HE-SIG-B content channel 1");
+                          m_expectedNumUsersPerCc.first,
+                          "Incorrect number of users in HE-SIG-B content channel 1");
     NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.second,
-                          6,
-                          "Six users should be on HE-SIG-B content channel 2");
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetDuration(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          MicroSeconds(16),
-                          "HE-SIG-B should last four OFDM symbols");
+                          m_expectedNumUsersPerCc.second,
+                          "Incorrect number of users in HE-SIG-B content channel 2");
+    NS_TEST_EXPECT_MSG_EQ(contentChannels.at(0).size(),
+                          m_expectedNumUsersPerCc.first,
+                          "Incorrect number of users in HE-SIG-B content channel 1");
+    NS_TEST_EXPECT_MSG_EQ((contentChannels.size() > 1 ? contentChannels.at(1).size() : 0),
+                          m_expectedNumUsersPerCc.second,
+                          "Incorrect number of users in HE-SIG-B content channel 2");
 
-    // 160 MHz band
-    userInfos.push_back({{HeRu::RU_996_TONE, 1, false}, 1, 1});
-    txVector = BuildTxVector(160, userInfos);
-    txVector.SetSigBMode(VhtPhy::GetVhtMcs1());
-    txVector.SetRuAllocation({96, 15, 192, 192, 208, 208, 208, 208});
-    NS_TEST_EXPECT_MSG_EQ(hePhy->GetSigMode(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          VhtPhy::GetVhtMcs1(),
-                          "HE-SIG-B should be sent at MCS 1");
-    numUsersPerCc = txVector.GetNumRusPerHeSigBContentChannel();
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.first,
-                          4,
-                          "Four users should be on HE-SIG-B content channel 1");
-    NS_TEST_EXPECT_MSG_EQ(numUsersPerCc.second,
-                          7,
-                          "Seven users should be on HE-SIG-B content channel 2");
+    // Verify total HE-SIG-B duration
     NS_TEST_EXPECT_MSG_EQ(hePhy->GetDuration(WIFI_PPDU_FIELD_SIG_B, txVector),
-                          MicroSeconds(20),
-                          "HE-SIG-B should last five OFDM symbols");
+                          m_expectedSigBDuration,
+                          "Incorrect duration for HE-SIG-B");
 }
 
 /**
@@ -1744,7 +1707,7 @@
     // -> HE MU format
     txVector.SetPreambleType(WIFI_PREAMBLE_HE_MU);
     txVector.SetSigBMode(sigBMode);
-    txVector.SetRuAllocation({96});
+    txVector.SetRuAllocation({96}, 0);
     sections[WIFI_PPDU_FIELD_SIG_A] = {{ppduStart + MicroSeconds(24), ppduStart + MicroSeconds(32)},
                                        sigAMode};
     sections[WIFI_PPDU_FIELD_SIG_B] = {
@@ -1756,7 +1719,7 @@
         sigBMode};
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
     txVector.SetChannelWidth(160); // shouldn't have any impact
-    txVector.SetRuAllocation({96, 113, 113, 113, 113, 113, 113, 113});
+    txVector.SetRuAllocation({96, 113, 113, 113, 113, 113, 113, 113}, 0);
 
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
 
@@ -1789,7 +1752,7 @@
     // -> EHT MU format
     txVector.SetPreambleType(WIFI_PREAMBLE_EHT_MU);
     txVector.SetEhtPpduType(0); // EHT MU transmission
-    txVector.SetRuAllocation({96});
+    txVector.SetRuAllocation({96}, 0);
     sections[WIFI_PPDU_FIELD_U_SIG] = {{ppduStart + MicroSeconds(24), ppduStart + MicroSeconds(32)},
                                        uSigMode};
     sections[WIFI_PPDU_FIELD_EHT_SIG] = {
@@ -1801,7 +1764,7 @@
         ehtSigMode};
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
     txVector.SetChannelWidth(160); // shouldn't have any impact
-    txVector.SetRuAllocation({96, 113, 113, 113, 113, 113, 113, 113});
+    txVector.SetRuAllocation({96, 113, 113, 113, 113, 113, 113, 113}, 0);
 
     CheckPhyHeaderSections(phyEntity->GetPhyHeaderSections(txVector, ppduStart), sections);
 }
@@ -1821,9 +1784,137 @@
 TxDurationTestSuite::TxDurationTestSuite()
     : TestSuite("wifi-devices-tx-duration", UNIT)
 {
-    AddTestCase(new HeSigBDurationTest, TestCase::QUICK);
     AddTestCase(new TxDurationTest, TestCase::QUICK);
+
     AddTestCase(new PhyHeaderSectionsTest, TestCase::QUICK);
+
+    // 20 MHz band, even number of users per HE-SIG-B content channel
+    AddTestCase(new HeSigBDurationTest(
+                    {{{HeRu::RU_106_TONE, 1, true}, 11, 1}, {{HeRu::RU_106_TONE, 2, true}, 10, 4}},
+                    VhtPhy::GetVhtMcs5(),
+                    20,
+                    {96},
+                    std::make_pair(2, 0), // both users in HE-SIG-B content channel 1
+                    MicroSeconds(4)),     // one OFDM symbol;
+                TestCase::QUICK);
+
+    // 40 MHz band, even number of users per HE-SIG-B content channel
+    AddTestCase(
+        new HeSigBDurationTest({{{HeRu::RU_106_TONE, 1, true}, 11, 1},
+                                {{HeRu::RU_106_TONE, 2, true}, 10, 4},
+                                {{HeRu::RU_52_TONE, 5, true}, 4, 1},
+                                {{HeRu::RU_52_TONE, 6, true}, 6, 2},
+                                {{HeRu::RU_52_TONE, 7, true}, 5, 3},
+                                {{HeRu::RU_52_TONE, 8, true}, 6, 2}},
+                               VhtPhy::GetVhtMcs4(),
+                               40,
+                               {96, 112},
+                               std::make_pair(2, 4), // two users in HE-SIG-B content channel 1 and
+                                                     // four users in HE-SIG-B content channel 2
+                               MicroSeconds(4)),     // one OFDM symbol;
+        TestCase::QUICK);
+
+    // 40 MHz band, odd number of users per HE-SIG-B content channel
+    AddTestCase(
+        new HeSigBDurationTest({{{HeRu::RU_106_TONE, 1, true}, 11, 1},
+                                {{HeRu::RU_106_TONE, 2, true}, 10, 4},
+                                {{HeRu::RU_52_TONE, 5, true}, 4, 1},
+                                {{HeRu::RU_52_TONE, 6, true}, 6, 2},
+                                {{HeRu::RU_52_TONE, 7, true}, 5, 3},
+                                {{HeRu::RU_52_TONE, 8, true}, 6, 2},
+                                {{HeRu::RU_26_TONE, 14, true}, 3, 1}},
+                               VhtPhy::GetVhtMcs3(),
+                               40,
+                               {96, 15},
+                               std::make_pair(2, 5), // two users in HE-SIG-B content channel 1 and
+                                                     // five users in HE-SIG-B content channel 2
+                               MicroSeconds(8)),     // two OFDM symbols
+        TestCase::QUICK);
+
+    // 80 MHz band
+    AddTestCase(
+        new HeSigBDurationTest({{{HeRu::RU_106_TONE, 1, true}, 11, 1},
+                                {{HeRu::RU_106_TONE, 2, true}, 10, 4},
+                                {{HeRu::RU_52_TONE, 5, true}, 4, 1},
+                                {{HeRu::RU_52_TONE, 6, true}, 6, 2},
+                                {{HeRu::RU_52_TONE, 7, true}, 5, 3},
+                                {{HeRu::RU_52_TONE, 8, true}, 6, 2},
+                                {{HeRu::RU_26_TONE, 14, true}, 3, 1},
+                                {{HeRu::RU_242_TONE, 3, true}, 1, 1},
+                                {{HeRu::RU_242_TONE, 4, true}, 4, 1}},
+                               VhtPhy::GetVhtMcs1(),
+                               80,
+                               {96, 15, 192, 192},
+                               std::make_pair(3, 6), // three users in HE-SIG-B content channel 1
+                                                     // and six users in HE-SIG-B content channel 2
+                               MicroSeconds(16)),    // four OFDM symbols
+        TestCase::QUICK);
+
+    // 160 MHz band
+    AddTestCase(
+        new HeSigBDurationTest({{{HeRu::RU_106_TONE, 1, true}, 11, 1},
+                                {{HeRu::RU_106_TONE, 2, true}, 10, 4},
+                                {{HeRu::RU_52_TONE, 5, true}, 4, 1},
+                                {{HeRu::RU_52_TONE, 6, true}, 6, 2},
+                                {{HeRu::RU_52_TONE, 7, true}, 5, 3},
+                                {{HeRu::RU_52_TONE, 8, true}, 6, 2},
+                                {{HeRu::RU_26_TONE, 14, true}, 3, 1},
+                                {{HeRu::RU_242_TONE, 3, true}, 1, 1},
+                                {{HeRu::RU_242_TONE, 4, true}, 4, 1},
+                                {{HeRu::RU_996_TONE, 1, false}, 1, 1}},
+                               VhtPhy::GetVhtMcs1(),
+                               160,
+                               {96, 15, 192, 192, 208, 208, 208, 208},
+                               std::make_pair(4, 7), // four users in HE-SIG-B content channel 1 and
+                                                     // seven users in HE-SIG-B content channel 2
+                               MicroSeconds(20)),    // five OFDM symbols
+        TestCase::QUICK);
+
+    // 20 MHz band, one unallocated RU at the middle
+    AddTestCase(
+        new HeSigBDurationTest({{{HeRu::RU_26_TONE, 1, true}, 11, 1},
+                                {{HeRu::RU_26_TONE, 2, true}, 11, 1},
+                                {{HeRu::RU_26_TONE, 3, true}, 11, 1},
+                                {{HeRu::RU_26_TONE, 4, true}, 11, 1},
+                                {{HeRu::RU_26_TONE, 6, true}, 11, 1},
+                                {{HeRu::RU_26_TONE, 7, true}, 11, 1},
+                                {{HeRu::RU_26_TONE, 8, true}, 11, 1},
+                                {{HeRu::RU_26_TONE, 9, true}, 11, 1}},
+                               VhtPhy::GetVhtMcs5(),
+                               20,
+                               {0},
+                               std::make_pair(9, 0), // 9 users in HE-SIG-B content channel 1
+                               MicroSeconds(8)),     // two OFDM symbols;
+        TestCase::QUICK);
+
+    // 40 MHz band, unallocated RUs at the begin and at the end of the first 20 MHz subband and in
+    // the middle of the second 20 MHz subband
+    AddTestCase(
+        new HeSigBDurationTest({{{HeRu::RU_52_TONE, 2, true}, 10, 1},
+                                {{HeRu::RU_52_TONE, 3, true}, 10, 2},
+                                {{HeRu::RU_52_TONE, 5, true}, 11, 1},
+                                {{HeRu::RU_52_TONE, 8, true}, 11, 2}},
+                               VhtPhy::GetVhtMcs5(),
+                               40,
+                               {112, 112},
+                               std::make_pair(4, 4), // 4 users in both HE-SIG-B content channels
+                               MicroSeconds(4)),     // two OFDM symbols;
+        TestCase::QUICK);
+
+    // 40 MHz band, one unallocated RUs in the first 20 MHz subband and two unallocated RUs in
+    // second 20 MHz subband
+    AddTestCase(
+        new HeSigBDurationTest({{{HeRu::RU_52_TONE, 1, true}, 10, 1},
+                                {{HeRu::RU_52_TONE, 2, true}, 10, 2},
+                                {{HeRu::RU_52_TONE, 3, true}, 11, 1},
+                                {{HeRu::RU_52_TONE, 5, true}, 11, 2},
+                                {{HeRu::RU_52_TONE, 6, true}, 11, 3}},
+                               VhtPhy::GetVhtMcs5(),
+                               40,
+                               {112, 112},
+                               std::make_pair(4, 4), // 4 users in both HE-SIG-B content channels
+                               MicroSeconds(4)),     // two OFDM symbols;
+        TestCase::QUICK);
 }
 
 static TxDurationTestSuite g_txDurationTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-eht-info-elems-test.cc ns-3.39/src/wifi/test/wifi-eht-info-elems-test.cc
--- ns-3.38/src/wifi/test/wifi-eht-info-elems-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-eht-info-elems-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -17,6 +17,7 @@
  * Author: Stefano Avallone <stavallo@unina.it>
  */
 
+#include "ns3/address-utils.h"
 #include "ns3/header-serialization-test.h"
 #include "ns3/log.h"
 #include "ns3/mgt-headers.h"
@@ -25,6 +26,7 @@
 #include "ns3/tid-to-link-mapping-element.h"
 #include "ns3/wifi-phy-operating-channel.h"
 
+#include <optional>
 #include <sstream>
 #include <vector>
 
@@ -62,13 +64,12 @@
   private:
     void DoRun() override;
 
-    WifiMacType m_frameType; //!< the type of frame possibly included in the MLE
+    MgtAssocRequestHeader m_outerAssoc; //!< the frame containing the MLE
 };
 
 BasicMultiLinkElementTest::BasicMultiLinkElementTest()
     : HeaderSerializationTestCase(
-          "Check serialization and deserialization of Basic variant Multi-Link elements"),
-      m_frameType(WIFI_MAC_MGT_ASSOCIATION_REQUEST)
+          "Check serialization and deserialization of Basic variant Multi-Link elements")
 {
 }
 
@@ -81,7 +82,7 @@
     const CommonInfoBasicMle& commonInfo,
     std::vector<MultiLinkElement::PerStaProfileSubelement> subelements)
 {
-    MultiLinkElement mle(MultiLinkElement::BASIC_VARIANT, m_frameType);
+    MultiLinkElement mle(MultiLinkElement::BASIC_VARIANT);
     mle.SetMldMacAddress(commonInfo.m_mldMacAddress);
     if (commonInfo.m_linkIdInfo.has_value())
     {
@@ -99,12 +100,22 @@
             commonInfo.m_mediumSyncDelayInfo->mediumSyncOfdmEdThreshold - 72);
         mle.SetMediumSyncMaxNTxops(commonInfo.m_mediumSyncDelayInfo->mediumSyncMaxNTxops + 1);
     }
+    if (commonInfo.m_emlCapabilities.has_value())
+    {
+        auto padding = commonInfo.m_emlCapabilities->emlsrPaddingDelay;
+        mle.SetEmlsrPaddingDelay(MicroSeconds(padding == 0 ? 0 : (1 << (4 + padding))));
+        auto transitionD = commonInfo.m_emlCapabilities->emlsrTransitionDelay;
+        mle.SetEmlsrTransitionDelay(MicroSeconds(transitionD == 0 ? 0 : (1 << (3 + transitionD))));
+        auto transitionT = commonInfo.m_emlCapabilities->transitionTimeout;
+        mle.SetTransitionTimeout(MicroSeconds(transitionT == 0 ? 0 : (1 << (6 + transitionT))));
+    }
 
     for (std::size_t i = 0; i < subelements.size(); ++i)
     {
         mle.AddPerStaProfileSubelement();
         mle.GetPerStaProfile(i) = std::move(subelements[i]);
     }
+
     return mle;
 }
 
@@ -116,17 +127,17 @@
     };
 
     // Common Info with MLD MAC address
-    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}), m_frameType);
+    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}));
 
     commonInfo.m_linkIdInfo = 3;
 
     // Adding Link ID Info
-    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}), m_frameType);
+    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}));
 
     commonInfo.m_bssParamsChangeCount = 1;
 
     // Adding BSS Parameters Change Count
-    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}), m_frameType);
+    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}));
 
     commonInfo.m_mediumSyncDelayInfo =
         CommonInfoBasicMle::MediumSyncDelayInfo{.mediumSyncDuration = 1,
@@ -134,37 +145,448 @@
                                                 .mediumSyncMaxNTxops = 5};
 
     // Adding Medium Sync Delay Information
-    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}), m_frameType);
+    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}));
+
+    commonInfo.m_emlCapabilities = CommonInfoBasicMle::EmlCapabilities{.emlsrSupport = 1,
+                                                                       .emlsrPaddingDelay = 4,
+                                                                       .emlsrTransitionDelay = 5,
+                                                                       .transitionTimeout = 10};
+
+    // Adding Medium Sync Delay Information
+    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {}));
 
-    SupportedRates rates;
-    rates.AddSupportedRate(6);
-    rates.AddSupportedRate(12);
-    rates.AddSupportedRate(24);
+    /**
+     * To test the serialization/deserialization of Per-STA Profile subelements, we include
+     * the Multi-Link Element in an Association Request frame
+     */
 
     CapabilityInformation capabilities;
     capabilities.SetShortPreamble(true);
     capabilities.SetShortSlotTime(true);
     capabilities.SetEss();
 
+    m_outerAssoc.SetListenInterval(0);
+    m_outerAssoc.Capabilities() = capabilities;
+    m_outerAssoc.Get<Ssid>() = Ssid("MySsid");
+
+    AllSupportedRates rates;
+    rates.AddSupportedRate(6e6);
+    rates.AddSupportedRate(9e6);
+    rates.AddSupportedRate(12e6);
+    rates.AddSupportedRate(18e6);
+    rates.AddSupportedRate(24e6);
+    rates.AddSupportedRate(36e6);
+    rates.AddSupportedRate(48e6);
+    rates.AddSupportedRate(54e6);
+    // extended rates
+    rates.AddSupportedRate(1e6);
+    rates.AddSupportedRate(2e6);
+
+    m_outerAssoc.Get<SupportedRates>() = rates.rates;
+    m_outerAssoc.Get<ExtendedSupportedRatesIE>() = rates.extendedRates;
+
+    EhtCapabilities ehtCapabilities;
+    for (auto maxMcs : {7, 9, 11, 13})
+    {
+        ehtCapabilities.SetSupportedRxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY,
+                                                   maxMcs,
+                                                   1);
+        ehtCapabilities.SetSupportedTxEhtMcsAndNss(EhtMcsAndNssSet::EHT_MCS_MAP_TYPE_20_MHZ_ONLY,
+                                                   maxMcs,
+                                                   1);
+    }
+
+    m_outerAssoc.Get<HeCapabilities>().emplace();
+    m_outerAssoc.Get<EhtCapabilities>() = ehtCapabilities;
+
+    // The Association Request included in the first Per-STA Profile subelement is identical
+    // to the containing frame, so that all the IEs are inherited and the Per-STA Profile
+    // does not contain any Information Element.
+
+    MultiLinkElement::PerStaProfileSubelement perStaProfile1(MultiLinkElement::BASIC_VARIANT);
+    perStaProfile1.SetLinkId(3);
+    perStaProfile1.SetCompleteProfile();
+    perStaProfile1.SetAssocRequest(m_outerAssoc);
+
+    /* Association Request included in the second Per-STA Profile subelement */
     MgtAssocRequestHeader assoc;
-    assoc.SetSsid(Ssid("MySsid"));
-    assoc.SetSupportedRates(rates);
-    assoc.SetCapabilities(capabilities);
-    assoc.SetListenInterval(0);
-
-    MultiLinkElement::PerStaProfileSubelement perStaProfile(MultiLinkElement::BASIC_VARIANT,
-                                                            m_frameType);
-    perStaProfile.SetLinkId(5);
-    perStaProfile.SetCompleteProfile();
-    perStaProfile.SetStaMacAddress(Mac48Address("ba:98:76:54:32:10"));
-    perStaProfile.SetAssocRequest(assoc);
-
-    // Adding Per-STA Profile Subelement
-    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {perStaProfile}), m_frameType);
-
-    // Adding two Per-STA Profile Subelements
-    TestHeaderSerialization(GetMultiLinkElement(commonInfo, {perStaProfile, perStaProfile}),
-                            m_frameType);
+    assoc.Capabilities() = capabilities;
+    // we simulate a "mistake" by adding an Ssid IE, which cannot be included in the
+    // Per-STA Profile subelement. We will check that this Ssid is not serialized
+    assoc.Get<Ssid>() = Ssid("OtherSsid");
+    // another "mistake" of the same type, except that a TID-To-Link Mapping element
+    // is not included in the containing frame
+    assoc.Get<TidToLinkMapping>().emplace();
+    // the SupportedRates IE is the same (hence not serialized) as in the containing frame,
+    // while the ExtendedSupportedRatesIE is different (hence serialized)
+    rates.AddSupportedRate(5.5e6);
+    rates.AddSupportedRate(11e6);
+    assoc.Get<SupportedRates>() = rates.rates;
+    assoc.Get<ExtendedSupportedRatesIE>() = rates.extendedRates;
+    // a VhtCapabilities IE is not present in the containing frame, hence it is serialized
+    assoc.Get<VhtCapabilities>().emplace();
+    // HeCapabilities IE is present in the containing frame and in the Per-STA Profile subelement,
+    // hence it is not serialized
+    assoc.Get<HeCapabilities>().emplace();
+    // EhtCapabilities IE is present in the containing frame but not in the Per-STA Profile
+    //  subelement, hence it is listed in a Non-Inheritance element
+
+    MultiLinkElement::PerStaProfileSubelement perStaProfile2(MultiLinkElement::BASIC_VARIANT);
+    perStaProfile2.SetLinkId(0);
+    perStaProfile2.SetCompleteProfile();
+    perStaProfile2.SetStaMacAddress(Mac48Address("ba:98:76:54:32:10"));
+    perStaProfile2.SetAssocRequest(assoc);
+
+    // The Association Request included in the third Per-STA Profile subelement has the
+    // EHT Capabilities element (which is inherited and not serialized) but it does not have the
+    // Ssid element, which is not listed in the Non-Inheritance element because it shall not
+    // appear in a Per-STA Profile subelement.
+    assoc.Get<Ssid>().reset();
+    assoc.Get<EhtCapabilities>() = ehtCapabilities;
+
+    auto perStaProfile3 = perStaProfile2;
+    perStaProfile3.SetAssocRequest(assoc);
+
+    // Adding MLE with two Per-STA Profile Subelements
+    m_outerAssoc.Get<MultiLinkElement>() =
+        GetMultiLinkElement(commonInfo, {perStaProfile1, perStaProfile2, perStaProfile3});
+
+    // first, check that serialization/deserialization of the whole Association Request works
+    TestHeaderSerialization(m_outerAssoc);
+
+    // now, "manually" serialize and deserialize the header to check that the expected elements
+    // have been serialized
+    Buffer buffer;
+    buffer.AddAtStart(m_outerAssoc.GetSerializedSize());
+    m_outerAssoc.Serialize(buffer.Begin());
+
+    auto i = buffer.Begin();
+    i = CapabilityInformation().Deserialize(i);
+    i.ReadLsbtohU16(); // Listen interval
+
+    auto tmp = i;
+    i = Ssid().DeserializeIfPresent(tmp);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp), 0, "Ssid element not present");
+
+    i = SupportedRates().DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp), 0, "SupportedRates element not present");
+
+    i = ExtendedSupportedRatesIE().DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp),
+                          0,
+                          "ExtendedSupportedRatesIE element not present");
+
+    i = HeCapabilities().DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp), 0, "HeCapabilities element not present");
+
+    // deserialize Multi-Link Element
+    NS_TEST_EXPECT_MSG_EQ(i.ReadU8(), IE_EXTENSION, "IE_EXTENSION expected at the begin of MLE");
+    i.ReadU8(); // length
+    NS_TEST_EXPECT_MSG_EQ(i.ReadU8(),
+                          IE_EXT_MULTI_LINK_ELEMENT,
+                          "IE_EXT_MULTI_LINK_ELEMENT expected");
+
+    uint16_t mlControl = i.ReadLsbtohU16();
+    auto nBytes = CommonInfoBasicMle().Deserialize(i, mlControl >> 4);
+    i.Next(nBytes);
+
+    // first Per-STA Profile subelement
+    NS_TEST_EXPECT_MSG_EQ(i.ReadU8(),
+                          MultiLinkElement::PER_STA_PROFILE_SUBELEMENT_ID,
+                          "PER_STA_PROFILE_SUBELEMENT_ID expected");
+    i.ReadU8();        // length
+    i.ReadLsbtohU16(); // STA Control field
+    i.ReadU8();        // STA Info Length
+    // no STA address
+    i = CapabilityInformation().Deserialize(i);
+    // no Information Element
+
+    // second Per-STA Profile subelement
+    NS_TEST_EXPECT_MSG_EQ(i.ReadU8(),
+                          MultiLinkElement::PER_STA_PROFILE_SUBELEMENT_ID,
+                          "PER_STA_PROFILE_SUBELEMENT_ID expected");
+    i.ReadU8();        // length
+    i.ReadLsbtohU16(); // STA Control field
+    i.ReadU8();        // STA Info Length
+    Mac48Address address;
+    ReadFrom(i, address);
+    i = CapabilityInformation().Deserialize(i);
+    // no Listen interval
+    // Ssid element not present (as mandated by specs)
+    // SupportedRates not present because it is inherited
+
+    i = ExtendedSupportedRatesIE().DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp),
+                          0,
+                          "ExtendedSupportedRatesIE element not present");
+
+    i = VhtCapabilities().DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp), 0, "VhtCapabilities element not present");
+
+    // HeCapabilities not present because it is inherited
+    NonInheritance nonInheritance;
+    i = nonInheritance.DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp), 0, "Non-Inheritance element not present");
+    NS_TEST_EXPECT_MSG_EQ(nonInheritance.IsPresent(IE_EXTENSION, IE_EXT_EHT_CAPABILITIES),
+                          true,
+                          "Non-Inheritance does not indicate EhtCapabilities");
+    NS_TEST_EXPECT_MSG_EQ(nonInheritance.m_elemIdList.size(),
+                          0,
+                          "Unexpected size for Elem ID list of Non-Inheritance element");
+    NS_TEST_EXPECT_MSG_EQ(nonInheritance.m_elemIdExtList.size(),
+                          1,
+                          "Unexpected size for Elem ID list of Non-Inheritance element");
+
+    // third Per-STA Profile subelement
+    NS_TEST_EXPECT_MSG_EQ(i.ReadU8(),
+                          MultiLinkElement::PER_STA_PROFILE_SUBELEMENT_ID,
+                          "PER_STA_PROFILE_SUBELEMENT_ID expected");
+    i.ReadU8();        // length
+    i.ReadLsbtohU16(); // STA Control field
+    i.ReadU8();        // STA Info Length
+    ReadFrom(i, address);
+    i = CapabilityInformation().Deserialize(i);
+    // no Listen interval
+    // Ssid element not present (as mandated by specs)
+    // SupportedRates not present because it is inherited
+
+    i = ExtendedSupportedRatesIE().DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp),
+                          0,
+                          "ExtendedSupportedRatesIE element not present");
+
+    i = VhtCapabilities().DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp), 0, "VhtCapabilities element not present");
+
+    // HeCapabilities not present because it is inherited
+    // EhtCapabilities not present because it is inherited
+
+    // the Multi-Link Element is done, we shall now find the EHT Capabilities of the
+    // containing Association Request frame
+    ehtCapabilities = EhtCapabilities(true, m_outerAssoc.Get<HeCapabilities>().value());
+    i = ehtCapabilities.DeserializeIfPresent(tmp = i);
+    NS_TEST_EXPECT_MSG_GT(i.GetDistanceFrom(tmp), 0, "EhtCapabilities element not present");
+
+    /**
+     * Yet another test: use the Deserialize method of the management frame and check that
+     * inherited Information Elements have been copied
+     */
+    MgtAssocRequestHeader frame;
+    auto count = frame.Deserialize(buffer.Begin());
+
+    NS_TEST_EXPECT_MSG_EQ(count, buffer.GetSize(), "Unexpected number of deserialized bytes");
+
+    // containing frame
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<Ssid>().has_value(),
+                          true,
+                          "Containing frame should have SSID IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Containing frame should have Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<ExtendedSupportedRatesIE>().has_value(),
+                          true,
+                          "Containing frame should have Extended Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<HtCapabilities>().has_value(),
+                          false,
+                          "Containing frame should not have HT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<ExtendedCapabilities>().has_value(),
+                          false,
+                          "Containing frame should not have Extended Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<VhtCapabilities>().has_value(),
+                          false,
+                          "Containing frame should not have VHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<HeCapabilities>().has_value(),
+                          true,
+                          "Containing frame should have HE Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<MultiLinkElement>().has_value(),
+                          true,
+                          "Containing frame should have Multi-Link Element IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<EhtCapabilities>().has_value(),
+                          true,
+                          "Containing frame should have EHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<TidToLinkMapping>().has_value(),
+                          false,
+                          "Containing frame should not have TID-to-Link Mapping IE");
+
+    auto& mle = frame.Get<MultiLinkElement>().value();
+
+    NS_TEST_EXPECT_MSG_EQ(mle.GetNPerStaProfileSubelements(),
+                          3,
+                          "Unexpected number of Per-STA Profile subelements");
+
+    // frame in first Per-STA Profile subelement has inherited all the IEs but SSID and
+    // Multi-Link Element IEs
+    auto& perSta1 = mle.GetPerStaProfile(0);
+    NS_TEST_EXPECT_MSG_EQ(perSta1.HasAssocRequest(),
+                          true,
+                          "First Per-STA Profile should contain an Association Request frame");
+    auto& perSta1Frame =
+        std::get<std::reference_wrapper<MgtAssocRequestHeader>>(perSta1.GetAssocRequest()).get();
+
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<Ssid>().has_value(),
+                          false,
+                          "Frame in first Per-STA Profile should not have SSID IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Frame in first Per-STA Profile should have Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta1Frame.Get<SupportedRates>() == frame.Get<SupportedRates>()),
+        true,
+        "Supported Rates IE not correctly inherited by frame in first Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<ExtendedSupportedRatesIE>().has_value(),
+                          true,
+                          "Frame in first Per-STA Profile should have Extended Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta1Frame.Get<ExtendedSupportedRatesIE>() == frame.Get<ExtendedSupportedRatesIE>()),
+        true,
+        "Extended Supported Rates IE not correctly inherited by frame in first Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<HtCapabilities>().has_value(),
+                          false,
+                          "Frame in first Per-STA Profile should not have HT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        perSta1Frame.Get<ExtendedCapabilities>().has_value(),
+        false,
+        "Frame in first Per-STA Profile should not have Extended Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<VhtCapabilities>().has_value(),
+                          false,
+                          "Frame in first Per-STA Profile should not have VHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<HeCapabilities>().has_value(),
+                          true,
+                          "Frame in first Per-STA Profile should have HE Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta1Frame.Get<HeCapabilities>() == frame.Get<HeCapabilities>()),
+        true,
+        "HE Capabilities IE not correctly inherited by frame in first Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<MultiLinkElement>().has_value(),
+                          false,
+                          "Frame in first Per-STA Profile should not have Multi-Link Element IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<EhtCapabilities>().has_value(),
+                          true,
+                          "Frame in first Per-STA Profile should have EHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta1Frame.Get<EhtCapabilities>() == frame.Get<EhtCapabilities>()),
+        true,
+        "EHT Capabilities IE not correctly inherited by frame in first Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta1Frame.Get<TidToLinkMapping>().has_value(),
+                          false,
+                          "Frame in first Per-STA Profile should not have TID-to-Link Mapping IE");
+
+    // frame in second Per-STA Profile subelement includes VHT Capabilities IE and has inherited
+    //  all the IEs but SSID IE, Multi-Link Element IE, Extended Supported Rates IE (different
+    // than in containing frame) and EHT Capabilities IE (listed in Non-Inheritance IE).
+    auto& perSta2 = mle.GetPerStaProfile(1);
+    NS_TEST_EXPECT_MSG_EQ(perSta2.HasAssocRequest(),
+                          true,
+                          "Second Per-STA Profile should contain an Association Request frame");
+    auto& perSta2Frame =
+        std::get<std::reference_wrapper<MgtAssocRequestHeader>>(perSta2.GetAssocRequest()).get();
+
+    NS_TEST_EXPECT_MSG_EQ(perSta2Frame.Get<Ssid>().has_value(),
+                          false,
+                          "Frame in second Per-STA Profile should not have SSID IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta2Frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Frame in second Per-STA Profile should have Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta2Frame.Get<SupportedRates>() == frame.Get<SupportedRates>()),
+        true,
+        "Supported Rates IE not correctly inherited by frame in second Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(
+        perSta2Frame.Get<ExtendedSupportedRatesIE>().has_value(),
+        true,
+        "Frame in second Per-STA Profile should have Extended Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta2Frame.Get<ExtendedSupportedRatesIE>() == frame.Get<ExtendedSupportedRatesIE>()),
+        false,
+        "Extended Supported Rates IE should have not been inherited by frame in second Per-STA "
+        "Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta2Frame.Get<HtCapabilities>().has_value(),
+                          false,
+                          "Frame in second Per-STA Profile should not have HT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        perSta2Frame.Get<ExtendedCapabilities>().has_value(),
+        false,
+        "Frame in second Per-STA Profile should not have Extended Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta2Frame.Get<VhtCapabilities>().has_value(),
+                          true,
+                          "Frame in second Per-STA Profile should have VHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta2Frame.Get<HeCapabilities>().has_value(),
+                          true,
+                          "Frame in second Per-STA Profile should have HE Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta2Frame.Get<HeCapabilities>() == frame.Get<HeCapabilities>()),
+        true,
+        "HE Capabilities IE not correctly inherited by frame in second Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta2Frame.Get<MultiLinkElement>().has_value(),
+                          false,
+                          "Frame in second Per-STA Profile should not have Multi-Link Element IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        perSta2Frame.Get<EhtCapabilities>().has_value(),
+        false,
+        "Frame in second Per-STA Profile should have not inherited EHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta2Frame.Get<TidToLinkMapping>().has_value(),
+                          false,
+                          "Frame in second Per-STA Profile should not have TID-to-Link Mapping IE");
+
+    // frame in third Per-STA Profile subelement includes VHT Capabilities IE and has inherited
+    //  all the IEs but SSID IE, Multi-Link Element IE and Extended Supported Rates IE (different
+    // than in containing frame).
+    auto& perSta3 = mle.GetPerStaProfile(2);
+    NS_TEST_EXPECT_MSG_EQ(perSta3.HasAssocRequest(),
+                          true,
+                          "Third Per-STA Profile should contain an Association Request frame");
+    auto& perSta3Frame =
+        std::get<std::reference_wrapper<MgtAssocRequestHeader>>(perSta3.GetAssocRequest()).get();
+
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<Ssid>().has_value(),
+                          false,
+                          "Frame in third Per-STA Profile should not have SSID IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Frame in third Per-STA Profile should have Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta3Frame.Get<SupportedRates>() == frame.Get<SupportedRates>()),
+        true,
+        "Supported Rates IE not correctly inherited by frame in third Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<ExtendedSupportedRatesIE>().has_value(),
+                          true,
+                          "Frame in third Per-STA Profile should have Extended Supported Rates IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta3Frame.Get<ExtendedSupportedRatesIE>() == frame.Get<ExtendedSupportedRatesIE>()),
+        false,
+        "Extended Supported Rates IE should have not been inherited by frame in third Per-STA "
+        "Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<HtCapabilities>().has_value(),
+                          false,
+                          "Frame in third Per-STA Profile should not have HT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        perSta3Frame.Get<ExtendedCapabilities>().has_value(),
+        false,
+        "Frame in third Per-STA Profile should not have Extended Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<VhtCapabilities>().has_value(),
+                          true,
+                          "Frame in third Per-STA Profile should have VHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<HeCapabilities>().has_value(),
+                          true,
+                          "Frame in third Per-STA Profile should have HE Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta3Frame.Get<HeCapabilities>() == frame.Get<HeCapabilities>()),
+        true,
+        "HE Capabilities IE not correctly inherited by frame in third Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<MultiLinkElement>().has_value(),
+                          false,
+                          "Frame in third Per-STA Profile should not have Multi-Link Element IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        perSta3Frame.Get<EhtCapabilities>().has_value(),
+        true,
+        "Frame in third Per-STA Profile should have inherited EHT Capabilities IE");
+    NS_TEST_EXPECT_MSG_EQ(
+        (perSta3Frame.Get<EhtCapabilities>() == frame.Get<EhtCapabilities>()),
+        true,
+        "EHT Capabilities IE not correctly inherited by frame in third Per-STA Profile");
+    NS_TEST_EXPECT_MSG_EQ(perSta3Frame.Get<TidToLinkMapping>().has_value(),
+                          false,
+                          "Frame in third Per-STA Profile should not have TID-to-Link Mapping IE");
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-emlsr-test.cc ns-3.39/src/wifi/test/wifi-emlsr-test.cc
--- ns-3.38/src/wifi/test/wifi-emlsr-test.cc	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/src/wifi/test/wifi-emlsr-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -0,0 +1,2554 @@
+/*
+ * 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 "ns3/ap-wifi-mac.h"
+#include "ns3/attribute-container.h"
+#include "ns3/boolean.h"
+#include "ns3/config.h"
+#include "ns3/ctrl-headers.h"
+#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/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"
+#include "ns3/rng-seed-manager.h"
+#include "ns3/rr-multi-user-scheduler.h"
+#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>
+#include <iomanip>
+#include <optional>
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE("WifiEmlsrTest");
+
+static uint32_t
+ContextToNodeId(std::string context)
+{
+    std::string sub = context.substr(10);
+    uint32_t pos = sub.find("/Device");
+    uint32_t nodeId = std::stoi(sub.substr(0, pos));
+    return nodeId;
+}
+
+/**
+ * \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")
+{
+}
+
+void
+EmlOperatingModeNotificationTest::DoRun()
+{
+    MgtEmlOperatingModeNotification frame;
+
+    // Both EMLSR Mode and EMLMR Mode subfields set to 0 (no link bitmap);
+    TestHeaderSerialization(frame);
+
+    frame.m_emlControl.emlsrMode = 1;
+    frame.SetLinkIdInBitmap(0);
+    frame.SetLinkIdInBitmap(5);
+    frame.SetLinkIdInBitmap(15);
+
+    // Adding Link Bitmap
+    TestHeaderSerialization(frame);
+
+    NS_TEST_EXPECT_MSG_EQ((frame.GetLinkBitmap() == std::list<uint8_t>{0, 5, 15}),
+                          true,
+                          "Unexpected link bitmap");
+
+    auto padding = MicroSeconds(64);
+    auto transition = MicroSeconds(128);
+
+    frame.m_emlControl.emlsrParamUpdateCtrl = 1;
+    frame.m_emlsrParamUpdate = MgtEmlOperatingModeNotification::EmlsrParamUpdate{};
+    frame.m_emlsrParamUpdate->paddingDelay = CommonInfoBasicMle::EncodeEmlsrPaddingDelay(padding);
+    frame.m_emlsrParamUpdate->transitionDelay =
+        CommonInfoBasicMle::EncodeEmlsrTransitionDelay(transition);
+
+    // Adding the EMLSR Parameter Update field
+    TestHeaderSerialization(frame);
+
+    NS_TEST_EXPECT_MSG_EQ(
+        CommonInfoBasicMle::DecodeEmlsrPaddingDelay(frame.m_emlsrParamUpdate->paddingDelay),
+        padding,
+        "Unexpected EMLSR Padding Delay");
+    NS_TEST_EXPECT_MSG_EQ(
+        CommonInfoBasicMle::DecodeEmlsrTransitionDelay(frame.m_emlsrParamUpdate->transitionDelay),
+        transition,
+        "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 linkId the ID of the link transmitting the PSDUs
+     * \param context the context
+     * \param psduMap the PSDU map
+     * \param txVector the TX vector
+     * \param txPowerW the tx power in Watts
+     */
+    virtual void Transmit(uint8_t linkId,
+                          std::string context,
+                          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;
+
+    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
+    };
+
+    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)
+{
+}
+
+void
+EmlsrOperationsTestBase::Transmit(uint8_t linkId,
+                                  std::string context,
+                                  WifiConstPsduMap psduMap,
+                                  WifiTxVector txVector,
+                                  double txPowerW)
+{
+    m_txPsdus.push_back({Simulator::Now(), psduMap, txVector, linkId});
+    auto txDuration =
+        WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
+
+    for (const auto& [aid, psdu] : psduMap)
+    {
+        std::stringstream ss;
+        ss << std::setprecision(10) << "PSDU #" << m_txPsdus.size() << " Link ID " << +linkId << " "
+           << psdu->GetHeader(0).GetTypeString();
+        if (psdu->GetHeader(0).IsAction())
+        {
+            ss << " ";
+            WifiActionHeader actionHdr;
+            psdu->GetPayload(0)->PeekHeader(actionHdr);
+            actionHdr.Print(ss);
+        }
+        ss << " #MPDUs " << psdu->GetNMpdus() << " duration/ID " << psdu->GetHeader(0).GetDuration()
+           << " RA = " << psdu->GetAddr1() << " TA = " << psdu->GetAddr2()
+           << " ADDR3 = " << psdu->GetHeader(0).GetAddr3()
+           << " ToDS = " << psdu->GetHeader(0).IsToDs()
+           << " FromDS = " << psdu->GetHeader(0).IsFromDs();
+        if (psdu->GetHeader(0).IsQosData())
+        {
+            ss << " seqNo = {";
+            for (auto& mpdu : *PeekPointer(psdu))
+            {
+                ss << mpdu->GetHeader().GetSequenceNumber() << ",";
+            }
+            ss << "} TID = " << +psdu->GetHeader(0).GetQosTid();
+        }
+        NS_LOG_INFO(ss.str());
+    }
+    NS_LOG_INFO("TX duration = " << txDuration.As(Time::MS) << "  TXVECTOR = " << txVector << "\n");
+}
+
+void
+EmlsrOperationsTestBase::DoSetup()
+{
+    RngSeedManager::SetSeed(1);
+    RngSeedManager::SetRun(2);
+    int64_t streamNumber = 100;
+
+    NodeContainer wifiApNode(1);
+    NodeContainer wifiStaNodes(m_nEmlsrStations);
+
+    WifiHelper wifi;
+    // wifi.EnableLogComponents ();
+    wifi.SetStandard(WIFI_STANDARD_80211be);
+    wifi.SetRemoteStationManager("ns3::ConstantRateWifiManager",
+                                 "DataMode",
+                                 StringValue("EhtMcs0"),
+                                 "ControlMode",
+                                 StringValue("HtMcs0"));
+    wifi.ConfigEhtOptions("EmlsrActivated",
+                          BooleanValue(true),
+                          "TransitionTimeout",
+                          TimeValue(m_transitionTimeout));
+
+    // MLDs are configured with three links
+    SpectrumWifiPhyHelper phyHelper(3);
+    phyHelper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
+    phyHelper.Set(0, "ChannelSettings", StringValue("{2, 0, BAND_2_4GHZ, 0}"));
+    phyHelper.Set(1, "ChannelSettings", StringValue("{36, 0, BAND_5GHZ, 0}"));
+    phyHelper.Set(2, "ChannelSettings", StringValue("{1, 0, BAND_6GHZ, 0}"));
+    // Add three spectrum channels to use multi-RF interface
+    phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_2_4_GHZ);
+    phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_5_GHZ);
+    phyHelper.AddChannel(CreateObject<MultiModelSpectrumChannel>(), WIFI_SPECTRUM_6_GHZ);
+
+    WifiMacHelper mac;
+    mac.SetType("ns3::ApWifiMac",
+                "Ssid",
+                SsidValue(Ssid("ns-3-ssid")),
+                "BeaconGeneration",
+                BooleanValue(true));
+
+    NetDeviceContainer apDevice = wifi.Install(phyHelper, mac, wifiApNode);
+
+    mac.SetType("ns3::StaWifiMac",
+                "Ssid",
+                SsidValue(Ssid("wrong-ssid")),
+                "ActiveProbing",
+                BooleanValue(false));
+    mac.SetEmlsrManager("ns3::DefaultEmlsrManager",
+                        "EmlsrLinkSet",
+                        AttributeContainerValue<UintegerValue>(m_linksToEnableEmlsrOn));
+
+    NetDeviceContainer staDevices = wifi.Install(phyHelper, mac, wifiStaNodes);
+
+    m_apMac = DynamicCast<ApWifiMac>(DynamicCast<WifiNetDevice>(apDevice.Get(0))->GetMac());
+
+    for (uint32_t i = 0; i < staDevices.GetN(); i++)
+    {
+        auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
+        auto staMac = DynamicCast<StaWifiMac>(device->GetMac());
+        NS_ASSERT_MSG(i < m_paddingDelay.size(), "Not enough padding delay values provided");
+        staMac->GetEmlsrManager()->SetAttribute("EmlsrPaddingDelay",
+                                                TimeValue(m_paddingDelay.at(i)));
+        NS_ASSERT_MSG(i < m_transitionDelay.size(), "Not enough transition delay values provided");
+        staMac->GetEmlsrManager()->SetAttribute("EmlsrTransitionDelay",
+                                                TimeValue(m_transitionDelay.at(i)));
+    }
+
+    if (m_nNonEmlsrStations > 0)
+    {
+        // create the other non-AP MLDs for which EMLSR is not activated
+        wifi.ConfigEhtOptions("EmlsrActivated", BooleanValue(false));
+        NodeContainer otherStaNodes(m_nNonEmlsrStations);
+        staDevices.Add(wifi.Install(phyHelper, mac, otherStaNodes));
+        wifiStaNodes.Add(otherStaNodes);
+    }
+
+    for (uint32_t i = 0; i < staDevices.GetN(); i++)
+    {
+        auto device = DynamicCast<WifiNetDevice>(staDevices.Get(i));
+        m_staMacs.push_back(DynamicCast<StaWifiMac>(device->GetMac()));
+    }
+
+    // Trace PSDUs passed to the PHY on AP MLD and non-AP MLDs
+    for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+    {
+        Config::Connect("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/Phys/" +
+                            std::to_string(linkId) + "/PhyTxPsduBegin",
+                        MakeCallback(&EmlsrOperationsTestBase::Transmit, this).Bind(linkId));
+    }
+
+    // Uncomment the lines below to write PCAP files
+    // phyHelper.EnablePcap("wifi-emlsr_AP", apDevice);
+    // phyHelper.EnablePcap("wifi-emlsr_STA", staDevices);
+
+    // Assign fixed streams to random variables in use
+    streamNumber += wifi.AssignStreams(apDevice, streamNumber);
+    streamNumber += wifi.AssignStreams(staDevices, streamNumber);
+
+    MobilityHelper mobility;
+    Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();
+
+    for (std::size_t id = 0; id <= m_nEmlsrStations + m_nNonEmlsrStations; id++)
+    {
+        // all non-AP MLDs are co-located
+        positionAlloc->Add(Vector(std::min<double>(id, 1), 0.0, 0.0));
+    }
+    mobility.SetPositionAllocator(positionAlloc);
+
+    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
+    mobility.Install(wifiApNode);
+    mobility.Install(wifiStaNodes);
+
+    // install packet socket on all nodes
+    PacketSocketHelper packetSocket;
+    packetSocket.Install(wifiApNode);
+    packetSocket.Install(wifiStaNodes);
+
+    // install a packet socket server on all nodes
+    for (auto nodeIt = NodeList::Begin(); nodeIt != NodeList::End(); nodeIt++)
+    {
+        PacketSocketAddress srvAddr;
+        auto device = DynamicCast<WifiNetDevice>((*nodeIt)->GetDevice(0));
+        NS_TEST_ASSERT_MSG_NE(device, nullptr, "Expected a WifiNetDevice");
+        srvAddr.SetSingleDevice(device->GetIfIndex());
+        srvAddr.SetProtocol(1);
+
+        auto server = CreateObject<PacketSocketServer>();
+        server->SetLocal(srvAddr);
+        (*nodeIt)->AddApplication(server);
+        server->SetStartTime(Seconds(0)); // now
+        server->SetStopTime(m_duration);
+    }
+
+    // set DL and UL packet sockets
+    for (const auto& staMac : m_staMacs)
+    {
+        m_dlSockets.emplace_back();
+        m_dlSockets.back().SetSingleDevice(m_apMac->GetDevice()->GetIfIndex());
+        m_dlSockets.back().SetPhysicalAddress(staMac->GetDevice()->GetAddress());
+        m_dlSockets.back().SetProtocol(1);
+
+        m_ulSockets.emplace_back();
+        m_ulSockets.back().SetSingleDevice(staMac->GetDevice()->GetIfIndex());
+        m_ulSockets.back().SetPhysicalAddress(m_apMac->GetDevice()->GetAddress());
+        m_ulSockets.back().SetProtocol(1);
+    }
+
+    // schedule ML setup for one station at a time
+    m_apMac->TraceConnectWithoutContext("AssociatedSta",
+                                        MakeCallback(&EmlsrOperationsTestBase::SetSsid, this));
+    Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
+}
+
+Ptr<PacketSocketClient>
+EmlsrOperationsTestBase::GetApplication(TrafficDirection dir,
+                                        std::size_t staId,
+                                        std::size_t count,
+                                        std::size_t pktSize) const
+{
+    auto client = CreateObject<PacketSocketClient>();
+    client->SetAttribute("PacketSize", UintegerValue(pktSize));
+    client->SetAttribute("MaxPackets", UintegerValue(count));
+    client->SetAttribute("Interval", TimeValue(MicroSeconds(0)));
+    client->SetRemote(dir == DOWNLINK ? m_dlSockets.at(staId) : m_ulSockets.at(staId));
+    client->SetStartTime(Seconds(0)); // now
+    client->SetStopTime(m_duration - Simulator::Now());
+
+    return client;
+}
+
+void
+EmlsrOperationsTestBase::SetSsid(uint16_t aid, Mac48Address /* addr */)
+{
+    if (m_lastAid == aid)
+    {
+        // another STA of this non-AP MLD has already fired this callback
+        return;
+    }
+    m_lastAid = aid;
+
+    // wait some time (5ms) to allow the completion of association
+    auto delay = MilliSeconds(5);
+
+    if (m_establishBaDl)
+    {
+        // trigger establishment of BA agreement with AP as originator
+        Simulator::Schedule(delay, [=]() {
+            m_apMac->GetDevice()->GetNode()->AddApplication(
+                GetApplication(DOWNLINK, aid - 1, 4, 1000));
+        });
+
+        delay += MilliSeconds(5);
+    }
+
+    if (m_establishBaUl)
+    {
+        // trigger establishment of BA agreement with AP as recipient
+        Simulator::Schedule(delay, [=]() {
+            m_staMacs[aid - 1]->GetDevice()->GetNode()->AddApplication(
+                GetApplication(UPLINK, aid - 1, 4, 1000));
+        });
+
+        delay += MilliSeconds(5);
+    }
+
+    Simulator::Schedule(delay, [=]() {
+        if (aid < m_nEmlsrStations + m_nNonEmlsrStations)
+        {
+            // make the next STA start ML discovery & setup
+            m_staMacs[aid]->SetSsid(Ssid("ns-3-ssid"));
+            return;
+        }
+        // all stations associated; start traffic if needed
+        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 EmlNotificationExchangeTest : 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
+     */
+    EmlNotificationExchangeTest(const std::set<uint8_t>& linksToEnableEmlsrOn,
+                                Time transitionTimeout);
+    ~EmlNotificationExchangeTest() override = default;
+
+  protected:
+    void DoSetup() override;
+    void DoRun() override;
+    void Transmit(uint8_t linkId,
+                  std::string context,
+                  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::optional<uint8_t> m_assocLinkId;      //!< ID of the link used to establish association
+    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
+};
+
+EmlNotificationExchangeTest::EmlNotificationExchangeTest(
+    const std::set<uint8_t>& linksToEnableEmlsrOn,
+    Time transitionTimeout)
+    : EmlsrOperationsTestBase("Check EML Notification exchange"),
+      m_checkEmlsrLinksCount(0),
+      m_emlNotificationDroppedCount(0)
+{
+    m_linksToEnableEmlsrOn = linksToEnableEmlsrOn;
+    m_nEmlsrStations = 1;
+    m_nNonEmlsrStations = 0;
+    m_transitionTimeout = transitionTimeout;
+    m_duration = Seconds(0.5);
+}
+
+void
+EmlNotificationExchangeTest::DoSetup()
+{
+    EmlsrOperationsTestBase::DoSetup();
+
+    m_errorModel = CreateObject<ListErrorModel>();
+    for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+    {
+        m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
+    }
+
+    m_staMacs[0]->TraceConnectWithoutContext(
+        "AckedMpdu",
+        MakeCallback(&EmlNotificationExchangeTest::TxOk, this));
+    m_staMacs[0]->TraceConnectWithoutContext(
+        "DroppedMpdu",
+        MakeCallback(&EmlNotificationExchangeTest::TxDropped, this));
+}
+
+void
+EmlNotificationExchangeTest::Transmit(uint8_t linkId,
+                                      std::string context,
+                                      WifiConstPsduMap psduMap,
+                                      WifiTxVector txVector,
+                                      double txPowerW)
+{
+    auto psdu = psduMap.begin()->second;
+
+    switch (psdu->GetHeader(0).GetType())
+    {
+    case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
+        m_assocLinkId = linkId;
+        CheckEmlCapabilitiesInAssocReq(*psdu->begin(), txVector, linkId);
+        break;
+
+    case WIFI_MAC_MGT_ASSOCIATION_RESPONSE:
+        CheckEmlCapabilitiesInAssocResp(*psdu->begin(), txVector, linkId);
+        break;
+
+    case WIFI_MAC_MGT_ACTION:
+        if (auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
+            category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            CheckEmlNotification(psdu, txVector, linkId);
+
+            if (m_emlNotificationDroppedCount == 0 &&
+                m_staMacs[0]->GetLinkIdByAddress(psdu->GetAddr2()) == linkId)
+            {
+                // transmitted by non-AP MLD, we need to corrupt it
+                m_uidList.push_front(psdu->GetPacket()->GetUid());
+                m_errorModel->SetList(m_uidList);
+            }
+            break;
+        }
+
+    default:;
+    }
+
+    EmlsrOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
+}
+
+void
+EmlNotificationExchangeTest::CheckEmlCapabilitiesInAssocReq(Ptr<const WifiMpdu> mpdu,
+                                                            const WifiTxVector& txVector,
+                                                            uint8_t linkId)
+{
+    MgtAssocRequestHeader frame;
+    mpdu->GetPacket()->PeekHeader(frame);
+
+    const auto& mle = frame.Get<MultiLinkElement>();
+    NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocReq");
+
+    NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
+                          true,
+                          "Multi-Link Element in AssocReq must have EML Capabilities");
+    NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
+                          true,
+                          "EML Support subfield of EML Capabilities in AssocReq must be set to 1");
+    NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrPaddingDelay(),
+                          m_paddingDelay.at(0),
+                          "Unexpected Padding Delay in EML Capabilities included in AssocReq");
+    NS_TEST_ASSERT_MSG_EQ(mle->GetEmlsrTransitionDelay(),
+                          m_transitionDelay.at(0),
+                          "Unexpected Transition Delay in EML Capabilities included in AssocReq");
+}
+
+void
+EmlNotificationExchangeTest::CheckEmlCapabilitiesInAssocResp(Ptr<const WifiMpdu> mpdu,
+                                                             const WifiTxVector& txVector,
+                                                             uint8_t linkId)
+{
+    bool sentToEmlsrClient =
+        (m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr1()) == linkId);
+
+    if (!sentToEmlsrClient)
+    {
+        // nothing to check
+        return;
+    }
+
+    MgtAssocResponseHeader frame;
+    mpdu->GetPacket()->PeekHeader(frame);
+
+    const auto& mle = frame.Get<MultiLinkElement>();
+    NS_TEST_ASSERT_MSG_EQ(mle.has_value(), true, "Multi-Link Element must be present in AssocResp");
+
+    NS_TEST_ASSERT_MSG_EQ(mle->HasEmlCapabilities(),
+                          true,
+                          "Multi-Link Element in AssocResp must have EML Capabilities");
+    NS_TEST_ASSERT_MSG_EQ(mle->IsEmlsrSupported(),
+                          true,
+                          "EML Support subfield of EML Capabilities in AssocResp must be set to 1");
+    NS_TEST_ASSERT_MSG_EQ(
+        mle->GetTransitionTimeout(),
+        m_transitionTimeout,
+        "Unexpected Transition Timeout in EML Capabilities included in AssocResp");
+}
+
+void
+EmlNotificationExchangeTest::CheckEmlNotification(Ptr<const WifiPsdu> psdu,
+                                                  const WifiTxVector& txVector,
+                                                  uint8_t linkId)
+{
+    MgtEmlOperatingModeNotification frame;
+    auto mpdu = *psdu->begin();
+    auto pkt = mpdu->GetPacket()->Copy();
+    WifiActionHeader::Remove(pkt);
+    pkt->RemoveHeader(frame);
+    NS_LOG_DEBUG(frame);
+
+    bool sentbyNonApMld = m_staMacs[0]->GetLinkIdByAddress(mpdu->GetHeader().GetAddr2()) == linkId;
+
+    NS_TEST_EXPECT_MSG_EQ(+frame.m_emlControl.emlsrMode,
+                          1,
+                          "EMLSR Mode subfield should be set to 1 (frame sent by non-AP MLD: "
+                              << std::boolalpha << sentbyNonApMld << ")");
+
+    NS_TEST_EXPECT_MSG_EQ(+frame.m_emlControl.emlmrMode,
+                          0,
+                          "EMLMR Mode subfield should be set to 0 (frame sent by non-AP MLD: "
+                              << std::boolalpha << sentbyNonApMld << ")");
+
+    NS_TEST_ASSERT_MSG_EQ(frame.m_emlControl.linkBitmap.has_value(),
+                          true,
+                          "Link Bitmap subfield should be present (frame sent by non-AP MLD: "
+                              << std::boolalpha << sentbyNonApMld << ")");
+
+    auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
+    std::list<uint8_t> expectedEmlsrLinks;
+    std::set_intersection(setupLinks.begin(),
+                          setupLinks.end(),
+                          m_linksToEnableEmlsrOn.begin(),
+                          m_linksToEnableEmlsrOn.end(),
+                          std::back_inserter(expectedEmlsrLinks));
+
+    NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == frame.GetLinkBitmap()),
+                          true,
+                          "Unexpected Link Bitmap subfield (frame sent by non-AP MLD: "
+                              << std::boolalpha << sentbyNonApMld << ")");
+
+    if (!sentbyNonApMld)
+    {
+        // the frame has been sent by the AP MLD
+        NS_TEST_ASSERT_MSG_EQ(
+            +frame.m_emlControl.emlsrParamUpdateCtrl,
+            0,
+            "EMLSR Parameter Update Control should be set to 0 in frames sent by the AP MLD");
+
+        // as soon as the non-AP MLD receives this frame, it sets the EMLSR links
+        auto delay = WifiPhy::CalculateTxDuration(psdu,
+                                                  txVector,
+                                                  m_staMacs[0]->GetWifiPhy(linkId)->GetPhyBand()) +
+                     MicroSeconds(1); // to account for propagation delay
+        Simulator::Schedule(delay, &EmlNotificationExchangeTest::CheckEmlsrLinks, this);
+    }
+
+    NS_TEST_ASSERT_MSG_EQ(m_assocLinkId.has_value(),
+                          true,
+                          "No link ID indicating the link on which association was performed");
+    NS_TEST_EXPECT_MSG_EQ(+m_assocLinkId.value(),
+                          +linkId,
+                          "EML Notification received on unexpected link (frame sent by non-AP MLD: "
+                              << std::boolalpha << sentbyNonApMld << ")");
+}
+
+void
+EmlNotificationExchangeTest::TxOk(Ptr<const WifiMpdu> mpdu)
+{
+    const auto& hdr = mpdu->GetHeader();
+
+    if (hdr.IsMgt() && hdr.IsAction())
+    {
+        if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
+            category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            // the EML Operating Mode Notification frame that the non-AP MLD sent has been
+            // acknowledged; after the transition timeout, the EMLSR links have been set
+            Simulator::Schedule(m_transitionTimeout + NanoSeconds(1),
+                                &EmlNotificationExchangeTest::CheckEmlsrLinks,
+                                this);
+        }
+    }
+}
+
+void
+EmlNotificationExchangeTest::TxDropped(WifiMacDropReason reason, Ptr<const WifiMpdu> mpdu)
+{
+    const auto& hdr = mpdu->GetHeader();
+
+    if (hdr.IsMgt() && hdr.IsAction())
+    {
+        if (auto [category, action] = WifiActionHeader::Peek(mpdu->GetPacket());
+            category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            // the EML Operating Mode Notification frame has been dropped. Don't
+            // corrupt it anymore
+            m_emlNotificationDroppedCount++;
+        }
+    }
+}
+
+void
+EmlNotificationExchangeTest::CheckEmlsrLinks()
+{
+    m_checkEmlsrLinksCount++;
+
+    auto setupLinks = m_staMacs[0]->GetSetupLinkIds();
+    std::set<uint8_t> expectedEmlsrLinks;
+    std::set_intersection(setupLinks.begin(),
+                          setupLinks.end(),
+                          m_linksToEnableEmlsrOn.begin(),
+                          m_linksToEnableEmlsrOn.end(),
+                          std::inserter(expectedEmlsrLinks, expectedEmlsrLinks.end()));
+
+    NS_TEST_EXPECT_MSG_EQ((expectedEmlsrLinks == m_staMacs[0]->GetEmlsrManager()->GetEmlsrLinks()),
+                          true,
+                          "Unexpected set of EMLSR links)");
+}
+
+void
+EmlNotificationExchangeTest::DoRun()
+{
+    Simulator::Stop(m_duration);
+    Simulator::Run();
+
+    NS_TEST_EXPECT_MSG_EQ(m_checkEmlsrLinksCount,
+                          2,
+                          "Unexpected number of times CheckEmlsrLinks() is called");
+    NS_TEST_EXPECT_MSG_EQ(
+        m_emlNotificationDroppedCount,
+        1,
+        "Unexpected number of times the EML Notification frame is dropped due to max retry limit");
+
+    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(uint8_t linkId,
+                  std::string context,
+                  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 linkId the ID of the given link
+     */
+    void CheckBlockAck(const WifiConstPsduMap& psduMap,
+                       const WifiTxVector& txVector,
+                       uint8_t linkId);
+
+  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 */
+    std::vector<std::optional<uint8_t>>
+        m_assocLinkId; /**< ID of the link used to establish association (vector index is the node
+                          ID) */
+    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),
+      m_assocLinkId(1 + nEmlsrStations + nNonEmlsrStations, std::nullopt),
+      m_emlsrEnabledTime(0),
+      m_fe2to3delay(MilliSeconds(20)),
+      m_countQoSframes(0),
+      m_countBlockAck(0)
+{
+    m_nEmlsrStations = nEmlsrStations;
+    m_nNonEmlsrStations = nNonEmlsrStations;
+    m_linksToEnableEmlsrOn = {}; // do not enable EMLSR right after association
+    m_paddingDelay = paddingDelay;
+    m_transitionDelay = transitionDelay;
+    m_transitionTimeout = transitionTimeout;
+    m_establishBaDl = true;
+    m_duration = Seconds(1.5);
+
+    NS_ABORT_MSG_IF(linksToEnableEmlsrOn.size() < 2,
+                    "This test requires at least two links to be configured as EMLSR links");
+}
+
+void
+EmlsrDlTxopTest::Transmit(uint8_t linkId,
+                          std::string context,
+                          WifiConstPsduMap psduMap,
+                          WifiTxVector txVector,
+                          double txPowerW)
+{
+    auto psdu = psduMap.begin()->second;
+    auto nodeId = ContextToNodeId(context);
+
+    switch (psdu->GetHeader(0).GetType())
+    {
+    case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
+        NS_ASSERT_MSG(nodeId > 0, "APs do not send AssocReq frames");
+        m_assocLinkId.at(nodeId) = linkId;
+        if (nodeId <= m_nEmlsrStations)
+        {
+            // this AssocReq is being sent by an EMLSR client. The other EMLSR links should be
+            // in powersave mode after association; we let the non-EMLSR links transition to
+            // active mode (by sending data null frames) after association
+            for (uint8_t id = 0; id < m_staMacs.at(nodeId - 1)->GetNLinks(); id++)
+            {
+                if (id != linkId && m_emlsrLinks.count(id) == 1)
+                {
+                    m_staMacs[nodeId - 1]->SetPowerSaveMode({true, id});
+                }
+            }
+        }
+        break;
+
+    case WIFI_MAC_MGT_ACTION: {
+        auto [category, action] = WifiActionHeader::Peek(psdu->GetPayload(0));
+
+        if (nodeId == 0 && category == WifiActionHeader::PROTECTED_EHT &&
+            action.protectedEhtAction ==
+                WifiActionHeader::PROTECTED_EHT_EML_OPERATING_MODE_NOTIFICATION)
+        {
+            CheckEmlNotificationFrame(*psdu->begin(), txVector, linkId);
+        }
+        else if (category == WifiActionHeader::BLOCK_ACK &&
+                 action.blockAck == WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST)
+        {
+            CheckPmModeAfterAssociation(psdu->GetAddr1());
+        }
+    }
+    break;
+
+    case WIFI_MAC_CTL_TRIGGER:
+        CheckInitialControlFrame(*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:;
+    }
+
+    EmlsrOperationsTestBase::Transmit(linkId, context, psduMap, txVector, txPowerW);
+}
+
+void
+EmlsrDlTxopTest::DoSetup()
+{
+    EmlsrOperationsTestBase::DoSetup();
+
+    m_errorModel = CreateObject<ListErrorModel>();
+    for (std::size_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+    {
+        m_apMac->GetWifiPhy(linkId)->SetPostReceptionErrorModel(m_errorModel);
+    }
+
+    m_apMac->GetQosTxop(AC_BE)->SetTxopLimits(
+        {MicroSeconds(3200), MicroSeconds(3200), MicroSeconds(3200)});
+
+    if (m_nEmlsrStations + m_nNonEmlsrStations > 1)
+    {
+        auto muScheduler =
+            CreateObjectWithAttributes<RrMultiUserScheduler>("EnableUlOfdma", BooleanValue(false));
+        m_apMac->AggregateObject(muScheduler);
+        for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+        {
+            m_apMac->GetFrameExchangeManager(linkId)->GetAckManager()->SetAttribute(
+                "DlMuAckSequenceType",
+                EnumValue(WifiAcknowledgment::DL_MU_AGGREGATE_TF));
+        }
+    }
+}
+
+void
+EmlsrDlTxopTest::StartTraffic()
+{
+    if (m_emlsrEnabledTime.IsZero())
+    {
+        // we are done with association and Block Ack agreement; we can now enable EMLSR mode
+        m_lastAid = 0;
+        EnableEmlsrMode();
+        return;
+    }
+
+    // we are done with sending EML Operating Mode Notification frames. We can now generate
+    // packets for all non-AP MLDs
+    for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
+    {
+        // when multiple non-AP MLDs are present, MU transmission are used. Given that the
+        // available bandwidth decreases as the number of non-AP MLDs increases, compute the
+        // number of packets to generate so that we always have two A-MPDUs per non-AP MLD
+        std::size_t count = 8 / (m_nEmlsrStations + m_nNonEmlsrStations);
+        m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, i, count, 450));
+    }
+
+    // in case of 2 EMLSR clients using no non-EMLSR link, generate one additional short
+    // packet to each EMLSR client to test transition delay
+    if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
+    {
+        Simulator::Schedule(m_fe2to3delay, [&]() {
+            m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 0, 1, 40));
+            m_apMac->GetDevice()->GetNode()->AddApplication(GetApplication(DOWNLINK, 1, 1, 40));
+        });
+    }
+
+    // schedule the transmission of EML Operating Mode Notification frames to disable EMLSR mode
+    // 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)), [=]() {
+            m_staMacs.at(id)->GetEmlsrManager()->SetAttribute(
+                "EmlsrLinkSet",
+                AttributeContainerValue<UintegerValue>({}));
+        });
+
+        Simulator::Schedule(m_fe2to3delay + MilliSeconds(5 * (m_nEmlsrStations + 1)), [=]() {
+            m_apMac->GetDevice()->GetNode()->AddApplication(
+                GetApplication(DOWNLINK, id, 8 / m_nEmlsrStations, 450));
+        });
+    }
+}
+
+void
+EmlsrDlTxopTest::EnableEmlsrMode()
+{
+    m_staMacs.at(m_lastAid)->GetEmlsrManager()->SetAttribute(
+        "EmlsrLinkSet",
+        AttributeContainerValue<UintegerValue>(m_emlsrLinks));
+    m_lastAid++;
+    Simulator::Schedule(MilliSeconds(5), [=]() {
+        if (m_lastAid < m_nEmlsrStations)
+        {
+            // make the next STA send EML Notification frame
+            EnableEmlsrMode();
+            return;
+        }
+        // all stations enabled EMLSR mode; start traffic
+        m_emlsrEnabledTime = Simulator::Now();
+        StartTraffic();
+    });
+}
+
+void
+EmlsrDlTxopTest::CheckResults()
+{
+    auto psduIt = m_txPsdus.cbegin();
+
+    // lambda to jump to the next QoS data frame or MU-RTS Trigger Frame transmitted
+    // to an EMLSR client
+    auto jumpToQosDataOrMuRts = [&]() {
+        while (psduIt != m_txPsdus.cend() &&
+               !psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData())
+        {
+            auto psdu = psduIt->psduMap.cbegin()->second;
+            if (psdu->GetHeader(0).IsTrigger())
+            {
+                CtrlTriggerHeader trigger;
+                psdu->GetPayload(0)->PeekHeader(trigger);
+                if (trigger.IsMuRts())
+                {
+                    break;
+                }
+            }
+            psduIt++;
+        }
+    };
+
+    /**
+     * Before enabling EMLSR mode, no MU-RTS TF should be sent. Four packets are generated
+     * after association to trigger the establishment of a Block Ack agreement. The TXOP Limit
+     * and the MCS are set such that two packets can be transmitted in a TXOP, hence we expect
+     * that the AP MLD sends two A-MPDUs to each non-AP MLD.
+     *
+     * EMLSR client with EMLSR mode to be enabled on all links: after ML setup, all other links
+     * stay in power save mode, hence BA establishment occurs on the same link.
+     *
+     *  [link 0]
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                              | power save mode
+     *
+     *                   â”Œâ”€â”€â”€â”€â”€â”�      â”Œâ”€â”€â”€â”€â”€â”�                   â”Œâ”€â”€â”€â”¬â”€â”€â”€â”�     â”Œâ”€â”€â”€â”¬â”€â”€â”€â”�
+     *            â”Œâ”€â”€â”€â”�  â”‚Assocâ”‚      â”‚ADDBAâ”‚             â”Œâ”€â”€â”€â”� â”‚QoSâ”‚QoSâ”‚     â”‚QoSâ”‚QoSâ”‚
+     *  [link 1]  â”‚ACKâ”‚  â”‚Resp â”‚      â”‚ Req â”‚             â”‚ACKâ”‚ â”‚ 0 â”‚ 1 â”‚     â”‚ 2 â”‚ 3 â”‚
+     *  â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”¬â”€â”€â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”´â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”´â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€
+     *     â”‚Assocâ”‚              â”‚ACKâ”‚        â”‚ACKâ”‚ â”‚ADDBAâ”‚               â”‚BAâ”‚          â”‚BAâ”‚
+     *     â”‚ Req â”‚              â””â”€â”€â”€â”˜        â””â”€â”€â”€â”˜ â”‚Resp â”‚               â””â”€â”€â”˜          â””â”€â”€â”˜
+     *     â””â”€â”€â”€â”€â”€â”˜                                 â””â”€â”€â”€â”€â”€â”˜
+     *
+     *  [link 2]
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                              | power save mode
+     *
+     *
+     * EMLSR client with EMLSR mode to be enabled on not all the links: after ML setup,
+     * the other EMLSR links stay in power save mode, the non-EMLSR link (link 1) transitions
+     * to active mode.
+     *
+     *                                             â”Œâ”€â”€â”€â”€â”€â”�                   â”Œâ”€â”€â”€â”¬â”€â”€â”€â”�
+     *                                      â”Œâ”€â”€â”€â”�  â”‚ADDBAâ”‚             â”Œâ”€â”€â”€â”� â”‚QoSâ”‚QoSâ”‚
+     *  [link 0 - non EMLSR]                â”‚ACKâ”‚  â”‚ Req â”‚             â”‚ACKâ”‚ â”‚ 2 â”‚ 3 â”‚
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”¬â”€â”€â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”´â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”¬â”€
+     *                                â”‚Dataâ”‚              â”‚ACKâ”‚ â”‚ADDBAâ”‚               â”‚BAâ”‚
+     *                                â”‚Nullâ”‚              â””â”€â”€â”€â”˜ â”‚Resp â”‚               â””â”€â”€â”˜
+     *                                â””â”€â”€â”€â”€â”˜                    â””â”€â”€â”€â”€â”€â”˜
+     *                   â”Œâ”€â”€â”€â”€â”€â”�                                       â”Œâ”€â”€â”€â”¬â”€â”€â”€â”�
+     *            â”Œâ”€â”€â”€â”�  â”‚Assocâ”‚                                       â”‚QoSâ”‚QoSâ”‚
+     *  [link 1]  â”‚ACKâ”‚  â”‚Resp â”‚                                       â”‚ 0 â”‚ 1 â”‚
+     *  â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€
+     *     â”‚Assocâ”‚              â”‚ACKâ”‚                                           â”‚BAâ”‚
+     *     â”‚ Req â”‚              â””â”€â”€â”€â”˜                                           â””â”€â”€â”˜
+     *     â””â”€â”€â”€â”€â”€â”˜
+     *
+     *  [link 2]
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                              | power save mode
+     *
+     * Non-EMLSR client (not shown): after ML setup, all other links transition to active mode
+     * by sending a Data Null frame; QoS data frame exchanges occur on two links simultaneously.
+     */
+    for (std::size_t i = 0; i < m_nEmlsrStations + m_nNonEmlsrStations; i++)
+    {
+        std::set<uint8_t> linkIds;
+
+        jumpToQosDataOrMuRts();
+        NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
+                               psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
+                              true,
+                              "Expected at least one QoS data frame before enabling EMLSR mode");
+        linkIds.insert(psduIt->linkId);
+        const auto firstAmpduTxEnd =
+            psduIt->startTx +
+            WifiPhy::CalculateTxDuration(psduIt->psduMap,
+                                         psduIt->txVector,
+                                         m_staMacs[i]->GetWifiPhy(psduIt->linkId)->GetPhyBand());
+        psduIt++;
+
+        jumpToQosDataOrMuRts();
+        NS_TEST_ASSERT_MSG_EQ((psduIt != m_txPsdus.cend() &&
+                               psduIt->psduMap.cbegin()->second->GetHeader(0).IsQosData()),
+                              true,
+                              "Expected at least two QoS data frames before enabling EMLSR mode");
+        linkIds.insert(psduIt->linkId);
+        const auto secondAmpduTxStart = psduIt->startTx;
+        psduIt++;
+
+        /**
+         * If this is an EMLSR client and there is no setup link other than the one used to
+         * establish association that is not an EMLSR link, then the two A-MPDUs are sent one
+         * after another on the link used to establish association.
+         */
+        auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
+        if (i < m_nEmlsrStations &&
+            std::none_of(setupLinks.begin(), setupLinks.end(), [&](auto&& linkId) {
+                return linkId != *m_assocLinkId[i + 1] && m_emlsrLinks.count(linkId) == 0;
+            }))
+        {
+            NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
+                                  1,
+                                  "Expected both A-MPDUs to be sent on the same link");
+            NS_TEST_EXPECT_MSG_EQ(*linkIds.begin(),
+                                  *m_assocLinkId[i + 1],
+                                  "A-MPDUs sent on incorrect link");
+            NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
+                                  secondAmpduTxStart,
+                                  "A-MPDUs are not sent one after another");
+        }
+        /**
+         * Otherwise, the two A-MPDUs can be sent concurrently on two distinct links (may be
+         * the link used to establish association and a non-EMLSR link).
+         */
+        else
+        {
+            NS_TEST_EXPECT_MSG_EQ(linkIds.size(),
+                                  2,
+                                  "Expected A-MPDUs to be sent on distinct links");
+            NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
+                                  secondAmpduTxStart,
+                                  "A-MPDUs are not sent concurrently");
+        }
+    }
+
+    /**
+     * After enabling EMLSR mode, MU-RTS TF should only be sent on EMLSR links. After the exchange
+     * of EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
+     * to prepare two A-MPDUs for each non-AP MLD.
+     *
+     * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
+     * non-EMLSR client):
+     *                                      â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”�
+     *                                      â”‚QoS 4â”‚QoS 5â”‚
+     *                                      â”‚ to Aâ”‚ to Aâ”‚
+     *                            â”Œâ”€â”€â”€â”�     â”œâ”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”¤
+     *                            â”‚MU â”‚     â”‚QoS 4â”‚QoS 5â”‚
+     *  [link 0]                  â”‚RTSâ”‚     â”‚ to Bâ”‚ to Bâ”‚
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                                 â”‚CTSâ”‚             â”‚BAâ”‚
+     *                                 â”œâ”€â”€â”€â”¤             â”œâ”€â”€â”¤
+     *                                 â”‚CTSâ”‚             â”‚BAâ”‚
+     *                                 â””â”€â”€â”€â”˜             â””â”€â”€â”˜
+     *                  â”Œâ”€â”€â”€â”�      â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”�
+     *           â”Œâ”€â”€â”€â”�  â”‚EMLâ”‚      â”‚QoS 6â”‚QoS 7â”‚
+     *  [link 1] â”‚ACKâ”‚  â”‚OM â”‚      â”‚ to Bâ”‚ to Bâ”‚
+     *  â”€â”€â”€â”€â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *      â”‚EMLâ”‚            â”‚ACKâ”‚              â”‚BAâ”‚
+     *      â”‚OM â”‚            â””â”€â”€â”€â”˜              â””â”€â”€â”˜
+     *      â””â”€â”€â”€â”˜
+     *                                                           â”Œâ”€â”€â”€â”�     â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”�
+     *                                                           â”‚MU â”‚     â”‚QoS 6â”‚QoS 7â”‚
+     *  [link 2]                                                 â”‚RTSâ”‚     â”‚ to Aâ”‚ to Aâ”‚
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€
+     *                                                                â”‚CTSâ”‚             â”‚BAâ”‚
+     *                                                                â””â”€â”€â”€â”˜             â””â”€â”€â”˜
+     *
+     * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
+     * B is the non-EMLSR client):
+     *                             â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”�
+     *                             â”‚QoS 4â”‚QoS 5â”‚
+     *                             â”‚ to Aâ”‚ to Aâ”‚
+     *                             â”œâ”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”¤
+     *                             â”‚QoS 4â”‚QoS 5â”‚
+     *  [link 0 - non EMLSR]       â”‚ to Bâ”‚ to Bâ”‚
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                                          â”‚BAâ”‚
+     *                                          â”œâ”€â”€â”¤
+     *                                          â”‚BAâ”‚
+     *                                          â””â”€â”€â”˜
+     *                                       â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”�
+     *                                       â”‚QoS 6â”‚QoS 7â”‚
+     *                                       â”‚ to Aâ”‚ to Aâ”‚
+     *                  â”Œâ”€â”€â”€â”�      â”Œâ”€â”€â”€â”�     â”œâ”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”¤
+     *           â”Œâ”€â”€â”€â”�  â”‚EMLâ”‚      â”‚MU â”‚     â”‚QoS 6â”‚QoS 7â”‚
+     *  [link 1] â”‚ACKâ”‚  â”‚OM â”‚      â”‚RTSâ”‚     â”‚ to Bâ”‚ to Bâ”‚
+     *  â”€â”€â”€â”€â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *      â”‚EMLâ”‚            â”‚ACKâ”‚      â”‚CTSâ”‚             â”‚BAâ”‚
+     *      â”‚OM â”‚            â””â”€â”€â”€â”˜      â”œâ”€â”€â”€â”¤             â”œâ”€â”€â”¤
+     *      â””â”€â”€â”€â”˜                       â”‚CTSâ”‚             â”‚BAâ”‚
+     *                                  â””â”€â”€â”€â”˜             â””â”€â”€â”˜
+     *
+     *  [link 2]
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     */
+
+    /// Store a QoS data frame or an MU-RTS TF followed by a QoS data frame
+    using FrameExchange = std::list<decltype(psduIt)>;
+
+    std::vector<std::list<FrameExchange>> frameExchanges(m_nEmlsrStations);
+
+    // compute all frame exchanges involving EMLSR clients
+    while (psduIt != m_txPsdus.cend())
+    {
+        jumpToQosDataOrMuRts();
+        if (psduIt == m_txPsdus.cend())
+        {
+            break;
+        }
+
+        if (IsTrigger(psduIt->psduMap))
+        {
+            CtrlTriggerHeader trigger;
+            psduIt->psduMap.cbegin()->second->GetPayload(0)->PeekHeader(trigger);
+            // this is an MU-RTS TF starting a new frame exchange sequence; add it to all
+            // the addressed EMLSR clients
+            NS_TEST_ASSERT_MSG_EQ(trigger.IsMuRts(),
+                                  true,
+                                  "jumpToQosDataOrMuRts does not return TFs other than MU-RTS");
+            for (const auto& userInfo : trigger)
+            {
+                for (std::size_t i = 0; i < m_nEmlsrStations; i++)
+                {
+                    if (m_staMacs.at(i)->GetAssociationId() == userInfo.GetAid12())
+                    {
+                        frameExchanges.at(i).emplace_back(FrameExchange{psduIt});
+                        break;
+                    }
+                }
+            }
+            psduIt++;
+            continue;
+        }
+
+        // we get here if psduIt points to a psduMap containing QoS data frame(s); find (if any)
+        // the QoS data frame(s) addressed to EMLSR clients and add them to the appropriate
+        // frame exchange sequence
+        for (const auto& staIdPsduPair : psduIt->psduMap)
+        {
+            std::for_each_n(m_staMacs.cbegin(), m_nEmlsrStations, [&](auto&& staMac) {
+                if (!staMac->GetLinkIdByAddress(staIdPsduPair.second->GetAddr1()))
+                {
+                    // not addressed to this non-AP MLD
+                    return;
+                }
+                // a QoS data frame starts a new frame exchange sequence if there is no previous
+                // MU-RTS TF that has been sent on the same link and is not already followed by
+                // a QoS data frame
+                std::size_t id = staMac->GetDevice()->GetNode()->GetId() - 1;
+                for (auto& frameExchange : frameExchanges.at(id))
+                {
+                    if (IsTrigger(frameExchange.front()->psduMap) &&
+                        frameExchange.front()->linkId == psduIt->linkId &&
+                        frameExchange.size() == 1)
+                    {
+                        auto it = std::next(frameExchange.front());
+                        while (it != m_txPsdus.end())
+                        {
+                            // stop at the first frame other than CTS sent on this link
+                            if (it->linkId == psduIt->linkId &&
+                                !it->psduMap.begin()->second->GetHeader(0).IsCts())
+                            {
+                                break;
+                            }
+                            ++it;
+                        }
+                        if (it == psduIt)
+                        {
+                            // the QoS data frame actually followed the MU-RTS TF
+                            frameExchange.emplace_back(psduIt);
+                            return;
+                        }
+                    }
+                }
+                frameExchanges.at(id).emplace_back(FrameExchange{psduIt});
+            });
+        }
+        psduIt++;
+    }
+
+    /**
+     * Let's focus on the first two frame exchanges for each EMLSR clients. If all setup links are
+     * EMLSR links, both frame exchanges are 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.
+     */
+    for (std::size_t i = 0; i < m_nEmlsrStations; i++)
+    {
+        NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
+                                    2,
+                                    "Expected at least 2 frame exchange sequences "
+                                        << "involving EMLSR client " << i);
+
+        auto firstExchangeIt = frameExchanges.at(i).begin();
+        auto secondExchangeIt = std::next(firstExchangeIt);
+
+        const auto firstAmpduTxEnd =
+            firstExchangeIt->back()->startTx +
+            WifiPhy::CalculateTxDuration(
+                firstExchangeIt->back()->psduMap,
+                firstExchangeIt->back()->txVector,
+                m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
+        const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
+
+        if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size())
+        {
+            // all links are EMLSR links
+            NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
+                                  true,
+                                  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
+            NS_TEST_EXPECT_MSG_EQ(
+                firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                true,
+                "Expected a QoS data frame in the first frame exchange sequence");
+
+            NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
+                                  true,
+                                  "Expected an MU-RTS TF as ICF of second frame exchange sequence");
+            NS_TEST_EXPECT_MSG_EQ(
+                secondExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                true,
+                "Expected a QoS data frame in the second frame exchange sequence");
+
+            NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
+                                  secondAmpduTxStart,
+                                  "A-MPDUs are not sent one after another");
+        }
+        else
+        {
+            std::vector<uint8_t> nonEmlsrIds;
+            auto setupLinks = m_staMacs[i]->GetSetupLinkIds();
+            std::set_difference(setupLinks.begin(),
+                                setupLinks.end(),
+                                m_emlsrLinks.begin(),
+                                m_emlsrLinks.end(),
+                                std::back_inserter(nonEmlsrIds));
+            NS_TEST_ASSERT_MSG_EQ(nonEmlsrIds.size(), 1, "Unexpected number of non-EMLSR links");
+
+            auto nonEmlsrLinkExchangeIt = firstExchangeIt->front()->linkId == nonEmlsrIds[0]
+                                              ? firstExchangeIt
+                                              : secondExchangeIt;
+            NS_TEST_EXPECT_MSG_EQ(IsTrigger(nonEmlsrLinkExchangeIt->front()->psduMap),
+                                  false,
+                                  "Did not expect an MU-RTS TF as ICF on non-EMLSR link");
+            NS_TEST_EXPECT_MSG_EQ(
+                nonEmlsrLinkExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                true,
+                "Expected a QoS data frame on the non-EMLSR link");
+
+            auto emlsrLinkExchangeIt =
+                nonEmlsrLinkExchangeIt == firstExchangeIt ? secondExchangeIt : firstExchangeIt;
+            NS_TEST_EXPECT_MSG_NE(+emlsrLinkExchangeIt->front()->linkId,
+                                  +nonEmlsrIds[0],
+                                  "Expected this exchange not to occur on non-EMLSR link");
+            NS_TEST_EXPECT_MSG_EQ(IsTrigger(emlsrLinkExchangeIt->front()->psduMap),
+                                  true,
+                                  "Expected an MU-RTS TF as ICF on the EMLSR link");
+            NS_TEST_EXPECT_MSG_EQ(
+                emlsrLinkExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                true,
+                "Expected a QoS data frame on the EMLSR link");
+
+            NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
+                                  secondAmpduTxStart,
+                                  "A-MPDUs are not sent concurrently");
+        }
+
+        // we are done with processing the first two frame exchanges, remove them
+        frameExchanges.at(i).erase(firstExchangeIt);
+        frameExchanges.at(i).erase(secondExchangeIt);
+    }
+
+    /**
+     * 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 switches to listening
+     *                        â”‚QoS xâ”‚          after transition delay
+     *                        â”‚ to Aâ”‚          |
+     *              â”Œâ”€â”€â”€â”�     â”œâ”€â”€â”€â”€â”€â”¤    â”Œâ”€â”€â”€â”€â”€â”�
+     *              â”‚MU â”‚     â”‚QoS xâ”‚    â”‚QoS yâ”‚
+     *  [link 0]    â”‚RTSâ”‚     â”‚ to Bâ”‚    â”‚ to Bâ”‚
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                   â”‚CTSâ”‚       â”‚BAâ”‚       â”‚BAâ”‚
+     *                   â”œâ”€â”€â”€â”¤       â”œâ”€â”€â”¤       â””â”€â”€â”˜
+     *                   â”‚CTSâ”‚       â”‚BAâ”‚
+     *                   â””â”€â”€â”€â”˜       â””â”€â”€â”˜                      A switches to listening
+     *                                                         after timeout + transition delay
+     *                                                         â”‚
+     *                                     â”Œâ”€â”€â”€â”�     â”Œâ”€â”€â”€â”€â”€â”�   â”‚       â”Œâ”€â”€â”€â”�
+     *                                     â”‚MU â”‚     â”‚QoS xâ”‚   â”‚       â”‚MU â”‚     â”Œâ”€â”€â”€â”�
+     *  [link 1]                           â”‚RTSâ”‚     â”‚ to Aâ”‚   â”‚       â”‚RTSâ”‚     â”‚BARâ”‚
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”¬â”€â”€â”¬â”€
+     *                                          â”‚CTSâ”‚       â”‚BAâ”‚            â”‚CTSâ”‚     â”‚BAâ”‚
+     *                                          â””â”€â”€â”€â”˜       â””â”€â”€x            â””â”€â”€â”€â”˜     â””â”€â”€â”˜
+     */
+    if (m_nEmlsrStations == 2 && m_apMac->GetNLinks() == m_emlsrLinks.size())
+    {
+        // the following checks are only done with 2 EMLSR clients having no non-EMLSR link
+        for (std::size_t i = 0; i < m_nEmlsrStations; i++)
+        {
+            NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
+                                        2,
+                                        "Expected at least 2 frame exchange sequences "
+                                            << "involving EMLSR client " << i);
+            // the first frame exchange must start with an ICF
+            auto firstExchangeIt = frameExchanges.at(i).begin();
+
+            NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
+                                  true,
+                                  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
+            NS_TEST_EXPECT_MSG_EQ(
+                firstExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+                true,
+                "Expected a QoS data frame in the first frame exchange sequence");
+        }
+
+        // the second frame exchange is the one that starts first
+        auto secondExchangeIt = std::next(frameExchanges.at(0).begin())->front()->startTx <
+                                        std::next(frameExchanges.at(1).begin())->front()->startTx
+                                    ? std::next(frameExchanges.at(0).begin())
+                                    : std::next(frameExchanges.at(1).begin());
+        decltype(secondExchangeIt) thirdExchangeIt;
+        std::size_t thirdExchangeStaId;
+
+        if (secondExchangeIt == std::next(frameExchanges.at(0).begin()))
+        {
+            thirdExchangeIt = std::next(frameExchanges.at(1).begin());
+            thirdExchangeStaId = 1;
+        }
+        else
+        {
+            thirdExchangeIt = std::next(frameExchanges.at(0).begin());
+            thirdExchangeStaId = 0;
+        }
+
+        // the second frame exchange is not protected by the ICF and starts a SIFS after the end
+        // of the previous one
+        NS_TEST_EXPECT_MSG_EQ(IsTrigger(secondExchangeIt->front()->psduMap),
+                              false,
+                              "Expected no ICF for the second frame exchange sequence");
+        NS_TEST_EXPECT_MSG_EQ(
+            secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+            true,
+            "Expected a QoS data frame in the second frame exchange sequence");
+
+        // the first two frame exchanges occur on the same link
+        NS_TEST_EXPECT_MSG_EQ(+secondExchangeIt->front()->linkId,
+                              +frameExchanges.at(0).begin()->front()->linkId,
+                              "Expected the first two frame exchanges to occur on the same link");
+
+        auto bAckRespIt = std::prev(secondExchangeIt->front());
+        NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
+                              true,
+                              "Expected a BlockAck response before the second frame exchange");
+        auto bAckRespTxEnd =
+            bAckRespIt->startTx +
+            WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
+                                         bAckRespIt->txVector,
+                                         m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetPhyBand());
+
+        // the second frame exchange starts a SIFS after the previous one
+        NS_TEST_EXPECT_MSG_EQ(
+            bAckRespTxEnd + m_apMac->GetWifiPhy(bAckRespIt->linkId)->GetSifs(),
+            secondExchangeIt->front()->startTx,
+            "Expected the second frame exchange to start a SIFS after the first one");
+
+        // the third frame exchange is protected by MU-RTS and occurs on a different link
+        NS_TEST_EXPECT_MSG_EQ(IsTrigger(thirdExchangeIt->front()->psduMap),
+                              true,
+                              "Expected an MU-RTS as ICF for the third frame exchange sequence");
+        NS_TEST_EXPECT_MSG_EQ(
+            thirdExchangeIt->back()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+            true,
+            "Expected a QoS data frame in the third frame exchange sequence");
+
+        NS_TEST_EXPECT_MSG_NE(
+            +secondExchangeIt->front()->linkId,
+            +thirdExchangeIt->front()->linkId,
+            "Expected the second and third frame exchanges to occur on distinct links");
+
+        auto secondQosIt = secondExchangeIt->front();
+        auto secondQosTxEnd =
+            secondQosIt->startTx +
+            WifiPhy::CalculateTxDuration(secondQosIt->psduMap,
+                                         secondQosIt->txVector,
+                                         m_apMac->GetWifiPhy(secondQosIt->linkId)->GetPhyBand());
+
+        NS_TEST_EXPECT_MSG_GT_OR_EQ(thirdExchangeIt->front()->startTx,
+                                    secondQosTxEnd + m_transitionDelay.at(thirdExchangeStaId),
+                                    "Transmission started before transition delay");
+
+        // the BlockAck of the third frame exchange is not received correctly, so there should be
+        // another frame exchange
+        NS_TEST_EXPECT_MSG_EQ((thirdExchangeIt != frameExchanges.at(thirdExchangeStaId).end()),
+                              true,
+                              "Expected a fourth frame exchange");
+        auto fourthExchangeIt = std::next(thirdExchangeIt);
+
+        // the fourth frame exchange is protected by MU-RTS
+        NS_TEST_EXPECT_MSG_EQ(IsTrigger(fourthExchangeIt->front()->psduMap),
+                              true,
+                              "Expected an MU-RTS as ICF for the fourth frame exchange sequence");
+
+        bAckRespIt = std::prev(fourthExchangeIt->front());
+        NS_TEST_EXPECT_MSG_EQ(bAckRespIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAck(),
+                              true,
+                              "Expected a BlockAck response before the fourth frame exchange");
+        auto phy = m_apMac->GetWifiPhy(bAckRespIt->linkId);
+        bAckRespTxEnd = bAckRespIt->startTx + WifiPhy::CalculateTxDuration(bAckRespIt->psduMap,
+                                                                           bAckRespIt->txVector,
+                                                                           phy->GetPhyBand());
+        auto timeout = phy->GetSifs() + phy->GetSlot() + MicroSeconds(20);
+
+        // the fourth frame exchange starts a SIFS after the previous one
+        NS_TEST_EXPECT_MSG_GT_OR_EQ(fourthExchangeIt->front()->startTx,
+                                    bAckRespTxEnd + timeout +
+                                        m_transitionDelay.at(thirdExchangeStaId),
+                                    "Transmission started before transition delay");
+
+        auto bAckReqIt = std::next(fourthExchangeIt->front(), 2);
+        NS_TEST_EXPECT_MSG_EQ(bAckReqIt->psduMap.cbegin()->second->GetHeader(0).IsBlockAckReq(),
+                              true,
+                              "Expected a BlockAck request in the fourth frame exchange");
+
+        // we are done with processing the frame exchanges, remove them (two frame exchanges
+        // per EMLSR client, plus the last one)
+        frameExchanges.at(0).pop_front();
+        frameExchanges.at(0).pop_front();
+        frameExchanges.at(1).pop_front();
+        frameExchanges.at(1).pop_front();
+        frameExchanges.at(thirdExchangeStaId).pop_front();
+    }
+
+    /**
+     * After disabling EMLSR mode, no MU-RTS TF should be sent. After the exchange of
+     * EML Operating Mode Notification frames, a number of packets are generated at the AP MLD
+     * to prepare two A-MPDUs for each EMLSR client.
+     *
+     * EMLSR client with EMLSR mode to be enabled on all links (A is the EMLSR client, B is the
+     * non-EMLSR client):
+     *
+     *  [link 0]                            | power save mode
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                                        â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”�        â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”�
+     *                                        â”‚QoS 8â”‚QoS 9â”‚        â”‚QoS 10â”‚QoS 11â”‚
+     *                                        â”‚ to Aâ”‚ to Aâ”‚        â”‚ to A â”‚ to A â”‚
+     *                  â”Œâ”€â”€â”€â”�     â”Œâ”€â”€â”€â”�       â”œâ”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”¤        â”œâ”€â”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”€â”¤
+     *           â”Œâ”€â”€â”€â”�  â”‚MU â”‚     â”‚EMLâ”‚       â”‚QoS 8â”‚QoS 9â”‚        â”‚QoS 10â”‚QoS 11â”‚
+     *  [link 1] â”‚ACKâ”‚  â”‚RTSâ”‚     â”‚OM â”‚       â”‚ to Bâ”‚ to Bâ”‚        â”‚ to B â”‚ to B â”‚
+     *  â”€â”€â”€â”€â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”€â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€
+     *      â”‚EMLâ”‚            â”‚CTSâ”‚     â”‚ACKâ”‚               â”‚BAâ”‚                   â”‚BAâ”‚
+     *      â”‚OM â”‚            â””â”€â”€â”€â”˜     â””â”€â”€â”€â”˜               â”œâ”€â”€â”¤                   â”œâ”€â”€â”¤
+     *      â””â”€â”€â”€â”˜                                          â”‚BAâ”‚                   â”‚BAâ”‚
+     *                                                     â””â”€â”€â”˜                   â””â”€â”€â”˜
+     *
+     *  [link 2]                            | power save mode
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *
+     *
+     * EMLSR client with EMLSR mode to be enabled on not all the links (A is the EMLSR client,
+     * B is the non-EMLSR client):
+     *                                           â”Œâ”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”�
+     *                                           â”‚QoS 8â”‚QoS 9â”‚
+     *                                           â”‚ to Aâ”‚ to Aâ”‚
+     *                                           â”œâ”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”¤
+     *                                           â”‚QoS 8â”‚QoS 9â”‚
+     *  [link 0 - non EMLSR]                     â”‚ to Bâ”‚ to Bâ”‚
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *                                                        â”‚BAâ”‚
+     *                                                        â”œâ”€â”€â”¤
+     *                                                        â”‚BAâ”‚
+     *                                                        â””â”€â”€â”˜
+     *                                        â”Œâ”€â”€â”€â”€â”€â”€â”¬â”€â”€â”€â”€â”€â”€â”�
+     *                                        â”‚QoS 10â”‚QoS 11â”‚
+     *                                        â”‚ to A â”‚ to A â”‚
+     *                  â”Œâ”€â”€â”€â”�     â”Œâ”€â”€â”€â”�       â”œâ”€â”€â”€â”€â”€â”€â”¼â”€â”€â”€â”€â”€â”€â”¤
+     *           â”Œâ”€â”€â”€â”�  â”‚MU â”‚     â”‚EMLâ”‚       â”‚QoS 10â”‚QoS 11â”‚
+     *  [link 1] â”‚ACKâ”‚  â”‚RTSâ”‚     â”‚OM â”‚       â”‚ to B â”‚ to B â”‚
+     *  â”€â”€â”€â”€â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”€â”€â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”´â”€â”€â”€â”´â”¬â”€â”€â”€â”¬â”€â”€â”´â”€â”€â”€â”€â”€â”€â”´â”€â”€â”€â”€â”€â”€â”´â”¬â”€â”€â”¬â”€â”€â”€â”€â”€
+     *      â”‚EMLâ”‚            â”‚CTSâ”‚     â”‚ACKâ”‚                 â”‚BAâ”‚
+     *      â”‚OM â”‚            â””â”€â”€â”€â”˜     â””â”€â”€â”€â”˜                 â”œâ”€â”€â”¤
+     *      â””â”€â”€â”€â”˜                                            â”‚BAâ”‚
+     *                                                       â””â”€â”€â”˜
+     *
+     *  [link 2]                            | power save mode
+     *  â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
+     *
+     */
+
+    // for each EMLSR client, there should be a frame exchange with ICF and no data frame
+    // (ICF protects the EML Notification response) and two frame exchanges with data frames
+    for (std::size_t i = 0; i < m_nEmlsrStations; i++)
+    {
+        // if the EML Notification frame disabling EMLSR mode is sent on an EMLSR link, it
+        // is protected by an ICF; otherwise, it is not protected. Given that the default
+        // EMLSR Manager sends EML Notification frames on the link used to establish association,
+        // ICF is sent to protect the EML Notification frame if the link used to establish
+        // association is an EMLSR link
+        if (m_emlsrLinks.count(*m_assocLinkId.at(i + 1)) == 1)
+        {
+            auto firstExchangeIt = frameExchanges.at(i).begin();
+
+            NS_TEST_EXPECT_MSG_EQ(IsTrigger(firstExchangeIt->front()->psduMap),
+                                  true,
+                                  "Expected an MU-RTS TF as ICF of first frame exchange sequence");
+            NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
+                                  1,
+                                  "Expected no data frame in the first frame exchange sequence");
+
+            frameExchanges.at(i).pop_front();
+        }
+
+        NS_TEST_EXPECT_MSG_GT_OR_EQ(frameExchanges.at(i).size(),
+                                    2,
+                                    "Expected at least 2 frame exchange sequences "
+                                        << "involving EMLSR client " << i);
+
+        auto firstExchangeIt = frameExchanges.at(i).begin();
+        auto secondExchangeIt = std::next(firstExchangeIt);
+
+        const auto firstAmpduTxEnd =
+            firstExchangeIt->back()->startTx +
+            WifiPhy::CalculateTxDuration(
+                firstExchangeIt->back()->psduMap,
+                firstExchangeIt->back()->txVector,
+                m_staMacs[i]->GetWifiPhy(firstExchangeIt->back()->linkId)->GetPhyBand());
+        const auto secondAmpduTxStart = secondExchangeIt->front()->startTx;
+
+        NS_TEST_EXPECT_MSG_EQ(
+            firstExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+            true,
+            "Expected a QoS data frame in the first frame exchange sequence");
+        NS_TEST_EXPECT_MSG_EQ(firstExchangeIt->size(),
+                              1,
+                              "Expected one frame only in the first frame exchange sequence");
+
+        NS_TEST_EXPECT_MSG_EQ(
+            secondExchangeIt->front()->psduMap.cbegin()->second->GetHeader(0).IsQosData(),
+            true,
+            "Expected a QoS data frame in the second frame exchange sequence");
+        NS_TEST_EXPECT_MSG_EQ(secondExchangeIt->size(),
+                              1,
+                              "Expected one frame only in the second frame exchange sequence");
+
+        if (m_staMacs[i]->GetNLinks() == m_emlsrLinks.size() ||
+            m_emlsrLinks.count(*m_assocLinkId.at(i + 1)) == 0)
+        {
+            // all links are EMLSR links or the non-EMLSR link is the link used for association:
+            // the two QoS data frames are sent one after another on the link used for association
+            NS_TEST_EXPECT_MSG_EQ(
+                +firstExchangeIt->front()->linkId,
+                +(*m_assocLinkId.at(i + 1)),
+                "First frame exchange expected to occur on link used for association");
+
+            NS_TEST_EXPECT_MSG_EQ(
+                +secondExchangeIt->front()->linkId,
+                +(*m_assocLinkId.at(i + 1)),
+                "Second frame exchange expected to occur on link used for association");
+
+            NS_TEST_EXPECT_MSG_LT(firstAmpduTxEnd,
+                                  secondAmpduTxStart,
+                                  "A-MPDUs are not sent one after another");
+        }
+        else
+        {
+            // the two QoS data frames are sent concurrently on distinct links
+            NS_TEST_EXPECT_MSG_NE(+firstExchangeIt->front()->linkId,
+                                  +secondExchangeIt->front()->linkId,
+                                  "Frame exchanges expected to occur on distinct links");
+
+            NS_TEST_EXPECT_MSG_GT(firstAmpduTxEnd,
+                                  secondAmpduTxStart,
+                                  "A-MPDUs are not sent concurrently");
+        }
+    }
+}
+
+void
+EmlsrDlTxopTest::CheckPmModeAfterAssociation(const Mac48Address& address)
+{
+    std::optional<std::size_t> staId;
+    for (std::size_t id = 0; id < m_nEmlsrStations + m_nNonEmlsrStations; id++)
+    {
+        if (m_staMacs.at(id)->GetLinkIdByAddress(address))
+        {
+            staId = id;
+            break;
+        }
+    }
+    NS_TEST_ASSERT_MSG_EQ(staId.has_value(), true, "Not an address of a non-AP MLD " << address);
+
+    // check that all EMLSR links (but the link used for ML setup) of the EMLSR clients
+    // are considered to be in power save mode by the AP MLD; all the other links have
+    // transitioned to active mode instead
+    for (uint8_t linkId = 0; linkId < m_apMac->GetNLinks(); linkId++)
+    {
+        bool psModeExpected = *staId < m_nEmlsrStations && linkId != m_assocLinkId.at(*staId + 1) &&
+                              m_emlsrLinks.count(linkId) == 1;
+        auto addr = m_staMacs.at(*staId)->GetAddress();
+        auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
+        NS_TEST_EXPECT_MSG_EQ(psMode,
+                              psModeExpected,
+                              "EMLSR link " << +linkId << " of EMLSR client " << *staId
+                                            << " not in " << (psModeExpected ? "PS" : "active")
+                                            << " mode");
+        // check that AP is blocking transmission of QoS data frames on this link
+        WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
+        auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
+        NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                              true,
+                              "Expected to find a mask for EMLSR link "
+                                  << +linkId << " of EMLSR client " << *staId);
+        auto reason = static_cast<std::size_t>(WifiQueueBlockedReason::POWER_SAVE_MODE);
+        NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                              psModeExpected,
+                              "Expected EMLSR link " << +linkId << " of EMLSR client " << *staId
+                                                     << " to be "
+                                                     << (psModeExpected ? "blocked" : "unblocked"));
+    }
+}
+
+void
+EmlsrDlTxopTest::CheckEmlNotificationFrame(Ptr<const WifiMpdu> mpdu,
+                                           const WifiTxVector& txVector,
+                                           uint8_t linkId)
+{
+    // the AP is replying to a received EMLSR Notification frame
+    auto pkt = mpdu->GetPacket()->Copy();
+    const auto& hdr = mpdu->GetHeader();
+    WifiActionHeader::Remove(pkt);
+    MgtEmlOperatingModeNotification frame;
+    pkt->RemoveHeader(frame);
+
+    std::optional<std::size_t> staId;
+    for (std::size_t id = 0; id < m_nEmlsrStations; id++)
+    {
+        if (m_staMacs.at(id)->GetFrameExchangeManager(linkId)->GetAddress() == hdr.GetAddr1())
+        {
+            staId = id;
+            break;
+        }
+    }
+    NS_TEST_ASSERT_MSG_EQ(staId.has_value(),
+                          true,
+                          "Not an address of an EMLSR client " << hdr.GetAddr1());
+
+    // The EMLSR mode change occurs a Transition Timeout after the end of the PPDU carrying the Ack
+    auto phy = m_apMac->GetWifiPhy(linkId);
+    auto txDuration =
+        WifiPhy::CalculateTxDuration(mpdu->GetSize() + 4, // A-MPDU Subframe header size
+                                     txVector,
+                                     phy->GetPhyBand());
+    WifiTxVector ackTxVector =
+        m_staMacs.at(*staId)->GetWifiRemoteStationManager(linkId)->GetAckTxVector(hdr.GetAddr2(),
+                                                                                  txVector);
+    auto ackDuration = WifiPhy::CalculateTxDuration(GetAckSize() + 4, // A-MPDU Subframe header
+                                                    ackTxVector,
+                                                    phy->GetPhyBand());
+
+    Simulator::Schedule(txDuration + phy->GetSifs() + ackDuration, [=]() {
+        if (frame.m_emlControl.emlsrMode == 1)
+        {
+            // EMLSR mode enabled. Check that all EMLSR links of the EMLSR clients are considered
+            // to be in active mode by the AP MLD
+            for (const auto linkId : m_emlsrLinks)
+            {
+                auto addr = m_staMacs.at(*staId)->GetAddress();
+                auto psMode = m_apMac->GetWifiRemoteStationManager(linkId)->IsInPsMode(addr);
+                NS_TEST_EXPECT_MSG_EQ(psMode,
+                                      false,
+                                      "EMLSR link " << +linkId << " of EMLSR client " << *staId
+                                                    << " not in active mode");
+                // check that AP is not blocking transmission of QoS data frames on this link
+                WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
+                auto mask =
+                    m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
+                NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                      true,
+                                      "Expected to find a mask for EMLSR link "
+                                          << +linkId << " of EMLSR client " << *staId);
+                auto reason = static_cast<std::size_t>(WifiQueueBlockedReason::POWER_SAVE_MODE);
+                NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                      false,
+                                      "Expected EMLSR link " << +linkId << " of EMLSR client "
+                                                             << *staId << " to be unblocked");
+            }
+        }
+        else
+        {
+            // EMLSR mode disabled. Check that all EMLSR links (but the link used to send the
+            // EML Notification frame) of the EMLSR clients are considered to be in power save
+            // mode by the AP MLD; the other links are in active mode
+            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+            {
+                bool psModeExpected = id != linkId && m_emlsrLinks.count(id) == 1;
+                auto addr = m_staMacs.at(*staId)->GetAddress();
+                auto psMode = m_apMac->GetWifiRemoteStationManager(id)->IsInPsMode(addr);
+                NS_TEST_EXPECT_MSG_EQ(psMode,
+                                      psModeExpected,
+                                      "EMLSR link "
+                                          << +id << " of EMLSR client " << *staId << " not in "
+                                          << (psModeExpected ? "PS" : "active") << " mode");
+                // check that AP is blocking transmission of QoS data frames on this link
+                WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
+                auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                      true,
+                                      "Expected to find a mask for EMLSR link "
+                                          << +id << " of EMLSR client " << *staId);
+                auto reason = static_cast<std::size_t>(WifiQueueBlockedReason::POWER_SAVE_MODE);
+                NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                      psModeExpected,
+                                      "Expected EMLSR link "
+                                          << +id << " of EMLSR client " << *staId << " to be "
+                                          << (psModeExpected ? "blocked" : "unblocked"));
+            }
+        }
+    });
+}
+
+void
+EmlsrDlTxopTest::CheckInitialControlFrame(Ptr<const WifiMpdu> mpdu,
+                                          const WifiTxVector& txVector,
+                                          uint8_t linkId)
+{
+    CtrlTriggerHeader trigger;
+    mpdu->GetPacket()->PeekHeader(trigger);
+    if (!trigger.IsMuRts())
+    {
+        return;
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(m_emlsrEnabledTime.IsStrictlyPositive(),
+                          true,
+                          "Did not expect an ICF before enabling EMLSR mode");
+
+    NS_TEST_EXPECT_MSG_LT(txVector.GetPreambleType(),
+                          WIFI_PREAMBLE_HT_MF,
+                          "Unexpected preamble type for the Initial Control frame");
+    auto rate = txVector.GetMode().GetDataRate(txVector);
+    NS_TEST_EXPECT_MSG_EQ((rate == 6e6 || rate == 12e6 || rate == 24e6),
+                          true,
+                          "Unexpected rate for the Initial Control frame: " << rate);
+
+    bool found = false;
+    Time maxPaddingDelay{};
+
+    for (const auto& userInfo : trigger)
+    {
+        auto addr = m_apMac->GetMldOrLinkAddressByAid(userInfo.GetAid12());
+        NS_TEST_ASSERT_MSG_EQ(addr.has_value(),
+                              true,
+                              "AID " << userInfo.GetAid12() << " not found");
+
+        if (m_apMac->GetWifiRemoteStationManager(linkId)->GetEmlsrEnabled(*addr))
+        {
+            found = true;
+
+            for (std::size_t i = 0; i < m_nEmlsrStations; i++)
+            {
+                if (m_staMacs.at(i)->GetAddress() == *addr)
+                {
+                    maxPaddingDelay = Max(maxPaddingDelay, m_paddingDelay.at(i));
+                    break;
+                }
+            }
+
+            // check that the AP has blocked transmission on all other EMLSR links
+            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+            {
+                if (!m_apMac->GetWifiRemoteStationManager(id)->GetEmlsrEnabled(*addr))
+                {
+                    continue;
+                }
+
+                if (id == linkId)
+                {
+                    WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0);
+                    auto mask =
+                        m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                    NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                          true,
+                                          "Expected to find a mask for EMLSR link "
+                                              << +id << " of EMLSR client " << *addr);
+                    NS_TEST_EXPECT_MSG_EQ(mask->none(),
+                                          true,
+                                          "Expected EMLSR link " << +id << " of EMLSR client "
+                                                                 << *addr << " to be unblocked");
+                    continue;
+                }
+
+                WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0);
+                auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                      true,
+                                      "Expected to find a mask for EMLSR link "
+                                          << +id << " of EMLSR client " << *addr);
+                auto reason =
+                    static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
+                NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                      true,
+                                      "Expected EMLSR link " << +id << " of EMLSR client " << *addr
+                                                             << " to be blocked");
+                NS_TEST_EXPECT_MSG_EQ(mask->count(),
+                                      1,
+                                      "Expected EMLSR link "
+                                          << +id << " of EMLSR client " << *addr
+                                          << " to be blocked for one reason only");
+            }
+        }
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(found, true, "Expected ICF to be addressed to at least an EMLSR client");
+
+    if (maxPaddingDelay.IsStrictlyPositive())
+    {
+        // compare the TX duration of this Trigger Frame to that of the Trigger Frame with no
+        // padding added
+        auto txDurationWith =
+            WifiPhy::CalculateTxDuration(mpdu->GetSize(),
+                                         txVector,
+                                         m_apMac->GetWifiPhy(linkId)->GetPhyBand());
+        trigger.SetPaddingSize(0);
+        auto pkt = Create<Packet>();
+        pkt->AddHeader(trigger);
+        auto txDurationWithout =
+            WifiPhy::CalculateTxDuration(Create<WifiPsdu>(pkt, mpdu->GetHeader()),
+                                         txVector,
+                                         m_apMac->GetWifiPhy(linkId)->GetPhyBand());
+
+        NS_TEST_EXPECT_MSG_EQ(txDurationWith,
+                              txDurationWithout + maxPaddingDelay,
+                              "Unexpected TX duration of the MU-RTS TF with padding "
+                                  << maxPaddingDelay.As(Time::US));
+    }
+}
+
+void
+EmlsrDlTxopTest::CheckQosFrames(const WifiConstPsduMap& psduMap,
+                                const WifiTxVector& txVector,
+                                uint8_t linkId)
+{
+    if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
+        m_emlsrEnabledTime.IsZero() || Simulator::Now() < m_emlsrEnabledTime + m_fe2to3delay)
+
+    {
+        // we are interested in frames sent to test transition delay
+        return;
+    }
+
+    std::size_t firstClientId = 0;
+    std::size_t secondClientId = 1;
+    auto addr = m_staMacs[secondClientId]->GetAddress();
+    auto txDuration =
+        WifiPhy::CalculateTxDuration(psduMap, txVector, m_apMac->GetWifiPhy(linkId)->GetPhyBand());
+
+    m_countQoSframes++;
+
+    switch (m_countQoSframes)
+    {
+    case 1:
+        // generate another small packet addressed to the first EMLSR client only
+        m_apMac->GetDevice()->GetNode()->AddApplication(
+            GetApplication(DOWNLINK, firstClientId, 1, 40));
+        break;
+    case 2:
+        // generate another small packet addressed to the second EMLSR client
+        m_apMac->GetDevice()->GetNode()->AddApplication(
+            GetApplication(DOWNLINK, secondClientId, 1, 40));
+
+        // the EMLSR client that is not the recipient of the QoS frame being transmitted will
+        // switch back to listening mode after a transition delay starting from the end of
+        // the PPDU carrying this QoS data frame
+
+        // immediately before the end of the PPDU, this link is not blocked for the EMLSR client
+        Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
+            WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
+            auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
+            NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                  true,
+                                  "Expected to find a mask for EMLSR link "
+                                      << +linkId << " of EMLSR client " << secondClientId);
+            NS_TEST_EXPECT_MSG_EQ(mask->none(),
+                                  true,
+                                  "Expected EMLSR link " << +linkId << " of EMLSR client "
+                                                         << secondClientId << " to be unblocked");
+        });
+        // immediately before the end of the PPDU, the other links are blocked for the EMLSR client
+        Simulator::Schedule(txDuration - NanoSeconds(1), [=]() {
+            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+            {
+                if (id == linkId)
+                {
+                    continue;
+                }
+
+                WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
+                auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                      true,
+                                      "Expected to find a mask for EMLSR link "
+                                          << +id << " of EMLSR client " << secondClientId);
+                auto reason =
+                    static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
+                NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                      true,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << secondClientId << " to be blocked");
+                NS_TEST_EXPECT_MSG_EQ(mask->count(),
+                                      1,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << secondClientId
+                                                             << " to be blocked "
+                                                                " for one reason only");
+            }
+        });
+        // immediately after the end of the PPDU, all links are blocked for the EMLSR client
+        Simulator::Schedule(txDuration + NanoSeconds(1), [=]() {
+            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+            {
+                WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
+                auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                      true,
+                                      "Expected to find a mask for EMLSR link "
+                                          << +id << " of EMLSR client " << secondClientId);
+                auto reason = static_cast<std::size_t>(
+                    WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
+                NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                      true,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << secondClientId << " to be blocked");
+                NS_TEST_EXPECT_MSG_EQ(mask->count(),
+                                      1,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << secondClientId
+                                                             << " to be blocked "
+                                                                " for one reason only");
+            }
+        });
+        // immediately before the transition delay, all links are still blocked for the EMLSR client
+        Simulator::Schedule(
+            txDuration + m_transitionDelay.at(secondClientId) - NanoSeconds(1),
+            [=]() {
+                for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+                {
+                    WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, addr, 0);
+                    auto mask =
+                        m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                    NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                          true,
+                                          "Expected to find a mask for EMLSR link "
+                                              << +id << " of EMLSR client " << secondClientId);
+                    auto reason = static_cast<std::size_t>(
+                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
+                    NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                          true,
+                                          "Expected EMLSR link " << +id << " of EMLSR client "
+                                                                 << secondClientId
+                                                                 << " to be blocked");
+                    NS_TEST_EXPECT_MSG_EQ(mask->count(),
+                                          1,
+                                          "Expected EMLSR link " << +id << " of EMLSR client "
+                                                                 << secondClientId
+                                                                 << " to be blocked "
+                                                                    " for one reason only");
+                }
+            });
+
+        break;
+    }
+}
+
+void
+EmlsrDlTxopTest::CheckBlockAck(const WifiConstPsduMap& psduMap,
+                               const WifiTxVector& txVector,
+                               uint8_t linkId)
+{
+    if (m_nEmlsrStations != 2 || m_apMac->GetNLinks() != m_emlsrLinks.size() ||
+        m_emlsrEnabledTime.IsZero() || Simulator::Now() < m_emlsrEnabledTime + m_fe2to3delay)
+    {
+        // we are interested in frames sent to test transition delay
+        return;
+    }
+
+    auto taddr = psduMap.cbegin()->second->GetAddr2();
+    std::size_t clientId;
+    if (m_staMacs[0]->GetLinkIdByAddress(taddr))
+    {
+        clientId = 0;
+    }
+    else
+    {
+        NS_TEST_ASSERT_MSG_EQ(m_staMacs[1]->GetLinkIdByAddress(taddr).has_value(),
+                              true,
+                              "Unexpected TA for BlockAck: " << taddr);
+        clientId = 1;
+    }
+    // we need the MLD address to check the status of the container queues
+    auto addr = m_apMac->GetWifiRemoteStationManager(linkId)->GetMldAddress(taddr);
+    NS_TEST_ASSERT_MSG_EQ(addr.has_value(), true, "MLD address not found for " << taddr);
+
+    auto apPhy = m_apMac->GetWifiPhy(linkId);
+    auto txDuration = WifiPhy::CalculateTxDuration(psduMap, txVector, apPhy->GetPhyBand());
+    auto timeout = apPhy->GetSifs() + apPhy->GetSlot() + MicroSeconds(20);
+
+    m_countBlockAck++;
+
+    switch (m_countBlockAck)
+    {
+    case 4:
+        // at the end of the PPDU carrying this BlockAck, the EMLSR client sending this
+        // frame will start a timeout interval, after which it will start the transition to
+        // the listening mode (such transition lasting the transition delay)
+
+        // immediately before the end of the PPDU plus timeout, this link is not blocked
+        // for the EMLSR client
+        Simulator::Schedule(txDuration + timeout - NanoSeconds(1), [=]() {
+            WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0);
+            auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, linkId);
+            NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                  true,
+                                  "Expected to find a mask for EMLSR link "
+                                      << +linkId << " of EMLSR client " << clientId);
+            NS_TEST_EXPECT_MSG_EQ(mask->none(),
+                                  true,
+                                  "Expected EMLSR link " << +linkId << " of EMLSR client "
+                                                         << clientId << " to be unblocked");
+        });
+        // immediately before the end of the PPDU plus timeout, the other links are blocked
+        // for the EMLSR client
+        Simulator::Schedule(txDuration + timeout - NanoSeconds(1), [=]() {
+            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+            {
+                if (id == linkId)
+                {
+                    continue;
+                }
+
+                WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0);
+                auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                      true,
+                                      "Expected to find a mask for EMLSR link "
+                                          << +id << " of EMLSR client " << clientId);
+                auto reason =
+                    static_cast<std::size_t>(WifiQueueBlockedReason::USING_OTHER_EMLSR_LINK);
+                NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                      true,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << clientId << " to be blocked");
+                NS_TEST_EXPECT_MSG_EQ(mask->count(),
+                                      1,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << clientId
+                                                             << " to be blocked "
+                                                                " for one reason only");
+            }
+        });
+        // immediately after the end of the PPDU plus timeout, all links are blocked for the EMLSR
+        // client
+        Simulator::Schedule(txDuration + timeout + MicroSeconds(1), [=]() {
+            for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+            {
+                WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0);
+                auto mask = m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                      true,
+                                      "Expected to find a mask for EMLSR link "
+                                          << +id << " of EMLSR client " << clientId);
+                auto reason = static_cast<std::size_t>(
+                    WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
+                NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                      true,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << clientId << " to be blocked");
+                NS_TEST_EXPECT_MSG_EQ(mask->count(),
+                                      1,
+                                      "Expected EMLSR link " << +id << " of EMLSR client "
+                                                             << clientId
+                                                             << " to be blocked "
+                                                                " for one reason only");
+            }
+        });
+        // immediately before the transition delay, all links are still blocked for the EMLSR client
+        Simulator::Schedule(
+            txDuration + timeout + m_transitionDelay.at(clientId) - NanoSeconds(1),
+            [=]() {
+                for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+                {
+                    WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0);
+                    auto mask =
+                        m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                    NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                          true,
+                                          "Expected to find a mask for EMLSR link "
+                                              << +id << " of EMLSR client " << clientId);
+                    auto reason = static_cast<std::size_t>(
+                        WifiQueueBlockedReason::WAITING_EMLSR_TRANSITION_DELAY);
+                    NS_TEST_EXPECT_MSG_EQ(mask->test(reason),
+                                          true,
+                                          "Expected EMLSR link " << +id << " of EMLSR client "
+                                                                 << clientId << " to be blocked");
+                    NS_TEST_EXPECT_MSG_EQ(mask->count(),
+                                          1,
+                                          "Expected EMLSR link " << +id << " of EMLSR client "
+                                                                 << clientId
+                                                                 << " to be blocked "
+                                                                    " for one reason only");
+                }
+            });
+        // immediately after the transition delay, all links are unblocked for the EMLSR client
+        Simulator::Schedule(
+            txDuration + timeout + m_transitionDelay.at(clientId) + MicroSeconds(1),
+            [=]() {
+                for (uint8_t id = 0; id < m_apMac->GetNLinks(); id++)
+                {
+                    WifiContainerQueueId queueId(WIFI_QOSDATA_QUEUE, WIFI_UNICAST, *addr, 0);
+                    auto mask =
+                        m_apMac->GetMacQueueScheduler()->GetQueueLinkMask(AC_BE, queueId, id);
+                    NS_TEST_EXPECT_MSG_EQ(mask.has_value(),
+                                          true,
+                                          "Expected to find a mask for EMLSR link "
+                                              << +id << " of EMLSR client " << clientId);
+                    NS_TEST_EXPECT_MSG_EQ(mask->none(),
+                                          true,
+                                          "Expected EMLSR link " << +id << " of EMLSR client "
+                                                                 << clientId << " to be unblocked");
+                }
+            });
+
+        // corrupt this BlockAck so that the AP MLD sends a BlockAckReq later on
+        auto uid = psduMap.cbegin()->second->GetPacket()->GetUid();
+        m_errorModel->SetList({uid});
+        break;
+    }
+}
+
+void
+EmlsrDlTxopTest::DoRun()
+{
+    Simulator::Stop(m_duration);
+    Simulator::Run();
+
+    CheckResults();
+
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief wifi EMLSR Test Suite
+ */
+class WifiEmlsrTestSuite : public TestSuite
+{
+  public:
+    WifiEmlsrTestSuite();
+};
+
+WifiEmlsrTestSuite::WifiEmlsrTestSuite()
+    : TestSuite("wifi-emlsr", UNIT)
+{
+    AddTestCase(new EmlOperatingModeNotificationTest(), TestCase::QUICK);
+    AddTestCase(new EmlNotificationExchangeTest({1, 2}, MicroSeconds(0)), TestCase::QUICK);
+    AddTestCase(new EmlNotificationExchangeTest({1, 2}, MicroSeconds(2048)), TestCase::QUICK);
+    AddTestCase(new EmlNotificationExchangeTest({0, 1, 2, 3}, MicroSeconds(0)), TestCase::QUICK);
+    AddTestCase(new EmlNotificationExchangeTest({0, 1, 2, 3}, MicroSeconds(2048)), TestCase::QUICK);
+    for (const auto& emlsrLinks : {std::set<uint8_t>{0, 1, 2},
+                                   std::set<uint8_t>{1, 2},
+                                   std::set<uint8_t>{0, 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)),
+                    TestCase::QUICK);
+        AddTestCase(new EmlsrDlTxopTest(2,
+                                        2,
+                                        emlsrLinks,
+                                        {MicroSeconds(128), MicroSeconds(256)},
+                                        {MicroSeconds(128), MicroSeconds(256)},
+                                        MicroSeconds(512)),
+                    TestCase::QUICK);
+    }
+}
+
+static WifiEmlsrTestSuite g_wifiEmlsrTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-ie-fragment-test.cc ns-3.39/src/wifi/test/wifi-ie-fragment-test.cc
--- ns-3.38/src/wifi/test/wifi-ie-fragment-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-ie-fragment-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -19,15 +19,21 @@
 
 #include "ns3/header-serialization-test.h"
 #include "ns3/log.h"
+#include "ns3/supported-rates.h"
 #include "ns3/wifi-information-element.h"
+#include "ns3/wifi-mgt-header.h"
 
 #include <list>
 #include <numeric>
+#include <optional>
 
 using namespace ns3;
 
 NS_LOG_COMPONENT_DEFINE("WifiIeFragmentTest");
 
+/// whether the test Information Element includes an Element ID Extension field
+static bool g_extendedIe = false;
+
 /**
  * \ingroup wifi-test
  * \ingroup tests
@@ -205,30 +211,31 @@
  *
  * Test header that can contain multiple test information elements.
  */
-class TestHeader : public Header
+class TestHeader
+    : public WifiMgtHeader<TestHeader, std::tuple<std::vector<TestWifiInformationElement>>>
 {
+    friend class WifiMgtHeader<TestHeader, std::tuple<std::vector<TestWifiInformationElement>>>;
+
   public:
+    ~TestHeader() override = default;
+
     /**
      * \brief Get the type ID.
      * \return the object TypeId
      */
     static TypeId GetTypeId();
 
-    TypeId GetInstanceTypeId() const override;
-    uint32_t GetSerializedSize() const override;
-    void Serialize(Buffer::Iterator start) const override;
-    uint32_t Deserialize(Buffer::Iterator start) override;
-    void Print(std::ostream& os) const override;
-
     /**
-     * Append the given wifi test information element.
-     *
-     * \param element the given wifi test information element
+     * \return the TypeId for this object.
      */
-    void AddTestIe(TestWifiInformationElement&& element);
+    TypeId GetInstanceTypeId() const override;
 
   private:
-    std::list<TestWifiInformationElement> m_elements; ///< test information elements
+    /**
+     * \param optElem the MultiLinkElement object to initialize for deserializing the
+     *                information element into
+     */
+    void InitForDeserialization(std::optional<TestWifiInformationElement>& optElem);
 };
 
 NS_OBJECT_ENSURE_REGISTERED(TestHeader);
@@ -250,60 +257,9 @@
 }
 
 void
-TestHeader::Print(std::ostream& os) const
-{
-}
-
-uint32_t
-TestHeader::GetSerializedSize() const
-{
-    uint32_t size = 0;
-    for (const auto& elem : m_elements)
-    {
-        size += elem.GetSerializedSize();
-    }
-    return size;
-}
-
-void
-TestHeader::Serialize(Buffer::Iterator start) const
+TestHeader::InitForDeserialization(std::optional<TestWifiInformationElement>& optElem)
 {
-    NS_LOG_FUNCTION(this);
-    for (const auto& elem : m_elements)
-    {
-        start = elem.Serialize(start);
-    }
-}
-
-uint32_t
-TestHeader::Deserialize(Buffer::Iterator start)
-{
-    NS_LOG_FUNCTION(this);
-    Buffer::Iterator i = start;
-    m_elements.clear();
-
-    while (!i.IsEnd())
-    {
-        std::optional<TestWifiInformationElement> elem;
-        i = WifiInformationElement::DeserializeIfPresent(elem, i, true);
-        if (!elem.has_value())
-        {
-            // try deserializing the test IE without Element ID Extension field
-            i = WifiInformationElement::DeserializeIfPresent(elem, i, false);
-        }
-        if (elem.has_value())
-        {
-            m_elements.push_back(std::move(*elem));
-        }
-    }
-    return i.GetDistanceFrom(start);
-}
-
-void
-TestHeader::AddTestIe(TestWifiInformationElement&& element)
-{
-    NS_LOG_FUNCTION(this);
-    m_elements.push_back(std::move(element));
+    optElem.emplace(g_extendedIe);
 }
 
 /**
@@ -376,6 +332,7 @@
     uint16_t limit = m_extended ? 254 : 255;
 
     TestHeader header;
+    g_extendedIe = m_extended;
 
     /*
      * Add an IE (containing 2 subelements). No fragmentation occurs
@@ -409,7 +366,7 @@
                             sub02Size - 2); // subelement 2 Length
     }
 
-    header.AddTestIe(std::move(testIe));
+    header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
     uint32_t expectedHdrSize = 2 + 255;
     NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
     TestHeaderSerialization(header);
@@ -450,7 +407,7 @@
                             1); // the length of the second element fragment is 1
     }
 
-    header.AddTestIe(std::move(testIe));
+    header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
     expectedHdrSize += 2 + 255  // first fragment
                        + 2 + 1; // second fragment
     NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
@@ -493,7 +450,7 @@
         CheckSerializedByte(buffer, 2 + 255 + 1, 255); // maximum length for second element fragment
     }
 
-    header.AddTestIe(std::move(testIe));
+    header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
     expectedHdrSize += 2 + 255    // first fragment
                        + 2 + 255; // second fragment
     NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
@@ -544,7 +501,7 @@
         CheckSerializedByte(buffer, 2 * (2 + 255) + 1, 1); // the length of the third fragment is 1
     }
 
-    header.AddTestIe(std::move(testIe));
+    header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
     expectedHdrSize += 2 + 255   // first fragment
                        + 2 + 255 // second fragment
                        + 2 + 1;  // third fragment
@@ -578,7 +535,7 @@
                             (m_extended ? 3 : 2)); // length of the second element fragment
     }
 
-    header.AddTestIe(std::move(testIe));
+    header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
     expectedHdrSize += 2 + 255                     // first fragment
                        + 2 + (m_extended ? 3 : 2); // second fragment
     NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
@@ -623,7 +580,7 @@
                             1); // Length for second subelement fragment
     }
 
-    header.AddTestIe(std::move(testIe));
+    header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
     expectedHdrSize += 2 + 255                     // first fragment
                        + 2 + (m_extended ? 6 : 5); // second fragment
     NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-mac-ofdma-test.cc ns-3.39/src/wifi/test/wifi-mac-ofdma-test.cc
--- ns-3.38/src/wifi/test/wifi-mac-ofdma-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-mac-ofdma-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -153,11 +153,17 @@
                                  ? GetMaxSizeOfQosNullAmpdu(m_trigger)
                                  : 3500; // allows aggregation of 2 MPDUs in TB PPDUs
 
-        Time duration =
-            WifiPhy::CalculateTxDuration(ampduSize,
-                                         txVector,
-                                         m_apMac->GetWifiPhy()->GetPhyBand(),
-                                         m_apMac->GetStaList(SINGLE_LINK_OP_ID).begin()->first);
+        auto staList = m_apMac->GetStaList(SINGLE_LINK_OP_ID);
+        // ignore non-HE stations
+        for (auto it = staList.begin(); it != staList.end();)
+        {
+            it = m_apMac->GetHeSupported(it->second) ? std::next(it) : staList.erase(it);
+        }
+
+        Time duration = WifiPhy::CalculateTxDuration(ampduSize,
+                                                     txVector,
+                                                     m_apMac->GetWifiPhy()->GetPhyBand(),
+                                                     staList.begin()->first);
 
         uint16_t length;
         std::tie(length, duration) = HePhy::ConvertHeTbPpduDurationToLSigLength(
@@ -200,7 +206,12 @@
     {
         // try to send a DL MU PPDU
         m_psduMap.clear();
-        const std::map<uint16_t, Mac48Address>& staList = m_apMac->GetStaList(SINGLE_LINK_OP_ID);
+        auto staList = m_apMac->GetStaList(SINGLE_LINK_OP_ID);
+        // ignore non-HE stations
+        for (auto it = staList.cbegin(); it != staList.cend();)
+        {
+            it = m_apMac->GetHeSupported(it->second) ? std::next(it) : staList.erase(it);
+        }
         NS_ABORT_MSG_IF(staList.size() != 4, "There must be 4 associated stations");
 
         /* Initialize TX params */
@@ -292,7 +303,12 @@
     m_txVector.SetTxPowerLevel(
         GetWifiRemoteStationManager(SINGLE_LINK_OP_ID)->GetDefaultTxPowerLevel());
 
-    const std::map<uint16_t, Mac48Address>& staList = m_apMac->GetStaList(SINGLE_LINK_OP_ID);
+    auto staList = m_apMac->GetStaList(SINGLE_LINK_OP_ID);
+    // ignore non-HE stations
+    for (auto it = staList.cbegin(); it != staList.cend();)
+    {
+        it = m_apMac->GetHeSupported(it->second) ? std::next(it) : staList.erase(it);
+    }
     NS_ABORT_MSG_IF(staList.size() != 4, "There must be 4 associated stations");
 
     HeRu::RuType ruType;
@@ -300,19 +316,19 @@
     {
     case 20:
         ruType = HeRu::RU_52_TONE;
-        m_txVector.SetRuAllocation({112});
+        m_txVector.SetRuAllocation({112}, 0);
         break;
     case 40:
         ruType = HeRu::RU_106_TONE;
-        m_txVector.SetRuAllocation({96, 96});
+        m_txVector.SetRuAllocation({96, 96}, 0);
         break;
     case 80:
         ruType = HeRu::RU_242_TONE;
-        m_txVector.SetRuAllocation({192, 192, 192, 192});
+        m_txVector.SetRuAllocation({192, 192, 192, 192}, 0);
         break;
     case 160:
         ruType = HeRu::RU_484_TONE;
-        m_txVector.SetRuAllocation({200, 200, 200, 200, 200, 200, 200, 200});
+        m_txVector.SetRuAllocation({200, 200, 200, 200, 200, 200, 200, 200}, 0);
         break;
     default:
         NS_ABORT_MSG("Unsupported channel width");
@@ -529,6 +545,8 @@
     default:
         NS_ABORT_MSG("Unhandled channel width (" << m_channelWidth << " MHz)");
     }
+
+    m_txPsdus.reserve(35);
 }
 
 OfdmaAckSequenceTest::~OfdmaAckSequenceTest()
@@ -993,27 +1011,6 @@
     NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, qosNullNavEnd, "Duration/ID in QoS Null is too short");
     NS_TEST_EXPECT_MSG_LT(qosNullNavEnd, navEnd + tolerance, "Duration/ID in QoS Null is too long");
 
-    // the AP sends another MU-RTS Trigger Frame to protect the Basic TF
-    NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 15, "Expected at least 15 transmitted packet");
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[10].psduMap.size() == 1 &&
-                           m_txPsdus[10].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
-                           m_txPsdus[10].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
-                          true,
-                          "Expected a Trigger Frame");
-    m_txPsdus[10].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(trigger);
-    NS_TEST_EXPECT_MSG_EQ(trigger.IsMuRts(), true, "Expected an MU-RTS Trigger Frame");
-    NS_TEST_EXPECT_MSG_EQ(trigger.GetNUserInfoFields(),
-                          4,
-                          "Expected one User Info field per station");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[10].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the MU-RTS to occupy the entire channel width");
-    for (const auto& userInfo : trigger)
-    {
-        NS_TEST_EXPECT_MSG_EQ(+userInfo.GetMuRtsRuAllocation(),
-                              +m_muRtsRuAllocation,
-                              "Unexpected RU Allocation value in MU-RTS");
-    }
     tEnd = m_txPsdus[9].endTx;
     tStart = m_txPsdus[10].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + ifs, tStart, "Basic Trigger Frame sent too early");
@@ -1027,89 +1024,136 @@
         NS_TEST_EXPECT_MSG_LT(muRtsNavEnd, navEnd + tolerance, "Duration/ID in MU-RTS is too long");
     }
 
-    // NAV end is now set by the Duration/ID of the second MU-RTS TF
-    tEnd = m_txPsdus[10].endTx;
-    navEnd = tEnd + m_txPsdus[10].psduMap[SU_STA_ID]->GetDuration();
-
-    // A first STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[11].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[11].psduMap.size() == 1 &&
-         m_txPsdus[11].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[11].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
-
-    tStart = m_txPsdus[11].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[11].endTx + m_txPsdus[11].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
-
-    // A second STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[12].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[12].psduMap.size() == 1 &&
-         m_txPsdus[12].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[12].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[12].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
-
-    tStart = m_txPsdus[12].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[12].endTx + m_txPsdus[12].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
+    // if the TXOP limit is not null, MU-RTS protection is not used because the next transmission
+    // is protected by the previous MU-RTS Trigger Frame
+    if (m_txopLimit == 0)
+    {
+        // the AP sends another MU-RTS Trigger Frame to protect the Basic TF
+        NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(),
+                                    15,
+                                    "Expected at least 15 transmitted packet");
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[10].psduMap.size() == 1 &&
+             m_txPsdus[10].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
+             m_txPsdus[10].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
+            true,
+            "Expected a Trigger Frame");
+        m_txPsdus[10].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(trigger);
+        NS_TEST_EXPECT_MSG_EQ(trigger.IsMuRts(), true, "Expected an MU-RTS Trigger Frame");
+        NS_TEST_EXPECT_MSG_EQ(trigger.GetNUserInfoFields(),
+                              4,
+                              "Expected one User Info field per station");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[10].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the MU-RTS to occupy the entire channel width");
+        for (const auto& userInfo : trigger)
+        {
+            NS_TEST_EXPECT_MSG_EQ(+userInfo.GetMuRtsRuAllocation(),
+                                  +m_muRtsRuAllocation,
+                                  "Unexpected RU Allocation value in MU-RTS");
+        }
+
+        // NAV end is now set by the Duration/ID of the second MU-RTS TF
+        tEnd = m_txPsdus[10].endTx;
+        navEnd = tEnd + m_txPsdus[10].psduMap[SU_STA_ID]->GetDuration();
+
+        // A first STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[11].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[11].psduMap.size() == 1 &&
+             m_txPsdus[11].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[11].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[11].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[11].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[11].endTx + m_txPsdus[11].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    // A third STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[13].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[13].psduMap.size() == 1 &&
-         m_txPsdus[13].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[13].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
+        // A second STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[12].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[12].psduMap.size() == 1 &&
+             m_txPsdus[12].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[12].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[12].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[12].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[12].endTx + m_txPsdus[12].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    tStart = m_txPsdus[13].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[13].endTx + m_txPsdus[13].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
+        // A third STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[13].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[13].psduMap.size() == 1 &&
+             m_txPsdus[13].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[13].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[13].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[13].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[13].endTx + m_txPsdus[13].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    // A fourth STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[14].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[14].psduMap.size() == 1 &&
-         m_txPsdus[14].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[14].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[14].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
+        // A fourth STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[14].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[14].psduMap.size() == 1 &&
+             m_txPsdus[14].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[14].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[14].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[14].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[14].endTx + m_txPsdus[14].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    tStart = m_txPsdus[14].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[14].endTx + m_txPsdus[14].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
+        tEnd = m_txPsdus[14].endTx;
+    }
+    else
+    {
+        // insert 5 elements in m_txPsdus to align the index of the following frames in the
+        // two cases (TXOP limit null and not null)
+        m_txPsdus.insert(std::next(m_txPsdus.begin(), 10), 5, {});
+        tEnd = m_txPsdus[9].endTx;
+    }
 
     // the AP sends a Basic Trigger Frame to solicit QoS data frames
     NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 21, "Expected at least 21 transmitted packets");
@@ -1123,7 +1167,6 @@
     NS_TEST_EXPECT_MSG_EQ(trigger.GetNUserInfoFields(),
                           4,
                           "Expected one User Info field per station");
-    tEnd = m_txPsdus[14].endTx;
     tStart = m_txPsdus[15].startTx;
     NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "Basic Trigger Frame sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "Basic Trigger Frame sent too late");
@@ -1234,112 +1277,137 @@
                           navEnd + tolerance,
                           "Duration/ID in Multi-STA BlockAck is too long");
 
-    // the AP sends an MU-RTS Trigger Frame to protect the DL MU PPDU
-    NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 26, "Expected at least 26 transmitted packet");
-    NS_TEST_EXPECT_MSG_EQ((m_txPsdus[21].psduMap.size() == 1 &&
-                           m_txPsdus[21].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
-                           m_txPsdus[21].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
-                          true,
-                          "Expected a Trigger Frame");
-    m_txPsdus[21].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(trigger);
-    NS_TEST_EXPECT_MSG_EQ(trigger.IsMuRts(), true, "Expected an MU-RTS Trigger Frame");
-    NS_TEST_EXPECT_MSG_EQ(trigger.GetNUserInfoFields(),
-                          4,
-                          "Expected one User Info field per station");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[21].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the MU-RTS to occupy the entire channel width");
-    for (const auto& userInfo : trigger)
+    // if the TXOP limit is not null, MU-RTS protection is not used because the next transmission
+    // is protected by the previous MU-RTS Trigger Frame
+    if (m_txopLimit == 0)
     {
-        NS_TEST_EXPECT_MSG_EQ(+userInfo.GetMuRtsRuAllocation(),
-                              +m_muRtsRuAllocation,
-                              "Unexpected RU Allocation value in MU-RTS");
-    }
-    tEnd = m_txPsdus[20].endTx;
-    tStart = m_txPsdus[21].startTx;
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(tEnd + ifs, tStart, "MU-RTS Trigger Frame sent too early");
-    tEnd = m_txPsdus[21].endTx;
-    navEnd = tEnd + m_txPsdus[21].psduMap[SU_STA_ID]->GetDuration();
-
-    // A first STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[22].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[22].psduMap.size() == 1 &&
-         m_txPsdus[22].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[22].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[22].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
-
-    tStart = m_txPsdus[22].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[22].endTx + m_txPsdus[22].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
-
-    // A second STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[23].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[23].psduMap.size() == 1 &&
-         m_txPsdus[23].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[23].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[23].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
-
-    tStart = m_txPsdus[23].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[23].endTx + m_txPsdus[23].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
+        // the AP sends an MU-RTS Trigger Frame to protect the DL MU PPDU
+        NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(),
+                                    26,
+                                    "Expected at least 26 transmitted packet");
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[21].psduMap.size() == 1 &&
+             m_txPsdus[21].psduMap[SU_STA_ID]->GetHeader(0).IsTrigger() &&
+             m_txPsdus[21].psduMap[SU_STA_ID]->GetHeader(0).GetAddr1().IsBroadcast()),
+            true,
+            "Expected a Trigger Frame");
+        m_txPsdus[21].psduMap[SU_STA_ID]->GetPayload(0)->PeekHeader(trigger);
+        NS_TEST_EXPECT_MSG_EQ(trigger.IsMuRts(), true, "Expected an MU-RTS Trigger Frame");
+        NS_TEST_EXPECT_MSG_EQ(trigger.GetNUserInfoFields(),
+                              4,
+                              "Expected one User Info field per station");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[21].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the MU-RTS to occupy the entire channel width");
+        for (const auto& userInfo : trigger)
+        {
+            NS_TEST_EXPECT_MSG_EQ(+userInfo.GetMuRtsRuAllocation(),
+                                  +m_muRtsRuAllocation,
+                                  "Unexpected RU Allocation value in MU-RTS");
+        }
+        tEnd = m_txPsdus[20].endTx;
+        tStart = m_txPsdus[21].startTx;
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(tEnd + ifs, tStart, "MU-RTS Trigger Frame sent too early");
+        tEnd = m_txPsdus[21].endTx;
+        navEnd = tEnd + m_txPsdus[21].psduMap[SU_STA_ID]->GetDuration();
+
+        // A first STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[22].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[22].psduMap.size() == 1 &&
+             m_txPsdus[22].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[22].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[22].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[22].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[22].endTx + m_txPsdus[22].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    // A third STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[24].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[24].psduMap.size() == 1 &&
-         m_txPsdus[24].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[24].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[24].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
+        // A second STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[23].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[23].psduMap.size() == 1 &&
+             m_txPsdus[23].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[23].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[23].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[23].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[23].endTx + m_txPsdus[23].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    tStart = m_txPsdus[24].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[24].endTx + m_txPsdus[24].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
+        // A third STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[24].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[24].psduMap.size() == 1 &&
+             m_txPsdus[24].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[24].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[24].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[24].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[24].endTx + m_txPsdus[24].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    // A fourth STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
-    NS_TEST_EXPECT_MSG_EQ(
-        (m_txPsdus[25].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
-         m_txPsdus[25].psduMap.size() == 1 &&
-         m_txPsdus[25].psduMap.begin()->second->GetNMpdus() == 1 &&
-         m_txPsdus[25].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
-        true,
-        "Expected a CTS frame");
-    NS_TEST_EXPECT_MSG_EQ(m_txPsdus[25].txVector.GetChannelWidth(),
-                          m_channelWidth,
-                          "Expected the CTS to occupy the entire channel width");
+        // A fourth STA sends a CTS frame a SIFS after the reception of the MU-RTS TF
+        NS_TEST_EXPECT_MSG_EQ(
+            (m_txPsdus[25].txVector.GetPreambleType() != WIFI_PREAMBLE_HE_TB &&
+             m_txPsdus[25].psduMap.size() == 1 &&
+             m_txPsdus[25].psduMap.begin()->second->GetNMpdus() == 1 &&
+             m_txPsdus[25].psduMap.begin()->second->GetHeader(0).GetType() == WIFI_MAC_CTL_CTS),
+            true,
+            "Expected a CTS frame");
+        NS_TEST_EXPECT_MSG_EQ(m_txPsdus[25].txVector.GetChannelWidth(),
+                              m_channelWidth,
+                              "Expected the CTS to occupy the entire channel width");
+
+        tStart = m_txPsdus[25].startTx;
+        NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
+        NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
+        ctsNavEnd = m_txPsdus[25].endTx + m_txPsdus[25].psduMap[SU_STA_ID]->GetDuration();
+        // navEnd <= ctsNavEnd < navEnd + tolerance
+        NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
+        NS_TEST_EXPECT_MSG_LT(ctsNavEnd,
+                              navEnd + tolerance,
+                              "Duration/ID in CTS frame is too long");
 
-    tStart = m_txPsdus[25].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "CTS frame sent too early");
-    NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "CTS frame sent too late");
-    ctsNavEnd = m_txPsdus[25].endTx + m_txPsdus[25].psduMap[SU_STA_ID]->GetDuration();
-    // navEnd <= ctsNavEnd < navEnd + tolerance
-    NS_TEST_EXPECT_MSG_LT_OR_EQ(navEnd, ctsNavEnd, "Duration/ID in CTS frame is too short");
-    NS_TEST_EXPECT_MSG_LT(ctsNavEnd, navEnd + tolerance, "Duration/ID in CTS frame is too long");
+        tEnd = m_txPsdus[25].endTx;
+    }
+    else
+    {
+        // insert 5 elements in m_txPsdus to align the index of the following frames in the
+        // two cases (TXOP limit null and not null)
+        m_txPsdus.insert(std::next(m_txPsdus.begin(), 21), 5, {});
+        tEnd = m_txPsdus[20].endTx;
+    }
 
     // the AP sends a DL MU PPDU
     NS_TEST_ASSERT_MSG_GT_OR_EQ(m_txPsdus.size(), 27, "Expected at least 27 transmitted packet");
@@ -1359,9 +1427,8 @@
                                     m_maxAmpduSize,
                                     "Max A-MPDU size exceeded");
     }
-    tEnd = m_txPsdus[25].endTx;
     tStart = m_txPsdus[26].startTx;
-    NS_TEST_EXPECT_MSG_LT(tEnd + sifs, tStart, "DL MU PPDU sent too early");
+    NS_TEST_EXPECT_MSG_LT_OR_EQ(tEnd + sifs, tStart, "DL MU PPDU sent too early");
     NS_TEST_EXPECT_MSG_LT(tStart, tEnd + sifs + tolerance, "DL MU PPDU sent too late");
 
     // The Duration/ID field is the same for all the PSDUs
@@ -1907,8 +1974,8 @@
 {
     uint32_t previousSeed = RngSeedManager::GetSeed();
     uint64_t previousRun = RngSeedManager::GetRun();
-    Config::SetGlobal("RngSeed", UintegerValue(1));
-    Config::SetGlobal("RngRun", UintegerValue(1));
+    Config::SetGlobal("RngSeed", UintegerValue(2));
+    Config::SetGlobal("RngRun", UintegerValue(2));
     int64_t streamNumber = 10;
 
     NodeContainer wifiApNode;
@@ -2053,6 +2120,13 @@
                                                          : WIFI_STANDARD_80211be);
     m_staDevices = NetDeviceContainer(m_staDevices, wifi.Install(phy, mac, wifiNewStaNodes));
 
+    // create a listening VHT station
+    wifi.SetStandard(WIFI_STANDARD_80211ac);
+    wifi.Install(phy, mac, Create<Node>());
+
+    wifi.SetStandard(m_scenario == WifiOfdmaScenario::HE ? WIFI_STANDARD_80211ax
+                                                         : WIFI_STANDARD_80211be);
+
     mac.SetType("ns3::ApWifiMac", "BeaconGeneration", BooleanValue(true));
     mac.SetMultiUserScheduler(
         "ns3::TestMultiUserScheduler",
@@ -2222,7 +2296,7 @@
             AddTestCase(new OfdmaAckSequenceTest(20,
                                                  WifiAcknowledgment::DL_MU_BAR_BA_SEQUENCE,
                                                  10000,
-                                                 5504,
+                                                 5632,
                                                  15,
                                                  muEdcaParameterSet,
                                                  scenario),
@@ -2230,7 +2304,7 @@
             AddTestCase(new OfdmaAckSequenceTest(20,
                                                  WifiAcknowledgment::DL_MU_AGGREGATE_TF,
                                                  10000,
-                                                 5504,
+                                                 5632,
                                                  15,
                                                  muEdcaParameterSet,
                                                  scenario),
@@ -2238,7 +2312,7 @@
             AddTestCase(new OfdmaAckSequenceTest(20,
                                                  WifiAcknowledgment::DL_MU_TF_MU_BAR,
                                                  10000,
-                                                 5504,
+                                                 5632,
                                                  15,
                                                  muEdcaParameterSet,
                                                  scenario),
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-mac-queue-test.cc ns-3.39/src/wifi/test/wifi-mac-queue-test.cc
--- ns-3.38/src/wifi/test/wifi-mac-queue-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-mac-queue-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -225,8 +225,8 @@
     Enqueue(rxAddr2, false, MilliSeconds(70));
     Enqueue(rxAddr2, false, MilliSeconds(75));
 
-    WifiContainerQueueId queueId1{WIFI_QOSDATA_UNICAST_QUEUE, rxAddr1, 0};
-    WifiContainerQueueId queueId2{WIFI_QOSDATA_UNICAST_QUEUE, rxAddr2, 0};
+    WifiContainerQueueId queueId1{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, rxAddr1, 0};
+    WifiContainerQueueId queueId2{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, rxAddr2, 0};
 
     Simulator::Schedule(MilliSeconds(25), [&]() {
         /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-mlo-test.cc ns-3.39/src/wifi/test/wifi-mlo-test.cc
--- ns-3.38/src/wifi/test/wifi-mlo-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-mlo-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -52,6 +52,7 @@
 #include <algorithm>
 #include <array>
 #include <iomanip>
+#include <optional>
 #include <sstream>
 #include <vector>
 
@@ -235,6 +236,9 @@
 
     void DoSetup() override;
 
+    /// PHY band-indexed map of spectrum channels
+    using ChannelMap = std::map<FrequencyRange, Ptr<MultiModelSpectrumChannel>>;
+
     /**
      * Uplink or Downlink direction
      */
@@ -279,11 +283,11 @@
      *
      * \param helper the given PHY helper
      * \param channels the strings specifying the operating channels to configure
-     * \param channel the created spectrum channel
+     * \param channelMap the created spectrum channels
      */
     void SetChannels(SpectrumWifiPhyHelper& helper,
                      const std::vector<std::string>& channels,
-                     Ptr<MultiModelSpectrumChannel> channel);
+                     const ChannelMap& channelMap);
 
     /**
      * Set the SSID on the next station that needs to start the association procedure.
@@ -425,7 +429,7 @@
 void
 MultiLinkOperationsTestBase::SetChannels(SpectrumWifiPhyHelper& helper,
                                          const std::vector<std::string>& channels,
-                                         Ptr<MultiModelSpectrumChannel> channel)
+                                         const ChannelMap& channelMap)
 {
     helper = SpectrumWifiPhyHelper(channels.size());
     helper.SetPcapDataLinkType(WifiPhyHelper::DLT_IEEE802_11_RADIO);
@@ -436,7 +440,12 @@
         helper.Set(linkId++, "ChannelSettings", StringValue(str));
     }
 
-    helper.SetChannel(channel);
+    // NOTE replace this for loop with the line below to use a single spectrum channel
+    // helper.SetChannel(channelMap.begin()->second);
+    for (const auto& [band, channel] : channelMap)
+    {
+        helper.AddChannel(channel, band);
+    }
 }
 
 void
@@ -444,7 +453,7 @@
 {
     RngSeedManager::SetSeed(1);
     RngSeedManager::SetRun(2);
-    int64_t streamNumber = 100;
+    int64_t streamNumber = 30;
 
     NodeContainer wifiApNode;
     wifiApNode.Create(1);
@@ -461,12 +470,14 @@
                                  "ControlMode",
                                  StringValue("HtMcs0"));
 
-    auto channel = CreateObject<MultiModelSpectrumChannel>();
+    ChannelMap channelMap{{WIFI_SPECTRUM_2_4_GHZ, CreateObject<MultiModelSpectrumChannel>()},
+                          {WIFI_SPECTRUM_5_GHZ, CreateObject<MultiModelSpectrumChannel>()},
+                          {WIFI_SPECTRUM_6_GHZ, CreateObject<MultiModelSpectrumChannel>()}};
 
     SpectrumWifiPhyHelper staPhyHelper;
     SpectrumWifiPhyHelper apPhyHelper;
-    SetChannels(staPhyHelper, m_staChannels, channel);
-    SetChannels(apPhyHelper, m_apChannels, channel);
+    SetChannels(staPhyHelper, m_staChannels, channelMap);
+    SetChannels(apPhyHelper, m_apChannels, channelMap);
 
     for (const auto& linkId : m_fixedPhyBands)
     {
@@ -537,7 +548,7 @@
     // schedule ML setup for one station at a time
     m_apMac->TraceConnectWithoutContext("AssociatedSta",
                                         MakeCallback(&MultiLinkOperationsTestBase::SetSsid, this));
-    Simulator::Schedule(Seconds(0), [&]() { m_staMacs[0]->SetSsid(Ssid("ns-3-ssid")); });
+    m_staMacs[0]->SetSsid(Ssid("ns-3-ssid"));
 }
 
 void
@@ -581,16 +592,19 @@
      *
      * \param staChannels the strings specifying the operating channels for the STA
      * \param apChannels the strings specifying the operating channels for the AP
+     * \param scanType the scan type (active or passive)
      * \param setupLinks a list of links (STA link ID, AP link ID) that are expected to be setup
      * \param fixedPhyBands list of IDs of STA links that cannot switch PHY band
      */
     MultiLinkSetupTest(std::vector<std::string> staChannels,
                        std::vector<std::string> apChannels,
+                       WifiScanType scanType,
                        std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
                        std::vector<uint8_t> fixedPhyBands = {});
     ~MultiLinkSetupTest() override = default;
 
   protected:
+    void DoSetup() override;
     void DoRun() override;
 
   private:
@@ -613,6 +627,14 @@
     void CheckBeacon(Ptr<WifiMpdu> mpdu, uint8_t linkId);
 
     /**
+     * Check correctness of the given Probe Response frame.
+     *
+     * \param mpdu the given Probe Response frame
+     * \param linkId the ID of the link on which the Probe Response frame was transmitted
+     */
+    void CheckProbeResponse(Ptr<WifiMpdu> mpdu, uint8_t linkId);
+
+    /**
      * Check correctness of the given Association Request frame.
      *
      * \param mpdu the given Association Request frame
@@ -630,10 +652,13 @@
 
     /// expected links to setup (STA link ID, AP link ID)
     const std::vector<std::pair<uint8_t, uint8_t>> m_setupLinks;
+    WifiScanType m_scanType;  //!< the scan type (active or passive)
+    std::size_t m_nProbeResp; //!< number of Probe Responses received by the non-AP MLD
 };
 
 MultiLinkSetupTest::MultiLinkSetupTest(std::vector<std::string> staChannels,
                                        std::vector<std::string> apChannels,
+                                       WifiScanType scanType,
                                        std::vector<std::pair<uint8_t, uint8_t>> setupLinks,
                                        std::vector<uint8_t> fixedPhyBands)
     : MultiLinkOperationsTestBase("Check correctness of Multi-Link Setup",
@@ -641,11 +666,21 @@
                                   staChannels,
                                   apChannels,
                                   fixedPhyBands),
-      m_setupLinks(setupLinks)
+      m_setupLinks(setupLinks),
+      m_scanType(scanType),
+      m_nProbeResp(0)
 {
 }
 
 void
+MultiLinkSetupTest::DoSetup()
+{
+    MultiLinkOperationsTestBase::DoSetup();
+
+    m_staMacs[0]->SetAttribute("ActiveProbing", BooleanValue(m_scanType == WifiScanType::ACTIVE));
+}
+
+void
 MultiLinkSetupTest::DoRun()
 {
     Simulator::Schedule(MilliSeconds(500), &MultiLinkSetupTest::CheckMlSetup, this);
@@ -667,6 +702,11 @@
             CheckBeacon(mpdu, linkId);
             break;
 
+        case WIFI_MAC_MGT_PROBE_RESPONSE:
+            CheckProbeResponse(mpdu, linkId);
+            m_nProbeResp++;
+            break;
+
         case WIFI_MAC_MGT_ASSOCIATION_REQUEST:
             CheckAssocRequest(mpdu, linkId);
             break;
@@ -682,6 +722,26 @@
 
     CheckDisabledLinks();
 
+    std::size_t expectedProbeResp = 0;
+    if (m_scanType == WifiScanType::ACTIVE)
+    {
+        // the number of Probe Response frames that we expect to receive in active mode equals
+        // the number of channels in common between AP MLD and non-AP MLD at initialization
+        for (const auto& staChannel : m_staChannels)
+        {
+            for (const auto& apChannel : m_apChannels)
+            {
+                if (staChannel == apChannel)
+                {
+                    expectedProbeResp++;
+                    break;
+                }
+            }
+        }
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(m_nProbeResp, expectedProbeResp, "Unexpected number of Probe Responses");
+
     Simulator::Destroy();
 }
 
@@ -697,8 +757,8 @@
                           "TA of Beacon frame is not the address of the link it is transmitted on");
     MgtBeaconHeader beacon;
     mpdu->GetPacket()->PeekHeader(beacon);
-    const auto& rnr = beacon.GetReducedNeighborReport();
-    const auto& mle = beacon.GetMultiLinkElement();
+    const auto& rnr = beacon.Get<ReducedNeighborReport>();
+    const auto& mle = beacon.Get<MultiLinkElement>();
 
     if (m_apMac->GetNLinks() == 1)
     {
@@ -744,6 +804,65 @@
 }
 
 void
+MultiLinkSetupTest::CheckProbeResponse(Ptr<WifiMpdu> mpdu, uint8_t linkId)
+{
+    NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_PROBE_RESPONSE);
+
+    CheckAddresses(Create<WifiPsdu>(mpdu, false), MultiLinkOperationsTestBase::DL);
+
+    NS_TEST_EXPECT_MSG_EQ(
+        m_apMac->GetFrameExchangeManager(linkId)->GetAddress(),
+        mpdu->GetHeader().GetAddr2(),
+        "TA of Probe Response is not the address of the link it is transmitted on");
+    MgtProbeResponseHeader probeResp;
+    mpdu->GetPacket()->PeekHeader(probeResp);
+    const auto& rnr = probeResp.Get<ReducedNeighborReport>();
+    const auto& mle = probeResp.Get<MultiLinkElement>();
+
+    if (m_apMac->GetNLinks() == 1)
+    {
+        NS_TEST_EXPECT_MSG_EQ(rnr.has_value(),
+                              false,
+                              "RNR Element in Probe Response frame from single link AP");
+        NS_TEST_EXPECT_MSG_EQ(mle.has_value(),
+                              false,
+                              "Multi-Link Element in Probe Response frame from single link AP");
+        return;
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(rnr.has_value(), true, "No RNR Element in Probe Response frame");
+    // All the other APs affiliated with the same AP MLD as the AP sending
+    // the Probe Response frame must be reported in a separate Neighbor AP Info field
+    NS_TEST_EXPECT_MSG_EQ(rnr->GetNNbrApInfoFields(),
+                          static_cast<std::size_t>(m_apMac->GetNLinks() - 1),
+                          "Unexpected number of Neighbor AP Info fields in RNR");
+    for (std::size_t nbrApInfoId = 0; nbrApInfoId < rnr->GetNNbrApInfoFields(); nbrApInfoId++)
+    {
+        NS_TEST_EXPECT_MSG_EQ(rnr->HasMldParameters(nbrApInfoId),
+                              true,
+                              "MLD Parameters not present");
+        NS_TEST_EXPECT_MSG_EQ(rnr->GetNTbttInformationFields(nbrApInfoId),
+                              1,
+                              "Expected only one TBTT Info subfield per Neighbor AP Info");
+        uint8_t nbrLinkId = rnr->GetLinkId(nbrApInfoId, 0);
+        NS_TEST_EXPECT_MSG_EQ(rnr->GetBssid(nbrApInfoId, 0),
+                              m_apMac->GetFrameExchangeManager(nbrLinkId)->GetAddress(),
+                              "BSSID advertised in Neighbor AP Info field "
+                                  << nbrApInfoId
+                                  << " does not match the address configured on the link "
+                                     "advertised in the same field");
+    }
+
+    NS_TEST_EXPECT_MSG_EQ(mle.has_value(), true, "No Multi-Link Element in Probe Response frame");
+    NS_TEST_EXPECT_MSG_EQ(mle->GetMldMacAddress(),
+                          m_apMac->GetAddress(),
+                          "Incorrect MLD address advertised in Multi-Link Element");
+    NS_TEST_EXPECT_MSG_EQ(mle->GetLinkIdInfo(),
+                          +linkId,
+                          "Incorrect Link ID advertised in Multi-Link Element");
+}
+
+void
 MultiLinkSetupTest::CheckAssocRequest(Ptr<WifiMpdu> mpdu, uint8_t linkId)
 {
     NS_ABORT_IF(mpdu->GetHeader().GetType() != WIFI_MAC_MGT_ASSOCIATION_REQUEST);
@@ -756,7 +875,7 @@
         "TA of Assoc Request frame is not the address of the link it is transmitted on");
     MgtAssocRequestHeader assoc;
     mpdu->GetPacket()->PeekHeader(assoc);
-    const auto& mle = assoc.GetMultiLinkElement();
+    const auto& mle = assoc.Get<MultiLinkElement>();
 
     if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
     {
@@ -818,7 +937,7 @@
         "TA of Assoc Response frame is not the address of the link it is transmitted on");
     MgtAssocResponseHeader assoc;
     mpdu->GetPacket()->PeekHeader(assoc);
-    const auto& mle = assoc.GetMultiLinkElement();
+    const auto& mle = assoc.Get<MultiLinkElement>();
 
     if (m_apMac->GetNLinks() == 1 || m_staMacs[0]->GetNLinks() == 1)
     {
@@ -1483,7 +1602,7 @@
         client2->SetRemote(socket);
         m_sourceMac->GetDevice()->GetNode()->AddApplication(client2);
         // start during transmission of first A-MPDU, if multiple links are setup
-        client2->SetStartTime(MilliSeconds(3));
+        client2->SetStartTime(MilliSeconds(4));
         client2->SetStopTime(duration);
     }
 
@@ -2577,7 +2696,17 @@
                       {{2, 0}},
                       {})})
     {
-        AddTestCase(new MultiLinkSetupTest(staChannels, apChannels, setupLinks, fixedPhyBands),
+        AddTestCase(new MultiLinkSetupTest(staChannels,
+                                           apChannels,
+                                           WifiScanType::PASSIVE,
+                                           setupLinks,
+                                           fixedPhyBands),
+                    TestCase::QUICK);
+        AddTestCase(new MultiLinkSetupTest(staChannels,
+                                           apChannels,
+                                           WifiScanType::ACTIVE,
+                                           setupLinks,
+                                           fixedPhyBands),
                     TestCase::QUICK);
 
         for (const auto& trafficPattern : {WifiTrafficPattern::STA_TO_STA,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-non-ht-dup-test.cc ns-3.39/src/wifi/test/wifi-non-ht-dup-test.cc
--- ns-3.38/src/wifi/test/wifi-non-ht-dup-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-non-ht-dup-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -425,13 +425,12 @@
     auto apNode = CreateObject<Node>();
     auto apDev = CreateObject<WifiNetDevice>();
     m_phyAp = CreateObject<SpectrumWifiPhy>();
-    m_phyAp->CreateWifiSpectrumPhyInterface(apDev);
     auto apInterferenceHelper = CreateObject<InterferenceHelper>();
     m_phyAp->SetInterferenceHelper(apInterferenceHelper);
     auto apErrorModel = CreateObject<NistErrorRateModel>();
     m_phyAp->SetErrorRateModel(apErrorModel);
     m_phyAp->SetDevice(apDev);
-    m_phyAp->SetChannel(spectrumChannel);
+    m_phyAp->AddChannel(spectrumChannel);
     m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     auto apMobility = CreateObject<ConstantPositionMobilityModel>();
     m_phyAp->SetMobility(apMobility);
@@ -444,13 +443,12 @@
         auto staNode = CreateObject<Node>();
         auto staDev = CreateObject<WifiNetDevice>();
         auto staPhy = CreateObject<SpectrumWifiPhy>();
-        staPhy->CreateWifiSpectrumPhyInterface(staDev);
         auto sta1InterferenceHelper = CreateObject<InterferenceHelper>();
         staPhy->SetInterferenceHelper(sta1InterferenceHelper);
         auto sta1ErrorModel = CreateObject<NistErrorRateModel>();
         staPhy->SetErrorRateModel(sta1ErrorModel);
         staPhy->SetDevice(staDev);
-        staPhy->SetChannel(spectrumChannel);
+        staPhy->AddChannel(spectrumChannel);
         staPhy->ConfigureStandard(std::get<0>(staParams));
         staPhy->SetReceiveOkCallback(
             MakeCallback(&TestNonHtDuplicatePhyReception::RxSuccess, this).Bind(m_phyStas.size()));
@@ -676,6 +674,7 @@
 
     Ptr<MuRtsCtsSpectrumWifiPhy> m_phyAp;                ///< AP PHY
     std::vector<Ptr<MuRtsCtsSpectrumWifiPhy>> m_phyStas; ///< STAs PHYs
+    Ptr<SpectrumWifiPhy> m_nonHePhySta;                  ///< non-HE STA PHY
 
     std::vector<uint16_t> m_bwPerSta; ///< Bandwidth per STA in MHz
 
@@ -799,14 +798,13 @@
     apMac->SetAttribute("BeaconGeneration", BooleanValue(false));
     apDev->SetMac(apMac);
     m_phyAp = CreateObject<MuRtsCtsSpectrumWifiPhy>();
-    m_phyAp->CreateWifiSpectrumPhyInterface(apDev);
     apDev->SetHeConfiguration(CreateObject<HeConfiguration>());
     auto apInterferenceHelper = CreateObject<InterferenceHelper>();
     m_phyAp->SetInterferenceHelper(apInterferenceHelper);
     auto apErrorModel = CreateObject<NistErrorRateModel>();
     m_phyAp->SetErrorRateModel(apErrorModel);
     m_phyAp->SetDevice(apDev);
-    m_phyAp->SetChannel(spectrumChannel);
+    m_phyAp->AddChannel(spectrumChannel);
     m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phyAp->AssignStreams(streamNumber);
 
@@ -816,10 +814,10 @@
         MakeCallback(&TestMultipleCtsResponsesFromMuRts::RxCtsFailure, this));
 
     const auto apBw = *std::max_element(m_bwPerSta.cbegin(), m_bwPerSta.cend());
-    auto channelNum = std::get<0>(
-        *WifiPhyOperatingChannel::FindFirst(0, 0, apBw, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ));
+    auto apChannelNum = std::get<0>(
+        *WifiPhyOperatingChannel::FindFirst(0, 0, apBw, WIFI_STANDARD_80211ac, WIFI_PHY_BAND_5GHZ));
 
-    m_phyAp->SetOperatingChannel(WifiPhy::ChannelTuple{channelNum, apBw, WIFI_PHY_BAND_5GHZ, 0});
+    m_phyAp->SetOperatingChannel(WifiPhy::ChannelTuple{apChannelNum, apBw, WIFI_PHY_BAND_5GHZ, 0});
 
     auto apMobility = CreateObject<ConstantPositionMobilityModel>();
     m_phyAp->SetMobility(apMobility);
@@ -835,23 +833,22 @@
         auto staNode = CreateObject<Node>();
         auto staDev = CreateObject<WifiNetDevice>();
         auto phySta = CreateObject<MuRtsCtsSpectrumWifiPhy>();
-        phySta->CreateWifiSpectrumPhyInterface(staDev);
         auto staInterferenceHelper = CreateObject<InterferenceHelper>();
         phySta->SetInterferenceHelper(staInterferenceHelper);
         auto staErrorModel = CreateObject<NistErrorRateModel>();
         phySta->SetErrorRateModel(staErrorModel);
         phySta->SetDevice(staDev);
-        phySta->SetChannel(spectrumChannel);
+        phySta->AddChannel(spectrumChannel);
         phySta->ConfigureStandard(WIFI_STANDARD_80211ax);
         phySta->AssignStreams(streamNumber);
         phySta->SetTxPowerStart(m_stasTxPowerDbm);
         phySta->SetTxPowerEnd(m_stasTxPowerDbm);
 
-        channelNum = std::get<0>(*WifiPhyOperatingChannel::FindFirst(0,
-                                                                     0,
-                                                                     m_bwPerSta.at(i),
-                                                                     WIFI_STANDARD_80211ax,
-                                                                     WIFI_PHY_BAND_5GHZ));
+        auto channelNum = std::get<0>(*WifiPhyOperatingChannel::FindFirst(0,
+                                                                          0,
+                                                                          m_bwPerSta.at(i),
+                                                                          WIFI_STANDARD_80211ac,
+                                                                          WIFI_PHY_BAND_5GHZ));
 
         phySta->SetOperatingChannel(
             WifiPhy::ChannelTuple{channelNum, m_bwPerSta.at(i), WIFI_PHY_BAND_5GHZ, 0});
@@ -865,6 +862,27 @@
         staNode->AddDevice(staDev);
         m_phyStas.push_back(phySta);
     }
+
+    // non-HE STA
+    auto nonHeStaNode = CreateObject<Node>();
+    auto nonHeStaDev = CreateObject<WifiNetDevice>();
+    m_nonHePhySta = CreateObject<SpectrumWifiPhy>();
+    auto nonHeStaInterferenceHelper = CreateObject<InterferenceHelper>();
+    m_nonHePhySta->SetInterferenceHelper(nonHeStaInterferenceHelper);
+    auto nonHeStaErrorModel = CreateObject<NistErrorRateModel>();
+    m_nonHePhySta->SetErrorRateModel(nonHeStaErrorModel);
+    m_nonHePhySta->SetDevice(nonHeStaDev);
+    m_nonHePhySta->AddChannel(spectrumChannel);
+    m_nonHePhySta->ConfigureStandard(WIFI_STANDARD_80211ac);
+    m_nonHePhySta->SetOperatingChannel(
+        WifiPhy::ChannelTuple{apChannelNum, apBw, WIFI_PHY_BAND_5GHZ, 0});
+    auto nonHeStaMobility = CreateObject<ConstantPositionMobilityModel>();
+    m_nonHePhySta->SetMobility(nonHeStaMobility);
+    nonHeStaDev->SetPhy(m_nonHePhySta);
+    nonHeStaDev->SetStandard(WIFI_STANDARD_80211ac);
+    m_nonHePhySta->AssignStreams(streamNumber);
+    nonHeStaNode->AggregateObject(nonHeStaMobility);
+    nonHeStaNode->AddDevice(nonHeStaDev);
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-phy-cca-test.cc ns-3.39/src/wifi/test/wifi-phy-cca-test.cc
--- ns-3.38/src/wifi/test/wifi-phy-cca-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-phy-cca-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -94,27 +94,31 @@
     Ptr<WifiPsdu> CreateDummyPsdu();
     /**
      * Create a non-HT PPDU
+     * \param channel the operating channel of the PHY used for the transmission
      * \return a non-HT PPDU
      */
-    Ptr<OfdmPpdu> CreateDummyNonHtPpdu();
+    Ptr<OfdmPpdu> CreateDummyNonHtPpdu(const WifiPhyOperatingChannel& channel);
     /**
      * Create a HT PPDU
      * \param bandwidth the bandwidth used for the transmission the PPDU in MHz
+     * \param channel the operating channel of the PHY used for the transmission
      * \return a HT PPDU
      */
-    Ptr<HtPpdu> CreateDummyHtPpdu(uint16_t bandwidth);
+    Ptr<HtPpdu> CreateDummyHtPpdu(uint16_t bandwidth, const WifiPhyOperatingChannel& channel);
     /**
      * Create a VHT PPDU
      * \param bandwidth the bandwidth used for the transmission the PPDU in MHz
+     * \param channel the operating channel of the PHY used for the transmission
      * \return a VHT PPDU
      */
-    Ptr<VhtPpdu> CreateDummyVhtPpdu(uint16_t bandwidth);
+    Ptr<VhtPpdu> CreateDummyVhtPpdu(uint16_t bandwidth, const WifiPhyOperatingChannel& channel);
     /**
      * Create a HE PPDU
      * \param bandwidth the bandwidth used for the transmission the PPDU in MHz
+     * \param channel the operating channel of the PHY used for the transmission
      * \return a HE PPDU
      */
-    Ptr<HePpdu> CreateDummyHePpdu(uint16_t bandwidth);
+    Ptr<HePpdu> CreateDummyHePpdu(uint16_t bandwidth, const WifiPhyOperatingChannel& channel);
 
     /**
      * Function to verify the CCA threshold that is being reported by a given PHY entity upon
@@ -167,54 +171,42 @@
 }
 
 Ptr<OfdmPpdu>
-WifiPhyCcaThresholdsTest::CreateDummyNonHtPpdu()
+WifiPhyCcaThresholdsTest::CreateDummyNonHtPpdu(const WifiPhyOperatingChannel& channel)
 {
     WifiTxVector txVector =
         WifiTxVector(OfdmPhy::GetOfdmRate6Mbps(), 0, WIFI_PREAMBLE_LONG, 800, 1, 1, 0, 20, false);
     Ptr<WifiPsdu> psdu = CreateDummyPsdu();
-    return Create<OfdmPpdu>(psdu, txVector, P20_CENTER_FREQUENCY, WIFI_PHY_BAND_5GHZ, 0);
+    return Create<OfdmPpdu>(psdu, txVector, channel, 0);
 }
 
 Ptr<HtPpdu>
-WifiPhyCcaThresholdsTest::CreateDummyHtPpdu(uint16_t bandwidth)
+WifiPhyCcaThresholdsTest::CreateDummyHtPpdu(uint16_t bandwidth,
+                                            const WifiPhyOperatingChannel& channel)
 {
     WifiTxVector txVector =
         WifiTxVector(HtPhy::GetHtMcs0(), 0, WIFI_PREAMBLE_HT_MF, 800, 1, 1, 0, bandwidth, false);
     Ptr<WifiPsdu> psdu = CreateDummyPsdu();
-    return Create<HtPpdu>(psdu,
-                          txVector,
-                          P20_CENTER_FREQUENCY,
-                          MicroSeconds(100),
-                          WIFI_PHY_BAND_5GHZ,
-                          0);
+    return Create<HtPpdu>(psdu, txVector, channel, MicroSeconds(100), 0);
 }
 
 Ptr<VhtPpdu>
-WifiPhyCcaThresholdsTest::CreateDummyVhtPpdu(uint16_t bandwidth)
+WifiPhyCcaThresholdsTest::CreateDummyVhtPpdu(uint16_t bandwidth,
+                                             const WifiPhyOperatingChannel& channel)
 {
     WifiTxVector txVector =
         WifiTxVector(VhtPhy::GetVhtMcs0(), 0, WIFI_PREAMBLE_VHT_SU, 800, 1, 1, 0, bandwidth, false);
     Ptr<WifiPsdu> psdu = CreateDummyPsdu();
-    return Create<VhtPpdu>(psdu,
-                           txVector,
-                           P20_CENTER_FREQUENCY,
-                           MicroSeconds(100),
-                           WIFI_PHY_BAND_5GHZ,
-                           0);
+    return Create<VhtPpdu>(psdu, txVector, channel, MicroSeconds(100), 0);
 }
 
 Ptr<HePpdu>
-WifiPhyCcaThresholdsTest::CreateDummyHePpdu(uint16_t bandwidth)
+WifiPhyCcaThresholdsTest::CreateDummyHePpdu(uint16_t bandwidth,
+                                            const WifiPhyOperatingChannel& channel)
 {
     WifiTxVector txVector =
         WifiTxVector(HePhy::GetHeMcs0(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, bandwidth, false);
     Ptr<WifiPsdu> psdu = CreateDummyPsdu();
-    return Create<HePpdu>(psdu,
-                          txVector,
-                          P20_CENTER_FREQUENCY,
-                          MicroSeconds(100),
-                          WIFI_PHY_BAND_5GHZ,
-                          0);
+    return Create<HePpdu>(psdu, txVector, channel, MicroSeconds(100), 0);
 }
 
 void
@@ -248,17 +240,15 @@
     m_vhtConfiguration = CreateObject<VhtConfiguration>();
     m_device->SetVhtConfiguration(m_vhtConfiguration);
 
-    m_phy = Create<SpectrumWifiPhy>();
+    m_phy = CreateObject<SpectrumWifiPhy>();
     m_phy->SetDevice(m_device);
     m_device->SetPhy(m_phy);
+    m_phy->SetInterferenceHelper(CreateObject<InterferenceHelper>());
+    m_phy->AddChannel(CreateObject<MultiModelSpectrumChannel>());
 
-    auto channelNum = std::get<0>(*WifiPhyOperatingChannel::FindFirst(0,
-                                                                      P20_CENTER_FREQUENCY,
-                                                                      20,
-                                                                      WIFI_STANDARD_80211ax,
-                                                                      WIFI_PHY_BAND_5GHZ));
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNum, 20, WIFI_PHY_BAND_5GHZ, 0});
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNum, 20, WIFI_PHY_BAND_5GHZ, 0});
+    auto channelNum = std::get<0>(
+        *WifiPhyOperatingChannel::FindFirst(0, 0, 160, WIFI_STANDARD_80211ax, WIFI_PHY_BAND_5GHZ));
+    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNum, 160, WIFI_PHY_BAND_5GHZ, 0});
     m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
 
     m_obssPdAlgorithm = CreateObject<ConstantObssPdAlgorithm>();
@@ -292,7 +282,7 @@
     // OFDM PHY: 20 MHz non-HT PPDU in primary channel (20 MHz) if power above CCA sensitivity
     // threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_OFDM),
-                       CreateDummyNonHtPpdu(),
+                       CreateDummyNonHtPpdu(m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
@@ -315,14 +305,14 @@
     // HT PHY: 20 MHz HT PPDU in primary channel (20 MHz) if power in primary above CCA sensitivity
     // threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HT),
-                       CreateDummyHtPpdu(20),
+                       CreateDummyHtPpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // HT PHY: 40 MHz HT PPDU in primary channel (20 MHz) if power in primary above CCA sensitivity
     // threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HT),
-                       CreateDummyHtPpdu(40),
+                       CreateDummyHtPpdu(40, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
@@ -357,28 +347,28 @@
     // VHT PHY: 20 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA
     // sensitivity threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(20),
+                       CreateDummyVhtPpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // VHT PHY: 40 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA
     // sensitivity threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(40),
+                       CreateDummyVhtPpdu(40, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // VHT PHY: 80 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA
     // sensitivity threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(80),
+                       CreateDummyVhtPpdu(80, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // VHT PHY: 160 MHz VHT PPDU in primary channel (20 MHz) if power in primary above CCA
     // sensitivity threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(160),
+                       CreateDummyVhtPpdu(160, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
@@ -387,42 +377,42 @@
     // VHT PHY: 20 MHz VHT PPDU in secondary channel (20 MHz) if power above the CCA sensitivity
     // threshold corresponding to a 20 MHz PPDU that does not occupy the primary 20 MHz
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(20),
+                       CreateDummyVhtPpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY,
                        std::get<0>(m_secondaryCcaSensitivityThresholds));
 
     // VHT PHY: 20 MHz VHT PPDU in secondary40 channel (40 MHz) if power above the CCA sensitivity
     // threshold corresponding to a 20 MHz PPDU that does not occupy the primary 20 MHz
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(20),
+                       CreateDummyVhtPpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY40,
                        std::get<0>(m_secondaryCcaSensitivityThresholds));
 
     // VHT PHY: 40 MHz VHT PPDU in secondary40 channel (40 MHz) if power above the CCA sensitivity
     // threshold corresponding to a 40 MHz PPDU that does not occupy the primary 20 MHz
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(40),
+                       CreateDummyVhtPpdu(40, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY40,
                        std::get<1>(m_secondaryCcaSensitivityThresholds));
 
     // VHT PHY: 20 MHz VHT PPDU in secondary80 channel (80 MHz) if power above the CCA sensitivity
     // threshold corresponding to a 20 MHz PPDU that does not occupy the primary 20 MHz
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(20),
+                       CreateDummyVhtPpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY80,
                        std::get<0>(m_secondaryCcaSensitivityThresholds));
 
     // VHT PHY: 40 MHz VHT PPDU in secondary80 channel (80 MHz) if power above the CCA sensitivity
     // threshold corresponding to a 40 MHz PPDU that does not occupy the primary 20 MHz
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(40),
+                       CreateDummyVhtPpdu(40, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY80,
                        std::get<1>(m_secondaryCcaSensitivityThresholds));
 
     // VHT PHY: 80 MHz VHT PPDU in secondary80 channel (80 MHz) if power above the CCA sensitivity
     // threshold corresponding to a 80 MHz PPDU that does not occupy the primary 20 MHz
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_VHT),
-                       CreateDummyVhtPpdu(80),
+                       CreateDummyVhtPpdu(80, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY80,
                        std::get<2>(m_secondaryCcaSensitivityThresholds));
 
@@ -457,28 +447,28 @@
     // HE PHY: 20 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivity
     // threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-                       CreateDummyHePpdu(20),
+                       CreateDummyHePpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // HE PHY: 40 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivity
     // threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-                       CreateDummyHePpdu(40),
+                       CreateDummyHePpdu(40, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // HE PHY: 80 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivity
     // threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-                       CreateDummyHePpdu(80),
+                       CreateDummyHePpdu(80, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
     // HE PHY: 160 MHz HE PPDU in primary channel (20 MHz) if power in primary above CCA sensitivity
     // threshold
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-                       CreateDummyHePpdu(160),
+                       CreateDummyHePpdu(160, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_PRIMARY,
                        m_CcaSensitivityDbm);
 
@@ -488,7 +478,7 @@
     // sensitivity threshold corresponding to a 20 MHz PPDU that does not occupy the primary 20 MHz
     // and the OBSS-PD level
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-                       CreateDummyHePpdu(20),
+                       CreateDummyHePpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY,
                        std::max(m_obssPdLevel, std::get<0>(m_secondaryCcaSensitivityThresholds)));
 
@@ -496,7 +486,7 @@
     // sensitivity threshold corresponding to a 20 MHz PPDU that does not occupy the primary 20 MHz
     // and the OBSS-PD level
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-                       CreateDummyHePpdu(20),
+                       CreateDummyHePpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY40,
                        std::max(m_obssPdLevel, std::get<0>(m_secondaryCcaSensitivityThresholds)));
 
@@ -505,7 +495,7 @@
     // and the OBSS-PD level plus 3 dB
     VerifyCcaThreshold(
         m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-        CreateDummyHePpdu(40),
+        CreateDummyHePpdu(40, m_phy->GetOperatingChannel()),
         WIFI_CHANLIST_SECONDARY40,
         std::max(m_obssPdLevel + 3.0, std::get<1>(m_secondaryCcaSensitivityThresholds)));
 
@@ -513,7 +503,7 @@
     // sensitivity threshold corresponding to a 20 MHz PPDU that does not occupy the primary 20 MHz
     // and the OBSS-PD level
     VerifyCcaThreshold(m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-                       CreateDummyHePpdu(20),
+                       CreateDummyHePpdu(20, m_phy->GetOperatingChannel()),
                        WIFI_CHANLIST_SECONDARY80,
                        std::max(m_obssPdLevel, std::get<0>(m_secondaryCcaSensitivityThresholds)));
 
@@ -522,7 +512,7 @@
     // and the OBSS-PD level plus 3 dB
     VerifyCcaThreshold(
         m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-        CreateDummyHePpdu(40),
+        CreateDummyHePpdu(40, m_phy->GetOperatingChannel()),
         WIFI_CHANLIST_SECONDARY80,
         std::max(m_obssPdLevel + 3.0, std::get<1>(m_secondaryCcaSensitivityThresholds)));
 
@@ -531,7 +521,7 @@
     // and the OBSS-PD level plus 6 dB
     VerifyCcaThreshold(
         m_phy->GetPhyEntity(WIFI_MOD_CLASS_HE),
-        CreateDummyHePpdu(80),
+        CreateDummyHePpdu(80, m_phy->GetOperatingChannel()),
         WIFI_CHANLIST_SECONDARY80,
         std::max(m_obssPdLevel + 6.0, std::get<2>(m_secondaryCcaSensitivityThresholds)));
 }
@@ -1027,10 +1017,8 @@
     Ptr<VhtConfiguration> vhtConfiguration = CreateObject<VhtConfiguration>();
     rxDev->SetVhtConfiguration(vhtConfiguration);
     m_rxPhy = CreateObject<SpectrumWifiPhy>();
-    m_rxPhy->CreateWifiSpectrumPhyInterface(rxDev);
     m_rxPhyStateListener = std::make_unique<CcaTestPhyListener>();
     m_rxPhy->RegisterListener(m_rxPhyStateListener.get());
-    m_rxPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> rxInterferenceHelper = CreateObject<InterferenceHelper>();
     m_rxPhy->SetInterferenceHelper(rxInterferenceHelper);
     Ptr<ErrorRateModel> rxErrorModel = CreateObject<NistErrorRateModel>();
@@ -1038,7 +1026,8 @@
     Ptr<ThresholdPreambleDetectionModel> preambleDetectionModel =
         CreateObject<ThresholdPreambleDetectionModel>();
     m_rxPhy->SetPreambleDetectionModel(preambleDetectionModel);
-    m_rxPhy->SetChannel(spectrumChannel);
+    m_rxPhy->AddChannel(spectrumChannel);
+    m_rxPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_rxPhy->SetDevice(rxDev);
     rxDev->SetPhy(m_rxPhy);
     rxNode->AddDevice(rxDev);
@@ -1046,14 +1035,13 @@
     Ptr<Node> txNode = CreateObject<Node>();
     Ptr<WifiNetDevice> txDev = CreateObject<WifiNetDevice>();
     m_txPhy = CreateObject<SpectrumWifiPhy>();
-    m_txPhy->CreateWifiSpectrumPhyInterface(txDev);
-    m_txPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_txPhy->SetAttribute("ChannelSwitchDelay", TimeValue(Seconds(0)));
     Ptr<InterferenceHelper> txInterferenceHelper = CreateObject<InterferenceHelper>();
     m_txPhy->SetInterferenceHelper(txInterferenceHelper);
     Ptr<ErrorRateModel> txErrorModel = CreateObject<NistErrorRateModel>();
     m_txPhy->SetErrorRateModel(txErrorModel);
-    m_txPhy->SetChannel(spectrumChannel);
+    m_txPhy->AddChannel(spectrumChannel);
+    m_txPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_txPhy->SetDevice(txDev);
     txDev->SetPhy(m_txPhy);
     txNode->AddDevice(txDev);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-phy-ofdma-test.cc ns-3.39/src/wifi/test/wifi-phy-ofdma-test.cc
--- ns-3.38/src/wifi/test/wifi-phy-ofdma-test.cc	2023-03-16 20:54:23.872869300 -0700
+++ ns-3.39/src/wifi/test/wifi-phy-ofdma-test.cc	2023-07-05 14:23:48.129428400 -0700
@@ -46,6 +46,7 @@
 #include "ns3/wifi-net-device.h"
 #include "ns3/wifi-phy-listener.h"
 #include "ns3/wifi-psdu.h"
+#include "ns3/wifi-spectrum-phy-interface.h"
 #include "ns3/wifi-spectrum-signal-parameters.h"
 #include "ns3/wifi-spectrum-value-helper.h"
 #include "ns3/wifi-utils.h"
@@ -191,7 +192,7 @@
      *          energy on the medium for a given band will
      *          be higher than the requested threshold.
      */
-    Time GetEnergyDuration(double energyW, WifiSpectrumBand band);
+    Time GetEnergyDuration(double energyW, WifiSpectrumBandInfo band);
 
     /**
      * \return a const pointer to the HE PHY instance
@@ -277,7 +278,7 @@
 }
 
 Time
-OfdmaSpectrumWifiPhy::GetEnergyDuration(double energyW, WifiSpectrumBand band)
+OfdmaSpectrumWifiPhy::GetEnergyDuration(double energyW, WifiSpectrumBandInfo band)
 {
     return m_interference->GetEnergyDuration(energyW, band);
 }
@@ -495,22 +496,22 @@
     if (m_channelWidth == 20)
     {
         ruType = HeRu::RU_106_TONE;
-        txVector.SetRuAllocation({96});
+        txVector.SetRuAllocation({96}, 0);
     }
     else if (m_channelWidth == 40)
     {
         ruType = HeRu::RU_242_TONE;
-        txVector.SetRuAllocation({192, 192});
+        txVector.SetRuAllocation({192, 192}, 0);
     }
     else if (m_channelWidth == 80)
     {
         ruType = HeRu::RU_484_TONE;
-        txVector.SetRuAllocation({200, 200, 200, 200});
+        txVector.SetRuAllocation({200, 200, 200, 200}, 0);
     }
     else if (m_channelWidth == 160)
     {
         ruType = HeRu::RU_996_TONE;
-        txVector.SetRuAllocation({208, 208, 208, 208, 208, 208, 208, 208});
+        txVector.SetRuAllocation({208, 208, 208, 208, 208, 208, 208, 208}, 0);
     }
     else
     {
@@ -709,14 +710,13 @@
     Ptr<Node> apNode = CreateObject<Node>();
     Ptr<WifiNetDevice> apDev = CreateObject<WifiNetDevice>();
     m_phyAp = CreateObject<SpectrumWifiPhy>();
-    m_phyAp->CreateWifiSpectrumPhyInterface(apDev);
-    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> apInterferenceHelper = CreateObject<InterferenceHelper>();
     m_phyAp->SetInterferenceHelper(apInterferenceHelper);
     Ptr<ErrorRateModel> apErrorModel = CreateObject<NistErrorRateModel>();
     m_phyAp->SetErrorRateModel(apErrorModel);
     m_phyAp->SetDevice(apDev);
-    m_phyAp->SetChannel(spectrumChannel);
+    m_phyAp->AddChannel(spectrumChannel);
+    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<ConstantPositionMobilityModel> apMobility = CreateObject<ConstantPositionMobilityModel>();
     m_phyAp->SetMobility(apMobility);
     apDev->SetPhy(m_phyAp);
@@ -726,14 +726,13 @@
     Ptr<Node> sta1Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta1Dev = CreateObject<WifiNetDevice>();
     m_phySta1 = CreateObject<OfdmaSpectrumWifiPhy>(1);
-    m_phySta1->CreateWifiSpectrumPhyInterface(sta1Dev);
-    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta1InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta1->SetInterferenceHelper(sta1InterferenceHelper);
     Ptr<ErrorRateModel> sta1ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta1->SetErrorRateModel(sta1ErrorModel);
     m_phySta1->SetDevice(sta1Dev);
-    m_phySta1->SetChannel(spectrumChannel);
+    m_phySta1->AddChannel(spectrumChannel);
+    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta1->SetReceiveOkCallback(MakeCallback(&TestDlOfdmaPhyTransmission::RxSuccessSta1, this));
     m_phySta1->SetReceiveErrorCallback(
         MakeCallback(&TestDlOfdmaPhyTransmission::RxFailureSta1, this));
@@ -746,14 +745,13 @@
     Ptr<Node> sta2Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta2Dev = CreateObject<WifiNetDevice>();
     m_phySta2 = CreateObject<OfdmaSpectrumWifiPhy>(2);
-    m_phySta2->CreateWifiSpectrumPhyInterface(sta2Dev);
-    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta2InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta2->SetInterferenceHelper(sta2InterferenceHelper);
     Ptr<ErrorRateModel> sta2ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta2->SetErrorRateModel(sta2ErrorModel);
     m_phySta2->SetDevice(sta2Dev);
-    m_phySta2->SetChannel(spectrumChannel);
+    m_phySta2->AddChannel(spectrumChannel);
+    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta2->SetReceiveOkCallback(MakeCallback(&TestDlOfdmaPhyTransmission::RxSuccessSta2, this));
     m_phySta2->SetReceiveErrorCallback(
         MakeCallback(&TestDlOfdmaPhyTransmission::RxFailureSta2, this));
@@ -766,14 +764,13 @@
     Ptr<Node> sta3Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta3Dev = CreateObject<WifiNetDevice>();
     m_phySta3 = CreateObject<OfdmaSpectrumWifiPhy>(3);
-    m_phySta3->CreateWifiSpectrumPhyInterface(sta3Dev);
-    m_phySta3->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta3InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta3->SetInterferenceHelper(sta3InterferenceHelper);
     Ptr<ErrorRateModel> sta3ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta3->SetErrorRateModel(sta3ErrorModel);
     m_phySta3->SetDevice(sta3Dev);
-    m_phySta3->SetChannel(spectrumChannel);
+    m_phySta3->AddChannel(spectrumChannel);
+    m_phySta3->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta3->SetReceiveOkCallback(MakeCallback(&TestDlOfdmaPhyTransmission::RxSuccessSta3, this));
     m_phySta3->SetReceiveErrorCallback(
         MakeCallback(&TestDlOfdmaPhyTransmission::RxFailureSta3, this));
@@ -1404,7 +1401,7 @@
                                                      : (puncturedSubchannels.at(3) ? 113 : 200));
     }
 
-    txVector.SetRuAllocation(ruAlloc);
+    txVector.SetRuAllocation(ruAlloc, 0);
     txVector.SetSigBMode(VhtPhy::GetVhtMcs5());
 
     Ptr<Packet> pkt1 = Create<Packet>(1000);
@@ -1555,14 +1552,13 @@
     Ptr<Node> apNode = CreateObject<Node>();
     Ptr<WifiNetDevice> apDev = CreateObject<WifiNetDevice>();
     m_phyAp = CreateObject<SpectrumWifiPhy>();
-    m_phyAp->CreateWifiSpectrumPhyInterface(apDev);
-    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> apInterferenceHelper = CreateObject<InterferenceHelper>();
     m_phyAp->SetInterferenceHelper(apInterferenceHelper);
     Ptr<ErrorRateModel> apErrorModel = CreateObject<NistErrorRateModel>();
     m_phyAp->SetErrorRateModel(apErrorModel);
     m_phyAp->SetDevice(apDev);
-    m_phyAp->SetChannel(spectrumChannel);
+    m_phyAp->AddChannel(spectrumChannel);
+    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<ConstantPositionMobilityModel> apMobility = CreateObject<ConstantPositionMobilityModel>();
     m_phyAp->SetMobility(apMobility);
     apDev->SetPhy(m_phyAp);
@@ -1572,14 +1568,13 @@
     Ptr<Node> sta1Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta1Dev = CreateObject<WifiNetDevice>();
     m_phySta1 = CreateObject<OfdmaSpectrumWifiPhy>(1);
-    m_phySta1->CreateWifiSpectrumPhyInterface(sta1Dev);
-    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta1InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta1->SetInterferenceHelper(sta1InterferenceHelper);
     Ptr<ErrorRateModel> sta1ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta1->SetErrorRateModel(sta1ErrorModel);
     m_phySta1->SetDevice(sta1Dev);
-    m_phySta1->SetChannel(spectrumChannel);
+    m_phySta1->AddChannel(spectrumChannel);
+    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta1->SetReceiveOkCallback(MakeCallback(&TestDlOfdmaPhyPuncturing::RxSuccessSta1, this));
     m_phySta1->SetReceiveErrorCallback(
         MakeCallback(&TestDlOfdmaPhyPuncturing::RxFailureSta1, this));
@@ -1592,14 +1587,13 @@
     Ptr<Node> sta2Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta2Dev = CreateObject<WifiNetDevice>();
     m_phySta2 = CreateObject<OfdmaSpectrumWifiPhy>(2);
-    m_phySta2->CreateWifiSpectrumPhyInterface(sta2Dev);
-    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta2InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta2->SetInterferenceHelper(sta2InterferenceHelper);
     Ptr<ErrorRateModel> sta2ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta2->SetErrorRateModel(sta2ErrorModel);
     m_phySta2->SetDevice(sta2Dev);
-    m_phySta2->SetChannel(spectrumChannel);
+    m_phySta2->AddChannel(spectrumChannel);
+    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta2->SetReceiveOkCallback(MakeCallback(&TestDlOfdmaPhyPuncturing::RxSuccessSta2, this));
     m_phySta2->SetReceiveErrorCallback(
         MakeCallback(&TestDlOfdmaPhyPuncturing::RxFailureSta2, this));
@@ -1916,12 +1910,12 @@
     Ptr<Node> apNode = CreateObject<Node>();
     Ptr<WifiNetDevice> apDev = CreateObject<WifiNetDevice>();
     m_phyAp = CreateObject<OfdmaSpectrumWifiPhy>(0);
-    m_phyAp->CreateWifiSpectrumPhyInterface(apDev);
-    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> apInterferenceHelper = CreateObject<InterferenceHelper>();
     m_phyAp->SetInterferenceHelper(apInterferenceHelper);
     Ptr<ErrorRateModel> apErrorModel = CreateObject<NistErrorRateModel>();
     m_phyAp->SetErrorRateModel(apErrorModel);
+    m_phyAp->AddChannel(spectrumChannel);
+    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     auto channelNum = std::get<0>(*WifiPhyOperatingChannel::FindFirst(0,
                                                                       DEFAULT_FREQUENCY,
                                                                       DEFAULT_CHANNEL_WIDTH,
@@ -1930,7 +1924,6 @@
     m_phyAp->SetOperatingChannel(
         WifiPhy::ChannelTuple{channelNum, DEFAULT_CHANNEL_WIDTH, (int)(WIFI_PHY_BAND_5GHZ), 0});
     m_phyAp->SetDevice(apDev);
-    m_phyAp->SetChannel(spectrumChannel);
     m_phyAp->TraceConnectWithoutContext("TxPpduUid",
                                         MakeCallback(&TestUlOfdmaPpduUid::TxPpduAp, this));
     Ptr<ConstantPositionMobilityModel> apMobility = CreateObject<ConstantPositionMobilityModel>();
@@ -1944,16 +1937,15 @@
     Ptr<Node> sta1Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta1Dev = CreateObject<WifiNetDevice>();
     m_phySta1 = CreateObject<OfdmaSpectrumWifiPhy>(1);
-    m_phySta1->CreateWifiSpectrumPhyInterface(sta1Dev);
-    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta1InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta1->SetInterferenceHelper(sta1InterferenceHelper);
     Ptr<ErrorRateModel> sta1ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta1->SetErrorRateModel(sta1ErrorModel);
+    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});
     m_phySta1->SetDevice(sta1Dev);
-    m_phySta1->SetChannel(spectrumChannel);
     m_phySta1->TraceConnectWithoutContext("TxPpduUid",
                                           MakeCallback(&TestUlOfdmaPpduUid::TxPpduSta1, this));
     Ptr<ConstantPositionMobilityModel> sta1Mobility = CreateObject<ConstantPositionMobilityModel>();
@@ -1965,16 +1957,15 @@
     Ptr<Node> sta2Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta2Dev = CreateObject<WifiNetDevice>();
     m_phySta2 = CreateObject<OfdmaSpectrumWifiPhy>(2);
-    m_phySta2->CreateWifiSpectrumPhyInterface(sta2Dev);
-    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta2InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta2->SetInterferenceHelper(sta2InterferenceHelper);
     Ptr<ErrorRateModel> sta2ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta2->SetErrorRateModel(sta2ErrorModel);
+    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});
     m_phySta2->SetDevice(sta2Dev);
-    m_phySta2->SetChannel(spectrumChannel);
     m_phySta2->TraceConnectWithoutContext("TxPpduUid",
                                           MakeCallback(&TestUlOfdmaPpduUid::TxPpduSta2, this));
     Ptr<ConstantPositionMobilityModel> sta2Mobility = CreateObject<ConstantPositionMobilityModel>();
@@ -2078,7 +2069,7 @@
     txVector.SetMode(HePhy::GetHeMcs9(), rxStaId2);
     txVector.SetNss(1, rxStaId2);
     txVector.SetSigBMode(VhtPhy::GetVhtMcs5());
-    txVector.SetRuAllocation({96});
+    txVector.SetRuAllocation({96}, 0);
 
     Ptr<Packet> pkt1 = Create<Packet>(1000);
     WifiMacHeader hdr1;
@@ -2433,9 +2424,8 @@
         m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand(), staId);
     Ptr<HePpdu> ppdu = Create<HePpdu>(psdus,
                                       txVector,
-                                      DEFAULT_FREQUENCY,
+                                      m_phy->GetOperatingChannel(),
                                       ppduDuration,
-                                      WIFI_PHY_BAND_5GHZ,
                                       uid,
                                       HePpdu::PSD_NON_HE_PORTION);
 
@@ -2465,18 +2455,18 @@
     auto hePhy = DynamicCast<HePhy>(m_phy->GetLatestPhyEntity());
     hePhy->SetTrigVector(m_trigVector, ppduDuration);
     ppdu->ResetTxVector();
-    m_phy->StartRx(rxParams);
+    m_phy->StartRx(rxParams, nullptr);
 
     // Schedule OFDMA part
     Ptr<HePpdu> ppduOfdma = DynamicCast<HePpdu>(ppdu->Copy()); // since flag will be modified
     ppduOfdma->SetTxPsdFlag(HePpdu::PSD_HE_PORTION);
-    WifiSpectrumBand band = m_phy->GetHePhy()->GetRuBandForRx(txVector, staId);
+    const auto band = m_phy->GetHePhy()->GetRuBandForRx(txVector, staId);
     Ptr<SpectrumValue> rxPsdOfdma =
         WifiSpectrumValueHelper::CreateHeMuOfdmTxPowerSpectralDensity(DEFAULT_FREQUENCY,
                                                                       DEFAULT_CHANNEL_WIDTH,
                                                                       txPowerWatts,
                                                                       DEFAULT_GUARD_WIDTH,
-                                                                      band);
+                                                                      band.indices);
     Ptr<WifiSpectrumSignalParameters> rxParamsOfdma = Create<WifiSpectrumSignalParameters>();
     rxParamsOfdma->psd = rxPsd;
     rxParamsOfdma->txPhy = nullptr;
@@ -2501,16 +2491,17 @@
     // This is needed to make sure the OFDMA part is started as the last event since HE-SIG-A should
     // end at the exact same time as the start For normal WifiNetDevices, this the reception of the
     // OFDMA part is scheduled after end of HE-SIG-A decoding.
-    m_phy->StartRx(rxParamsOfdma);
+    m_phy->StartRx(rxParamsOfdma, nullptr);
 }
 
 void
 TestMultipleHeTbPreambles::DoSetup()
 {
+    Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    Ptr<Node> node = CreateObject<Node>();
     Ptr<WifiNetDevice> dev = CreateObject<WifiNetDevice>();
     dev->SetStandard(WIFI_STANDARD_80211ax);
     m_phy = CreateObject<OfdmaSpectrumWifiPhy>(0);
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
     Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
     Ptr<ApWifiMac> mac = CreateObject<ApWifiMac>();
@@ -2518,9 +2509,11 @@
     dev->SetMac(mac);
     m_phy->SetInterferenceHelper(interferenceHelper);
     m_phy->SetErrorRateModel(error);
+    m_phy->AddChannel(spectrumChannel);
+    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{DEFAULT_CHANNEL_NUMBER,
                                                      DEFAULT_CHANNEL_WIDTH,
-                                                     (int)(WIFI_PHY_BAND_5GHZ),
+                                                     WIFI_PHY_BAND_5GHZ,
                                                      0});
     m_phy->TraceConnectWithoutContext("PhyRxDrop",
                                       MakeCallback(&TestMultipleHeTbPreambles::RxDropped, this));
@@ -2533,6 +2526,8 @@
     Ptr<HeConfiguration> heConfiguration = CreateObject<HeConfiguration>();
     heConfiguration->SetMaxTbPpduDelay(NanoSeconds(400));
     dev->SetHeConfiguration(heConfiguration);
+    dev->SetPhy(m_phy);
+    node->AddDevice(dev);
 }
 
 void
@@ -3057,20 +3052,20 @@
     /**
      * Check the received power for the non-OFDMA of the HE TB PPDUs over the given band
      * \param phy the PHY
-     * \param band the WifiSpectrumBand over which the power is measured
+     * \param band the indices of the band over which the power is measured
      * \param expectedRxPower the expected received power in W
      */
     void CheckNonOfdmaRxPower(Ptr<OfdmaSpectrumWifiPhy> phy,
-                              WifiSpectrumBand band,
+                              WifiSpectrumBandInfo band,
                               double expectedRxPower);
     /**
      * Check the received power for the OFDMA part of the HE TB PPDUs over the given band
      * \param phy the PHY
-     * \param band the WifiSpectrumBand over which the power is measured
+     * \param band the indices of the band over which the power is measured
      * \param expectedRxPower the expected received power in W
      */
     void CheckOfdmaRxPower(Ptr<OfdmaSpectrumWifiPhy> phy,
-                           WifiSpectrumBand band,
+                           WifiSpectrumBandInfo band,
                            double expectedRxPower);
 
     /**
@@ -3379,7 +3374,7 @@
     txVector.SetMode(HePhy::GetHeMcs7(), aid1);
     txVector.SetNss(1, aid1);
 
-    HeRu::RuSpec ru2(ruType, (channelWidth == 160 ? 1 : 2), (channelWidth == 160 ? false : true));
+    HeRu::RuSpec ru2(ruType, (channelWidth == 160 ? 1 : 2), (channelWidth != 160));
     txVector.SetRu(ru2, aid2);
     txVector.SetMode(HePhy::GetHeMcs7(), aid2);
     txVector.SetNss(1, aid2);
@@ -3539,25 +3534,25 @@
 
 void
 TestUlOfdmaPhyTransmission::CheckNonOfdmaRxPower(Ptr<OfdmaSpectrumWifiPhy> phy,
-                                                 WifiSpectrumBand band,
+                                                 WifiSpectrumBandInfo band,
                                                  double expectedRxPower)
 {
     Ptr<Event> event = phy->GetCurrentEvent();
     NS_ASSERT(event);
-    double rxPower = event->GetRxPowerW(band);
-    NS_LOG_FUNCTION(this << band.first << band.second << expectedRxPower << rxPower);
+    auto rxPower = event->GetRxPowerW(band);
+    NS_LOG_FUNCTION(this << band << expectedRxPower << rxPower);
     // Since there is out of band emission due to spectrum mask, the tolerance cannot be very low
     NS_TEST_ASSERT_MSG_EQ_TOL(rxPower,
                               expectedRxPower,
                               5e-3,
-                              "RX power " << rxPower << " over (" << band.first << ", "
-                                          << band.second << ") does not match expected power "
-                                          << expectedRxPower << " at " << Simulator::Now());
+                              "RX power " << rxPower << " over (" << band
+                                          << ") does not match expected power " << expectedRxPower
+                                          << " at " << Simulator::Now());
 }
 
 void
 TestUlOfdmaPhyTransmission::CheckOfdmaRxPower(Ptr<OfdmaSpectrumWifiPhy> phy,
-                                              WifiSpectrumBand band,
+                                              WifiSpectrumBandInfo band,
                                               double expectedRxPower)
 {
     /**
@@ -3565,28 +3560,28 @@
      * We will have to check if the expected power is indeed the max power returning a positive
      * duration when calling GetEnergyDuration.
      */
-    NS_LOG_FUNCTION(this << band.first << band.second << expectedRxPower);
+    NS_LOG_FUNCTION(this << band << expectedRxPower);
     double step = 5e-3;
     if (expectedRxPower > 0.0)
     {
         NS_TEST_ASSERT_MSG_EQ(
             phy->GetEnergyDuration(expectedRxPower - step, band).IsStrictlyPositive(),
             true,
-            "At least " << expectedRxPower << " W expected for OFDMA part over (" << band.first
-                        << ", " << band.second << ") at " << Simulator::Now());
+            "At least " << expectedRxPower << " W expected for OFDMA part over (" << band << ") at "
+                        << Simulator::Now());
         NS_TEST_ASSERT_MSG_EQ(
             phy->GetEnergyDuration(expectedRxPower + step, band).IsStrictlyPositive(),
             false,
-            "At most " << expectedRxPower << " W expected for OFDMA part over (" << band.first
-                       << ", " << band.second << ") at " << Simulator::Now());
+            "At most " << expectedRxPower << " W expected for OFDMA part over (" << band << ") at "
+                       << Simulator::Now());
     }
     else
     {
         NS_TEST_ASSERT_MSG_EQ(
             phy->GetEnergyDuration(expectedRxPower + step, band).IsStrictlyPositive(),
             false,
-            "At most " << expectedRxPower << " W expected for OFDMA part over (" << band.first
-                       << ", " << band.second << ") at " << Simulator::Now());
+            "At most " << expectedRxPower << " W expected for OFDMA part over (" << band << ") at "
+                       << Simulator::Now());
     }
 }
 
@@ -3728,8 +3723,6 @@
     apMac->SetAttribute("BeaconGeneration", BooleanValue(false));
     apDev->SetMac(apMac);
     m_phyAp = CreateObject<OfdmaSpectrumWifiPhy>(0);
-    m_phyAp->CreateWifiSpectrumPhyInterface(apDev);
-    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<HeConfiguration> heConfiguration = CreateObject<HeConfiguration>();
     apDev->SetHeConfiguration(heConfiguration);
     Ptr<InterferenceHelper> apInterferenceHelper = CreateObject<InterferenceHelper>();
@@ -3737,7 +3730,8 @@
     Ptr<ErrorRateModel> apErrorModel = CreateObject<NistErrorRateModel>();
     m_phyAp->SetErrorRateModel(apErrorModel);
     m_phyAp->SetDevice(apDev);
-    m_phyAp->SetChannel(spectrumChannel);
+    m_phyAp->AddChannel(spectrumChannel);
+    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phyAp->SetReceiveOkCallback(MakeCallback(&TestUlOfdmaPhyTransmission::RxSuccess, this));
     m_phyAp->SetReceiveErrorCallback(MakeCallback(&TestUlOfdmaPhyTransmission::RxFailure, this));
     m_phyAp->SetPreambleDetectionModel(preambleDetectionModel);
@@ -3755,14 +3749,13 @@
     sta1Dev->SetStandard(WIFI_STANDARD_80211ax);
     sta1Dev->SetHeConfiguration(CreateObject<HeConfiguration>());
     m_phySta1 = CreateObject<OfdmaSpectrumWifiPhy>(1);
-    m_phySta1->CreateWifiSpectrumPhyInterface(sta1Dev);
-    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta1InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta1->SetInterferenceHelper(sta1InterferenceHelper);
     Ptr<ErrorRateModel> sta1ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta1->SetErrorRateModel(sta1ErrorModel);
     m_phySta1->SetDevice(sta1Dev);
-    m_phySta1->SetChannel(spectrumChannel);
+    m_phySta1->AddChannel(spectrumChannel);
+    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta1->SetPreambleDetectionModel(preambleDetectionModel);
     Ptr<ConstantPositionMobilityModel> sta1Mobility = CreateObject<ConstantPositionMobilityModel>();
     m_phySta1->SetMobility(sta1Mobility);
@@ -3775,14 +3768,13 @@
     sta2Dev->SetStandard(WIFI_STANDARD_80211ax);
     sta2Dev->SetHeConfiguration(CreateObject<HeConfiguration>());
     m_phySta2 = CreateObject<OfdmaSpectrumWifiPhy>(2);
-    m_phySta2->CreateWifiSpectrumPhyInterface(sta2Dev);
-    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta2InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta2->SetInterferenceHelper(sta2InterferenceHelper);
     Ptr<ErrorRateModel> sta2ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta2->SetErrorRateModel(sta2ErrorModel);
     m_phySta2->SetDevice(sta2Dev);
-    m_phySta2->SetChannel(spectrumChannel);
+    m_phySta2->AddChannel(spectrumChannel);
+    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta2->SetPreambleDetectionModel(preambleDetectionModel);
     Ptr<ConstantPositionMobilityModel> sta2Mobility = CreateObject<ConstantPositionMobilityModel>();
     m_phySta2->SetMobility(sta2Mobility);
@@ -3795,14 +3787,13 @@
     sta3Dev->SetStandard(WIFI_STANDARD_80211ax);
     sta3Dev->SetHeConfiguration(CreateObject<HeConfiguration>());
     m_phySta3 = CreateObject<OfdmaSpectrumWifiPhy>(3);
-    m_phySta3->CreateWifiSpectrumPhyInterface(sta3Dev);
-    m_phySta3->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta3InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta3->SetInterferenceHelper(sta3InterferenceHelper);
     Ptr<ErrorRateModel> sta3ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta3->SetErrorRateModel(sta3ErrorModel);
     m_phySta3->SetDevice(sta3Dev);
-    m_phySta3->SetChannel(spectrumChannel);
+    m_phySta3->AddChannel(spectrumChannel);
+    m_phySta3->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phySta3->SetPreambleDetectionModel(preambleDetectionModel);
     Ptr<ConstantPositionMobilityModel> sta3Mobility = CreateObject<ConstantPositionMobilityModel>();
     m_phySta3->SetMobility(sta3Mobility);
@@ -3995,11 +3986,11 @@
     NS_ASSERT(nonOfdmaDuration == hePhy->CalculateNonOfdmaDurationForHeTb(txVectorSta1));
 
     std::vector<double> rxPowerNonOfdma{rxPowerNonOfdmaRu1, rxPowerNonOfdmaRu2};
-    std::vector<WifiSpectrumBand> nonOfdmaBand{hePhy->GetNonOfdmaBand(txVectorSta1, 1),
-                                               hePhy->GetNonOfdmaBand(txVectorSta2, 2)};
+    std::vector<WifiSpectrumBandInfo> nonOfdmaBand{hePhy->GetNonOfdmaBand(txVectorSta1, 1),
+                                                   hePhy->GetNonOfdmaBand(txVectorSta2, 2)};
     std::vector<double> rxPowerOfdma{rxPowerOfdmaRu1, rxPowerOfdmaRu2};
-    std::vector<WifiSpectrumBand> ofdmaBand{hePhy->GetRuBandForRx(txVectorSta1, 1),
-                                            hePhy->GetRuBandForRx(txVectorSta2, 2)};
+    std::vector<WifiSpectrumBandInfo> ofdmaBand{hePhy->GetRuBandForRx(txVectorSta1, 1),
+                                                hePhy->GetRuBandForRx(txVectorSta2, 2)};
 
     for (uint8_t i = 0; i < 2; ++i)
     {
@@ -5090,8 +5081,6 @@
     apMac->SetAttribute("BeaconGeneration", BooleanValue(false));
     apDev->SetMac(apMac);
     m_phyAp = CreateObject<OfdmaSpectrumWifiPhy>(0);
-    m_phyAp->CreateWifiSpectrumPhyInterface(apDev);
-    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<HeConfiguration> heConfiguration = CreateObject<HeConfiguration>();
     apDev->SetHeConfiguration(heConfiguration);
     Ptr<InterferenceHelper> apInterferenceHelper = CreateObject<InterferenceHelper>();
@@ -5099,7 +5088,8 @@
     Ptr<ErrorRateModel> apErrorModel = CreateObject<NistErrorRateModel>();
     m_phyAp->SetErrorRateModel(apErrorModel);
     m_phyAp->SetDevice(apDev);
-    m_phyAp->SetChannel(spectrumChannel);
+    m_phyAp->AddChannel(spectrumChannel);
+    m_phyAp->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phyAp->AssignStreams(streamNumber);
     auto channelNum = std::get<0>(*WifiPhyOperatingChannel::FindFirst(0,
                                                                       DEFAULT_FREQUENCY,
@@ -5123,14 +5113,13 @@
     Ptr<Node> sta1Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta1Dev = CreateObject<WifiNetDevice>();
     m_phySta1 = CreateObject<OfdmaSpectrumWifiPhy>(1);
-    m_phySta1->CreateWifiSpectrumPhyInterface(sta1Dev);
-    m_phySta1->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta1InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta1->SetInterferenceHelper(sta1InterferenceHelper);
     Ptr<ErrorRateModel> sta1ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta1->SetErrorRateModel(sta1ErrorModel);
     m_phySta1->SetDevice(sta1Dev);
-    m_phySta1->SetChannel(spectrumChannel);
+    m_phySta1->AddChannel(spectrumChannel);
+    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});
@@ -5145,14 +5134,13 @@
     Ptr<Node> sta2Node = CreateObject<Node>();
     Ptr<WifiNetDevice> sta2Dev = CreateObject<WifiNetDevice>();
     m_phySta2 = CreateObject<OfdmaSpectrumWifiPhy>(2);
-    m_phySta2->CreateWifiSpectrumPhyInterface(sta2Dev);
-    m_phySta2->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> sta2InterferenceHelper = CreateObject<InterferenceHelper>();
     m_phySta2->SetInterferenceHelper(sta2InterferenceHelper);
     Ptr<ErrorRateModel> sta2ErrorModel = CreateObject<NistErrorRateModel>();
     m_phySta2->SetErrorRateModel(sta2ErrorModel);
     m_phySta2->SetDevice(sta2Dev);
-    m_phySta2->SetChannel(spectrumChannel);
+    m_phySta2->AddChannel(spectrumChannel);
+    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});
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-phy-reception-test.cc ns-3.39/src/wifi/test/wifi-phy-reception-test.cc
--- ns-3.38/src/wifi/test/wifi-phy-reception-test.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wifi/test/wifi-phy-reception-test.cc	2023-07-05 14:23:48.133428300 -0700
@@ -37,14 +37,17 @@
 #include "ns3/pointer.h"
 #include "ns3/rng-seed-manager.h"
 #include "ns3/simple-frame-capture-model.h"
+#include "ns3/single-model-spectrum-channel.h"
 #include "ns3/spectrum-wifi-helper.h"
 #include "ns3/spectrum-wifi-phy.h"
 #include "ns3/test.h"
 #include "ns3/threshold-preamble-detection-model.h"
+#include "ns3/wifi-bandwidth-filter.h"
 #include "ns3/wifi-mac-header.h"
 #include "ns3/wifi-mpdu.h"
 #include "ns3/wifi-net-device.h"
 #include "ns3/wifi-psdu.h"
+#include "ns3/wifi-spectrum-phy-interface.h"
 #include "ns3/wifi-spectrum-signal-parameters.h"
 #include "ns3/wifi-spectrum-value-helper.h"
 #include "ns3/wifi-utils.h"
@@ -65,44 +68,33 @@
  * \ingroup wifi-test
  * \ingroup tests
  *
- * \brief Preamble detection test w/o frame capture
+ * \brief Wifi Phy Reception Test base class
  */
-class TestThresholdPreambleDetectionWithoutFrameCapture : public TestCase
+class WifiPhyReceptionTest : public TestCase
 {
   public:
-    TestThresholdPreambleDetectionWithoutFrameCapture();
-    ~TestThresholdPreambleDetectionWithoutFrameCapture() override;
+    /**
+     * Constructor
+     *
+     * \param test_name the test name
+     */
+    WifiPhyReceptionTest(std::string test_name);
+    /**
+     * Destructor
+     */
+    ~WifiPhyReceptionTest() override = default;
 
   protected:
     void DoSetup() override;
     void DoTeardown() override;
-    Ptr<SpectrumWifiPhy> m_phy; ///< Phy
+
     /**
      * Send packet function
      * \param rxPowerDbm the transmit power in dBm
+     * \param packetSize the size of the packet in bytes
+     * \param mcs the MCS to transmit the packet
      */
-    void SendPacket(double rxPowerDbm);
-    /**
-     * Spectrum wifi receive success function
-     * \param psdu the PSDU
-     * \param rxSignalInfo the info on the received signal (\see RxSignalInfo)
-     * \param txVector the transmit vector
-     * \param statusPerMpdu reception status per MPDU
-     */
-    void RxSuccess(Ptr<const WifiPsdu> psdu,
-                   RxSignalInfo rxSignalInfo,
-                   WifiTxVector txVector,
-                   std::vector<bool> statusPerMpdu);
-    /**
-     * Spectrum wifi receive failure function
-     * \param psdu the PSDU
-     */
-    void RxFailure(Ptr<const WifiPsdu> psdu);
-    uint32_t m_countRxSuccess; ///< count RX success
-    uint32_t m_countRxFailure; ///< count RX failure
-
-  private:
-    void DoRun() override;
+    void SendPacket(double rxPowerDbm, uint32_t packetSize, uint8_t mcs);
 
     /**
      * Schedule now to check  the PHY state
@@ -114,32 +106,23 @@
      * \param expectedState the expected PHY state
      */
     void DoCheckPhyState(WifiPhyState expectedState);
-    /**
-     * Check the number of received packets
-     * \param expectedSuccessCount the number of successfully received packets
-     * \param expectedFailureCount the number of unsuccessfuly received packets
-     */
-    void CheckRxPacketCount(uint32_t expectedSuccessCount, uint32_t expectedFailureCount);
 
-    uint64_t m_uid; //!< the UID to use for the PPDU
+    Ptr<SpectrumWifiPhy> m_phy; //!< the PHY
+    uint64_t m_uid{0};          //!< the UID to use for the PPDU
 };
 
-TestThresholdPreambleDetectionWithoutFrameCapture::
-    TestThresholdPreambleDetectionWithoutFrameCapture()
-    : TestCase("Threshold preamble detection model test when no frame capture model is applied"),
-      m_countRxSuccess(0),
-      m_countRxFailure(0),
-      m_uid(0)
+WifiPhyReceptionTest::WifiPhyReceptionTest(std::string test_name)
+    : TestCase{test_name}
 {
 }
 
 void
-TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket(double rxPowerDbm)
+WifiPhyReceptionTest::SendPacket(double rxPowerDbm, uint32_t packetSize, uint8_t mcs)
 {
     WifiTxVector txVector =
-        WifiTxVector(HePhy::GetHeMcs7(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, 20, false);
+        WifiTxVector(HePhy::GetHeMcs(mcs), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, 20, false);
 
-    Ptr<Packet> pkt = Create<Packet>(1000);
+    Ptr<Packet> pkt = Create<Packet>(packetSize);
     WifiMacHeader hdr;
 
     hdr.SetType(WIFI_MAC_QOSDATA);
@@ -149,7 +132,7 @@
     Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
 
     Ptr<WifiPpdu> ppdu =
-        Create<HePpdu>(psdu, txVector, FREQUENCY, txDuration, WIFI_PHY_BAND_5GHZ, m_uid++);
+        Create<HePpdu>(psdu, txVector, m_phy->GetOperatingChannel(), txDuration, m_uid++);
 
     Ptr<SpectrumValue> txPowerSpectrum =
         WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(FREQUENCY,
@@ -164,21 +147,19 @@
     txParams->ppdu = ppdu;
     txParams->txWidth = CHANNEL_WIDTH;
 
-    m_phy->StartRx(txParams);
+    m_phy->StartRx(txParams, nullptr);
 }
 
 void
-TestThresholdPreambleDetectionWithoutFrameCapture::CheckPhyState(WifiPhyState expectedState)
+WifiPhyReceptionTest::CheckPhyState(WifiPhyState expectedState)
 {
     // This is needed to make sure PHY state will be checked as the last event if a state change
     // occurred at the exact same time as the check
-    Simulator::ScheduleNow(&TestThresholdPreambleDetectionWithoutFrameCapture::DoCheckPhyState,
-                           this,
-                           expectedState);
+    Simulator::ScheduleNow(&WifiPhyReceptionTest::DoCheckPhyState, this, expectedState);
 }
 
 void
-TestThresholdPreambleDetectionWithoutFrameCapture::DoCheckPhyState(WifiPhyState expectedState)
+WifiPhyReceptionTest::DoCheckPhyState(WifiPhyState expectedState)
 {
     WifiPhyState currentState;
     PointerValue ptr;
@@ -193,6 +174,83 @@
 }
 
 void
+WifiPhyReceptionTest::DoSetup()
+{
+    Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    Ptr<Node> node = CreateObject<Node>();
+    Ptr<WifiNetDevice> dev = CreateObject<WifiNetDevice>();
+    m_phy = CreateObject<SpectrumWifiPhy>();
+    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
+    m_phy->SetInterferenceHelper(interferenceHelper);
+    Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
+    m_phy->SetErrorRateModel(error);
+    m_phy->SetDevice(dev);
+    m_phy->AddChannel(spectrumChannel);
+    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
+    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
+    dev->SetPhy(m_phy);
+    node->AddDevice(dev);
+}
+
+void
+WifiPhyReceptionTest::DoTeardown()
+{
+    m_phy->Dispose();
+    m_phy = nullptr;
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Preamble detection test w/o frame capture
+ */
+class TestThresholdPreambleDetectionWithoutFrameCapture : public WifiPhyReceptionTest
+{
+  public:
+    TestThresholdPreambleDetectionWithoutFrameCapture();
+
+  protected:
+    void DoSetup() override;
+
+    /**
+     * Spectrum wifi receive success function
+     * \param psdu the PSDU
+     * \param rxSignalInfo the info on the received signal (\see RxSignalInfo)
+     * \param txVector the transmit vector
+     * \param statusPerMpdu reception status per MPDU
+     */
+    void RxSuccess(Ptr<const WifiPsdu> psdu,
+                   RxSignalInfo rxSignalInfo,
+                   WifiTxVector txVector,
+                   std::vector<bool> statusPerMpdu);
+    /**
+     * Spectrum wifi receive failure function
+     * \param psdu the PSDU
+     */
+    void RxFailure(Ptr<const WifiPsdu> psdu);
+    uint32_t m_countRxSuccess{0}; ///< count RX success
+    uint32_t m_countRxFailure{0}; ///< count RX failure
+
+  private:
+    void DoRun() override;
+
+    /**
+     * Check the number of received packets
+     * \param expectedSuccessCount the number of successfully received packets
+     * \param expectedFailureCount the number of unsuccessfully received packets
+     */
+    void CheckRxPacketCount(uint32_t expectedSuccessCount, uint32_t expectedFailureCount);
+};
+
+TestThresholdPreambleDetectionWithoutFrameCapture::
+    TestThresholdPreambleDetectionWithoutFrameCapture()
+    : WifiPhyReceptionTest(
+          "Threshold preamble detection model test when no frame capture model is applied")
+{
+}
+
+void
 TestThresholdPreambleDetectionWithoutFrameCapture::CheckRxPacketCount(uint32_t expectedSuccessCount,
                                                                       uint32_t expectedFailureCount)
 {
@@ -221,22 +279,11 @@
     m_countRxFailure++;
 }
 
-TestThresholdPreambleDetectionWithoutFrameCapture::
-    ~TestThresholdPreambleDetectionWithoutFrameCapture()
-{
-    m_phy = nullptr;
-}
-
 void
 TestThresholdPreambleDetectionWithoutFrameCapture::DoSetup()
 {
-    m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
-    m_phy->SetErrorRateModel(error);
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
+    WifiPhyReceptionTest::DoSetup();
+
     m_phy->SetReceiveOkCallback(
         MakeCallback(&TestThresholdPreambleDetectionWithoutFrameCapture::RxSuccess, this));
     m_phy->SetReceiveErrorCallback(
@@ -250,13 +297,6 @@
 }
 
 void
-TestThresholdPreambleDetectionWithoutFrameCapture::DoTeardown()
-{
-    m_phy->Dispose();
-    m_phy = nullptr;
-}
-
-void
 TestThresholdPreambleDetectionWithoutFrameCapture::DoRun()
 {
     RngSeedManager::SetSeed(1);
@@ -274,7 +314,9 @@
     Simulator::Schedule(Seconds(1.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(1.0) + NanoSeconds(3999),
@@ -320,11 +362,15 @@
     Simulator::Schedule(Seconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(2.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, no preamble is successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(2.0) + NanoSeconds(3999),
@@ -362,11 +408,15 @@
     Simulator::Schedule(Seconds(3.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(3.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 4us, no preamble is successfully detected, hence STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(3.0) + NanoSeconds(3999),
@@ -403,11 +453,15 @@
     Simulator::Schedule(Seconds(4.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(4.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 6);
+                        rxPowerDbm - 6,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(4.0) + NanoSeconds(3999),
@@ -463,11 +517,15 @@
     Simulator::Schedule(Seconds(5.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(5.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 3);
+                        rxPowerDbm + 3,
+                        1000,
+                        7);
     // At 6us (hence 4us after the last signal is received), no preamble is successfully detected,
     // hence STA PHY STATE should move from IDLE to CCA_BUSY
     Simulator::Schedule(Seconds(5.0) + NanoSeconds(5999),
@@ -506,7 +564,9 @@
     Simulator::Schedule(Seconds(6.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(6.0) + NanoSeconds(3999),
@@ -552,11 +612,15 @@
     Simulator::Schedule(Seconds(7.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(7.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(7.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::CheckPhyState,
@@ -580,11 +644,15 @@
     Simulator::Schedule(Seconds(8.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(8.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(8.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::CheckPhyState,
@@ -606,11 +674,15 @@
     Simulator::Schedule(Seconds(9.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(9.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 6);
+                        rxPowerDbm - 6,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(9.0) + NanoSeconds(3999),
@@ -656,11 +728,15 @@
     Simulator::Schedule(Seconds(10.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(10.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 3);
+                        rxPowerDbm + 3,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(10.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::CheckPhyState,
@@ -683,7 +759,9 @@
     Simulator::Schedule(Seconds(11.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(11.0) + NanoSeconds(3999),
@@ -724,7 +802,9 @@
     Simulator::Schedule(Seconds(12.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, STA PHY state should be IDLE
     Simulator::Schedule(Seconds(12.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithoutFrameCapture::CheckPhyState,
@@ -741,21 +821,14 @@
  *
  * \brief Preamble detection test w/o frame capture
  */
-class TestThresholdPreambleDetectionWithFrameCapture : public TestCase
+class TestThresholdPreambleDetectionWithFrameCapture : public WifiPhyReceptionTest
 {
   public:
     TestThresholdPreambleDetectionWithFrameCapture();
-    ~TestThresholdPreambleDetectionWithFrameCapture() override;
 
   protected:
     void DoSetup() override;
-    void DoTeardown() override;
-    Ptr<SpectrumWifiPhy> m_phy; ///< Phy
-    /**
-     * Send packet function
-     * \param rxPowerDbm the transmit power in dBm
-     */
-    void SendPacket(double rxPowerDbm);
+
     /**
      * Spectrum wifi receive success function
      * \param psdu the PSDU
@@ -772,100 +845,27 @@
      * \param psdu the PSDU
      */
     void RxFailure(Ptr<const WifiPsdu> psdu);
-    uint32_t m_countRxSuccess; ///< count RX success
-    uint32_t m_countRxFailure; ///< count RX failure
+    uint32_t m_countRxSuccess{0}; ///< count RX success
+    uint32_t m_countRxFailure{0}; ///< count RX failure
 
   private:
     void DoRun() override;
 
     /**
-     * Schedule now to check  the PHY state
-     * \param expectedState the expected PHY state
-     */
-    void CheckPhyState(WifiPhyState expectedState);
-    /**
-     * Check the PHY state now
-     * \param expectedState the expected PHY state
-     */
-    void DoCheckPhyState(WifiPhyState expectedState);
-    /**
      * Check the number of received packets
      * \param expectedSuccessCount the number of successfully received packets
      * \param expectedFailureCount the number of unsuccessfuly received packets
      */
     void CheckRxPacketCount(uint32_t expectedSuccessCount, uint32_t expectedFailureCount);
-
-    uint64_t m_uid; //!< the UID to use for the PPDU
 };
 
 TestThresholdPreambleDetectionWithFrameCapture::TestThresholdPreambleDetectionWithFrameCapture()
-    : TestCase(
-          "Threshold preamble detection model test when simple frame capture model is applied"),
-      m_countRxSuccess(0),
-      m_countRxFailure(0),
-      m_uid(0)
+    : WifiPhyReceptionTest(
+          "Threshold preamble detection model test when simple frame capture model is applied")
 {
 }
 
 void
-TestThresholdPreambleDetectionWithFrameCapture::SendPacket(double rxPowerDbm)
-{
-    WifiTxVector txVector =
-        WifiTxVector(HePhy::GetHeMcs7(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, 20, false);
-
-    Ptr<Packet> pkt = Create<Packet>(1000);
-    WifiMacHeader hdr;
-
-    hdr.SetType(WIFI_MAC_QOSDATA);
-    hdr.SetQosTid(0);
-
-    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
-    Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
-
-    Ptr<WifiPpdu> ppdu =
-        Create<HePpdu>(psdu, txVector, FREQUENCY, txDuration, WIFI_PHY_BAND_5GHZ, m_uid++);
-
-    Ptr<SpectrumValue> txPowerSpectrum =
-        WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(FREQUENCY,
-                                                                    CHANNEL_WIDTH,
-                                                                    DbmToW(rxPowerDbm),
-                                                                    GUARD_WIDTH);
-
-    Ptr<WifiSpectrumSignalParameters> txParams = Create<WifiSpectrumSignalParameters>();
-    txParams->psd = txPowerSpectrum;
-    txParams->txPhy = nullptr;
-    txParams->duration = txDuration;
-    txParams->ppdu = ppdu;
-
-    m_phy->StartRx(txParams);
-}
-
-void
-TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState(WifiPhyState expectedState)
-{
-    // This is needed to make sure PHY state will be checked as the last event if a state change
-    // occurred at the exact same time as the check
-    Simulator::ScheduleNow(&TestThresholdPreambleDetectionWithFrameCapture::DoCheckPhyState,
-                           this,
-                           expectedState);
-}
-
-void
-TestThresholdPreambleDetectionWithFrameCapture::DoCheckPhyState(WifiPhyState expectedState)
-{
-    WifiPhyState currentState;
-    PointerValue ptr;
-    m_phy->GetAttribute("State", ptr);
-    Ptr<WifiPhyStateHelper> state = DynamicCast<WifiPhyStateHelper>(ptr.Get<WifiPhyStateHelper>());
-    currentState = state->GetState();
-    NS_LOG_FUNCTION(this << currentState);
-    NS_TEST_ASSERT_MSG_EQ(currentState,
-                          expectedState,
-                          "PHY State " << currentState << " does not match expected state "
-                                       << expectedState << " at " << Simulator::Now());
-}
-
-void
 TestThresholdPreambleDetectionWithFrameCapture::CheckRxPacketCount(uint32_t expectedSuccessCount,
                                                                    uint32_t expectedFailureCount)
 {
@@ -894,21 +894,11 @@
     m_countRxFailure++;
 }
 
-TestThresholdPreambleDetectionWithFrameCapture::~TestThresholdPreambleDetectionWithFrameCapture()
-{
-    m_phy = nullptr;
-}
-
 void
 TestThresholdPreambleDetectionWithFrameCapture::DoSetup()
 {
-    m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
-    m_phy->SetErrorRateModel(error);
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
+    WifiPhyReceptionTest::DoSetup();
+
     m_phy->SetReceiveOkCallback(
         MakeCallback(&TestThresholdPreambleDetectionWithFrameCapture::RxSuccess, this));
     m_phy->SetReceiveErrorCallback(
@@ -927,13 +917,6 @@
 }
 
 void
-TestThresholdPreambleDetectionWithFrameCapture::DoTeardown()
-{
-    m_phy->Dispose();
-    m_phy = nullptr;
-}
-
-void
 TestThresholdPreambleDetectionWithFrameCapture::DoRun()
 {
     RngSeedManager::SetSeed(1);
@@ -951,7 +934,9 @@
     Simulator::Schedule(Seconds(1.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(1.0) + NanoSeconds(3999),
@@ -997,11 +982,15 @@
     Simulator::Schedule(Seconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(2.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, no preamble is successfully detected, hence STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(2.0) + NanoSeconds(3999),
@@ -1039,11 +1028,15 @@
     Simulator::Schedule(Seconds(3.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(3.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 4us, no preamble is successfully detected, hence STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(3.0) + NanoSeconds(3999),
@@ -1080,11 +1073,15 @@
     Simulator::Schedule(Seconds(4.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(4.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 6);
+                        rxPowerDbm - 6,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(4.0) + NanoSeconds(3999),
@@ -1140,11 +1137,15 @@
     Simulator::Schedule(Seconds(5.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(5.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 3);
+                        rxPowerDbm + 3,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(5.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState,
@@ -1186,11 +1187,15 @@
     Simulator::Schedule(Seconds(6.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(6.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 6);
+                        rxPowerDbm + 6,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(6.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState,
@@ -1242,11 +1247,15 @@
     Simulator::Schedule(Seconds(7.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(7.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, no preamble is successfully detected, hence STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(7.0) + NanoSeconds(3999),
@@ -1284,11 +1293,15 @@
     Simulator::Schedule(Seconds(8.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(8.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 4us, no preamble is successfully detected, hence STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(8.0) + NanoSeconds(3999),
@@ -1324,11 +1337,15 @@
     Simulator::Schedule(Seconds(9.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(9.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 6);
+                        rxPowerDbm - 6,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(9.0) + NanoSeconds(3999),
@@ -1374,11 +1391,15 @@
     Simulator::Schedule(Seconds(10.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(10.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 3);
+                        rxPowerDbm + 3,
+                        1000,
+                        7);
     // At 4us, no preamble is successfully detected, hence STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(10.0) + NanoSeconds(3999),
@@ -1415,11 +1436,15 @@
     Simulator::Schedule(Seconds(11.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(11.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 6);
+                        rxPowerDbm + 6,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(11.0) + NanoSeconds(3999),
@@ -1466,7 +1491,9 @@
     Simulator::Schedule(Seconds(12.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(12.0) + NanoSeconds(3999),
@@ -1512,11 +1539,15 @@
     Simulator::Schedule(Seconds(13.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(13.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(13.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState,
@@ -1540,11 +1571,15 @@
     Simulator::Schedule(Seconds(14.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(14.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(14.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState,
@@ -1566,11 +1601,15 @@
     Simulator::Schedule(Seconds(15.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(15.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm - 6);
+                        rxPowerDbm - 6,
+                        1000,
+                        7);
     // At 4us, preamble should be successfully detected and STA PHY STATE should move from IDLE to
     // CCA_BUSY
     Simulator::Schedule(Seconds(15.0) + NanoSeconds(3999),
@@ -1618,11 +1657,15 @@
     Simulator::Schedule(Seconds(16.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(16.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 3);
+                        rxPowerDbm + 3,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(16.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState,
@@ -1650,11 +1693,15 @@
     Simulator::Schedule(Seconds(17.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(17.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 6);
+                        rxPowerDbm + 6,
+                        1000,
+                        7);
     // At 4us, STA PHY STATE should stay IDLE
     Simulator::Schedule(Seconds(17.0) + MicroSeconds(4.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckPhyState,
@@ -1703,11 +1750,15 @@
     Simulator::Schedule(Seconds(18.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(18.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 50);
+                        rxPowerDbm + 50,
+                        1000,
+                        7);
     // The second packet should be received successfully
     Simulator::Schedule(Seconds(18.1),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckRxPacketCount,
@@ -1720,11 +1771,15 @@
     Simulator::Schedule(Seconds(19.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(19.0) + MicroSeconds(2.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 10);
+                        rxPowerDbm + 10,
+                        1000,
+                        7);
     // The second packet should be captured, but not decoded since SNR to low for used MCS
     Simulator::Schedule(Seconds(19.1),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckRxPacketCount,
@@ -1737,11 +1792,15 @@
     Simulator::Schedule(Seconds(20.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(20.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 50);
+                        rxPowerDbm + 50,
+                        1000,
+                        7);
     // The second packet should be received successfully, same as in CASE 13
     Simulator::Schedule(Seconds(20.1),
                         &TestThresholdPreambleDetectionWithFrameCapture::CheckRxPacketCount,
@@ -1754,11 +1813,15 @@
     Simulator::Schedule(Seconds(21.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(21.0),
                         &TestThresholdPreambleDetectionWithFrameCapture::SendPacket,
                         this,
-                        rxPowerDbm + 10);
+                        rxPowerDbm + 10,
+                        1000,
+                        7);
     // The second packet should be captured, but not decoded since SNR to low for used MCS, same as
     // in CASE 19
     Simulator::Schedule(Seconds(21.1),
@@ -1777,17 +1840,13 @@
  *
  * \brief Simple frame capture model test
  */
-class TestSimpleFrameCaptureModel : public TestCase
+class TestSimpleFrameCaptureModel : public WifiPhyReceptionTest
 {
   public:
     TestSimpleFrameCaptureModel();
-    ~TestSimpleFrameCaptureModel() override;
-
-  protected:
-    void DoSetup() override;
-    void DoTeardown() override;
 
   private:
+    void DoSetup() override;
     void DoRun() override;
 
     /**
@@ -1795,12 +1854,6 @@
      */
     void Reset();
     /**
-     * Send packet function
-     * \param rxPowerDbm the transmit power in dBm
-     * \param packetSize the size of the packet in bytes
-     */
-    void SendPacket(double rxPowerDbm, uint32_t packetSize);
-    /**
      * Spectrum wifi receive success function
      * \param psdu the PSDU
      * \param rxSignalInfo the info on the received signal (\see RxSignalInfo)
@@ -1835,60 +1888,18 @@
      */
     void Expect1500BPacketDropped();
 
-    Ptr<SpectrumWifiPhy> m_phy; ///< Phy
-
-    bool m_rxSuccess1000B; ///< count received packets with 1000B payload
-    bool m_rxSuccess1500B; ///< count received packets with 1500B payload
-    bool m_rxDropped1000B; ///< count dropped packets with 1000B payload
-    bool m_rxDropped1500B; ///< count dropped packets with 1500B payload
-
-    uint64_t m_uid; //!< the UID to use for the PPDU
+    bool m_rxSuccess1000B{false}; ///< count received packets with 1000B payload
+    bool m_rxSuccess1500B{false}; ///< count received packets with 1500B payload
+    bool m_rxDropped1000B{false}; ///< count dropped packets with 1000B payload
+    bool m_rxDropped1500B{false}; ///< count dropped packets with 1500B payload
 };
 
 TestSimpleFrameCaptureModel::TestSimpleFrameCaptureModel()
-    : TestCase("Simple frame capture model test"),
-      m_rxSuccess1000B(false),
-      m_rxSuccess1500B(false),
-      m_rxDropped1000B(false),
-      m_rxDropped1500B(false),
-      m_uid(0)
+    : WifiPhyReceptionTest("Simple frame capture model test")
 {
 }
 
 void
-TestSimpleFrameCaptureModel::SendPacket(double rxPowerDbm, uint32_t packetSize)
-{
-    WifiTxVector txVector =
-        WifiTxVector(HePhy::GetHeMcs0(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, 20, false);
-
-    Ptr<Packet> pkt = Create<Packet>(packetSize);
-    WifiMacHeader hdr;
-
-    hdr.SetType(WIFI_MAC_QOSDATA);
-    hdr.SetQosTid(0);
-
-    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
-    Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
-
-    Ptr<WifiPpdu> ppdu =
-        Create<HePpdu>(psdu, txVector, FREQUENCY, txDuration, WIFI_PHY_BAND_5GHZ, m_uid++);
-
-    Ptr<SpectrumValue> txPowerSpectrum =
-        WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(FREQUENCY,
-                                                                    CHANNEL_WIDTH,
-                                                                    DbmToW(rxPowerDbm),
-                                                                    GUARD_WIDTH);
-
-    Ptr<WifiSpectrumSignalParameters> txParams = Create<WifiSpectrumSignalParameters>();
-    txParams->psd = txPowerSpectrum;
-    txParams->txPhy = nullptr;
-    txParams->duration = txDuration;
-    txParams->ppdu = ppdu;
-
-    m_phy->StartRx(txParams);
-}
-
-void
 TestSimpleFrameCaptureModel::RxSuccess(Ptr<const WifiPsdu> psdu,
                                        RxSignalInfo rxSignalInfo,
                                        WifiTxVector txVector,
@@ -1953,21 +1964,10 @@
     NS_TEST_ASSERT_MSG_EQ(m_rxDropped1500B, true, "Didn't drop 1500B packet");
 }
 
-TestSimpleFrameCaptureModel::~TestSimpleFrameCaptureModel()
-{
-    m_phy = nullptr;
-}
-
 void
 TestSimpleFrameCaptureModel::DoSetup()
 {
-    m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
-    m_phy->SetErrorRateModel(error);
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
+    WifiPhyReceptionTest::DoSetup();
 
     m_phy->SetReceiveOkCallback(MakeCallback(&TestSimpleFrameCaptureModel::RxSuccess, this));
     m_phy->TraceConnectWithoutContext("PhyRxDrop",
@@ -1985,13 +1985,6 @@
 }
 
 void
-TestSimpleFrameCaptureModel::DoTeardown()
-{
-    m_phy->Dispose();
-    m_phy = nullptr;
-}
-
-void
 TestSimpleFrameCaptureModel::DoRun()
 {
     RngSeedManager::SetSeed(1);
@@ -2007,12 +2000,14 @@
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm,
-                        1000);
+                        1000,
+                        0);
     Simulator::Schedule(Seconds(1.0) + MicroSeconds(10.0),
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm,
-                        1500);
+                        1500,
+                        0);
     Simulator::Schedule(Seconds(1.1), &TestSimpleFrameCaptureModel::Expect1500BPacketDropped, this);
     Simulator::Schedule(Seconds(1.2), &TestSimpleFrameCaptureModel::Reset, this);
 
@@ -2023,12 +2018,14 @@
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm,
-                        1000);
+                        1000,
+                        0);
     Simulator::Schedule(Seconds(2.0) + MicroSeconds(10.0),
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm - 6,
-                        1500);
+                        1500,
+                        0);
     Simulator::Schedule(Seconds(2.1),
                         &TestSimpleFrameCaptureModel::Expect1000BPacketReceived,
                         this);
@@ -2042,12 +2039,14 @@
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm,
-                        1000);
+                        1000,
+                        0);
     Simulator::Schedule(Seconds(3.0) + MicroSeconds(10.0),
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm + 6,
-                        1500);
+                        1500,
+                        0);
     Simulator::Schedule(Seconds(3.1), &TestSimpleFrameCaptureModel::Expect1000BPacketDropped, this);
     Simulator::Schedule(Seconds(3.1),
                         &TestSimpleFrameCaptureModel::Expect1500BPacketReceived,
@@ -2062,12 +2061,14 @@
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm,
-                        1000);
+                        1000,
+                        0);
     Simulator::Schedule(Seconds(4.0) + MicroSeconds(25.0),
                         &TestSimpleFrameCaptureModel::SendPacket,
                         this,
                         rxPowerDbm + 6,
-                        1500);
+                        1500,
+                        0);
     Simulator::Schedule(Seconds(4.1), &TestSimpleFrameCaptureModel::Expect1500BPacketDropped, this);
     Simulator::Schedule(Seconds(4.2), &TestSimpleFrameCaptureModel::Reset, this);
 
@@ -2081,123 +2082,18 @@
  *
  * \brief Test PHY state upon success or failure of L-SIG and SIG-A
  */
-class TestPhyHeadersReception : public TestCase
+class TestPhyHeadersReception : public WifiPhyReceptionTest
 {
   public:
     TestPhyHeadersReception();
-    ~TestPhyHeadersReception() override;
-
-  protected:
-    void DoSetup() override;
-    void DoTeardown() override;
-    Ptr<SpectrumWifiPhy> m_phy; ///< Phy
-    /**
-     * Send packet function
-     * \param rxPowerDbm the transmit power in dBm
-     */
-    void SendPacket(double rxPowerDbm);
 
   private:
     void DoRun() override;
-
-    /**
-     * Schedule now to check  the PHY state
-     * \param expectedState the expected PHY state
-     */
-    void CheckPhyState(WifiPhyState expectedState);
-    /**
-     * Check the PHY state now
-     * \param expectedState the expected PHY state
-     */
-    void DoCheckPhyState(WifiPhyState expectedState);
-
-    uint64_t m_uid; //!< the UID to use for the PPDU
 };
 
 TestPhyHeadersReception::TestPhyHeadersReception()
-    : TestCase("PHY headers reception test"),
-      m_uid(0)
-{
-}
-
-void
-TestPhyHeadersReception::SendPacket(double rxPowerDbm)
-{
-    WifiTxVector txVector =
-        WifiTxVector(HePhy::GetHeMcs7(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, 20, false);
-
-    Ptr<Packet> pkt = Create<Packet>(1000);
-    WifiMacHeader hdr;
-
-    hdr.SetType(WIFI_MAC_QOSDATA);
-    hdr.SetQosTid(0);
-
-    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
-    Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
-
-    Ptr<WifiPpdu> ppdu =
-        Create<HePpdu>(psdu, txVector, FREQUENCY, txDuration, WIFI_PHY_BAND_5GHZ, m_uid++);
-
-    Ptr<SpectrumValue> txPowerSpectrum =
-        WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(FREQUENCY,
-                                                                    CHANNEL_WIDTH,
-                                                                    DbmToW(rxPowerDbm),
-                                                                    GUARD_WIDTH);
-
-    Ptr<WifiSpectrumSignalParameters> txParams = Create<WifiSpectrumSignalParameters>();
-    txParams->psd = txPowerSpectrum;
-    txParams->txPhy = nullptr;
-    txParams->duration = txDuration;
-    txParams->ppdu = ppdu;
-
-    m_phy->StartRx(txParams);
-}
-
-void
-TestPhyHeadersReception::CheckPhyState(WifiPhyState expectedState)
+    : WifiPhyReceptionTest("PHY headers reception test")
 {
-    // This is needed to make sure PHY state will be checked as the last event if a state change
-    // occurred at the exact same time as the check
-    Simulator::ScheduleNow(&TestPhyHeadersReception::DoCheckPhyState, this, expectedState);
-}
-
-void
-TestPhyHeadersReception::DoCheckPhyState(WifiPhyState expectedState)
-{
-    WifiPhyState currentState;
-    PointerValue ptr;
-    m_phy->GetAttribute("State", ptr);
-    Ptr<WifiPhyStateHelper> state = DynamicCast<WifiPhyStateHelper>(ptr.Get<WifiPhyStateHelper>());
-    currentState = state->GetState();
-    NS_LOG_FUNCTION(this << currentState);
-    NS_TEST_ASSERT_MSG_EQ(currentState,
-                          expectedState,
-                          "PHY State " << currentState << " does not match expected state "
-                                       << expectedState << " at " << Simulator::Now());
-}
-
-TestPhyHeadersReception::~TestPhyHeadersReception()
-{
-    m_phy = nullptr;
-}
-
-void
-TestPhyHeadersReception::DoSetup()
-{
-    m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
-    m_phy->SetErrorRateModel(error);
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
-}
-
-void
-TestPhyHeadersReception::DoTeardown()
-{
-    m_phy->Dispose();
-    m_phy = nullptr;
 }
 
 void
@@ -2215,11 +2111,18 @@
     // preamble detection window and the start of L-SIG of the first packet: reception should be
     // aborted since L-SIG cannot be decoded (SNR too low).
 
-    Simulator::Schedule(Seconds(1.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(1.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(1.0) + MicroSeconds(10),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 10 us, STA PHY STATE should be CCA_BUSY.
     Simulator::Schedule(Seconds(1.0) + MicroSeconds(10.0),
                         &TestPhyHeadersReception::CheckPhyState,
@@ -2246,11 +2149,18 @@
     // preamble detection window and the start of L-SIG of the first packet: reception should not be
     // aborted since L-SIG can be decoded (SNR high enough).
 
-    Simulator::Schedule(Seconds(2.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(2.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(2.0) + MicroSeconds(10),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 10 us, STA PHY STATE should be CCA_BUSY.
     Simulator::Schedule(Seconds(2.0) + MicroSeconds(10.0),
                         &TestPhyHeadersReception::CheckPhyState,
@@ -2290,11 +2200,18 @@
     // the start of HE-SIG of the first packet: PHY header reception should not succeed but PHY
     // should stay in RX state for the duration estimated from L-SIG.
 
-    Simulator::Schedule(Seconds(3.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(3.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(3.0) + MicroSeconds(25),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 44us (end of PHY header), STA PHY STATE should not have moved to RX (HE-SIG failed) and be
     // kept to CCA_BUSY.
     Simulator::Schedule(Seconds(3.0) + MicroSeconds(44.0),
@@ -2324,11 +2241,18 @@
     // CASE 4: send one packet followed by a second one 3 dB weaker between the end of L-SIG and the
     // start of HE-SIG of the first packet: PHY header reception should succeed.
 
-    Simulator::Schedule(Seconds(4.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(4.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(4.0) + MicroSeconds(25),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 10 us, STA PHY STATE should be CCA_BUSY.
     Simulator::Schedule(Seconds(4.0) + MicroSeconds(10.0),
                         &TestPhyHeadersReception::CheckPhyState,
@@ -2371,11 +2295,18 @@
     // preamble detection window and the start of L-SIG of the first packet: reception should be
     // aborted since L-SIG cannot be decoded (SNR too low).
 
-    Simulator::Schedule(Seconds(5.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(5.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(5.0) + MicroSeconds(10),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 10 us, STA PHY STATE should be CCA_BUSY.
     Simulator::Schedule(Seconds(5.0) + MicroSeconds(10.0),
                         &TestPhyHeadersReception::CheckPhyState,
@@ -2392,11 +2323,18 @@
     // preamble detection window and the start of L-SIG of the first packet: reception should not be
     // aborted since L-SIG can be decoded (SNR high enough).
 
-    Simulator::Schedule(Seconds(6.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(6.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(6.0) + MicroSeconds(10),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 10 us, STA PHY STATE should be CCA_BUSY.
     Simulator::Schedule(Seconds(6.0) + MicroSeconds(10.0),
                         &TestPhyHeadersReception::CheckPhyState,
@@ -2433,11 +2371,18 @@
     // the start of HE-SIG of the first packet: PHY header reception should not succeed but PHY
     // should stay in RX state for the duration estimated from L-SIG.
 
-    Simulator::Schedule(Seconds(7.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(7.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(7.0) + MicroSeconds(25),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm);
+                        rxPowerDbm,
+                        1000,
+                        7);
     // At 10 us, STA PHY STATE should be CCA_BUSY.
     Simulator::Schedule(Seconds(7.0) + MicroSeconds(10.0),
                         &TestPhyHeadersReception::CheckPhyState,
@@ -2465,11 +2410,18 @@
     // CASE 8: send one packet followed by a second one 3 dB weaker between the end of L-SIG and the
     // start of HE-SIG of the first packet: PHY header reception should succeed.
 
-    Simulator::Schedule(Seconds(8.0), &TestPhyHeadersReception::SendPacket, this, rxPowerDbm);
+    Simulator::Schedule(Seconds(8.0),
+                        &TestPhyHeadersReception::SendPacket,
+                        this,
+                        rxPowerDbm,
+                        1000,
+                        7);
     Simulator::Schedule(Seconds(8.0) + MicroSeconds(25),
                         &TestPhyHeadersReception::SendPacket,
                         this,
-                        rxPowerDbm - 3);
+                        rxPowerDbm - 3,
+                        1000,
+                        7);
     // At 10 us, STA PHY STATE should be CCA_BUSY.
     Simulator::Schedule(Seconds(8.0) + MicroSeconds(10.0),
                         &TestPhyHeadersReception::CheckPhyState,
@@ -2512,17 +2464,13 @@
  *
  * \brief A-MPDU reception test
  */
-class TestAmpduReception : public TestCase
+class TestAmpduReception : public WifiPhyReceptionTest
 {
   public:
     TestAmpduReception();
-    ~TestAmpduReception() override;
-
-  protected:
-    void DoSetup() override;
-    void DoTeardown() override;
 
   private:
+    void DoSetup() override;
     void DoRun() override;
 
     /**
@@ -2609,35 +2557,19 @@
      */
     void CheckPhyState(WifiPhyState expectedState);
 
-    Ptr<SpectrumWifiPhy> m_phy; ///< Phy
+    uint8_t m_rxSuccessBitmapAmpdu1{0}; ///< bitmap of successfully received MPDUs in A-MPDU #1
+    uint8_t m_rxSuccessBitmapAmpdu2{0}; ///< bitmap of successfully received MPDUs in A-MPDU #2
 
-    uint8_t m_rxSuccessBitmapAmpdu1; ///< bitmap of successfully received MPDUs in A-MPDU #1
-    uint8_t m_rxSuccessBitmapAmpdu2; ///< bitmap of successfully received MPDUs in A-MPDU #2
+    uint8_t m_rxFailureBitmapAmpdu1{0}; ///< bitmap of unsuccessfully received MPDUs in A-MPDU #1
+    uint8_t m_rxFailureBitmapAmpdu2{0}; ///< bitmap of unsuccessfully received MPDUs in A-MPDU #2
 
-    uint8_t m_rxFailureBitmapAmpdu1; ///< bitmap of unsuccessfuly received MPDUs in A-MPDU #1
-    uint8_t m_rxFailureBitmapAmpdu2; ///< bitmap of unsuccessfuly received MPDUs in A-MPDU #2
-
-    uint8_t m_rxDroppedBitmapAmpdu1; ///< bitmap of dropped MPDUs in A-MPDU #1
-    uint8_t m_rxDroppedBitmapAmpdu2; ///< bitmap of dropped MPDUs in A-MPDU #2
-
-    uint64_t m_uid; ///< UID
+    uint8_t m_rxDroppedBitmapAmpdu1{0}; ///< bitmap of dropped MPDUs in A-MPDU #1
+    uint8_t m_rxDroppedBitmapAmpdu2{0}; ///< bitmap of dropped MPDUs in A-MPDU #2
 };
 
 TestAmpduReception::TestAmpduReception()
-    : TestCase("A-MPDU reception test"),
-      m_rxSuccessBitmapAmpdu1(0),
-      m_rxSuccessBitmapAmpdu2(0),
-      m_rxFailureBitmapAmpdu1(0),
-      m_rxFailureBitmapAmpdu2(0),
-      m_rxDroppedBitmapAmpdu1(0),
-      m_rxDroppedBitmapAmpdu2(0),
-      m_uid(0)
-{
-}
-
-TestAmpduReception::~TestAmpduReception()
+    : WifiPhyReceptionTest("A-MPDU reception test")
 {
-    m_phy = nullptr;
 }
 
 void
@@ -2860,7 +2792,7 @@
     Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
 
     Ptr<WifiPpdu> ppdu =
-        Create<HePpdu>(psdu, txVector, FREQUENCY, txDuration, WIFI_PHY_BAND_5GHZ, m_uid++);
+        Create<HePpdu>(psdu, txVector, m_phy->GetOperatingChannel(), txDuration, m_uid++);
 
     Ptr<SpectrumValue> txPowerSpectrum =
         WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(FREQUENCY,
@@ -2875,19 +2807,13 @@
     txParams->ppdu = ppdu;
     txParams->txWidth = CHANNEL_WIDTH;
 
-    m_phy->StartRx(txParams);
+    m_phy->StartRx(txParams, nullptr);
 }
 
 void
 TestAmpduReception::DoSetup()
 {
-    m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
-    m_phy->SetErrorRateModel(error);
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
+    WifiPhyReceptionTest::DoSetup();
 
     m_phy->SetReceiveOkCallback(MakeCallback(&TestAmpduReception::RxSuccess, this));
     m_phy->SetReceiveErrorCallback(MakeCallback(&TestAmpduReception::RxFailure, this));
@@ -2906,13 +2832,6 @@
 }
 
 void
-TestAmpduReception::DoTeardown()
-{
-    m_phy->Dispose();
-    m_phy = nullptr;
-}
-
-void
 TestAmpduReception::DoRun()
 {
     RngSeedManager::SetSeed(1);
@@ -4103,11 +4022,11 @@
 class TestUnsupportedModulationReception : public TestCase
 {
   public:
-    /**
-     * Constructor
-     */
     TestUnsupportedModulationReception();
-    ~TestUnsupportedModulationReception() override;
+    ~TestUnsupportedModulationReception() override = default;
+
+  private:
+    void DoRun() override;
 
     /**
      * Callback invoked when PHY drops an incoming packet
@@ -4121,19 +4040,12 @@
      */
     void CheckResults();
 
-  private:
-    void DoRun() override;
-    uint16_t m_dropped; ///< number of packets dropped by the AP because it was already receiving
+    uint16_t m_dropped{0}; ///< number of packets dropped by the AP because it was already receiving
 };
 
 TestUnsupportedModulationReception::TestUnsupportedModulationReception()
     : TestCase("Check correct behavior when a STA is receiving a transmission using an unsupported "
-               "modulation"),
-      m_dropped(0)
-{
-}
-
-TestUnsupportedModulationReception::~TestUnsupportedModulationReception()
+               "modulation")
 {
 }
 
@@ -4157,8 +4069,6 @@
     NetDeviceContainer m_staDevices; ///< container for stations' NetDevices
     NetDeviceContainer m_apDevices;  ///< container for AP's NetDevice
 
-    // RngSeedManager::SetSeed (1);
-    // RngSeedManager::SetRun (40);
     int64_t streamNumber = 100;
 
     NodeContainer wifiApNode;
@@ -4317,11 +4227,10 @@
     /**
      * Function to create a PPDU
      *
-     * \param band the PHY band to use
      * \param centerFreqMhz the center frequency used for the transmission of the PPDU (in MHz)
      * \param bandwidthMhz the bandwidth used for the transmission of the PPDU (in MHz)
      */
-    void SendPpdu(WifiPhyBand band, uint16_t centerFreqMhz, uint16_t bandwidthMhz);
+    void SendPpdu(uint16_t centerFreqMhz, uint16_t bandwidthMhz);
 
     /**
      * Function called upon a PSDU received successfully
@@ -4374,7 +4283,8 @@
     std::optional<Time> m_lastRxFailed;    ///< time of last RX failure, if any
     std::optional<Time> m_lastRxDropped;   ///< time of last RX drop, if any
 
-    Ptr<SpectrumWifiPhy> m_phy; ///< PHY
+    Ptr<SpectrumWifiPhy> m_rxPhy; ///< RX PHY
+    Ptr<SpectrumWifiPhy> m_txPhy; ///< TX PHY
 };
 
 TestUnsupportedBandwidthReception::TestUnsupportedBandwidthReception()
@@ -4390,9 +4300,7 @@
 }
 
 void
-TestUnsupportedBandwidthReception::SendPpdu(WifiPhyBand band,
-                                            uint16_t centerFreqMhz,
-                                            uint16_t bandwidthMhz)
+TestUnsupportedBandwidthReception::SendPpdu(uint16_t centerFreqMhz, uint16_t bandwidthMhz)
 {
     auto txVector =
         WifiTxVector(HePhy::GetHeMcs0(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, bandwidthMhz, false);
@@ -4404,9 +4312,10 @@
     hdr.SetQosTid(0);
 
     Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
-    Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
+    Time txDuration =
+        m_rxPhy->CalculateTxDuration(psdu->GetSize(), txVector, m_rxPhy->GetPhyBand());
 
-    auto ppdu = Create<HePpdu>(psdu, txVector, centerFreqMhz, txDuration, WIFI_PHY_BAND_5GHZ, 0);
+    auto ppdu = Create<HePpdu>(psdu, txVector, m_txPhy->GetOperatingChannel(), txDuration, 0);
 
     auto txPowerSpectrum =
         WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(centerFreqMhz,
@@ -4421,7 +4330,7 @@
     txParams->ppdu = ppdu;
     txParams->txWidth = bandwidthMhz;
 
-    m_phy->StartRx(txParams);
+    m_rxPhy->StartRx(txParams, nullptr);
 }
 
 void
@@ -4503,26 +4412,45 @@
 void
 TestUnsupportedBandwidthReception::DoSetup()
 {
-    m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    auto interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    auto error = CreateObject<NistErrorRateModel>();
-    m_phy->SetErrorRateModel(error);
-
-    m_phy->SetReceiveOkCallback(MakeCallback(&TestUnsupportedBandwidthReception::RxSuccess, this));
-    m_phy->SetReceiveErrorCallback(
+    Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    Ptr<Node> node = CreateObject<Node>();
+    Ptr<WifiNetDevice> dev = CreateObject<WifiNetDevice>();
+    m_rxPhy = CreateObject<SpectrumWifiPhy>();
+    auto rxInterferenceHelper = CreateObject<InterferenceHelper>();
+    m_rxPhy->SetInterferenceHelper(rxInterferenceHelper);
+    auto rxErrorRateModel = CreateObject<NistErrorRateModel>();
+    m_rxPhy->SetErrorRateModel(rxErrorRateModel);
+    m_rxPhy->SetDevice(dev);
+    m_rxPhy->AddChannel(spectrumChannel);
+    m_rxPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
+    dev->SetPhy(m_rxPhy);
+    node->AddDevice(dev);
+
+    m_rxPhy->SetReceiveOkCallback(
+        MakeCallback(&TestUnsupportedBandwidthReception::RxSuccess, this));
+    m_rxPhy->SetReceiveErrorCallback(
         MakeCallback(&TestUnsupportedBandwidthReception::RxFailure, this));
-    m_phy->TraceConnectWithoutContext(
+    m_rxPhy->TraceConnectWithoutContext(
         "PhyRxDrop",
         MakeCallback(&TestUnsupportedBandwidthReception::RxDropped, this));
+
+    m_txPhy = CreateObject<SpectrumWifiPhy>();
+    auto txInterferenceHelper = CreateObject<InterferenceHelper>();
+    m_txPhy->SetInterferenceHelper(txInterferenceHelper);
+    auto txErrorRateModel = CreateObject<NistErrorRateModel>();
+    m_txPhy->SetErrorRateModel(txErrorRateModel);
+    m_txPhy->AddChannel(spectrumChannel);
+    m_txPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
 }
 
 void
 TestUnsupportedBandwidthReception::DoTeardown()
 {
-    m_phy->Dispose();
-    m_phy = nullptr;
+    m_rxPhy->Dispose();
+    m_rxPhy = nullptr;
+
+    m_txPhy->Dispose();
+    m_txPhy = nullptr;
 }
 
 void
@@ -4532,20 +4460,16 @@
     RngSeedManager::SetRun(1);
 
     int64_t streamNumber = 0;
-    m_phy->AssignStreams(streamNumber);
+    m_rxPhy->AssignStreams(streamNumber);
 
     // Case 1: the PHY is operating on channel 36 (20 MHz) and receives a 40 MHz PPDU (channel 38).
     // The PPDU should be dropped once HE-SIG-A is successfully received, since it contains
     // indication about the BW used for the transmission and the PHY shall detect it is larger than
     // its operating BW.
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{36, 20, WIFI_PHY_BAND_5GHZ, 0});
+    m_txPhy->SetOperatingChannel(WifiPhy::ChannelTuple{38, 40, WIFI_PHY_BAND_5GHZ, 0});
+    m_rxPhy->SetOperatingChannel(WifiPhy::ChannelTuple{36, 20, WIFI_PHY_BAND_5GHZ, 0});
 
-    Simulator::Schedule(Seconds(1.0),
-                        &TestUnsupportedBandwidthReception::SendPpdu,
-                        this,
-                        WIFI_PHY_BAND_5GHZ,
-                        5190,
-                        40);
+    Simulator::Schedule(Seconds(1.0), &TestUnsupportedBandwidthReception::SendPpdu, this, 5190, 40);
 
     auto heSigAExpectedRxTime = Seconds(1.0) + MicroSeconds(32);
     Simulator::Schedule(Seconds(1.5),
@@ -4585,11 +4509,10 @@
     /**
      * Function to create a PPDU
      *
-     * \param band the PHY band to use
      * \param ppduCenterFreqMhz the center frequency used for the transmission of the PPDU (in MHz)
      * \return the created PPDU
      */
-    Ptr<HePpdu> CreatePpdu(WifiPhyBand band, uint16_t ppduCenterFreqMhz);
+    Ptr<HePpdu> CreatePpdu(uint16_t ppduCenterFreqMhz);
 
     /**
      * Run one function
@@ -4610,7 +4533,8 @@
                 bool expectedP20Overlap,
                 bool expectedP20Covered);
 
-    Ptr<SpectrumWifiPhy> m_phy; ///< PHY
+    Ptr<SpectrumWifiPhy> m_rxPhy; ///< RX PHY
+    Ptr<SpectrumWifiPhy> m_txPhy; ///< TX PHY
 };
 
 TestPrimary20CoveredByPpdu::TestPrimary20CoveredByPpdu()
@@ -4620,11 +4544,15 @@
 }
 
 Ptr<HePpdu>
-TestPrimary20CoveredByPpdu::CreatePpdu(WifiPhyBand band, uint16_t ppduCenterFreqMhz)
+TestPrimary20CoveredByPpdu::CreatePpdu(uint16_t ppduCenterFreqMhz)
 {
     [[maybe_unused]] auto [channelNumber, centerFreq, channelWidth, type, phyBand] =
-        (*WifiPhyOperatingChannel::FindFirst(0, ppduCenterFreqMhz, 0, WIFI_STANDARD_80211ax, band));
-
+        (*WifiPhyOperatingChannel::FindFirst(0,
+                                             ppduCenterFreqMhz,
+                                             0,
+                                             WIFI_STANDARD_80211ax,
+                                             m_rxPhy->GetPhyBand()));
+    m_txPhy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNumber, channelWidth, phyBand, 0});
     auto txVector =
         WifiTxVector(HePhy::GetHeMcs7(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, channelWidth, false);
 
@@ -4632,27 +4560,38 @@
     WifiMacHeader hdr(WIFI_MAC_QOSDATA);
 
     auto psdu = Create<WifiPsdu>(pkt, hdr);
-    auto txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, band);
+    auto txDuration = m_txPhy->CalculateTxDuration(psdu->GetSize(), txVector, phyBand);
 
-    return Create<HePpdu>(psdu, txVector, ppduCenterFreqMhz, txDuration, band, 0);
+    return Create<HePpdu>(psdu, txVector, m_txPhy->GetOperatingChannel(), txDuration, 0);
 }
 
 void
 TestPrimary20CoveredByPpdu::DoSetup()
 {
-    m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
-    auto interferenceHelper = CreateObject<InterferenceHelper>();
-    m_phy->SetInterferenceHelper(interferenceHelper);
-    auto error = CreateObject<NistErrorRateModel>();
-    m_phy->SetErrorRateModel(error);
+    m_rxPhy = CreateObject<SpectrumWifiPhy>();
+    auto rxInterferenceHelper = CreateObject<InterferenceHelper>();
+    m_rxPhy->SetInterferenceHelper(rxInterferenceHelper);
+    auto rxErrorRateModel = CreateObject<NistErrorRateModel>();
+    m_rxPhy->SetErrorRateModel(rxErrorRateModel);
+    m_rxPhy->AddChannel(CreateObject<MultiModelSpectrumChannel>());
+    m_rxPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
+
+    m_txPhy = CreateObject<SpectrumWifiPhy>();
+    auto txInterferenceHelper = CreateObject<InterferenceHelper>();
+    m_txPhy->SetInterferenceHelper(txInterferenceHelper);
+    auto txErrorRateModel = CreateObject<NistErrorRateModel>();
+    m_txPhy->SetErrorRateModel(txErrorRateModel);
+    m_txPhy->AddChannel(CreateObject<MultiModelSpectrumChannel>());
+    m_txPhy->ConfigureStandard(WIFI_STANDARD_80211ax);
 }
 
 void
 TestPrimary20CoveredByPpdu::DoTeardown()
 {
-    m_phy->Dispose();
-    m_phy = nullptr;
+    m_rxPhy->Dispose();
+    m_rxPhy = nullptr;
+    m_txPhy->Dispose();
+    m_txPhy = nullptr;
 }
 
 void
@@ -4666,12 +4605,13 @@
     [[maybe_unused]] const auto [channelNumber, centerFreq, channelWidth, type, phyBand] =
         (*WifiPhyOperatingChannel::FindFirst(0, phyCenterFreqMhz, 0, WIFI_STANDARD_80211ax, band));
 
-    m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{channelNumber, channelWidth, band, p20Index});
-    auto p20CenterFreq = m_phy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(20);
+    m_rxPhy->SetOperatingChannel(
+        WifiPhy::ChannelTuple{channelNumber, channelWidth, band, p20Index});
+    auto p20CenterFreq = m_rxPhy->GetOperatingChannel().GetPrimaryChannelCenterFrequency(20);
     auto p20MinFreq = p20CenterFreq - 10;
     auto p20MaxFreq = p20CenterFreq + 10;
 
-    auto ppdu = CreatePpdu(band, ppduCenterFreqMhz);
+    auto ppdu = CreatePpdu(ppduCenterFreqMhz);
 
     auto p20Overlap = ppdu->DoesOverlapChannel(p20MinFreq, p20MaxFreq);
     NS_TEST_ASSERT_MSG_EQ(p20Overlap,
@@ -4680,7 +4620,7 @@
                                      << " to overlap with the P20");
 
     auto p20Covered =
-        m_phy->GetPhyEntity(WIFI_STANDARD_80211ax)
+        m_rxPhy->GetPhyEntity(WIFI_STANDARD_80211ax)
             ->CanStartRx(
                 ppdu,
                 ppdu->GetTxVector()
@@ -4704,7 +4644,7 @@
 
     /*
      * Receiver PHY Operating Channel: 2.4 GHz Channel 4 (2417 MHz â€“ 2437 MHz)
-     * Transmitted 40 MHz PPDU: 2.4 GHz Channel 6 (2427 MHz â€“ 2447 MHz)
+     * Transmitted 20 MHz PPDU: 2.4 GHz Channel 6 (2427 MHz â€“ 2447 MHz)
      * Overlap with primary 20 MHz: yes
      * Primary 20 MHz fully covered: no
      */
@@ -4749,6 +4689,176 @@
  * \ingroup wifi-test
  * \ingroup tests
  *
+ * \brief This test verifies the correct function of the WifiBandwidthFilter. 2 SpectrumWifiPhy are
+ * setup and connected on the same spectrum channel. The test will
+ * send a packet over the channel and if the signal plus guardband overlaps the channel the
+ * filter will not discard the signal but if there is no overlap the filter will filter it out.
+ */
+class TestSpectrumChannelWithBandwidthFilter : public TestCase
+{
+  public:
+    /**
+     * Constructor
+     *
+     * \param channel channel to be used by transmitter
+     * \param expectedValue expected number of received packets
+     */
+    TestSpectrumChannelWithBandwidthFilter(uint16_t channel, uint16_t expectedValue);
+
+  protected:
+    void DoSetup() override;
+    void DoTeardown() override;
+
+  private:
+    /**
+     * Callback invoked when the PHY model starts to process a signal
+     *
+     * \param signalType whether signal is WiFi (true) or foreign (false)
+     * \param senderNodeId node Id of the sender of the signal
+     * \param rxPower received signal power (dBm)
+     * \param duration signal duration
+     */
+    void RxBegin(bool signalType, uint32_t senderNodeId, double rxPower, Time duration);
+
+    /**
+     * Send function (sends a single packet)
+     */
+    void Send() const;
+
+    /**
+     * Event scheduled at end of simulation for validation
+     *
+     * \param expectedValue expected number of receive events
+     */
+    void CheckRxPacketCount(uint16_t expectedValue);
+
+    void DoRun() override;
+
+    Ptr<SpectrumWifiPhy> m_tx{nullptr}; ///< transmit function
+    Ptr<SpectrumWifiPhy> m_rx{nullptr}; ///< receive function
+    uint32_t m_countRxBegin{0};         ///< count of receive events
+    uint16_t m_channel{36};             ///< channel for packet transmission
+    uint16_t m_expectedValue{0};        ///< expected count of receive events
+};
+
+TestSpectrumChannelWithBandwidthFilter::TestSpectrumChannelWithBandwidthFilter(
+    uint16_t channel,
+    uint16_t expectedValue)
+    : TestCase("Test for early discard of signal in single-model-spectrum-channel::StartTx()"),
+      m_channel(channel),
+      m_expectedValue(expectedValue)
+{
+}
+
+void
+TestSpectrumChannelWithBandwidthFilter::Send() const
+{
+    WifiTxVector txVector =
+        WifiTxVector(HePhy::GetHeMcs7(), 0, WIFI_PREAMBLE_HE_SU, 800, 1, 1, 0, 20, false);
+
+    Ptr<Packet> pkt = Create<Packet>(1000);
+    WifiMacHeader hdr;
+
+    hdr.SetType(WIFI_MAC_QOSDATA);
+    hdr.SetQosTid(0);
+
+    Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
+    m_tx->Send(psdu, txVector);
+}
+
+void
+TestSpectrumChannelWithBandwidthFilter::CheckRxPacketCount(uint16_t expectedValue)
+{
+    NS_TEST_ASSERT_MSG_EQ(m_countRxBegin,
+                          expectedValue,
+                          "Received a different amount of packets than expected.");
+}
+
+void
+TestSpectrumChannelWithBandwidthFilter::RxBegin(bool signalType [[maybe_unused]],
+                                                uint32_t senderNodeId [[maybe_unused]],
+                                                double rxPower [[maybe_unused]],
+                                                Time duration [[maybe_unused]])
+{
+    NS_LOG_FUNCTION(this << signalType << senderNodeId << rxPower << duration);
+    m_countRxBegin++;
+}
+
+void
+TestSpectrumChannelWithBandwidthFilter::DoSetup()
+{
+    NS_LOG_FUNCTION(this);
+    Ptr<SingleModelSpectrumChannel> channel = CreateObject<SingleModelSpectrumChannel>();
+
+    Ptr<WifiBandwidthFilter> wifiFilter = CreateObject<WifiBandwidthFilter>();
+    channel->AddSpectrumTransmitFilter(wifiFilter);
+
+    Ptr<Node> node = CreateObject<Node>();
+    Ptr<WifiNetDevice> dev = CreateObject<WifiNetDevice>();
+    m_tx = CreateObject<SpectrumWifiPhy>();
+    m_tx->SetDevice(dev);
+    m_tx->SetTxPowerStart(20);
+    m_tx->SetTxPowerEnd(20);
+
+    Ptr<Node> nodeRx = CreateObject<Node>();
+    Ptr<WifiNetDevice> devRx = CreateObject<WifiNetDevice>();
+    m_rx = CreateObject<SpectrumWifiPhy>();
+    m_rx->SetDevice(devRx);
+
+    Ptr<InterferenceHelper> interferenceTx = CreateObject<InterferenceHelper>();
+    m_tx->SetInterferenceHelper(interferenceTx);
+    Ptr<ErrorRateModel> errorTx = CreateObject<NistErrorRateModel>();
+    m_tx->SetErrorRateModel(errorTx);
+
+    Ptr<InterferenceHelper> interferenceRx = CreateObject<InterferenceHelper>();
+    m_rx->SetInterferenceHelper(interferenceRx);
+    Ptr<ErrorRateModel> errorRx = CreateObject<NistErrorRateModel>();
+    m_rx->SetErrorRateModel(errorRx);
+
+    m_tx->AddChannel(channel);
+    m_rx->AddChannel(channel);
+
+    m_tx->ConfigureStandard(WIFI_STANDARD_80211ax);
+    m_rx->ConfigureStandard(WIFI_STANDARD_80211ax);
+
+    dev->SetPhy(m_tx);
+    node->AddDevice(dev);
+    devRx->SetPhy(m_rx);
+    nodeRx->AddDevice(devRx);
+
+    m_rx->TraceConnectWithoutContext(
+        "SignalArrival",
+        MakeCallback(&TestSpectrumChannelWithBandwidthFilter::RxBegin, this));
+}
+
+void
+TestSpectrumChannelWithBandwidthFilter::DoTeardown()
+{
+    m_tx->Dispose();
+    m_rx->Dispose();
+}
+
+void
+TestSpectrumChannelWithBandwidthFilter::DoRun()
+{
+    NS_LOG_FUNCTION(this);
+    m_tx->SetOperatingChannel(WifiPhy::ChannelTuple{m_channel, 0, WIFI_PHY_BAND_5GHZ, 0});
+    m_rx->SetOperatingChannel(WifiPhy::ChannelTuple{36, 0, WIFI_PHY_BAND_5GHZ, 0});
+
+    Simulator::Schedule(MilliSeconds(100), &TestSpectrumChannelWithBandwidthFilter::Send, this);
+    Simulator::Schedule(MilliSeconds(101),
+                        &TestSpectrumChannelWithBandwidthFilter::CheckRxPacketCount,
+                        this,
+                        m_expectedValue);
+
+    Simulator::Run();
+    Simulator::Destroy();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
  * \brief wifi PHY reception Test Suite
  */
 class WifiPhyReceptionTestSuite : public TestSuite
@@ -4768,6 +4878,14 @@
     AddTestCase(new TestUnsupportedModulationReception(), TestCase::QUICK);
     AddTestCase(new TestUnsupportedBandwidthReception(), TestCase::QUICK);
     AddTestCase(new TestPrimary20CoveredByPpdu(), TestCase::QUICK);
+    // The below three test cases are related.  The test involves a receiver tuned to
+    // channel 36 and a transmitter sending on channels 36, 40, and 44, respectively.
+    // The second argument corresponds to the number of signals expected to be received.
+    // Signals on channel 36 and 40 will fall within the receiver bandwidth, while
+    // a signal on channel 44 will fall completely outside and will be filtered.
+    AddTestCase(new TestSpectrumChannelWithBandwidthFilter(36, 1), TestCase::QUICK);
+    AddTestCase(new TestSpectrumChannelWithBandwidthFilter(40, 1), TestCase::QUICK);
+    AddTestCase(new TestSpectrumChannelWithBandwidthFilter(44, 0), TestCase::QUICK);
 }
 
 static WifiPhyReceptionTestSuite wifiPhyReceptionTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-phy-thresholds-test.cc ns-3.39/src/wifi/test/wifi-phy-thresholds-test.cc
--- ns-3.38/src/wifi/test/wifi-phy-thresholds-test.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wifi/test/wifi-phy-thresholds-test.cc	2023-07-05 14:23:48.133428300 -0700
@@ -19,14 +19,18 @@
 
 #include "ns3/interference-helper.h"
 #include "ns3/log.h"
+#include "ns3/multi-model-spectrum-channel.h"
 #include "ns3/nist-error-rate-model.h"
 #include "ns3/ofdm-phy.h"
 #include "ns3/ofdm-ppdu.h"
+#include "ns3/spectrum-phy.h"
 #include "ns3/spectrum-wifi-helper.h"
 #include "ns3/spectrum-wifi-phy.h"
 #include "ns3/test.h"
 #include "ns3/wifi-mac-header.h"
+#include "ns3/wifi-net-device.h"
 #include "ns3/wifi-psdu.h"
+#include "ns3/wifi-spectrum-phy-interface.h"
 #include "ns3/wifi-spectrum-signal-parameters.h"
 #include "ns3/wifi-spectrum-value-helper.h"
 #include "ns3/wifi-utils.h"
@@ -63,9 +67,11 @@
     /**
      * Make wifi signal function
      * \param txPowerWatts the transmit power in watts
+     * \param channel the operating channel of the PHY used for the transmission
      * \returns Ptr<SpectrumSignalParameters>
      */
-    virtual Ptr<SpectrumSignalParameters> MakeWifiSignal(double txPowerWatts);
+    virtual Ptr<SpectrumSignalParameters> MakeWifiSignal(double txPowerWatts,
+                                                         const WifiPhyOperatingChannel& channel);
     /**
      * Make foreign signal function
      * \param txPowerWatts the transmit power in watts
@@ -139,10 +145,17 @@
 }
 
 Ptr<SpectrumSignalParameters>
-WifiPhyThresholdsTest::MakeWifiSignal(double txPowerWatts)
+WifiPhyThresholdsTest::MakeWifiSignal(double txPowerWatts, const WifiPhyOperatingChannel& channel)
 {
-    WifiTxVector txVector =
-        WifiTxVector(OfdmPhy::GetOfdmRate6Mbps(), 0, WIFI_PREAMBLE_LONG, 800, 1, 1, 0, 20, false);
+    WifiTxVector txVector = WifiTxVector(OfdmPhy::GetOfdmRate6Mbps(),
+                                         0,
+                                         WIFI_PREAMBLE_LONG,
+                                         800,
+                                         1,
+                                         1,
+                                         0,
+                                         CHANNEL_WIDTH,
+                                         false);
 
     Ptr<Packet> pkt = Create<Packet>(1000);
     WifiMacHeader hdr;
@@ -153,13 +166,14 @@
     Ptr<WifiPsdu> psdu = Create<WifiPsdu>(pkt, hdr);
     Time txDuration = m_phy->CalculateTxDuration(psdu->GetSize(), txVector, m_phy->GetPhyBand());
 
-    Ptr<WifiPpdu> ppdu = Create<OfdmPpdu>(psdu, txVector, FREQUENCY, WIFI_PHY_BAND_5GHZ, 0);
+    Ptr<WifiPpdu> ppdu = Create<OfdmPpdu>(psdu, txVector, channel, 0);
 
     Ptr<SpectrumValue> txPowerSpectrum =
-        WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(FREQUENCY,
-                                                                    CHANNEL_WIDTH,
-                                                                    txPowerWatts,
-                                                                    CHANNEL_WIDTH);
+        WifiSpectrumValueHelper::CreateHeOfdmTxPowerSpectralDensity(
+            channel.GetPrimaryChannelCenterFrequency(CHANNEL_WIDTH),
+            CHANNEL_WIDTH,
+            txPowerWatts,
+            CHANNEL_WIDTH);
     Ptr<WifiSpectrumSignalParameters> txParams = Create<WifiSpectrumSignalParameters>();
     txParams->psd = txPowerSpectrum;
     txParams->txPhy = nullptr;
@@ -189,11 +203,11 @@
 {
     if (wifiSignal)
     {
-        m_phy->StartRx(MakeWifiSignal(txPowerWatts));
+        m_phy->StartRx(MakeWifiSignal(txPowerWatts, m_phy->GetOperatingChannel()), nullptr);
     }
     else
     {
-        m_phy->StartRx(MakeForeignSignal(txPowerWatts));
+        m_phy->StartRx(MakeForeignSignal(txPowerWatts), nullptr);
     }
 }
 
@@ -243,13 +257,18 @@
 void
 WifiPhyThresholdsTest::DoSetup()
 {
+    Ptr<MultiModelSpectrumChannel> spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
+    Ptr<Node> node = CreateObject<Node>();
+    Ptr<WifiNetDevice> dev = CreateObject<WifiNetDevice>();
     m_phy = CreateObject<SpectrumWifiPhy>();
-    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
     Ptr<InterferenceHelper> interferenceHelper = CreateObject<InterferenceHelper>();
     m_phy->SetInterferenceHelper(interferenceHelper);
     Ptr<ErrorRateModel> error = CreateObject<NistErrorRateModel>();
     m_phy->SetErrorRateModel(error);
+    m_phy->SetDevice(dev);
+    m_phy->AddChannel(spectrumChannel);
     m_phy->SetOperatingChannel(WifiPhy::ChannelTuple{CHANNEL_NUMBER, 0, WIFI_PHY_BAND_5GHZ, 0});
+    m_phy->ConfigureStandard(WIFI_STANDARD_80211ax);
     m_phy->SetReceiveOkCallback(MakeCallback(&WifiPhyThresholdsTest::RxSuccess, this));
     m_phy->SetReceiveErrorCallback(MakeCallback(&WifiPhyThresholdsTest::RxFailure, this));
     m_phy->TraceConnectWithoutContext("PhyRxDrop",
@@ -257,6 +276,8 @@
     m_phy->GetState()->TraceConnectWithoutContext(
         "State",
         MakeCallback(&WifiPhyThresholdsTest::PhyStateChanged, this));
+    dev->SetPhy(m_phy);
+    node->AddDevice(dev);
 }
 
 void
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-primary-channels-test.cc ns-3.39/src/wifi/test/wifi-primary-channels-test.cc
--- ns-3.38/src/wifi/test/wifi-primary-channels-test.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wifi/test/wifi-primary-channels-test.cc	2023-07-05 14:23:48.133428300 -0700
@@ -845,7 +845,7 @@
     auto IsOddNum = (nRus / numRuAllocs) % 2 == 1;
     auto ruAlloc = HeRu::GetEqualizedRuAllocation(ruType, IsOddNum);
     std::fill_n(ruAllocations.begin(), numRuAllocs, ruAlloc);
-    txVector.SetRuAllocation(ruAllocations);
+    txVector.SetRuAllocation(ruAllocations, 0);
 
     apDev->GetPhy()->Send(psduMap, txVector);
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wifi/test/wifi-test.cc ns-3.39/src/wifi/test/wifi-test.cc
--- ns-3.38/src/wifi/test/wifi-test.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wifi/test/wifi-test.cc	2023-07-05 14:23:48.133428300 -0700
@@ -27,6 +27,7 @@
 #include "ns3/error-model.h"
 #include "ns3/fcfs-wifi-queue-scheduler.h"
 #include "ns3/frame-exchange-manager.h"
+#include "ns3/header-serialization-test.h"
 #include "ns3/ht-configuration.h"
 #include "ns3/interference-helper.h"
 #include "ns3/mgt-headers.h"
@@ -47,6 +48,7 @@
 #include "ns3/wifi-default-ack-manager.h"
 #include "ns3/wifi-default-assoc-manager.h"
 #include "ns3/wifi-default-protection-manager.h"
+#include "ns3/wifi-mgt-header.h"
 #include "ns3/wifi-net-device.h"
 #include "ns3/wifi-ppdu.h"
 #include "ns3/wifi-psdu.h"
@@ -55,6 +57,8 @@
 #include "ns3/yans-wifi-helper.h"
 #include "ns3/yans-wifi-phy.h"
 
+#include <optional>
+
 using namespace ns3;
 
 // Helper function to assign streams to random variables, to control
@@ -1795,7 +1799,7 @@
     {
         MgtBeaconHeader beacon;
         packet->RemoveHeader(beacon);
-        const auto& htOperation = beacon.GetHtOperation();
+        const auto& htOperation = beacon.Get<HtOperation>();
         if (htOperation.has_value() && htOperation->GetStaChannelWidth() > 0)
         {
             m_countOperationalChannelWidth40++;
@@ -3626,6 +3630,157 @@
                           "Data rate verification for RUs above 52-tone RU (included) failed");
 }
 
+/// List of Information Elements included in the test management frame
+using MgtTestElems =
+    std::tuple<SupportedRates, std::optional<ExtendedSupportedRatesIE>, std::vector<Ssid>>;
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Test management header
+ */
+class MgtTestHeader : public WifiMgtHeader<MgtTestHeader, MgtTestElems>
+{
+  public:
+    ~MgtTestHeader() override = default;
+
+    /**
+     * Register this type.
+     * \return The TypeId.
+     */
+    static TypeId GetTypeId();
+
+    /**
+     * \return the TypeId for this object.
+     */
+    TypeId GetInstanceTypeId() const override;
+
+    using WifiMgtHeader<MgtTestHeader, MgtTestElems>::GetSerializedSize;
+    using WifiMgtHeader<MgtTestHeader, MgtTestElems>::Serialize;
+    using WifiMgtHeader<MgtTestHeader, MgtTestElems>::Deserialize;
+};
+
+TypeId
+MgtTestHeader::GetTypeId()
+{
+    static TypeId tid = TypeId("ns3::MgtTestHeader")
+                            .SetParent<Header>()
+                            .SetGroupName("Wifi")
+                            .AddConstructor<MgtTestHeader>();
+    return tid;
+}
+
+TypeId
+MgtTestHeader::GetInstanceTypeId() const
+{
+    return GetTypeId();
+}
+
+/**
+ * \ingroup wifi-test
+ * \ingroup tests
+ *
+ * \brief Mgt header (de)serialization Test Suite
+ */
+class WifiMgtHeaderTest : public HeaderSerializationTestCase
+{
+  public:
+    WifiMgtHeaderTest();
+    ~WifiMgtHeaderTest() override = default;
+
+  private:
+    void DoRun() override;
+};
+
+WifiMgtHeaderTest::WifiMgtHeaderTest()
+    : HeaderSerializationTestCase("Check (de)serialization of a test management header")
+{
+}
+
+void
+WifiMgtHeaderTest::DoRun()
+{
+    MgtTestHeader frame;
+
+    // Add the mandatory Information Element (SupportedRates)
+    AllSupportedRates allRates;
+    allRates.AddSupportedRate(1000000);
+    allRates.AddSupportedRate(2000000);
+    allRates.AddSupportedRate(3000000);
+    allRates.AddSupportedRate(4000000);
+    allRates.AddSupportedRate(5000000);
+
+    frame.Get<SupportedRates>() = allRates.rates;
+    frame.Get<ExtendedSupportedRatesIE>() = allRates.extendedRates;
+
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Expected a SupportedRates IE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<ExtendedSupportedRatesIE>().has_value(),
+                          false,
+                          "Expected no ExtendedSupportedRatesIE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<Ssid>().size(), 0, "Expected no Ssid IE to be included");
+
+    TestHeaderSerialization(frame);
+
+    // Add more rates, so that the optional Information Element (ExtendedSupportedRatesIE) is added
+    allRates.AddSupportedRate(6000000);
+    allRates.AddSupportedRate(7000000);
+    allRates.AddSupportedRate(8000000);
+    allRates.AddSupportedRate(9000000);
+    allRates.AddSupportedRate(10000000);
+
+    frame.Get<SupportedRates>() = allRates.rates;
+    frame.Get<ExtendedSupportedRatesIE>() = allRates.extendedRates;
+
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Expected a SupportedRates IE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<ExtendedSupportedRatesIE>().has_value(),
+                          true,
+                          "Expected an ExtendedSupportedRatesIE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<Ssid>().size(), 0, "Expected no Ssid IE to be included");
+
+    TestHeaderSerialization(frame);
+
+    // Add a first Ssid IE
+    Ssid one("Ssid One");
+    frame.Get<Ssid>().push_back(one);
+
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Expected a SupportedRates IE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<ExtendedSupportedRatesIE>().has_value(),
+                          true,
+                          "Expected an ExtendedSupportedRatesIE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<Ssid>().size(), 1, "Expected one Ssid IE to be included");
+    NS_TEST_EXPECT_MSG_EQ(std::string(frame.Get<Ssid>().front().PeekString()),
+                          "Ssid One",
+                          "Incorrect SSID");
+
+    TestHeaderSerialization(frame);
+
+    // Add a second Ssid IE
+    frame.Get<Ssid>().emplace_back("Ssid Two");
+
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<SupportedRates>().has_value(),
+                          true,
+                          "Expected a SupportedRates IE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<ExtendedSupportedRatesIE>().has_value(),
+                          true,
+                          "Expected an ExtendedSupportedRatesIE to be included");
+    NS_TEST_EXPECT_MSG_EQ(frame.Get<Ssid>().size(), 2, "Expected two Ssid IEs to be included");
+    NS_TEST_EXPECT_MSG_EQ(std::string(frame.Get<Ssid>().front().PeekString()),
+                          "Ssid One",
+                          "Incorrect first SSID");
+    NS_TEST_EXPECT_MSG_EQ(std::string(frame.Get<Ssid>().back().PeekString()),
+                          "Ssid Two",
+                          "Incorrect second SSID");
+
+    TestHeaderSerialization(frame);
+}
+
 /**
  * \ingroup wifi-test
  * \ingroup tests
@@ -3658,6 +3813,7 @@
     AddTestCase(new IdealRateManagerChannelWidthTest, TestCase::QUICK);
     AddTestCase(new IdealRateManagerMimoTest, TestCase::QUICK);
     AddTestCase(new HeRuMcsDataRateTestCase, TestCase::QUICK);
+    AddTestCase(new WifiMgtHeaderTest, TestCase::QUICK);
 }
 
 static WifiTestSuite g_wifiTestSuite; ///< the test suite
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/examples/wimax-ipv4.cc ns-3.39/src/wimax/examples/wimax-ipv4.cc
--- ns-3.38/src/wimax/examples/wimax-ipv4.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/examples/wimax-ipv4.cc	2023-07-05 14:23:48.133428300 -0700
@@ -50,7 +50,6 @@
 //       +-----+    +-----+    +-----+
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/global-route-manager.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/examples/wimax-multicast.cc ns-3.39/src/wimax/examples/wimax-multicast.cc
--- ns-3.38/src/wimax/examples/wimax-multicast.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/examples/wimax-multicast.cc	2023-07-05 14:23:48.133428300 -0700
@@ -50,7 +50,6 @@
 //       +-----+    +-----+    +-----+    +-----+    +-----+
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/csma-module.h"
 #include "ns3/global-route-manager.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/examples/wimax-simple.cc ns-3.39/src/wimax/examples/wimax-simple.cc
--- ns-3.38/src/wimax/examples/wimax-simple.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/examples/wimax-simple.cc	2023-07-05 14:23:48.133428300 -0700
@@ -42,7 +42,6 @@
 //       +-----+
 
 #include "ns3/applications-module.h"
-#include "ns3/config-store-module.h"
 #include "ns3/core-module.h"
 #include "ns3/global-route-manager.h"
 #include "ns3/internet-module.h"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-net-device.cc ns-3.39/src/wimax/model/bs-net-device.cc
--- ns-3.38/src/wimax/model/bs-net-device.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-net-device.cc	2023-07-05 14:23:48.133428300 -0700
@@ -639,7 +639,7 @@
     NS_LOG_INFO("BS (" << source << "):");
     NS_LOG_INFO("\tSending packet...");
     NS_LOG_INFO("\t\tDestination: " << dest);
-    NS_LOG_INFO("\t\tPaket Size:  " << packet->GetSize());
+    NS_LOG_INFO("\t\tPacket Size:  " << packet->GetSize());
     NS_LOG_INFO("\t\tProtocol:    " << protocolNumber);
 
     if (protocolNumber == 2048)
@@ -716,7 +716,7 @@
     packet->RemoveHeader(gnrcMacHdr);
     if (gnrcMacHdr.GetHt() == MacHeaderType::HEADER_TYPE_GENERIC)
     {
-        if (gnrcMacHdr.check_hcs() == false)
+        if (!gnrcMacHdr.check_hcs())
         {
             // The header is noisy
             m_bsRxDropTrace(packet);
@@ -882,7 +882,7 @@
         packet->RemoveHeader(bwRequestHdr);
         NS_ASSERT_MSG(bwRequestHdr.GetHt() == MacHeaderType::HEADER_TYPE_BANDWIDTH,
                       "A bandwidth request should be carried by a bandwidth header type");
-        if (bwRequestHdr.check_hcs() == false)
+        if (!bwRequestHdr.check_hcs())
         {
             // The header is noisy
             NS_LOG_INFO("BS:Header HCS ERROR");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-scheduler-rtps.cc ns-3.39/src/wimax/model/bs-scheduler-rtps.cc
--- ns-3.38/src/wimax/model/bs-scheduler-rtps.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-scheduler-rtps.cc	2023-07-05 14:23:48.133428300 -0700
@@ -111,7 +111,7 @@
     }
     NS_LOG_INFO(", modulation: " << modulationType << ", DIUC: " << (uint32_t)diuc);
 
-    m_downlinkBursts->push_back(std::make_pair(dlMapIe, burst));
+    m_downlinkBursts->emplace_back(dlMapIe, burst);
 }
 
 /**
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-scheduler-simple.cc ns-3.39/src/wimax/model/bs-scheduler-simple.cc
--- ns-3.38/src/wimax/model/bs-scheduler-simple.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-scheduler-simple.cc	2023-07-05 14:23:48.133428300 -0700
@@ -109,7 +109,7 @@
     }
     NS_LOG_INFO(", modulation: " << modulationType << ", DIUC: " << (uint32_t)diuc);
 
-    m_downlinkBursts->push_back(std::make_pair(dlMapIe, burst));
+    m_downlinkBursts->emplace_back(dlMapIe, burst);
 }
 
 void
@@ -187,7 +187,7 @@
         else
         {
             burst = Create<PacketBurst>();
-            while (connection->HasPackets() == true)
+            while (connection->HasPackets())
             {
                 uint32_t FirstPacketSize = connection->GetQueue()->GetFirstPacketRequiredByte(
                     MacHeaderType::HEADER_TYPE_GENERIC);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-service-flow-manager.cc ns-3.39/src/wimax/model/bs-service-flow-manager.cc
--- ns-3.38/src/wimax/model/bs-service-flow-manager.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-service-flow-manager.cc	2023-07-05 14:23:48.133428300 -0700
@@ -221,8 +221,7 @@
 }
 
 void
-BsServiceFlowManager::AddMulticastServiceFlow(ServiceFlow sf,
-                                              enum WimaxPhy::ModulationType modulation)
+BsServiceFlowManager::AddMulticastServiceFlow(ServiceFlow sf, WimaxPhy::ModulationType modulation)
 {
     ServiceFlow* serviceFlow = new ServiceFlow();
     serviceFlow->CopyParametersFrom(sf);
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-service-flow-manager.h ns-3.39/src/wimax/model/bs-service-flow-manager.h
--- ns-3.38/src/wimax/model/bs-service-flow-manager.h	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-service-flow-manager.h	2023-07-05 14:23:48.133428300 -0700
@@ -108,7 +108,7 @@
      * \param sf the service flow
      * \param modulation the wimax phy modulation type
      */
-    void AddMulticastServiceFlow(ServiceFlow sf, enum WimaxPhy::ModulationType modulation);
+    void AddMulticastServiceFlow(ServiceFlow sf, WimaxPhy::ModulationType modulation);
     /**
      * \brief process a DSA-ACK message
      * \param dsaAck the message to process
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-uplink-scheduler.h ns-3.39/src/wimax/model/bs-uplink-scheduler.h
--- ns-3.38/src/wimax/model/bs-uplink-scheduler.h	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-uplink-scheduler.h	2023-07-05 14:23:48.133428300 -0700
@@ -167,7 +167,7 @@
      * \param availableSymbols the available symbols
      */
     virtual void ServiceUnsolicitedGrants(const SSRecord* ssRecord,
-                                          enum ServiceFlow::SchedulingType schedulingType,
+                                          ServiceFlow::SchedulingType schedulingType,
                                           OfdmUlMapIe& ulMapIe,
                                           const WimaxPhy::ModulationType modulationType,
                                           uint32_t& symbolsToAllocation,
@@ -182,7 +182,7 @@
      * \param availableSymbols the available symbols
      */
     virtual void ServiceBandwidthRequests(const SSRecord* ssRecord,
-                                          enum ServiceFlow::SchedulingType schedulingType,
+                                          ServiceFlow::SchedulingType schedulingType,
                                           OfdmUlMapIe& ulMapIe,
                                           const WimaxPhy::ModulationType modulationType,
                                           uint32_t& symbolsToAllocation,
@@ -198,7 +198,7 @@
      * \returns true if successful
      */
     virtual bool ServiceBandwidthRequests(ServiceFlow* serviceFlow,
-                                          enum ServiceFlow::SchedulingType schedulingType,
+                                          ServiceFlow::SchedulingType schedulingType,
                                           OfdmUlMapIe& ulMapIe,
                                           const WimaxPhy::ModulationType modulationType,
                                           uint32_t& symbolsToAllocation,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-uplink-scheduler-mbqos.cc ns-3.39/src/wimax/model/bs-uplink-scheduler-mbqos.cc
--- ns-3.38/src/wimax/model/bs-uplink-scheduler-mbqos.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-uplink-scheduler-mbqos.cc	2023-07-05 14:23:48.133428300 -0700
@@ -402,7 +402,7 @@
         Ptr<UlJob> job = m_uplinkJobs_high.front();
         OfdmUlMapIe ulMapIe;
         SSRecord* ssRecord = job->GetSsRecord();
-        enum ServiceFlow::SchedulingType schedulingType = job->GetSchedulingType();
+        ServiceFlow::SchedulingType schedulingType = job->GetSchedulingType();
 
         Cid cid = ssRecord->GetBasicCid();
         ulMapIe.SetCid(cid);
@@ -447,7 +447,7 @@
         Ptr<UlJob> job = m_uplinkJobs_inter.front();
         OfdmUlMapIe ulMapIe;
         SSRecord* ssRecord = job->GetSsRecord();
-        enum ServiceFlow::SchedulingType schedulingType = job->GetSchedulingType();
+        ServiceFlow::SchedulingType schedulingType = job->GetSchedulingType();
 
         Cid cid = ssRecord->GetBasicCid();
         ulMapIe.SetCid(cid);
@@ -481,7 +481,7 @@
         Ptr<UlJob> job = m_uplinkJobs_low.front();
         OfdmUlMapIe ulMapIe;
         SSRecord* ssRecord = job->GetSsRecord();
-        enum ServiceFlow::SchedulingType schedulingType = job->GetSchedulingType();
+        ServiceFlow::SchedulingType schedulingType = job->GetSchedulingType();
 
         Cid cid = ssRecord->GetBasicCid();
         ulMapIe.SetCid(cid);
@@ -522,7 +522,7 @@
 
 bool
 UplinkSchedulerMBQoS::ServiceBandwidthRequestsBytes(ServiceFlow* serviceFlow,
-                                                    enum ServiceFlow::SchedulingType schedulingType,
+                                                    ServiceFlow::SchedulingType schedulingType,
                                                     OfdmUlMapIe& ulMapIe,
                                                     const WimaxPhy::ModulationType modulationType,
                                                     uint32_t& symbolsToAllocation,
@@ -587,7 +587,7 @@
 
 Ptr<UlJob>
 UplinkSchedulerMBQoS::CreateUlJob(SSRecord* ssRecord,
-                                  enum ServiceFlow::SchedulingType schedType,
+                                  ServiceFlow::SchedulingType schedType,
                                   ReqType reqType)
 {
     Ptr<UlJob> job = CreateObject<UlJob>();
@@ -680,7 +680,7 @@
 void
 UplinkSchedulerMBQoS::CheckDeadline(uint32_t& availableSymbols)
 {
-    // for each request in the imermediate queue
+    // for each request in the intermediate queue
     if (!m_uplinkJobs_inter.empty())
     {
         std::list<Ptr<UlJob>>::iterator iter = m_uplinkJobs_inter.begin();
@@ -869,7 +869,7 @@
 
 void
 UplinkSchedulerMBQoS::ServiceUnsolicitedGrants(const SSRecord* ssRecord,
-                                               enum ServiceFlow::SchedulingType schedulingType,
+                                               ServiceFlow::SchedulingType schedulingType,
                                                OfdmUlMapIe& ulMapIe,
                                                const WimaxPhy::ModulationType modulationType,
                                                uint32_t& symbolsToAllocation,
@@ -937,7 +937,7 @@
 
 void
 UplinkSchedulerMBQoS::ServiceBandwidthRequests(const SSRecord* ssRecord,
-                                               enum ServiceFlow::SchedulingType schedulingType,
+                                               ServiceFlow::SchedulingType schedulingType,
                                                OfdmUlMapIe& ulMapIe,
                                                const WimaxPhy::ModulationType modulationType,
                                                uint32_t& symbolsToAllocation,
@@ -963,7 +963,7 @@
 
 bool
 UplinkSchedulerMBQoS::ServiceBandwidthRequests(ServiceFlow* serviceFlow,
-                                               enum ServiceFlow::SchedulingType schedulingType,
+                                               ServiceFlow::SchedulingType schedulingType,
                                                OfdmUlMapIe& ulMapIe,
                                                const WimaxPhy::ModulationType modulationType,
                                                uint32_t& symbolsToAllocation,
@@ -1079,7 +1079,7 @@
     switch (serviceFlow->GetSchedulingType())
     {
     case ServiceFlow::SF_TYPE_UGS: {
-        if (serviceFlow->GetIsMulticast() == true)
+        if (serviceFlow->GetIsMulticast())
         {
             modulation = serviceFlow->GetModulation();
         }
@@ -1172,7 +1172,7 @@
 
     Time deadline = DetermineDeadline(serviceFlow);
     Time currentTime = Simulator::Now();
-    Time period = deadline; // So that deadline is properly updated..
+    const Time& period = deadline; // So that deadline is properly updated..
 
     NS_LOG_DEBUG("At " << Simulator::Now().As(Time::S)
                        << " at BS uplink scheduler, processing bandwidth request from."
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-uplink-scheduler-mbqos.h ns-3.39/src/wimax/model/bs-uplink-scheduler-mbqos.h
--- ns-3.38/src/wimax/model/bs-uplink-scheduler-mbqos.h	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-uplink-scheduler-mbqos.h	2023-07-05 14:23:48.133428300 -0700
@@ -136,7 +136,7 @@
      * \param availableSymbols the available symbols
      */
     void ServiceUnsolicitedGrants(const SSRecord* ssRecord,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
@@ -151,7 +151,7 @@
      * \param availableSymbols the available symbols
      */
     void ServiceBandwidthRequests(const SSRecord* ssRecord,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
@@ -167,7 +167,7 @@
      * \returns true if successful
      */
     bool ServiceBandwidthRequests(ServiceFlow* serviceFlow,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
@@ -277,7 +277,7 @@
      * Create and fill information of a job.
      */
     Ptr<UlJob> CreateUlJob(SSRecord* ssRecord,
-                           enum ServiceFlow::SchedulingType schedType,
+                           ServiceFlow::SchedulingType schedType,
                            ReqType reqType);
 
     /**
@@ -300,7 +300,7 @@
      * \returns true if successful
      */
     bool ServiceBandwidthRequestsBytes(ServiceFlow* serviceFlow,
-                                       enum ServiceFlow::SchedulingType schedulingType,
+                                       ServiceFlow::SchedulingType schedulingType,
                                        OfdmUlMapIe& ulMapIe,
                                        const WimaxPhy::ModulationType modulationType,
                                        uint32_t& symbolsToAllocation,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-uplink-scheduler-rtps.cc ns-3.39/src/wimax/model/bs-uplink-scheduler-rtps.cc
--- ns-3.38/src/wimax/model/bs-uplink-scheduler-rtps.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-uplink-scheduler-rtps.cc	2023-07-05 14:23:48.133428300 -0700
@@ -361,7 +361,7 @@
 
 void
 UplinkSchedulerRtps::ServiceUnsolicitedGrants(const SSRecord* ssRecord,
-                                              enum ServiceFlow::SchedulingType schedulingType,
+                                              ServiceFlow::SchedulingType schedulingType,
                                               OfdmUlMapIe& ulMapIe,
                                               const WimaxPhy::ModulationType modulationType,
                                               uint32_t& symbolsToAllocation,
@@ -450,7 +450,7 @@
 
 void
 UplinkSchedulerRtps::ServiceBandwidthRequests(const SSRecord* ssRecord,
-                                              enum ServiceFlow::SchedulingType schedulingType,
+                                              ServiceFlow::SchedulingType schedulingType,
                                               OfdmUlMapIe& ulMapIe,
                                               const WimaxPhy::ModulationType modulationType,
                                               uint32_t& symbolsToAllocation,
@@ -476,7 +476,7 @@
 
 bool
 UplinkSchedulerRtps::ServiceBandwidthRequests(ServiceFlow* serviceFlow,
-                                              enum ServiceFlow::SchedulingType schedulingType,
+                                              ServiceFlow::SchedulingType schedulingType,
                                               OfdmUlMapIe& ulMapIe,
                                               const WimaxPhy::ModulationType modulationType,
                                               uint32_t& symbolsToAllocation,
@@ -632,7 +632,7 @@
         NS_LOG_INFO("\t\tUpdateGrantedBandwidth for " << i << " = " << allocSizeBytes);
         if (record_[i]->GetRequestedBandwidth() < allocSizeBytes)
         {
-            // the flow need new poll to set the newer requredBandwidth
+            // the flow need new poll to set the newer requiredBandwidth
             record_[i]->SetGrantedBandwidth(0);
             record_[i]->SetRequestedBandwidth(0);
         }
@@ -697,7 +697,7 @@
     switch (serviceFlow->GetSchedulingType())
     {
     case ServiceFlow::SF_TYPE_UGS: {
-        if (serviceFlow->GetIsMulticast() == true)
+        if (serviceFlow->GetIsMulticast())
         {
             modulation = serviceFlow->GetModulation();
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-uplink-scheduler-rtps.h ns-3.39/src/wimax/model/bs-uplink-scheduler-rtps.h
--- ns-3.38/src/wimax/model/bs-uplink-scheduler-rtps.h	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-uplink-scheduler-rtps.h	2023-07-05 14:23:48.133428300 -0700
@@ -118,7 +118,7 @@
      * \param availableSymbols the available symbols
      */
     void ServiceUnsolicitedGrants(const SSRecord* ssRecord,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
@@ -133,7 +133,7 @@
      * \param availableSymbols the available symbols
      */
     void ServiceBandwidthRequests(const SSRecord* ssRecord,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
@@ -149,7 +149,7 @@
      * \returns true if successful
      */
     bool ServiceBandwidthRequests(ServiceFlow* serviceFlow,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-uplink-scheduler-simple.cc ns-3.39/src/wimax/model/bs-uplink-scheduler-simple.cc
--- ns-3.38/src/wimax/model/bs-uplink-scheduler-simple.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-uplink-scheduler-simple.cc	2023-07-05 14:23:48.133428300 -0700
@@ -340,7 +340,7 @@
 
 void
 UplinkSchedulerSimple::ServiceUnsolicitedGrants(const SSRecord* ssRecord,
-                                                enum ServiceFlow::SchedulingType schedulingType,
+                                                ServiceFlow::SchedulingType schedulingType,
                                                 OfdmUlMapIe& ulMapIe,
                                                 const WimaxPhy::ModulationType modulationType,
                                                 uint32_t& symbolsToAllocation,
@@ -415,7 +415,7 @@
 
 void
 UplinkSchedulerSimple::ServiceBandwidthRequests(const SSRecord* ssRecord,
-                                                enum ServiceFlow::SchedulingType schedulingType,
+                                                ServiceFlow::SchedulingType schedulingType,
                                                 OfdmUlMapIe& ulMapIe,
                                                 const WimaxPhy::ModulationType modulationType,
                                                 uint32_t& symbolsToAllocation,
@@ -441,7 +441,7 @@
 
 bool
 UplinkSchedulerSimple::ServiceBandwidthRequests(ServiceFlow* serviceFlow,
-                                                enum ServiceFlow::SchedulingType schedulingType,
+                                                ServiceFlow::SchedulingType schedulingType,
                                                 OfdmUlMapIe& ulMapIe,
                                                 const WimaxPhy::ModulationType modulationType,
                                                 uint32_t& symbolsToAllocation,
@@ -542,7 +542,7 @@
     switch (serviceFlow->GetSchedulingType())
     {
     case ServiceFlow::SF_TYPE_UGS: {
-        if (serviceFlow->GetIsMulticast() == true)
+        if (serviceFlow->GetIsMulticast())
         {
             modulation = serviceFlow->GetModulation();
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/bs-uplink-scheduler-simple.h ns-3.39/src/wimax/model/bs-uplink-scheduler-simple.h
--- ns-3.38/src/wimax/model/bs-uplink-scheduler-simple.h	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/bs-uplink-scheduler-simple.h	2023-07-05 14:23:48.133428300 -0700
@@ -91,7 +91,7 @@
      * \param availableSymbols the available symbols
      */
     void ServiceUnsolicitedGrants(const SSRecord* ssRecord,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
@@ -106,7 +106,7 @@
      * \param availableSymbols the available symbols
      */
     void ServiceBandwidthRequests(const SSRecord* ssRecord,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
@@ -122,7 +122,7 @@
      * \returns true if successful
      */
     bool ServiceBandwidthRequests(ServiceFlow* serviceFlow,
-                                  enum ServiceFlow::SchedulingType schedulingType,
+                                  ServiceFlow::SchedulingType schedulingType,
                                   OfdmUlMapIe& ulMapIe,
                                   const WimaxPhy::ModulationType modulationType,
                                   uint32_t& symbolsToAllocation,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/cid-factory.cc ns-3.39/src/wimax/model/cid-factory.cc
--- ns-3.38/src/wimax/model/cid-factory.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/cid-factory.cc	2023-07-05 14:23:48.133428300 -0700
@@ -71,7 +71,7 @@
 }
 
 Cid
-CidFactory::Allocate(enum Cid::Type type)
+CidFactory::Allocate(Cid::Type type)
 {
     switch (type)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/cid-factory.h ns-3.39/src/wimax/model/cid-factory.h
--- ns-3.38/src/wimax/model/cid-factory.h	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/cid-factory.h	2023-07-05 14:23:48.133428300 -0700
@@ -35,7 +35,7 @@
  * This class uses the definitions from Table 345 in ieee-802.16-2004 to
  * allocate cids in different integer ranges according to their type.
  * The relative allocation of the ranges is controlled by the parameter 'm'
- * introduced in the above-mentionned table.
+ * introduced in the above-mentioned table.
  *
  * Note: The current implementation merely allocates cids sequentially from
  * the start of the allowed range. A proper implementation would be able
@@ -74,7 +74,7 @@
      * \param type CID type
      * \returns the next CID.
      */
-    Cid Allocate(enum Cid::Type type);
+    Cid Allocate(Cid::Type type);
 
     /**
      * \brief This function determines if the CID is a transport.
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/cs-parameters.cc ns-3.39/src/wimax/model/cs-parameters.cc
--- ns-3.38/src/wimax/model/cs-parameters.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/cs-parameters.cc	2023-07-05 14:23:48.133428300 -0700
@@ -43,7 +43,7 @@
         {
         case CsParamVectorTlvValue::Classifier_DSC_Action: {
             m_classifierDscAction =
-                (enum CsParameters::Action)((U8TlvValue*)((*iter)->PeekValue()))->GetValue();
+                (CsParameters::Action)((U8TlvValue*)((*iter)->PeekValue()))->GetValue();
             break;
         }
         case CsParamVectorTlvValue::Packet_Classification_Rule: {
@@ -54,7 +54,7 @@
     }
 }
 
-CsParameters::CsParameters(enum CsParameters::Action classifierDscAction,
+CsParameters::CsParameters(CsParameters::Action classifierDscAction,
                            IpcsClassifierRecord classifier)
 {
     m_classifierDscAction = classifierDscAction;
@@ -62,7 +62,7 @@
 }
 
 void
-CsParameters::SetClassifierDscAction(enum CsParameters::Action action)
+CsParameters::SetClassifierDscAction(CsParameters::Action action)
 {
     m_classifierDscAction = action;
 }
@@ -73,7 +73,7 @@
     m_packetClassifierRule = packetClassifierRule;
 }
 
-enum CsParameters::Action
+CsParameters::Action
 CsParameters::GetClassifierDscAction() const
 {
     return m_classifierDscAction;
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/dl-mac-messages.cc ns-3.39/src/wimax/model/dl-mac-messages.cc
--- ns-3.38/src/wimax/model/dl-mac-messages.cc	2023-03-16 20:54:23.876869400 -0700
+++ ns-3.39/src/wimax/model/dl-mac-messages.cc	2023-07-05 14:23:48.133428300 -0700
@@ -407,11 +407,8 @@
 {
     uint32_t dlBurstProfilesSize = 0;
 
-    for (std::vector<OfdmDlBurstProfile>::const_iterator iter = m_dlBurstProfiles.begin();
-         iter != m_dlBurstProfiles.end();
-         ++iter)
+    for (const auto& burstProfile : m_dlBurstProfiles)
     {
-        OfdmDlBurstProfile burstProfile = *iter;
         dlBurstProfilesSize += burstProfile.GetSize();
     }
 
@@ -426,11 +423,8 @@
     i.WriteU8(m_configurationChangeCount);
     i = m_channelEncodings.Write(i);
 
-    for (std::vector<OfdmDlBurstProfile>::const_iterator iter = m_dlBurstProfiles.begin();
-         iter != m_dlBurstProfiles.end();
-         ++iter)
+    for (const auto& burstProfile : m_dlBurstProfiles)
     {
-        OfdmDlBurstProfile burstProfile = *iter;
         i = burstProfile.Write(i);
     }
 }
@@ -625,11 +619,8 @@
 {
     uint32_t dlMapElementsSize = 0;
 
-    for (std::list<OfdmDlMapIe>::const_iterator iter = m_dlMapElements.begin();
-         iter != m_dlMapElements.end();
-         ++iter)
+    for (const auto& dlMapIe : m_dlMapElements)
     {
-        OfdmDlMapIe dlMapIe = *iter;
         dlMapElementsSize += dlMapIe.GetSize();
     }
 
@@ -643,11 +634,8 @@
     i.WriteU8(m_dcdCount);
     WriteTo(i, m_baseStationId);
 
-    for (std::list<OfdmDlMapIe>::const_iterator iter = m_dlMapElements.begin();
-         iter != m_dlMapElements.end();
-         ++iter)
+    for (const auto& dlMapIe : m_dlMapElements)
     {
-        OfdmDlMapIe dlMapIe = *iter;
         i = dlMapIe.Write(i);
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ofdm-downlink-frame-prefix.cc ns-3.39/src/wimax/model/ofdm-downlink-frame-prefix.cc
--- ns-3.38/src/wimax/model/ofdm-downlink-frame-prefix.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ofdm-downlink-frame-prefix.cc	2023-07-05 14:23:48.137428500 -0700
@@ -232,11 +232,8 @@
 {
     int dlFramePrefixElementsSize = 0;
 
-    for (std::vector<DlFramePrefixIe>::const_iterator iter = m_dlFramePrefixElements.begin();
-         iter != m_dlFramePrefixElements.end();
-         iter++)
+    for (const auto& dlFramePrefixElement : m_dlFramePrefixElements)
     {
-        DlFramePrefixIe dlFramePrefixElement = *iter;
         dlFramePrefixElementsSize += dlFramePrefixElement.GetSize();
     }
 
@@ -251,11 +248,8 @@
     i.WriteU32(m_frameNumber);
     i.WriteU8(m_configurationChangeCount);
 
-    for (std::vector<DlFramePrefixIe>::const_iterator iter = m_dlFramePrefixElements.begin();
-         iter != m_dlFramePrefixElements.end();
-         iter++)
+    for (const auto& dlFramePrefixElement : m_dlFramePrefixElements)
     {
-        DlFramePrefixIe dlFramePrefixElement = *iter;
         i = dlFramePrefixElement.Write(i);
     }
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/service-flow.cc ns-3.39/src/wimax/model/service-flow.cc
--- ns-3.38/src/wimax/model/service-flow.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/service-flow.cc	2023-07-05 14:23:48.137428500 -0700
@@ -151,7 +151,7 @@
         }
         case SfVectorTlvValue::CS_Specification: {
             m_csSpecification =
-                (enum CsSpecification)(((U8TlvValue*)((*iter)->PeekValue()))->GetValue());
+                (CsSpecification)(((U8TlvValue*)((*iter)->PeekValue()))->GetValue());
             break;
         }
 
@@ -246,7 +246,7 @@
     return m_connection->GetQueue();
 }
 
-enum ServiceFlow::SchedulingType
+ServiceFlow::SchedulingType
 ServiceFlow::GetSchedulingType() const
 {
     return m_schedulingType;
@@ -362,7 +362,7 @@
     return m_minTolerableTrafficRate;
 }
 
-enum ServiceFlow::SchedulingType
+ServiceFlow::SchedulingType
 ServiceFlow::GetServiceSchedulingType() const
 {
     return m_schedulingType;
@@ -458,7 +458,7 @@
     return m_arqBlockSize;
 }
 
-enum ServiceFlow::CsSpecification
+ServiceFlow::CsSpecification
 ServiceFlow::GetCsSpecification() const
 {
     return m_csSpecification;
@@ -488,7 +488,7 @@
     return m_isMulticast;
 }
 
-enum WimaxPhy::ModulationType
+WimaxPhy::ModulationType
 ServiceFlow::GetModulation() const
 {
     return m_modulationType;
@@ -545,7 +545,7 @@
 }
 
 void
-ServiceFlow::SetServiceSchedulingType(enum ServiceFlow::SchedulingType schedType)
+ServiceFlow::SetServiceSchedulingType(ServiceFlow::SchedulingType schedType)
 {
     m_schedulingType = schedType;
 }
@@ -641,7 +641,7 @@
 }
 
 void
-ServiceFlow::SetCsSpecification(enum ServiceFlow::CsSpecification spec)
+ServiceFlow::SetCsSpecification(ServiceFlow::CsSpecification spec)
 {
     m_csSpecification = spec;
 }
@@ -671,7 +671,7 @@
 }
 
 void
-ServiceFlow::SetModulation(enum WimaxPhy::ModulationType modulationType)
+ServiceFlow::SetModulation(WimaxPhy::ModulationType modulationType)
 {
     m_modulationType = modulationType;
 }
@@ -827,16 +827,12 @@
     {
     case SF_TYPE_UGS:
         return (char*)"UGS";
-        break;
     case SF_TYPE_RTPS:
         return (char*)"rtPS";
-        break;
     case SF_TYPE_NRTPS:
         return (char*)"nrtPS";
-        break;
     case SF_TYPE_BE:
         return (char*)"BE";
-        break;
     default:
         NS_FATAL_ERROR("Invalid scheduling type");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/service-flow.h ns-3.39/src/wimax/model/service-flow.h
--- ns-3.38/src/wimax/model/service-flow.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/service-flow.h	2023-07-05 14:23:48.137428500 -0700
@@ -229,7 +229,7 @@
      * Get scheduling type
      * \returns the scheduling type
      */
-    enum ServiceFlow::SchedulingType GetSchedulingType() const;
+    ServiceFlow::SchedulingType GetSchedulingType() const;
     /**
      * Check if packets are present
      * \returns true if there are packets
@@ -307,7 +307,7 @@
      * Get service scheduling type
      * \returns the scheduling type
      */
-    enum ServiceFlow::SchedulingType GetServiceSchedulingType() const;
+    ServiceFlow::SchedulingType GetServiceSchedulingType() const;
     /**
      * Get request transmission policy
      * \returns the request transmission policy
@@ -412,7 +412,7 @@
      * Get modulation
      * \returns the modulation
      */
-    enum WimaxPhy::ModulationType GetModulation() const;
+    WimaxPhy::ModulationType GetModulation() const;
 
     /**
      * Set SFID
@@ -458,7 +458,7 @@
      * Set service scheduling type
      * \param schedType the service scheduling type
      */
-    void SetServiceSchedulingType(enum ServiceFlow::SchedulingType schedType);
+    void SetServiceSchedulingType(ServiceFlow::SchedulingType schedType);
     /**
      * Set request transmission policy
      * \param policy the request transmission policy
@@ -564,43 +564,43 @@
      * Set modulation
      * \param modulationType the modulation type
      */
-    void SetModulation(enum WimaxPhy::ModulationType modulationType);
+    void SetModulation(WimaxPhy::ModulationType modulationType);
 
   private:
-    uint32_t m_sfid;                                   ///< SFID
-    std::string m_serviceClassName;                    ///< service class name
-    uint8_t m_qosParamSetType;                         ///< QOS parameter type
-    uint8_t m_trafficPriority;                         ///< traffic priority
-    uint32_t m_maxSustainedTrafficRate;                ///< maximum sustained traffic rate
-    uint32_t m_maxTrafficBurst;                        ///< maximum traffic burst
-    uint32_t m_minReservedTrafficRate;                 ///< minimum reserved traffic rate
-    uint32_t m_minTolerableTrafficRate;                ///< minimum tolerable traffic rate
-    enum ServiceFlow::SchedulingType m_schedulingType; ///< scheduling type
-    uint32_t m_requestTransmissionPolicy;              ///< request transmission policy
-    uint32_t m_toleratedJitter;                        ///< tolerated jitter
-    uint32_t m_maximumLatency;                         ///< maximum latency
-    uint8_t m_fixedversusVariableSduIndicator;         ///< fixed versus variable SDI indicator
-    uint8_t m_sduSize;                                 ///< SDU size
-    uint16_t m_targetSAID;                             ///< target SAID
-    uint8_t m_arqEnable;                               ///< ARQ enable
-    uint16_t m_arqWindowSize;                          ///< ARQ window size
-    uint16_t m_arqRetryTimeoutTx;                      ///< ARQ retry timeout transmit
-    uint16_t m_arqRetryTimeoutRx;                      ///< ARQ retry timeout receive
-    uint16_t m_arqBlockLifeTime;                       ///< ARQ block life time
-    uint16_t m_arqSyncLoss;                            ///< ARQ sync loss
-    uint8_t m_arqDeliverInOrder;                       ///< ARQ deliver in order
-    uint16_t m_arqPurgeTimeout;                        ///< ARQ purge timeout
-    uint16_t m_arqBlockSize;                           ///< ARQ block size
-    CsSpecification m_csSpecification;                 ///< CS specification
-    CsParameters m_convergenceSublayerParam;           ///< convergence sublayer parameters
-    uint16_t m_unsolicitedGrantInterval;               ///< unsolicited grant interval
-    uint16_t m_unsolicitedPollingInterval;             ///< unsolicited polling interval
-    Direction m_direction;                             ///< direction
-    Type m_type;                                       ///< type
-    Ptr<WimaxConnection> m_connection;                 ///< connection
-    bool m_isEnabled;                                  ///< is enabled?
-    bool m_isMulticast;                                ///< is multicast?
-    enum WimaxPhy::ModulationType m_modulationType;    ///< modulation type
+    uint32_t m_sfid;                              ///< SFID
+    std::string m_serviceClassName;               ///< service class name
+    uint8_t m_qosParamSetType;                    ///< QOS parameter type
+    uint8_t m_trafficPriority;                    ///< traffic priority
+    uint32_t m_maxSustainedTrafficRate;           ///< maximum sustained traffic rate
+    uint32_t m_maxTrafficBurst;                   ///< maximum traffic burst
+    uint32_t m_minReservedTrafficRate;            ///< minimum reserved traffic rate
+    uint32_t m_minTolerableTrafficRate;           ///< minimum tolerable traffic rate
+    ServiceFlow::SchedulingType m_schedulingType; ///< scheduling type
+    uint32_t m_requestTransmissionPolicy;         ///< request transmission policy
+    uint32_t m_toleratedJitter;                   ///< tolerated jitter
+    uint32_t m_maximumLatency;                    ///< maximum latency
+    uint8_t m_fixedversusVariableSduIndicator;    ///< fixed versus variable SDI indicator
+    uint8_t m_sduSize;                            ///< SDU size
+    uint16_t m_targetSAID;                        ///< target SAID
+    uint8_t m_arqEnable;                          ///< ARQ enable
+    uint16_t m_arqWindowSize;                     ///< ARQ window size
+    uint16_t m_arqRetryTimeoutTx;                 ///< ARQ retry timeout transmit
+    uint16_t m_arqRetryTimeoutRx;                 ///< ARQ retry timeout receive
+    uint16_t m_arqBlockLifeTime;                  ///< ARQ block life time
+    uint16_t m_arqSyncLoss;                       ///< ARQ sync loss
+    uint8_t m_arqDeliverInOrder;                  ///< ARQ deliver in order
+    uint16_t m_arqPurgeTimeout;                   ///< ARQ purge timeout
+    uint16_t m_arqBlockSize;                      ///< ARQ block size
+    CsSpecification m_csSpecification;            ///< CS specification
+    CsParameters m_convergenceSublayerParam;      ///< convergence sublayer parameters
+    uint16_t m_unsolicitedGrantInterval;          ///< unsolicited grant interval
+    uint16_t m_unsolicitedPollingInterval;        ///< unsolicited polling interval
+    Direction m_direction;                        ///< direction
+    Type m_type;                                  ///< type
+    Ptr<WimaxConnection> m_connection;            ///< connection
+    bool m_isEnabled;                             ///< is enabled?
+    bool m_isMulticast;                           ///< is multicast?
+    WimaxPhy::ModulationType m_modulationType;    ///< modulation type
     // will be used by the BS
     ServiceFlowRecord* m_record; ///< service flow record
 };
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/service-flow-manager.cc ns-3.39/src/wimax/model/service-flow-manager.cc
--- ns-3.38/src/wimax/model/service-flow-manager.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/service-flow-manager.cc	2023-07-05 14:23:48.137428500 -0700
@@ -142,7 +142,7 @@
 }
 
 std::vector<ServiceFlow*>
-ServiceFlowManager::GetServiceFlows(enum ServiceFlow::SchedulingType schedulingType) const
+ServiceFlowManager::GetServiceFlows(ServiceFlow::SchedulingType schedulingType) const
 {
     std::vector<ServiceFlow*> tmpServiceFlows;
     for (std::vector<ServiceFlow*>::iterator iter = m_serviceFlows->begin();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/service-flow-manager.h ns-3.39/src/wimax/model/service-flow-manager.h
--- ns-3.38/src/wimax/model/service-flow-manager.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/service-flow-manager.h	2023-07-05 14:23:48.137428500 -0700
@@ -82,10 +82,9 @@
     /**
      * Get service flows function
      * \param schedulingType the scheduling type
-     * \returns vector of pointers to service flows corresponding to the schedulin type
+     * \returns vector of pointers to service flows corresponding to the scheduling type
      */
-    std::vector<ServiceFlow*> GetServiceFlows(
-        enum ServiceFlow::SchedulingType schedulingType) const;
+    std::vector<ServiceFlow*> GetServiceFlows(ServiceFlow::SchedulingType schedulingType) const;
 
     /**
      * \return true if all service flows are allocated, false otherwise
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.cc ns-3.39/src/wimax/model/simple-ofdm-wimax-phy.cc
--- ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/simple-ofdm-wimax-phy.cc	2023-07-05 14:23:48.137428500 -0700
@@ -180,7 +180,7 @@
     m_txPower = 30;         // dBm
     SetBandwidth(10000000); // 10Mhz
     m_nbErroneousBlock = 0;
-    m_nrRecivedFecBlocks = 0;
+    m_nrReceivedFecBlocks = 0;
     m_snrToBlockErrorRateManager = new SNRToBlockErrorRateManager();
 }
 
@@ -319,14 +319,7 @@
         dynamic_cast<SimpleOfdmWimaxChannel*>(PeekPointer(GetChannel()));
     NS_ASSERT(channel != nullptr);
 
-    if (m_nrRemainingBlocksToSend == 1)
-    {
-        isLastFecBlock = true;
-    }
-    else
-    {
-        isLastFecBlock = false;
-    }
+    isLastFecBlock = (m_nrRemainingBlocksToSend == 1);
     channel->Send(m_blockTime,
                   m_currentBurstSize,
                   this,
@@ -412,7 +405,7 @@
     delete record;
 
     NS_LOG_INFO("PHY: Receive rxPower=" << rxPower << ", Nwb=" << Nwb << ", SNR=" << SNR
-                                        << ", Modulation=" << modulationType << ", BlocErrorRate="
+                                        << ", Modulation=" << modulationType << ", BlockErrorRate="
                                         << blockErrorRate << ", drop=" << std::boolalpha << drop);
 
     switch (GetState())
@@ -433,7 +426,7 @@
             {
                 NotifyRxBegin(burst);
                 m_receivedFecBlocks->clear();
-                m_nrRecivedFecBlocks = 0;
+                m_nrReceivedFecBlocks = 0;
                 SetBlockParameters(burstSize, modulationType);
                 m_blockTime = GetBlockTransmissionTime(modulationType);
             }
@@ -469,14 +462,14 @@
                                        Ptr<PacketBurst> burst)
 {
     SetState(PHY_STATE_IDLE);
-    m_nrRecivedFecBlocks++;
+    m_nrReceivedFecBlocks++;
 
-    if (drop == true)
+    if (drop)
     {
         m_nbErroneousBlock++;
     }
 
-    if ((uint32_t)m_nrRecivedFecBlocks * m_blockSize == burstSize * 8 + m_paddingBits)
+    if ((uint32_t)m_nrReceivedFecBlocks * m_blockSize == burstSize * 8 + m_paddingBits)
     {
         NotifyRxEnd(burst);
         if (m_nbErroneousBlock == 0)
@@ -488,7 +481,7 @@
             NotifyRxDrop(burst);
         }
         m_nbErroneousBlock = 0;
-        m_nrRecivedFecBlocks = 0;
+        m_nrReceivedFecBlocks = 0;
     }
 }
 
@@ -554,7 +547,7 @@
         for (int l = 0; l < 8; l++)
         {
             bool bin = buffer.at(i + l);
-            temp += (uint8_t)(bin * std::pow(2.0, (7 - l)));
+            temp |= (bin << (7 - l));
         }
 
         *(pstart + j) = temp;
@@ -619,8 +612,8 @@
     uint32_t i = 0;
     for (uint32_t j = 0; j < m_nrBlocks; j++)
     {
-        Bvec tmpRecFecBloc = m_receivedFecBlocks->front();
-        buffer.insert(buffer.begin() + i, tmpRecFecBloc.begin(), tmpRecFecBloc.end());
+        Bvec tmpRecFecBlock = m_receivedFecBlocks->front();
+        buffer.insert(buffer.begin() + i, tmpRecFecBlock.begin(), tmpRecFecBlock.end());
         m_receivedFecBlocks->pop_front();
         i += m_blockSize;
     }
@@ -697,25 +690,18 @@
     {
     case MODULATION_TYPE_BPSK_12:
         return m_dataRateBpsk12;
-        break;
     case MODULATION_TYPE_QPSK_12:
         return m_dataRateQpsk12;
-        break;
     case MODULATION_TYPE_QPSK_34:
         return m_dataRateQpsk34;
-        break;
     case MODULATION_TYPE_QAM16_12:
         return m_dataRateQam16_12;
-        break;
     case MODULATION_TYPE_QAM16_34:
         return m_dataRateQam16_34;
-        break;
     case MODULATION_TYPE_QAM64_23:
         return m_dataRateQam64_23;
-        break;
     case MODULATION_TYPE_QAM64_34:
         return m_dataRateQam64_34;
-        break;
     }
     NS_FATAL_ERROR("Invalid modulation type");
     return 0;
@@ -900,25 +886,18 @@
     {
     case FRAME_DURATION_2_POINT_5_MS:
         return Seconds(2.5);
-        break;
     case FRAME_DURATION_4_MS:
         return Seconds(4);
-        break;
     case FRAME_DURATION_5_MS:
         return Seconds(5);
-        break;
     case FRAME_DURATION_8_MS:
         return Seconds(8);
-        break;
     case FRAME_DURATION_10_MS:
         return Seconds(10);
-        break;
     case FRAME_DURATION_12_POINT_5_MS:
         return Seconds(12.5);
-        break;
     case FRAME_DURATION_20_MS:
         return Seconds(20);
-        break;
     default:
         NS_FATAL_ERROR("Invalid modulation type");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.h ns-3.39/src/wimax/model/simple-ofdm-wimax-phy.h
--- ns-3.38/src/wimax/model/simple-ofdm-wimax-phy.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/simple-ofdm-wimax-phy.h	2023-07-05 14:23:48.137428500 -0700
@@ -467,7 +467,7 @@
     uint16_t m_blockSize;               ///< block size
     uint32_t m_paddingBits;             ///< padding bits
     uint16_t m_nbErroneousBlock;        ///< erroneous blocks
-    uint16_t m_nrRecivedFecBlocks;      ///< number received FEC blocks
+    uint16_t m_nrReceivedFecBlocks;     ///< number received FEC blocks
     uint16_t m_nfft;                    ///< NFFT
     double m_g;                         ///< G value
     double m_bandWidth;                 ///< bandwidth
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/snr-to-block-error-rate-manager.cc ns-3.39/src/wimax/model/snr-to-block-error-rate-manager.cc
--- ns-3.38/src/wimax/model/snr-to-block-error-rate-manager.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/snr-to-block-error-rate-manager.cc	2023-07-05 14:23:48.137428500 -0700
@@ -98,7 +98,7 @@
         traceFilePath << m_traceFilePath << "/modulation" << i << ".txt";
 
         traceFile.open(traceFilePath.str(), std::ifstream::in);
-        if (traceFile.good() == false)
+        if (!traceFile.good())
         {
             NS_LOG_INFO("Unable to load " << traceFilePath.str() << "!! Loading default traces...");
             LoadDefaultTraces();
@@ -237,7 +237,7 @@
         traceFilePath << m_traceFilePath << "/Modulation" << i << ".txt";
 
         traceFile.open(traceFilePath.str(), std::ifstream::in);
-        if (traceFile.good() == false)
+        if (!traceFile.good())
         {
             NS_LOG_INFO("Unable to load " << traceFilePath.str() << "!!Loading default traces...");
             LoadDefaultTraces();
@@ -275,7 +275,7 @@
 double
 SNRToBlockErrorRateManager::GetBlockErrorRate(double SNR, uint8_t modulation)
 {
-    if (m_activateLoss == false)
+    if (!m_activateLoss)
     {
         return 0;
     }
@@ -312,7 +312,7 @@
 SNRToBlockErrorRateRecord*
 SNRToBlockErrorRateManager::GetSNRToBlockErrorRateRecord(double SNR, uint8_t modulation)
 {
-    if (m_activateLoss == false)
+    if (!m_activateLoss)
     {
         return new SNRToBlockErrorRateRecord(SNR, 0, 0, 0, 0, 0);
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ss-link-manager.cc ns-3.39/src/wimax/model/ss-link-manager.cc
--- ns-3.38/src/wimax/model/ss-link-manager.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ss-link-manager.cc	2023-07-05 14:23:48.137428500 -0700
@@ -284,7 +284,7 @@
 void
 SSLinkManager::PerformBackoff()
 {
-    Time defferTime = Seconds(0);
+    Time deferTime = Seconds(0);
     Time timeToAllocation = Seconds(0);
     uint16_t nrPsPerRangOpp = m_ss->GetCurrentUcd().GetChannelEncodings().GetRangReqOppSize();
     uint16_t oppSize = m_ss->GetCurrentUcd().GetChannelEncodings().GetRangReqOppSize() /
@@ -294,9 +294,9 @@
     {
         if (m_rangingBO == 0)
         {
-            defferTime =
+            deferTime =
                 Seconds(deferTOs * nrPsPerRangOpp * m_ss->GetPhy()->GetPsDuration().GetSeconds());
-            timeToAllocation = m_ss->GetTimeToAllocation(defferTime);
+            timeToAllocation = m_ss->GetTimeToAllocation(deferTime);
 
             Simulator::Schedule(timeToAllocation,
                                 &SSLinkManager::SendRangingRequest,
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ss-net-device.cc ns-3.39/src/wimax/model/ss-net-device.cc
--- ns-3.38/src/wimax/model/ss-net-device.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ss-net-device.cc	2023-07-05 14:23:48.137428500 -0700
@@ -672,7 +672,7 @@
     else
     {
         NS_LOG_DEBUG("SS (" << GetMacAddress() << ")");
-        NS_LOG_INFO("\tCan't send packet! (NotRegitered with the network)");
+        NS_LOG_INFO("\tCan't send packet! (NotRegistered with the network)");
         return false;
     }
 
@@ -742,7 +742,7 @@
         if (connection->GetSchedulingType() == ServiceFlow::SF_TYPE_UGS && m_scheduler->GetPollMe())
         {
             NS_ASSERT_MSG(hdrType.GetType() != MacHeaderType::HEADER_TYPE_BANDWIDTH,
-                          "Error while equeuing  packet: incorrect header type");
+                          "Error while enqueuing  packet: incorrect header type");
 
             GrantManagementSubheader grantMgmntSubhdr;
             grantMgmntSubhdr.SetPm(true);
@@ -819,7 +819,7 @@
 
     if (gnrcMacHdr.GetHt() == MacHeaderType::HEADER_TYPE_GENERIC)
     {
-        if (gnrcMacHdr.check_hcs() == false)
+        if (!gnrcMacHdr.check_hcs())
         {
             // The header is noisy
             NS_LOG_INFO("Header HCS ERROR");
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ss-record.cc ns-3.39/src/wimax/model/ss-record.cc
--- ns-3.38/src/wimax/model/ss-record.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ss-record.cc	2023-07-05 14:23:48.137428500 -0700
@@ -230,7 +230,7 @@
 }
 
 std::vector<ServiceFlow*>
-SSRecord::GetServiceFlows(enum ServiceFlow::SchedulingType schedulingType) const
+SSRecord::GetServiceFlows(ServiceFlow::SchedulingType schedulingType) const
 {
     std::vector<ServiceFlow*> tmpServiceFlows;
     for (std::vector<ServiceFlow*>::iterator iter = m_serviceFlows->begin();
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ss-record.h ns-3.39/src/wimax/model/ss-record.h
--- ns-3.38/src/wimax/model/ss-record.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ss-record.h	2023-07-05 14:23:48.137428500 -0700
@@ -171,8 +171,7 @@
      * \param schedulingType the scheduling type
      * \returns the service flow
      */
-    std::vector<ServiceFlow*> GetServiceFlows(
-        enum ServiceFlow::SchedulingType schedulingType) const;
+    std::vector<ServiceFlow*> GetServiceFlows(ServiceFlow::SchedulingType schedulingType) const;
     /**
      * Check if at least one flow has scheduling type SF_TYPE_UGS
      * \return true if at least one flow has scheduling type SF_TYPE_UGS
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ul-job.cc ns-3.39/src/wimax/model/ul-job.cc
--- ns-3.38/src/wimax/model/ul-job.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ul-job.cc	2023-07-05 14:23:48.137428500 -0700
@@ -36,7 +36,7 @@
 }
 
 SSRecord*
-UlJob::GetSsRecord()
+UlJob::GetSsRecord() const
 {
     return m_ssRecord;
 }
@@ -47,8 +47,8 @@
     m_ssRecord = ssRecord;
 }
 
-enum ServiceFlow::SchedulingType
-UlJob::GetSchedulingType()
+ServiceFlow::SchedulingType
+UlJob::GetSchedulingType() const
 {
     return m_schedulingType;
 }
@@ -60,7 +60,7 @@
 }
 
 ReqType
-UlJob::GetType()
+UlJob::GetType() const
 {
     return m_type;
 }
@@ -72,7 +72,7 @@
 }
 
 ServiceFlow*
-UlJob::GetServiceFlow()
+UlJob::GetServiceFlow() const
 {
     return m_serviceFlow;
 }
@@ -84,7 +84,7 @@
 }
 
 Time
-UlJob::GetReleaseTime()
+UlJob::GetReleaseTime() const
 {
     return m_releaseTime;
 }
@@ -96,7 +96,7 @@
 }
 
 Time
-UlJob::GetPeriod()
+UlJob::GetPeriod() const
 {
     return m_period;
 }
@@ -108,7 +108,7 @@
 }
 
 Time
-UlJob::GetDeadline()
+UlJob::GetDeadline() const
 {
     return m_deadline;
 }
@@ -140,14 +140,7 @@
 bool
 operator==(const UlJob& a, const UlJob& b)
 {
-    UlJob A = a;
-    UlJob B = b;
-
-    if ((A.GetServiceFlow() == B.GetServiceFlow()) && (A.GetSsRecord() == B.GetSsRecord()))
-    {
-        return true;
-    }
-    return false;
+    return a.GetServiceFlow() == b.GetServiceFlow() && a.GetSsRecord() == b.GetSsRecord();
 }
 
 PriorityUlJob::PriorityUlJob()
@@ -167,7 +160,7 @@
 }
 
 Ptr<UlJob>
-PriorityUlJob::GetUlJob()
+PriorityUlJob::GetUlJob() const
 {
     return m_job;
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ul-job.h ns-3.39/src/wimax/model/ul-job.h
--- ns-3.38/src/wimax/model/ul-job.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ul-job.h	2023-07-05 14:23:48.137428500 -0700
@@ -63,7 +63,7 @@
      * Get SS record
      * \returns the SS record
      */
-    SSRecord* GetSsRecord();
+    SSRecord* GetSsRecord() const;
     /**
      * Set SS record
      * \param ssRecord the SS record
@@ -73,7 +73,7 @@
      * Get scheduling type
      * \returns the scheduling type
      */
-    enum ServiceFlow::SchedulingType GetSchedulingType();
+    ServiceFlow::SchedulingType GetSchedulingType() const;
     /**
      * Set scheduling type
      * \param schedulingType the scheduling type
@@ -83,7 +83,7 @@
      * Get service flow
      * \returns the service flow
      */
-    ServiceFlow* GetServiceFlow();
+    ServiceFlow* GetServiceFlow() const;
     /**
      * Set service flow
      * \param serviceFlow
@@ -94,7 +94,7 @@
      * Get type
      * \returns the request type
      */
-    ReqType GetType();
+    ReqType GetType() const;
     /**
      * Set type
      * \param type the type
@@ -105,7 +105,7 @@
      * Get release time
      * \returns the release time
      */
-    Time GetReleaseTime();
+    Time GetReleaseTime() const;
     /**
      * Set release time
      * \param releaseTime the release time
@@ -116,7 +116,7 @@
      * Get period
      * \returns the period time
      */
-    Time GetPeriod();
+    Time GetPeriod() const;
     /**
      * Set period
      * \param period the period
@@ -127,7 +127,7 @@
      * Get deadline
      * \returns the deadline time
      */
-    Time GetDeadline();
+    Time GetDeadline() const;
     /**
      * Set deadline
      * \param deadline the dead line
@@ -149,11 +149,11 @@
     /// equality operator
     friend bool operator==(const UlJob& a, const UlJob& b);
 
-    Time m_releaseTime; ///< The time after which the job can be processed
-    Time m_period;      ///< For periodic jobs
-    Time m_deadline;    ///< Request should be satisfied by this time
-    uint32_t m_size;    ///< Number of minislots requested
-    enum ServiceFlow::SchedulingType m_schedulingType; ///< Scheduling type of flow
+    Time m_releaseTime;                           ///< The time after which the job can be processed
+    Time m_period;                                ///< For periodic jobs
+    Time m_deadline;                              ///< Request should be satisfied by this time
+    uint32_t m_size;                              ///< Number of minislots requested
+    ServiceFlow::SchedulingType m_schedulingType; ///< Scheduling type of flow
 
     SSRecord* m_ssRecord; ///< Pointer to SSRecord
 
@@ -187,7 +187,7 @@
      * Get UL job function
      * \returns the UL job
      */
-    Ptr<UlJob> GetUlJob();
+    Ptr<UlJob> GetUlJob() const;
     /**
      * Set UL job
      * \param job the UL job
@@ -220,14 +220,7 @@
                 left.GetUlJob()->GetServiceFlow()->GetRecord()->GetBacklogged();
             int32_t rightBacklogged =
                 left.GetUlJob()->GetServiceFlow()->GetRecord()->GetBacklogged();
-            if (leftBacklogged <= rightBacklogged)
-            {
-                return true;
-            }
-            else
-            {
-                return false;
-            }
+            return leftBacklogged <= rightBacklogged;
         }
         else
         {
@@ -257,14 +250,7 @@
                 left->GetUlJob()->GetServiceFlow()->GetRecord()->GetBacklogged();
             int32_t rightBacklogged =
                 left->GetUlJob()->GetServiceFlow()->GetRecord()->GetBacklogged();
-            if (leftBacklogged <= rightBacklogged)
-            {
-                return true;
-            }
-            else
-            {
-                return false;
-            }
+            return leftBacklogged <= rightBacklogged;
         }
         else
         {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/ul-mac-messages.cc ns-3.39/src/wimax/model/ul-mac-messages.cc
--- ns-3.38/src/wimax/model/ul-mac-messages.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/ul-mac-messages.cc	2023-07-05 14:23:48.137428500 -0700
@@ -400,11 +400,8 @@
 {
     uint32_t ulBurstProfilesSize = 0;
 
-    for (std::vector<OfdmUlBurstProfile>::const_iterator iter = m_ulBurstProfiles.begin();
-         iter != m_ulBurstProfiles.end();
-         ++iter)
+    for (const auto& burstProfile : m_ulBurstProfiles)
     {
-        OfdmUlBurstProfile burstProfile = *iter;
         ulBurstProfilesSize += burstProfile.GetSize();
     }
 
@@ -422,11 +419,8 @@
     i.WriteU8(m_requestBackoffEnd);
     i = m_channelEncodings.Write(i);
 
-    for (std::vector<OfdmUlBurstProfile>::const_iterator iter = m_ulBurstProfiles.begin();
-         iter != m_ulBurstProfiles.end();
-         ++iter)
+    for (const auto& burstProfile : m_ulBurstProfiles)
     {
-        OfdmUlBurstProfile burstProfile = *iter;
         i = burstProfile.Write(i);
     }
 }
@@ -655,11 +649,9 @@
 UlMap::GetSerializedSize() const
 {
     uint32_t ulMapElementsSize = 0;
-    for (std::list<OfdmUlMapIe>::const_iterator iter = m_ulMapElements.begin();
-         iter != m_ulMapElements.end();
-         ++iter)
+
+    for (const auto& ulMapIe : m_ulMapElements)
     {
-        OfdmUlMapIe ulMapIe = *iter;
         ulMapElementsSize += ulMapIe.GetSize();
     }
 
@@ -674,11 +666,8 @@
     i.WriteU8(m_ucdCount);
     i.WriteU32(m_allocationStartTime);
 
-    for (std::list<OfdmUlMapIe>::const_iterator iter = m_ulMapElements.begin();
-         iter != m_ulMapElements.end();
-         ++iter)
+    for (const auto& ulMapIe : m_ulMapElements)
     {
-        OfdmUlMapIe ulMapIe = *iter;
         i = ulMapIe.Write(i);
     }
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-connection.cc ns-3.39/src/wimax/model/wimax-connection.cc
--- ns-3.38/src/wimax/model/wimax-connection.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-connection.cc	2023-07-05 14:23:48.137428500 -0700
@@ -67,7 +67,7 @@
     return tid;
 }
 
-WimaxConnection::WimaxConnection(Cid cid, enum Cid::Type type)
+WimaxConnection::WimaxConnection(Cid cid, Cid::Type type)
     : m_cid(cid),
       m_cidType(type),
       m_queue(CreateObject<WimaxMacQueue>(1024)),
@@ -92,7 +92,7 @@
     return m_cid;
 }
 
-enum Cid::Type
+Cid::Type
 WimaxConnection::GetType() const
 {
     return m_cidType;
@@ -161,22 +161,16 @@
     {
     case Cid::BROADCAST:
         return "Broadcast";
-        break;
     case Cid::INITIAL_RANGING:
         return "Initial Ranging";
-        break;
     case Cid::BASIC:
         return "Basic";
-        break;
     case Cid::PRIMARY:
         return "Primary";
-        break;
     case Cid::TRANSPORT:
         return "Transport";
-        break;
     case Cid::MULTICAST:
         return "Multicast";
-        break;
     default:
         NS_FATAL_ERROR("Invalid connection type");
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-connection.h ns-3.39/src/wimax/model/wimax-connection.h
--- ns-3.38/src/wimax/model/wimax-connection.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-connection.h	2023-07-05 14:23:48.137428500 -0700
@@ -56,7 +56,7 @@
      * \param cid connection ID
      * \param type CID type
      */
-    WimaxConnection(Cid cid, enum Cid::Type type);
+    WimaxConnection(Cid cid, Cid::Type type);
     ~WimaxConnection() override;
 
     /**
@@ -69,7 +69,7 @@
      * Get type function
      * \returns the type
      */
-    enum Cid::Type GetType() const;
+    Cid::Type GetType() const;
     /**
      * \return the queue of the connection
      */
@@ -152,7 +152,7 @@
     void DoDispose() override;
 
     Cid m_cid;                  ///< CID
-    enum Cid::Type m_cidType;   ///< CID type
+    Cid::Type m_cidType;        ///< CID type
     Ptr<WimaxMacQueue> m_queue; ///< queue
     ServiceFlow* m_serviceFlow; ///< service flow
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-net-device.cc ns-3.39/src/wimax/model/wimax-net-device.cc
--- ns-3.38/src/wimax/model/wimax-net-device.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-net-device.cc	2023-07-05 14:23:48.137428500 -0700
@@ -169,8 +169,6 @@
     m_connectionManager = nullptr;
     m_burstProfileManager = nullptr;
     m_bandwidthManager = nullptr;
-    m_connectionManager = nullptr;
-    m_bandwidthManager = nullptr;
 
     NetDevice::DoDispose();
 }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-net-device.h ns-3.39/src/wimax/model/wimax-net-device.h
--- ns-3.38/src/wimax/model/wimax-net-device.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-net-device.h	2023-07-05 14:23:48.137428500 -0700
@@ -64,7 +64,7 @@
  * \ingroup wimax
  *
  * This class holds together ns3::WimaxPhy, ns3::WimaxConnection,
- * ns3::ConectionManager, ns3::BurstProfileManager, and
+ * ns3::ConnectionManager, ns3::BurstProfileManager, and
  * ns3::BandwidthManager.
  */
 class WimaxNetDevice : public NetDevice
@@ -560,8 +560,6 @@
     Ptr<ConnectionManager> m_connectionManager;     ///< connection manager
     Ptr<BurstProfileManager> m_burstProfileManager; ///< burst profile manager
     Ptr<BandwidthManager> m_bandwidthManager;       ///< badnwidth manager
-
-    Ptr<Object> m_mobility; ///< modility model
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-phy.cc ns-3.39/src/wimax/model/wimax-phy.cc
--- ns-3.38/src/wimax/model/wimax-phy.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-phy.cc	2023-07-05 14:23:48.137428500 -0700
@@ -90,7 +90,6 @@
       m_psPerFrame(0),
       m_symbolsPerFrame(0)
 {
-    m_mobility = nullptr;
     m_duplex = 0;
     m_txFrequency = 0;
     m_rxFrequency = 0;
@@ -425,16 +424,4 @@
     return m_symbolsPerFrame;
 }
 
-Ptr<Object>
-WimaxPhy::GetMobility()
-{
-    return m_mobility;
-}
-
-void
-WimaxPhy::SetMobility(Ptr<Object> mobility)
-{
-    m_mobility = mobility;
-}
-
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-phy.h ns-3.39/src/wimax/model/wimax-phy.h
--- ns-3.38/src/wimax/model/wimax-phy.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-phy.h	2023-07-05 14:23:48.137428500 -0700
@@ -358,16 +358,6 @@
      */
     void SetPhyParameters();
     void DoDispose() override;
-    /**
-     * Get the mobility model of the device
-     * \return the mobility model of the device
-     */
-    virtual Ptr<Object> GetMobility();
-    /**
-     * \brief set the mobility model of the device
-     * \param mobility the mobility model to set
-     */
-    virtual void SetMobility(Ptr<Object> mobility);
 
     /**
      * Assign a fixed random variable stream number to the random variables
@@ -494,7 +484,6 @@
     uint16_t m_psPerSymbol;      ///< ps per sumbol
     uint16_t m_psPerFrame;       ///< ps per framce
     uint32_t m_symbolsPerFrame;  ///< symbols per frame
-    Ptr<Object> m_mobility;      ///< modility model
 };
 
 } // namespace ns3
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-tlv.cc ns-3.39/src/wimax/model/wimax-tlv.cc
--- ns-3.38/src/wimax/model/wimax-tlv.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-tlv.cc	2023-07-05 14:23:48.137428500 -0700
@@ -870,7 +870,7 @@
 // ==============================================================================
 PortRangeTlvValue::PortRangeTlvValue()
 {
-    m_portRange = new std::vector<struct PortRange>;
+    m_portRange = new std::vector<PortRange>;
 }
 
 PortRangeTlvValue::~PortRangeTlvValue()
@@ -888,7 +888,7 @@
 void
 PortRangeTlvValue::Serialize(Buffer::Iterator i) const
 {
-    for (std::vector<struct PortRange>::const_iterator iter = m_portRange->begin();
+    for (std::vector<PortRange>::const_iterator iter = m_portRange->begin();
          iter != m_portRange->end();
          ++iter)
     {
@@ -936,7 +936,7 @@
 PortRangeTlvValue::Copy() const
 {
     PortRangeTlvValue* tmp = new PortRangeTlvValue();
-    for (std::vector<struct PortRange>::const_iterator iter = m_portRange->begin();
+    for (std::vector<PortRange>::const_iterator iter = m_portRange->begin();
          iter != m_portRange->end();
          ++iter)
     {
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/model/wimax-tlv.h ns-3.39/src/wimax/model/wimax-tlv.h
--- ns-3.38/src/wimax/model/wimax-tlv.h	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/model/wimax-tlv.h	2023-07-05 14:23:48.137428500 -0700
@@ -489,8 +489,9 @@
         uint16_t PortLow;  ///< low
         uint16_t PortHigh; ///< high
     };
+
     /// PortRange vector iterator typedef
-    typedef std::vector<struct PortRange>::const_iterator Iterator;
+    typedef std::vector<PortRange>::const_iterator Iterator;
     PortRangeTlvValue();
     ~PortRangeTlvValue() override;
     uint32_t GetSerializedSize() const override;
@@ -519,7 +520,7 @@
     PortRangeTlvValue* Copy() const override;
 
   private:
-    std::vector<struct PortRange>* m_portRange; ///< port range
+    std::vector<PortRange>* m_portRange; ///< port range
 };
 
 // ==============================================================================
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/test/examples-to-run.py ns-3.39/src/wimax/test/examples-to-run.py
--- ns-3.38/src/wimax/test/examples-to-run.py	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/test/examples-to-run.py	2023-07-05 14:23:48.137428500 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 # A list of C++ examples to run in order to ensure that they remain
 # buildable and runnable over time.  Each tuple in the list contains
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/test/phy-test.cc ns-3.39/src/wimax/test/phy-test.cc
--- ns-3.38/src/wimax/test/phy-test.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/test/phy-test.cc	2023-07-05 14:23:48.137428500 -0700
@@ -96,7 +96,7 @@
     Simulator::Run();
     for (int i = 0; i < 3; i++)
     {
-        if (ssDevs.Get(i)->GetObject<SubscriberStationNetDevice>()->IsRegistered() == false)
+        if (!ssDevs.Get(i)->GetObject<SubscriberStationNetDevice>()->IsRegistered())
         {
             NS_LOG_DEBUG("SS[" << i << "] not registered");
             return true; // Test fail because SS[i] is not registered
@@ -113,7 +113,7 @@
     for (int i = 0; i < 7; i++)
     {
         NS_LOG_DEBUG("Frame Duration = " << frameDuratioTab[i]);
-        if (DoRunOnce(frameDuratioTab[i]) != false)
+        if (DoRunOnce(frameDuratioTab[i]))
         {
             return;
         }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/src/wimax/test/qos-test.cc ns-3.39/src/wimax/test/qos-test.cc
--- ns-3.38/src/wimax/test/qos-test.cc	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/src/wimax/test/qos-test.cc	2023-07-05 14:23:48.137428500 -0700
@@ -154,11 +154,11 @@
 void
 Ns3WimaxSchedulingTestCase::DoRun()
 {
-    if (DoRunOnce(WimaxHelper::SCHED_TYPE_SIMPLE) == true)
+    if (DoRunOnce(WimaxHelper::SCHED_TYPE_SIMPLE))
     {
         return;
     }
-    if (DoRunOnce(WimaxHelper::SCHED_TYPE_RTPS) == true)
+    if (DoRunOnce(WimaxHelper::SCHED_TYPE_RTPS))
     {
         return;
     }
@@ -284,15 +284,15 @@
 void
 Ns3WimaxSFTypeTestCase::DoRun()
 {
-    if (DoRunOnce(ServiceFlow::SF_TYPE_UGS) == true)
+    if (DoRunOnce(ServiceFlow::SF_TYPE_UGS))
     {
         return;
     }
-    if (DoRunOnce(ServiceFlow::SF_TYPE_RTPS) == true)
+    if (DoRunOnce(ServiceFlow::SF_TYPE_RTPS))
     {
         return;
     }
-    if (DoRunOnce(ServiceFlow::SF_TYPE_BE) == true)
+    if (DoRunOnce(ServiceFlow::SF_TYPE_BE))
     {
         return;
     }
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/test.py ns-3.39/test.py
--- ns-3.38/test.py	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/test.py	2023-07-05 14:23:48.137428500 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 #
 # Copyright (c) 2009 University of Washington
 #
@@ -19,7 +18,7 @@
 import os
 import sys
 import time
-import optparse
+import argparse
 import subprocess
 import threading
 import signal
@@ -623,7 +622,7 @@
                 if line.startswith(item):
                     exec(line, globals())
 
-    if options.verbose:
+    if args.verbose:
         for item in interesting_config_items:
             print("%s ==" % item, eval(item))
 
@@ -661,7 +660,7 @@
     else:
         os.environ["PYTHONPATH"] += ":" + pypath
 
-    if options.verbose:
+    if args.verbose:
         print("os.environ[\"PYTHONPATH\"] == %s" % os.environ["PYTHONPATH"])
 
     if sys.platform == "darwin":
@@ -669,28 +668,28 @@
             os.environ["DYLD_LIBRARY_PATH"] = ""
         for path in NS3_MODULE_PATH:
             os.environ["DYLD_LIBRARY_PATH"] += ":" + path
-        if options.verbose:
+        if args.verbose:
             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
-        if options.verbose:
+        if args.verbose:
             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 options.verbose:
+        if args.verbose:
             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 options.verbose:
+        if args.verbose:
             print("os.environ[\"LD_LIBRARY_PATH\"] == %s" % os.environ["LD_LIBRARY_PATH"])
 
 #
@@ -801,7 +800,7 @@
     else:
         cmd = path_cmd
 
-    if options.verbose:
+    if args.verbose:
         print("Synchronously execute %s" % cmd)
 
     start_time = time.time()
@@ -833,7 +832,7 @@
     stdout_results = decode_stream_results(stdout_results, "stdout")
     stderr_results = decode_stream_results(stderr_results, "stderr")
 
-    if options.verbose:
+    if args.verbose:
         print("Return code = ", retval)
         print("stderr = ", stderr_results)
 
@@ -1009,7 +1008,7 @@
             # if is_skip is true, returncode is undefined.
             #
             if job.is_skip:
-                if options.verbose:
+                if args.verbose:
                     print("Skip %s" % job.shell_command)
                 self.output_queue.put(job)
                 continue
@@ -1018,7 +1017,7 @@
             # Otherwise go about the business of running tests as normal.
             #
             else:
-                if options.verbose:
+                if args.verbose:
                     print("Launch %s" % job.shell_command)
 
                 if job.is_example or job.is_pyexample:
@@ -1028,24 +1027,24 @@
                     # "examples/wireless/mixed-wireless.py"
                     #
                     (job.returncode, standard_out, standard_err, et) = run_job_synchronously(job.shell_command,
-                        job.cwd, options.valgrind, job.is_pyexample, job.build_path)
+                        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
                     # to the test runner, specifically the base directory and temp
                     # file name
                     #
-                    if options.update_data:
+                    if args.update_data:
                         update_data = '--update-data'
                     else:
                         update_data = ''
                     (job.returncode, standard_out, 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, options.valgrind, False)
+                        job.cwd, args.valgrind, False)
 
                 job.set_elapsed_time(et)
 
-                if options.verbose:
+                if args.verbose:
                     print("returncode = %d" % job.returncode)
                     print("---------- begin standard out ----------")
                     print(standard_out)
@@ -1090,7 +1089,7 @@
     # our users pain while waiting for extraneous stuff to compile and link, so
     # we allow users that know what they're doing to not invoke ns3 at all.
     #
-    if not options.no_build:
+    if not args.no_build:
 
         # If the user only wants to run a single example, then we can just build
         # that example.
@@ -1098,15 +1097,15 @@
         # If there is no constraint, then we have to build everything since the
         # user wants to run everything.
         #
-        if len(options.example):
-            build_cmd = "./ns3 build %s" % os.path.basename(options.example)
+        if len(args.example):
+            build_cmd = "./ns3 build %s" % os.path.basename(args.example)
         else:
             build_cmd = "./ns3"
 
         if sys.platform == "win32":
             build_cmd = sys.executable + " " + build_cmd
 
-        if options.verbose:
+        if args.verbose:
             print("Building: %s" % build_cmd)
 
         proc = subprocess.run(build_cmd, shell=True)
@@ -1216,16 +1215,16 @@
     # up a bunch of threads and running tests.  Let's detect these cases and
     # handle them without doing all of the hard work.
     #
-    if options.kinds:
+    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)
         print(standard_out)
 
-    if options.list:
+    if args.list:
         list_items = []
         if ENABLE_TESTS:
-            if len(options.constrain):
-                path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list --print-test-types --test-type=%s" % options.constrain)
+            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)
             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)
@@ -1258,7 +1257,7 @@
                 print("example     ", item)
         print()
 
-    if options.kinds or options.list:
+    if args.kinds or args.list:
         return
 
     #
@@ -1326,7 +1325,7 @@
     # Flag indicating a specific suite was explicitly requested
     single_suite = False
 
-    if len(options.suite):
+    if len(args.suite):
         # See if this is a valid test suite.
         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)
@@ -1334,7 +1333,7 @@
         if isinstance(suites, bytes):
             suites = suites.decode()
 
-        suites_found = fnmatch.filter(suites.split('\n'), options.suite)
+        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)
@@ -1344,9 +1343,9 @@
 
         suites = '\n'.join(suites_found)
 
-    elif ENABLE_TESTS and len(options.example) == 0 and len(options.pyexample) == 0:
-        if len(options.constrain):
-            path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list --test-type=%s" % options.constrain)
+    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)
         else:
             path_cmd = os.path.join("utils", test_runner_name + " --print-test-name-list")
@@ -1371,7 +1370,7 @@
     # 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 options.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")
@@ -1414,11 +1413,11 @@
     else:
         processors = os.cpu_count()
 
-    if (options.process_limit):
-        if (processors < options.process_limit):
+    if args.process_limit:
+        if processors < args.process_limit:
             print('Using all %s processors' % processors)
         else:
-            processors = options.process_limit
+            processors = args.process_limit
             print('Limiting to %s worker processes' % processors)
 
     #
@@ -1458,12 +1457,12 @@
             job.set_cwd(os.getcwd())
             job.set_basedir(os.getcwd())
             job.set_tempdir(testpy_output_dir)
-            if (options.multiple):
+            if args.multiple:
                 multiple = ""
             else:
                 multiple = " --stop-on-failure"
-            if (len(options.fullness)):
-                fullness = options.fullness.upper()
+            if len(args.fullness):
+                fullness = args.fullness.upper()
                 fullness = " --fullness=%s" % fullness
             else:
                 fullness = " --fullness=QUICK"
@@ -1472,11 +1471,11 @@
 
             job.set_shell_command(path_cmd)
 
-            if options.valgrind and test in core_valgrind_skip_tests:
+            if args.valgrind and test in core_valgrind_skip_tests:
                 job.set_is_skip(True)
                 job.set_skip_reason("crashes valgrind")
 
-            if options.verbose:
+            if args.verbose:
                 print("Queue %s" % test)
 
             input_queue.put(job)
@@ -1515,8 +1514,8 @@
     #  ./test.py --suite=some-suite --example=some-example: run the single example
     #
     #
-    if len(options.suite) == 0 and len(options.example) == 0 and len(options.pyexample) == 0:
-        if len(options.constrain) == 0 or options.constrain == "example":
+    if len(args.suite) == 0 and len(args.example) == 0 and len(args.pyexample) == 0:
+        if len(args.constrain) == 0 or args.constrain == "example":
             if ENABLE_EXAMPLES:
                 for name, test, do_run, do_valgrind_run in example_tests:
                     # Remove any arguments and directory names from test.
@@ -1536,23 +1535,23 @@
                             job.set_basedir(os.getcwd())
                             job.set_tempdir(testpy_output_dir)
                             job.set_shell_command(test)
-                            job.set_build_path(options.buildpath)
+                            job.set_build_path(args.buildpath)
 
-                            if options.valgrind and not eval(do_valgrind_run):
+                            if args.valgrind and not eval(do_valgrind_run):
                                 job.set_is_skip(True)
                                 job.set_skip_reason("skip in valgrind runs")
 
-                            if options.verbose:
+                            if args.verbose:
                                 print("Queue %s" % test)
 
                             input_queue.put(job)
                             jobs = jobs + 1
                             total_tests = total_tests + 1
 
-    elif len(options.example):
+    elif len(args.example):
         # Add the proper prefix and suffix to the example name to
         # match what is done in the CMakeLists.txt file.
-        example_name = "%s%s-%s%s" % (APPNAME, VERSION, options.example, BUILD_PROFILE_SUFFIX)
+        example_name = "%s%s-%s%s" % (APPNAME, VERSION, args.example, BUILD_PROFILE_SUFFIX)
 
         key_list = []
         for key in ns3_runnable_programs_dictionary:
@@ -1560,7 +1559,7 @@
         example_name_key_list = fnmatch.filter(key_list, example_name)
 
         if len(example_name_key_list) == 0:
-            print("No example matching the name %s" % options.example)
+            print("No example matching the name %s" % args.example)
         else:
             #
             # If you tell me to run an example, I will try and run the example
@@ -1578,9 +1577,9 @@
                 job.set_basedir(os.getcwd())
                 job.set_tempdir(testpy_output_dir)
                 job.set_shell_command(example_path)
-                job.set_build_path(options.buildpath)
+                job.set_build_path(args.buildpath)
 
-                if options.verbose:
+                if args.verbose:
                     print("Queue %s" % example_name_iter)
 
                 input_queue.put(job)
@@ -1607,8 +1606,8 @@
     #  ./test.py --constrain=pyexample           run all of the python examples
     #  ./test.py --pyexample=some-example.py:    run the single python example
     #
-    if len(options.suite) == 0 and len(options.example) == 0 and len(options.pyexample) == 0:
-        if len(options.constrain) == 0 or options.constrain == "pyexample":
+    if len(args.suite) == 0 and len(args.example) == 0 and len(args.pyexample) == 0:
+        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]
@@ -1635,7 +1634,7 @@
                         # work to report the skipped tests in a consistent
                         # way through the output formatter.
                         #
-                        if options.valgrind:
+                        if args.valgrind:
                             job.set_is_skip(True)
                             job.set_skip_reason("skip in valgrind runs")
 
@@ -1648,26 +1647,26 @@
                             job.set_is_skip(True)
                             job.set_skip_reason("requires Python bindings")
 
-                        if options.verbose:
+                        if args.verbose:
                             print("Queue %s" % test)
 
                         input_queue.put(job)
                         jobs = jobs + 1
                         total_tests = total_tests + 1
 
-    elif len(options.pyexample):
+    elif len(args.pyexample):
         # Find the full relative path to file if only a partial path has been given.
-        if not os.path.exists(options.pyexample):
+        if not os.path.exists(args.pyexample):
             import glob
-            files = glob.glob("./**/%s" % options.pyexample, recursive=True)
+            files = glob.glob("./**/%s" % args.pyexample, recursive=True)
             if files:
-                options.pyexample = files[0]
+                args.pyexample = files[0]
 
         # Don't try to run this example if it isn't runnable.
-        example_name = os.path.basename(options.pyexample)
+        example_name = os.path.basename(args.pyexample)
         if example_name not in ns3_runnable_scripts:
             print("Example %s is not runnable." % example_name)
-        elif not os.path.exists(options.pyexample):
+        elif not os.path.exists(args.pyexample):
             print("Example %s does not exist." % example_name)
         else:
             #
@@ -1676,16 +1675,16 @@
             #
             job = Job()
             job.set_is_pyexample(True)
-            job.set_display_name(options.pyexample)
+            job.set_display_name(args.pyexample)
             job.set_tmp_file_name("")
             job.set_cwd(testpy_output_dir)
             job.set_basedir(os.getcwd())
             job.set_tempdir(testpy_output_dir)
-            job.set_shell_command(options.pyexample)
+            job.set_shell_command(args.pyexample)
             job.set_build_path("")
 
-            if options.verbose:
-                print("Queue %s" % options.pyexample)
+            if args.verbose:
+                print("Queue %s" % args.pyexample)
 
             input_queue.put(job)
             jobs = jobs + 1
@@ -1754,7 +1753,7 @@
                 status_print = colors.PINK + status + colors.NORMAL
 
         print("[%d/%d]" % (passed_tests + failed_tests + skipped_tests + crashed_tests, total_tests), end=' ')
-        if options.duration or options.constrain == "performance":
+        if args.duration or args.constrain == "performance":
             print("%s (%.3f): %s %s" % (status_print, job.elapsed_time, kind, job.display_name))
         else:
             print("%s: %s %s" % (status_print, kind, job.display_name))
@@ -1892,17 +1891,17 @@
     # 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)
     #
-    if len(options.html) + len(options.text) + len(options.xml):
+    if len(args.html) + len(args.text) + len(args.xml):
         print()
 
-    if len(options.html):
-        translate_to_html(xml_results_file, options.html)
+    if len(args.html):
+        translate_to_html(xml_results_file, args.html)
 
-    if len(options.text):
-        translate_to_text(xml_results_file, options.text)
+    if len(args.text):
+        translate_to_text(xml_results_file, args.text)
 
-    if len(options.xml):
-        xml_file = options.xml + '.xml'
+    if len(args.xml):
+        xml_file = args.xml + '.xml'
         print('Writing results to xml file %s...' % xml_file, end='')
         shutil.copyfile(xml_results_file, xml_file)
         print('done.')
@@ -1925,7 +1924,7 @@
     # Let the user know if they tried to use valgrind but it was not
     # present on their machine.
     #
-    if options.valgrind and not VALGRIND_FOUND:
+    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.')
@@ -1937,7 +1936,7 @@
     # directory we just created.  We don't want to happily delete any retained
     # directories, which will probably surprise the user.
     #
-    if not options.retain:
+    if not args.retain:
         shutil.rmtree(testpy_output_dir)
 
     if passed_tests + skipped_tests == total_tests:
@@ -1946,83 +1945,79 @@
         return 1 # catchall for general errors
 
 def main(argv):
-    parser = optparse.OptionParser()
-    parser.add_option("-b", "--buildpath", action="store", type="string", dest="buildpath", default="",
-                      metavar="BUILDPATH",
-                      help="specify the path where ns-3 was built (defaults to the build directory for the current variant)")
+    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_option("-c", "--constrain", action="store", type="string", dest="constrain", default="",
-                      metavar="KIND",
-                      help="constrain the test-runner by kind of test")
+    parser.add_argument("-c", "--constrain", action="store", type=str, default="",
+                        help="constrain the test-runner by kind of test")
 
-    parser.add_option("-d", "--duration", action="store_true", dest="duration", default=False,
-                      help="print the duration of each test suite and example")
+    parser.add_argument("-d", "--duration", action="store_true", default=False,
+                        help="print the duration of each test suite and example")
 
-    parser.add_option("-e", "--example", action="store", type="string", dest="example", default="",
-                      metavar="EXAMPLE",
-                      help="specify a single example to run (no relative path is needed)")
+    parser.add_argument("-e", "--example", action="store", type=str, default="",
+                        help="specify a single example to run (no relative path is needed)")
 
-    parser.add_option("-u", "--update-data", action="store_true", dest="update_data", default=False,
-                      help="If examples use reference data files, get them to re-generate them")
+    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_option("-f", "--fullness", action="store", type="choice", dest="fullness", default="QUICK",
-                      metavar="FULLNESS", 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("-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_option("-g", "--grind", action="store_true", dest="valgrind", default=False,
-                      help="run the test suites and examples using valgrind")
+    parser.add_argument("-g", "--grind", action="store_true", dest="valgrind", default=False,
+                        help="run the test suites and examples using valgrind")
 
-    parser.add_option("-k", "--kinds", action="store_true", dest="kinds", default=False,
-                      help="print the kinds of tests available")
+    parser.add_argument("-k", "--kinds", action="store_true", default=False,
+                        help="print the kinds of tests available")
 
-    parser.add_option("-l", "--list", action="store_true", dest="list", default=False,
-                      help="print the list of known tests")
+    parser.add_argument("-l", "--list", action="store_true", default=False,
+                        help="print the list of known tests")
 
-    parser.add_option("-m", "--multiple", action="store_true", dest="multiple", default=False,
-                      help="report multiple failures from test suites and test cases")
+    parser.add_argument("-m", "--multiple", action="store_true", default=False,
+                        help="report multiple failures from test suites and test cases")
 
-    parser.add_option("-n", "--no-build", action="store_true", dest="no_build", default=False,
-                      help="do not build before starting testing")
+    parser.add_argument("-n", "--no-build", action="store_true", default=False,
+                        help="do not build before starting testing")
 
-    parser.add_option("-p", "--pyexample", action="store", type="string", dest="pyexample", default="",
-                      metavar="PYEXAMPLE",
-                      help="specify a single python example to run (with relative path)")
+    parser.add_argument("-p", "--pyexample", action="store", type=str, default="",
+                        help="specify a single python example to run (with relative path)")
 
-    parser.add_option("-r", "--retain", action="store_true", dest="retain", default=False,
-                      help="retain all temporary files (which are normally deleted)")
+    parser.add_argument("-r", "--retain", action="store_true", default=False,
+                        help="retain all temporary files (which are normally deleted)")
 
-    parser.add_option("-s", "--suite", action="store", type="string", dest="suite", default="",
-                      metavar="TEST-SUITE",
-                      help="specify a single test suite to run")
+    parser.add_argument("-s", "--suite", action="store", type=str, default="",
+                        help="specify a single test suite to run")
 
-    parser.add_option("-t", "--text", action="store", type="string", dest="text", default="",
-                      metavar="TEXT-FILE",
-                      help="write detailed test results into TEXT-FILE.txt")
+    parser.add_argument("-t", "--text", action="store", type=str, default="",
+                        metavar="TEXT-FILE",
+                        help="write detailed test results into TEXT-FILE.txt")
 
-    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
-                      help="print progress and informational messages")
+    parser.add_argument("-v", "--verbose", action="store_true", default=False,
+                        help="print progress and informational messages")
 
-    parser.add_option("-w", "--web", "--html", action="store", type="string", dest="html", default="",
-                      metavar="HTML-FILE",
-                      help="write detailed test results into HTML-FILE.html")
+    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_option("-x", "--xml", action="store", type="string", dest="xml", default="",
-                      metavar="XML-FILE",
-                      help="write detailed test results into XML-FILE.xml")
+    parser.add_argument("-x", "--xml", action="store", type=str, default="",
+                        metavar="XML-FILE",
+                        help="write detailed test results into XML-FILE.xml")
 
-    parser.add_option("--nocolor", action="store_true", dest="nocolor", default=False,
-                      help="do not use colors in the standard output")
-    parser.add_option("--jobs", action="store", type="int", dest="process_limit", default=0,
-                      help="limit number of worker threads")
+    parser.add_argument("--nocolor", action="store_true", default=False,
+                        help="do not use colors in the standard output")
 
-    global options
-    options = parser.parse_args()[0]
+    parser.add_argument("--jobs", action="store", type=int, dest="process_limit", default=0,
+                        help="limit number of worker threads")
+
+    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'
 
-    if options.nocolor or envcolor == 'no':
+    if args.nocolor or envcolor == 'no':
         colors_lst['USE'] = False
 
     return run_tests()
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/check-style-clang-format.py ns-3.39/utils/check-style-clang-format.py
--- ns-3.38/utils/check-style-clang-format.py	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/utils/check-style-clang-format.py	2023-07-05 14:23:48.137428500 -0700
@@ -44,8 +44,8 @@
 import shutil
 import subprocess
 import sys
-from typing import List, Tuple
 
+from typing import Dict, List, Tuple
 
 ###########################################################
 # PARAMETERS
@@ -302,6 +302,7 @@
                 enable_check_whitespace: bool,
                 enable_check_tabs: bool,
                 fix: bool,
+                verbose: bool,
                 n_jobs: int = 1,
                 ) -> None:
     """
@@ -314,6 +315,7 @@
     @param enable_check_formatting Whether to enable code formatting checking.
     @param enable_check_whitespace Whether to enable trailing whitespace checking.
     @param enable_check_tabs Whether to enable tabs checking.
+    @param verbose Show the lines that are not well-formatted.
     @param n_jobs Number of parallel jobs.
     """
 
@@ -327,23 +329,37 @@
 
     if enable_check_formatting:
         check_formatting_successful = check_formatting(
-            files_to_check_formatting, fix, n_jobs)
+            files_to_check_formatting,
+            fix,
+            verbose,
+            n_jobs,
+        )
 
         print('')
 
     if enable_check_whitespace:
         check_whitespace_successful = check_trailing_whitespace(
-            files_to_check_whitespace, fix, n_jobs)
+            files_to_check_whitespace,
+            fix,
+            verbose,
+            n_jobs,
+        )
 
         print('')
 
     if enable_check_tabs:
         check_tabs_successful = check_tabs(
-            files_to_check_tabs, fix, n_jobs)
+            files_to_check_tabs,
+            fix,
+            verbose,
+            n_jobs,
+        )
 
-    if check_formatting_successful and \
-       check_whitespace_successful and \
-       check_tabs_successful:
+    if all([
+        check_formatting_successful,
+        check_whitespace_successful,
+        check_tabs_successful,
+    ]):
         sys.exit(0)
     else:
         sys.exit(1)
@@ -352,13 +368,18 @@
 ###########################################################
 # CHECK FORMATTING
 ###########################################################
-def check_formatting(filenames: List[str], fix: bool, n_jobs: int) -> bool:
+def check_formatting(filenames: List[str],
+                     fix: bool,
+                     verbose: bool,
+                     n_jobs: int,
+                     ) -> bool:
     """
     Check / fix the coding style of a list of files with clang-format.
 
     @param filenames List of filenames to be checked.
     @param fix Whether to fix the formatting of the file (True) or
                just check if the file is well-formatted (False).
+    @param verbose Show the lines that are not well-formatted.
     @param n_jobs Number of parallel jobs.
     @return True if all files are well formatted after the check process.
             False if there are non-formatted files after the check process.
@@ -367,6 +388,7 @@
     # Check files
     clang_format_path = find_clang_format_path()
     files_not_formatted: List[str] = []
+    files_verbose_infos: Dict[str, List[str]] = {}
 
     with concurrent.futures.ProcessPoolExecutor(n_jobs) as executor:
         files_not_formatted_results = executor.map(
@@ -374,29 +396,34 @@
             filenames,
             itertools.repeat(clang_format_path),
             itertools.repeat(fix),
+            itertools.repeat(verbose),
         )
 
-        for (filename, formatted) in files_not_formatted_results:
+        for (filename, formatted, verbose_infos) in files_not_formatted_results:
             if not formatted:
                 files_not_formatted.append(filename)
 
-    files_not_formatted.sort()
+            if verbose:
+                files_verbose_infos[filename] = verbose_infos
 
     # Output results
     if not files_not_formatted:
-        print('All files are well formatted')
+        print('- All files are well formatted')
         return True
 
     else:
         n_non_formatted_files = len(files_not_formatted)
 
         if fix:
-            print(f'Fixed formatting of the files ({n_non_formatted_files}):')
+            print(f'- Fixed formatting of the files ({n_non_formatted_files}):')
         else:
-            print(f'Detected bad formatting in the files ({n_non_formatted_files}):')
+            print(f'- Detected bad formatting in the files ({n_non_formatted_files}):')
 
         for f in files_not_formatted:
-            print(f'- {f}')
+            if verbose:
+                print(*[f'    {l}' for l in files_verbose_infos[f]], sep='\n')
+            else:
+                print(f'    - {f}')
 
         # Return True if all files were fixed
         return fix
@@ -405,7 +432,8 @@
 def check_formatting_file(filename: str,
                           clang_format_path: str,
                           fix: bool,
-                          ) -> Tuple[str, bool]:
+                          verbose: bool,
+                          ) -> Tuple[str, bool, List[str]]:
     """
     Check / fix the coding style of a file with clang-format.
 
@@ -413,9 +441,12 @@
     @param clang_format_path Path to clang-format.
     @param fix Whether to fix the style of the file (True) or
                just check if the file is well-formatted (False).
-    @return Tuple [Filename, Whether the file is well-formatted].
+    @param verbose Show the lines that are not well-formatted.
+    @return Tuple [Filename, Whether the file is well-formatted, Verbose information].
     """
 
+    verbose_infos: List[str] = []
+
     # Check if the file is well formatted
     process = subprocess.run(
         [
@@ -424,16 +455,19 @@
             '-style=file',
             '--dry-run',
             '--Werror',
-            # Optimization: Only 1 error is needed to check that the file is not formatted
-            '--ferror-limit=1',
+            # Optimization: In non-verbose mode, only 1 error is needed to check that the file is not formatted
+            f'--ferror-limit={0 if verbose else 1}',
         ],
         check=False,
-        stdout=subprocess.DEVNULL,
-        stderr=subprocess.DEVNULL,
+        capture_output=True,
+        text=True,
     )
 
     file_formatted = (process.returncode == 0)
 
+    if verbose:
+        verbose_infos = process.stderr.splitlines()
+
     # Fix file
     if fix and not file_formatted:
         process = subprocess.run(
@@ -448,19 +482,24 @@
             stderr=subprocess.DEVNULL,
         )
 
-    return (filename, file_formatted)
+    return (filename, file_formatted, verbose_infos)
 
 
 ###########################################################
 # CHECK TRAILING WHITESPACE
 ###########################################################
-def check_trailing_whitespace(filenames: List[str], fix: bool, n_jobs: int) -> bool:
+def check_trailing_whitespace(filenames: List[str],
+                              fix: bool,
+                              verbose: bool,
+                              n_jobs: int,
+                              ) -> bool:
     """
     Check / fix trailing whitespace in a list of files.
 
     @param filename Name of the file to be checked.
     @param fix Whether to fix the file (True) or
                just check if it has trailing whitespace (False).
+    @param verbose Show the lines that are not well-formatted.
     @param n_jobs Number of parallel jobs.
     @return True if no files have trailing whitespace after the check process.
             False if there are trailing whitespace after the check process.
@@ -468,23 +507,26 @@
 
     # Check files
     files_with_whitespace: List[str] = []
+    files_verbose_infos: Dict[str, List[str]] = {}
 
     with concurrent.futures.ProcessPoolExecutor(n_jobs) as executor:
         files_with_whitespace_results = executor.map(
             check_trailing_whitespace_file,
             filenames,
             itertools.repeat(fix),
+            itertools.repeat(verbose),
         )
 
-        for (filename, has_whitespace) in files_with_whitespace_results:
+        for (filename, has_whitespace, verbose_infos) in files_with_whitespace_results:
             if has_whitespace:
                 files_with_whitespace.append(filename)
 
-    files_with_whitespace.sort()
+            if verbose:
+                files_verbose_infos[filename] = verbose_infos
 
     # Output results
     if not files_with_whitespace:
-        print('No files detected with trailing whitespace')
+        print('- No files detected with trailing whitespace')
         return True
 
     else:
@@ -492,29 +534,37 @@
 
         if fix:
             print(
-                f'Fixed trailing whitespace in the files ({n_files_with_whitespace}):')
+                f'- Fixed trailing whitespace in the files ({n_files_with_whitespace}):')
         else:
             print(
-                f'Detected trailing whitespace in the files ({n_files_with_whitespace}):')
+                f'- Detected trailing whitespace in the files ({n_files_with_whitespace}):')
 
         for f in files_with_whitespace:
-            print(f'- {f}')
+            if verbose:
+                print(*[f'    {l}' for l in files_verbose_infos[f]], sep='\n')
+            else:
+                print(f'    - {f}')
 
         # If all files were fixed, there are no more trailing whitespace
         return fix
 
 
-def check_trailing_whitespace_file(filename: str, fix: bool) -> Tuple[str, bool]:
+def check_trailing_whitespace_file(filename: str,
+                                   fix: bool,
+                                   verbose: bool,
+                                   ) -> Tuple[str, bool, List[str]]:
     """
     Check / fix trailing whitespace in a file.
 
     @param filename Name of the file to be checked.
     @param fix Whether to fix the file (True) or
                just check if it has trailing whitespace (False).
-    @return Tuple [Filename, Whether the file has trailing whitespace].
+    @param verbose Show the lines that are not well-formatted.
+    @return Tuple [Filename, Whether the file has trailing whitespace, Verbose information].
     """
 
     has_trailing_whitespace = False
+    verbose_infos: List[str] = []
 
     with open(filename, 'r', encoding='utf-8') as f:
         file_lines = f.readlines()
@@ -525,30 +575,41 @@
 
         if line_fixed != line:
             has_trailing_whitespace = True
+            file_lines[i] = line_fixed
 
-            # Optimization: if only checking, skip the rest of the file
-            if not fix:
-                break
+            if verbose:
+                verbose_infos.extend([
+                    f'{filename}:{i + 1}: error: Trailing whitespace detected',
+                    f'    {line_fixed.rstrip()}',
+                    f'    {"":{len(line_fixed) - 1}}^',
+                ])
 
-            file_lines[i] = line_fixed
+            # Optimization: If running in non-verbose check mode, only one error is needed to check that the file is not formatted
+            if not fix and not verbose:
+                break
 
     # Update file with the fixed lines
     if fix and has_trailing_whitespace:
         with open(filename, 'w', encoding='utf-8') as f:
             f.writelines(file_lines)
 
-    return (filename, has_trailing_whitespace)
+    return (filename, has_trailing_whitespace, verbose_infos)
 
 
 ###########################################################
 # CHECK TABS
 ###########################################################
-def check_tabs(filenames: List[str], fix: bool, n_jobs: int) -> bool:
+def check_tabs(filenames: List[str],
+               fix: bool,
+               verbose: bool,
+               n_jobs: int,
+               ) -> bool:
     """
     Check / fix tabs in a list of files.
 
     @param filename Name of the file to be checked.
     @param fix Whether to fix the file (True) or just check if it has tabs (False).
+    @param verbose Show the lines that are not well-formatted.
     @param n_jobs Number of parallel jobs.
     @return True if no files have tabs after the check process.
             False if there are tabs after the check process.
@@ -556,23 +617,26 @@
 
     # Check files
     files_with_tabs: List[str] = []
+    files_verbose_infos: Dict[str, List[str]] = {}
 
     with concurrent.futures.ProcessPoolExecutor(n_jobs) as executor:
         files_with_tabs_results = executor.map(
             check_tabs_file,
             filenames,
             itertools.repeat(fix),
+            itertools.repeat(verbose),
         )
 
-        for (filename, has_tabs) in files_with_tabs_results:
+        for (filename, has_tabs, verbose_infos) in files_with_tabs_results:
             if has_tabs:
                 files_with_tabs.append(filename)
 
-    files_with_tabs.sort()
+            if verbose:
+                files_verbose_infos[filename] = verbose_infos
 
     # Output results
     if not files_with_tabs:
-        print('No files detected with tabs')
+        print('- No files detected with tabs')
         return True
 
     else:
@@ -580,30 +644,39 @@
 
         if fix:
             print(
-                f'Fixed tabs in the files ({n_files_with_tabs}):')
+                f'- Fixed tabs in the files ({n_files_with_tabs}):')
         else:
             print(
-                f'Detected tabs in the files ({n_files_with_tabs}):')
+                f'- Detected tabs in the files ({n_files_with_tabs}):')
 
         for f in files_with_tabs:
-            print(f'- {f}')
+            if verbose:
+                print(*[f'    {l}' for l in files_verbose_infos[f]], sep='\n')
+            else:
+                print(f'    - {f}')
 
         # If all files were fixed, there are no more trailing whitespace
         return fix
 
 
-def check_tabs_file(filename: str, fix: bool) -> Tuple[str, bool]:
+def check_tabs_file(filename: str,
+                    fix: bool,
+                    verbose: bool,
+                    ) -> Tuple[str, bool, List[str]]:
     """
     Check / fix tabs in a file.
 
     @param filename Name of the file to be checked.
     @param fix Whether to fix the file (True) or just check if it has tabs (False).
-    @return Tuple [Filename, Whether the file has tabs].
+    @param verbose Show the lines that are not well-formatted.
+    @return Tuple [Filename, Whether the file has tabs, Verbose information].
     """
 
     has_tabs = False
     clang_format_enabled = True
 
+    verbose_infos: List[str] = []
+
     with open(filename, 'r', encoding='utf-8') as f:
         file_lines = f.readlines()
 
@@ -622,21 +695,29 @@
             continue
 
         # Check if there are tabs and fix them
-        if line.find('\t') != -1:
+        tab_index = line.find('\t')
+
+        if tab_index != -1:
             has_tabs = True
+            file_lines[i] = line.expandtabs(TAB_SIZE)
 
-            # Optimization: if only checking, skip the rest of the file
-            if not fix:
-                break
+            if verbose:
+                verbose_infos.extend([
+                    f'{filename}:{i + 1}:{tab_index + 1}: error: Tab detected',
+                    f'    {line.rstrip()}',
+                    f'    {"":{tab_index}}^',
+                ])
 
-            file_lines[i] = line.expandtabs(TAB_SIZE)
+            # Optimization: If running in non-verbose check mode, only one error is needed to check that the file is not formatted
+            if not fix and not verbose:
+                break
 
     # Update file with the fixed lines
     if fix and has_tabs:
         with open(filename, 'w', encoding='utf-8') as f:
             f.writelines(file_lines)
 
-    return (filename, has_tabs)
+    return (filename, has_tabs, verbose_infos)
 
 
 ###########################################################
@@ -669,6 +750,9 @@
     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('-j', '--jobs', type=int, default=max(1, os.cpu_count() - 1),
                         help='Number of parallel jobs')
 
@@ -681,6 +765,7 @@
             enable_check_whitespace=(not args.no_whitespace),
             enable_check_tabs=(not args.no_tabs),
             fix=args.fix,
+            verbose=args.verbose,
             n_jobs=args.jobs,
         )
 
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/CMakeLists.txt ns-3.39/utils/CMakeLists.txt
--- ns-3.38/utils/CMakeLists.txt	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/utils/CMakeLists.txt	2023-07-05 14:23:48.137428500 -0700
@@ -1,8 +1,24 @@
-if(${ENABLE_TESTS} AND (test IN_LIST libs_to_build))
+if(${ENABLE_TESTS})
+  set(test_sources)
+  if(test IN_LIST libs_to_build)
+    # Create a list of imported directories to include during libtest build
+    set(test_imported_include_directories)
+    foreach(libname ${ns3-libs} ${ns3-contrib-libs})
+      list(APPEND test_imported_include_directories $<TARGET_PROPERTY:${libname},INTERFACE_INCLUDE_DIRECTORIES>)
+    endforeach()
+
+    # Import include directories of dependencies to build libtest
+    target_include_directories(${libtest} PUBLIC ${test_imported_include_directories})
+    unset(test_imported_include_directories)
+
+    # Define the libtest as an object to build test-runner
+    set(test_sources $<TARGET_OBJECTS:${libtest}>)
+  endif()
+
   if(WIN32)
     # DLL linking shenanigans prevent loading symbols unused by a certain program,
     # so link the tests libraries (here built as objects) directly to the test runner
-    add_executable(test-runner test-runner.cc ${ns3-libs-tests} $<TARGET_OBJECTS:${libtest}>)
+    add_executable(test-runner test-runner.cc ${ns3-libs-tests} ${test_sources})
     if(${NS3_MONOLIB})
       target_link_libraries(
               test-runner ${lib-ns3-monolib} ${ns3-contrib-libs}
@@ -13,7 +29,7 @@
       )
     endif()
   else()
-    add_executable(test-runner $<TARGET_OBJECTS:${libtest}> test-runner.cc)
+    add_executable(test-runner ${test_sources} test-runner.cc)
 
     if(${NS3_MONOLIB})
       target_link_libraries(
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/codespell-ignored-lines ns-3.39/utils/codespell-ignored-lines
--- ns-3.38/utils/codespell-ignored-lines	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/utils/codespell-ignored-lines	2023-07-05 14:23:48.137428500 -0700
@@ -44,3 +44,78 @@
 
 #./src/internet-apps/model/v4ping.h:37
  * \brief an application which sends one ICMP ECHO request, waits for a REPLYs
+
+#./src/openflow/model/openflow-switch-net-device.cc:544, 548
+                arp_h->ar_tha);                                       // Target hardware address.
+            arp_h->ar_hln = sizeof arp_h->ar_tha;                     // Hardware address length.
+
+./src/dsr/model/dsr-routing.cc:243, 246, 3360, 3363
+            .AddAttribute("GratReplyHoldoff",
+                          MakeTimeAccessor(&DsrRouting::m_gratReplyHoldoff),
+                                   m_gratReplyHoldoff))) // Find the gratuitous reply entry
+        GraReplyEntry graReplyEntry(source, srcAddress, m_gratReplyHoldoff + Simulator::Now());
+
+./src/dsr/model/dsr-routing.h:882
+    Time m_gratReplyHoldoff; ///< The max gratuitous reply hold off time
+
+./src/dsr/model/dsr-gratuitous-reply-table.cc:75, 76
+            i->m_gratReplyHoldoff =
+                std::max(gratReplyHoldoff + Simulator::Now(), i->m_gratReplyHoldoff);
+
+./src/dsr/model/dsr-gratuitous-reply-table.h:54, 66, 137
+    Time m_gratReplyHoldoff; ///< gratuitous reply holdoff time
+          m_gratReplyHoldoff(h)
+            return (b.m_gratReplyHoldoff < Simulator::Now());
+
+./RELEASE_NOTES.md:1087
+- Bug 2530 - aodv: Rename aodv::SetBalckListTimeout to aodv::SetBlackListTimeout
+
+./doc/installation/source/linux.rst:122
+  apt install gir1.2-goocanvas-2.0 python3-gi python3-gi-cairo python3-pygraphviz gir1.2-gtk-3.0 ipython3
+
+./doc/tutorial/source/getting-started.rst:347:
+  >> Searching for system dependency gir-bindings - OK
+
+./src/wave/examples/vanet-routing-compare.cc:1565, 1971
+static ns3::GlobalValue g_rate("VRCrate",
+    GlobalValue::GetValueByName("VRCrate", stringValue);
+
+./src/lte/model/lte-mi-error-model.h:23:
+ *      for mobIlE Video AppLications) project, which is a
+
+./src/traffic-control/model/codel-queue-disc.h:17:
+ * Codel, the COntrolled DELay Queueing discipline
+
+./src/internet/model/global-route-manager-impl.h:555:
+     * \param vs a list of SPFVertexes
+
+./src/internet/model/candidate-queue.h:166:
+     * SPFVertexes are added into the queue according to the ordering
+
+./src/internet/model/global-route-manager-impl.h:555
+    typedef std::list<SPFVertex*> ListOfSPFVertex_t; //!< container of SPFVertexes
+
+./CHANGES.md:
+  * (dsr) Class `DsrOptionRerrUnsupportHeader` from `dsr-option-header.h` was renamed `DsrOptionRerrUnsupportedHeader`.
+  * (internet) Enumerated value `IPV6_EXT_AUTHENTIFICATION` from `ipv6-header.h` was renamed `IPV6_EXT_AUTHENTICATION`.
+  * (lr-wpan) Constant `aMaxBeaconPayloadLenght` from `lr-wpan-constants.h` was renamed `aMaxBeaconPayloadLength`.
+  * (lte) Enumeration `ControPduType_t` from `lte-rlc-am-header.h` was renamed `ControlPduType_t`.
+  * (lte) Function `LteUeCphySapProvider::StartInSnycDetection()` from `lte-ue-cphy-sap.h` was renamed `LteUeCphySapProvider::StartInSyncDetection()`.
+  * (lte) Function `MemberLteUeCphySapProvider::StartInSnycDetection()` from `lte-ue-cphy-sap.h` was renamed `MemberLteUeCphySapProvider::StartInSyncDetection()`.
+  * (lte) Function `LteUePhy::StartInSnycDetection()` from `lte-ue-phy.h` was renamed `LteUePhy::StartInSyncDetection()`.
+  * (lte) Function `DoUlInfoListElementHarqFeeback` from `lte-enb-phy-sap.h` and `lte-enb-mac.h` was renamed `DoUlInfoListElementHarqFeedback`.
+  * (lte) Function `DoDlInfoListElementHarqFeeback` from `lte-enb-phy-sap.h` and `lte-enb-mac.h` was renamed `DoDlInfoListElementHarqFeedback`.
+  * (mesh) Function `PeerManagementProtocolMac::SetPeerManagerProtcol` from `peer-management-protocol-mac.h` was renamed `PeerManagementProtocolMac::SetPeerManagerProtocol`.
+  * (network) File `lollipop-comparisions.cc` was renamed `lollipop-comparisons.cc`.
+  * (network) Attribute `currentTrimedFromStart` from `packet-metadata.h` was renamed `currentTrimmedFromStart`.
+  * (network) Attribute `currentTrimedFromEnd` from `packet-metadata.h` was renamed `currentTrimmedFromEnd`.
+  * (sixlowpan) Function `SixLowPanNetDevice::Fragments::GetFraments` from `sixlowpan-net-device.cc` was renamed `SixLowPanNetDevice::Fragments::GetFragments`.
+  * (wave) Function `OcbWifiMac::CancleTx()` from `ocb-wifi-mac.h` was renamed `OcbWifiMac::CancelTx()`.
+  * (wifi) Attribute `m_succesMax1` from `aparf-wifi-manager.h` was renamed `m_successMax1`.
+  * (wifi) Attribute `m_succesMax2` from `aparf-wifi-manager.h` was renamed `m_successMax2`.
+  * (wifi) Enumerated value `MDAOP_ADVERTISMENT_REQUEST` from `mgmt-headers.h` was renamed `MDAOP_ADVERTISEMENT_REQUEST`.
+  * (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`.
+* 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.38/utils/codespell-ignored-words ns-3.39/utils/codespell-ignored-words
--- ns-3.38/utils/codespell-ignored-words	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/utils/codespell-ignored-words	2023-07-05 14:23:48.137428500 -0700
@@ -1,3 +1,6 @@
+#LaTeX
+te
+
 #Core
 anid
 anumber
@@ -17,6 +20,7 @@
 iif
 nce
 inflight
+upto
 
 #Internet Apps
 clen
@@ -26,6 +30,7 @@
 
 #DSR
 cleanp
+grat
 
 #LTE
 commun
@@ -48,8 +53,10 @@
 
 #Traffic Control
 taht
+interm
 
 #Wimax
+aas
 opps
 
 #Propagation
@@ -70,3 +77,6 @@
 
 #Utils
 filetest
+
+#test-ns3.py
+dependant
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/grid.py ns-3.39/utils/grid.py
--- ns-3.38/utils/grid.py	2023-03-16 20:54:23.880869400 -0700
+++ ns-3.39/utils/grid.py	2023-07-05 14:23:48.137428500 -0700
@@ -1,5 +1,4 @@
 #!/usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 
 import cairo
 import sys
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/python-unit-tests.py ns-3.39/utils/python-unit-tests.py
--- ns-3.38/utils/python-unit-tests.py	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/python-unit-tests.py	2023-07-05 14:23:48.137428500 -0700
@@ -302,7 +302,7 @@
         cmd = ns.core.CommandLine(__file__)
         cmd.AddValue("Test1", "this is a test option", test1)
         cmd.AddValue("Test2", "this is a test option", test2)
-        cmd.AddValue("Test3", "this is a test option", test3)
+        cmd.AddValue["double"]("Test3", "this is a test option", test3)
         cmd.AddValue("Test4", "this is a test option", test4, BUFFLEN)
 
         cmd.Parse(["python"])
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/run-examples-with-full-logging.sh ns-3.39/utils/run-examples-with-full-logging.sh
--- ns-3.38/utils/run-examples-with-full-logging.sh	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/run-examples-with-full-logging.sh	2023-07-05 14:23:48.137428500 -0700
@@ -1,5 +1,4 @@
 #!/bin/bash
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 #
 # Copyright (c) 2012 University of Washington
 #
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-alpine.yml ns-3.39/utils/tests/gitlab-ci-alpine.yml
--- ns-3.38/utils/tests/gitlab-ci-alpine.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-alpine.yml	2023-07-05 14:23:48.137428500 -0700
@@ -7,16 +7,11 @@
 # Alpine base
 .weekly-build-alpine:
   extends: .base-build
-  only:
-    variables:
-      - $RELEASE == "weekly"
-      - $ALPINE == "True"
-  variables:
-    COMPILER: g++
-
-weekly-build-alpine-latest-default:
-  extends: .weekly-build-alpine
   image: alpine:latest
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
   before_script:
     - apk update
     - apk upgrade
@@ -26,4 +21,30 @@
       bash
       iproute2 linux-headers
   variables:
+    COMPILER: g++
+
+weekly-alpine:
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      when: manual
+      allow_failure: true
+  stage: pre-build
+  script:
+    - echo "Starting Alpine jobs"
+
+weekly-alpine-latest-default:
+  extends: .weekly-build-alpine
+  needs: ["weekly-alpine"]
+  stage: build
+  variables:
+    MODE: default
+
+weekly-alpine-latest-default-test:
+  extends: .weekly-build-alpine
+  needs: ["weekly-alpine-latest-default"]
+  stage: test
+  dependencies:
+    - weekly-alpine-latest-default
+  variables:
     MODE: default
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-clang.yml ns-3.39/utils/tests/gitlab-ci-clang.yml
--- ns-3.38/utils/tests/gitlab-ci-clang.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-clang.yml	2023-07-05 14:23:48.137428500 -0700
@@ -8,13 +8,13 @@
 # We test only CLANG 6 and 10 on Ubuntu 18.04, and 11 and 14 on Ubuntu 22.04
 # The test is done under Linux.
 
-.weekly-build-clang-ubuntu18.04:
+.weekly-clang-ubuntu18.04:
   image: ubuntu:18.04
   extends: .base-build
-  only:
-    variables:
-      - $RELEASE == "weekly"
-      - $CLANG_BUILD_ENABLE == "True"
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
   before_script:
     - apt update
     - apt upgrade -y
@@ -31,13 +31,13 @@
     COMPILER: clang++-$CLANG
     LIBGSL: libgsl23
 
-.weekly-build-clang-ubuntu22.04:
+.weekly-clang-ubuntu22.04:
   image: ubuntu:22.04
   extends: .base-build
-  only:
-    variables:
-      - $RELEASE == "weekly"
-      - $CLANG_BUILD_ENABLE == "True"
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
   before_script:
     - apt update
     - apt upgrade -y
@@ -54,78 +54,192 @@
     COMPILER: clang++-$CLANG
     LIBGSL: libgsl27
 
+weekly-clang:
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      when: manual
+      allow_failure: true
+  stage: pre-build
+  script:
+    - echo "Starting clang jobs"
+
 # CLANG 6
 weekly-build-clang-6-debug:
-  extends: .weekly-build-clang-ubuntu18.04
+  extends: .weekly-clang-ubuntu18.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: "6.0"
     MODE: debug
 
 weekly-build-clang-6-default:
-  extends: .weekly-build-clang-ubuntu18.04
+  extends: .weekly-clang-ubuntu18.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: "6.0"
     MODE: default
 
 weekly-build-clang-6-optimized:
-  extends: .weekly-build-clang-ubuntu18.04
+  extends: .weekly-clang-ubuntu18.04
+  needs: ["weekly-clang"]
+  stage: build
+  variables:
+    CLANG: "6.0"
+    MODE: optimized
+
+weekly-test-clang-6-default:
+  extends: .weekly-clang-ubuntu18.04
+  stage: test
+  needs: ["weekly-build-clang-6-default"]
+  dependencies:
+    - weekly-build-clang-6-default
+  variables:
+    CLANG: "6.0"
+    MODE: default
+
+weekly-test-clang-6-optimized:
+  extends: .weekly-clang-ubuntu18.04
+  stage: test
+  needs: ["weekly-build-clang-6-optimized"]
+  dependencies:
+    - weekly-build-clang-6-optimized
   variables:
     CLANG: "6.0"
     MODE: optimized
 
 # CLANG 10
 weekly-build-clang-10-debug:
-  extends: .weekly-build-clang-ubuntu18.04
+  extends: .weekly-clang-ubuntu18.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: 10
     MODE: debug
 
 weekly-build-clang-10-default:
-  extends: .weekly-build-clang-ubuntu18.04
+  extends: .weekly-clang-ubuntu18.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: 10
     MODE: default
 
 weekly-build-clang-10-optimized:
-  extends: .weekly-build-clang-ubuntu18.04
+  extends: .weekly-clang-ubuntu18.04
+  needs: ["weekly-clang"]
+  stage: build
+  variables:
+    CLANG: 10
+    MODE: optimized
+
+weekly-test-clang-10-default:
+  extends: .weekly-clang-ubuntu18.04
+  stage: test
+  needs: ["weekly-build-clang-10-default"]
+  dependencies:
+    - weekly-build-clang-10-default
+  variables:
+    CLANG: 10
+    MODE: default
+
+weekly-test-clang-10-optimized:
+  extends: .weekly-clang-ubuntu18.04
+  stage: test
+  needs: ["weekly-build-clang-10-optimized"]
+  dependencies:
+    - weekly-build-clang-10-optimized
   variables:
     CLANG: 10
     MODE: optimized
 
 # CLANG 11
 weekly-build-clang-11-debug:
-  extends: .weekly-build-clang-ubuntu22.04
+  extends: .weekly-clang-ubuntu22.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: 11
     MODE: debug
 
 weekly-build-clang-11-default:
-  extends: .weekly-build-clang-ubuntu22.04
+  extends: .weekly-clang-ubuntu22.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: 11
     MODE: default
 
 weekly-build-clang-11-optimized:
-  extends: .weekly-build-clang-ubuntu22.04
+  extends: .weekly-clang-ubuntu22.04
+  needs: ["weekly-clang"]
+  stage: build
+  variables:
+    CLANG: 11
+    MODE: optimized
+
+weekly-test-clang-11-default:
+  extends: .weekly-clang-ubuntu22.04
+  stage: test
+  needs: ["weekly-build-clang-11-default"]
+  dependencies:
+    - weekly-build-clang-11-default
+  variables:
+    CLANG: 11
+    MODE: default
+
+weekly-test-clang-11-optimized:
+  extends: .weekly-clang-ubuntu22.04
+  stage: test
+  needs: ["weekly-build-clang-11-optimized"]
+  dependencies:
+    - weekly-build-clang-11-optimized
   variables:
     CLANG: 11
     MODE: optimized
 
 # CLANG 14
 weekly-build-clang-14-debug:
-  extends: .weekly-build-clang-ubuntu22.04
+  extends: .weekly-clang-ubuntu22.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: 14
     MODE: debug
 
 weekly-build-clang-14-default:
-  extends: .weekly-build-clang-ubuntu22.04
+  extends: .weekly-clang-ubuntu22.04
+  needs: ["weekly-clang"]
+  stage: build
   variables:
     CLANG: 14
     MODE: default
 
 weekly-build-clang-14-optimized:
-  extends: .weekly-build-clang-ubuntu22.04
+  extends: .weekly-clang-ubuntu22.04
+  needs: ["weekly-clang"]
+  stage: build
+  variables:
+    CLANG: 14
+    MODE: optimized
+
+weekly-test-clang-14-default:
+  extends: .weekly-clang-ubuntu22.04
+  stage: test
+  needs: ["weekly-build-clang-14-default"]
+  dependencies:
+    - weekly-build-clang-14-default
+  variables:
+    CLANG: 14
+    MODE: default
+
+weekly-test-clang-14-optimized:
+  extends: .weekly-clang-ubuntu22.04
+  stage: test
+  needs: ["weekly-build-clang-14-optimized"]
+  dependencies:
+    - weekly-build-clang-14-optimized
   variables:
     CLANG: 14
     MODE: optimized
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-code-linting.yml ns-3.39/utils/tests/gitlab-ci-code-linting.yml
--- ns-3.38/utils/tests/gitlab-ci-code-linting.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-code-linting.yml	2023-07-05 14:23:48.137428500 -0700
@@ -3,111 +3,120 @@
 # Contains jobs to check the ns-3 coding style and perform lint checking.
 
 # Clang-format
-check-style-clang-format:
-  stage: code-linting
-  image: ubuntu:latest
+.check-style-clang-format:
+  stage: .pre
+  image: ubuntu:rolling
   before_script:
     - apt update
     - DEBIAN_FRONTEND=noninteractive apt install -y
       python3
-      clang-format-14
+      clang-format-$CLANG_FORMAT_VERSION
   script:
-    - python3 utils/check-style-clang-format.py .
+    - python3 utils/check-style-clang-format.py --verbose .
   timeout: 1h
 
+check-style-clang-format-14:
+  extends: .check-style-clang-format
+  variables:
+    CLANG_FORMAT_VERSION: 14
+
+check-style-clang-format-15:
+  extends: .check-style-clang-format
+  variables:
+    CLANG_FORMAT_VERSION: 15
+
+check-style-clang-format-16:
+  extends: .check-style-clang-format
+  variables:
+    CLANG_FORMAT_VERSION: 16
+
 # Clang-tidy
-.base-clang-tidy:
+clang-tidy-16:
   stage: code-linting
-  image: ubuntu:latest
+  image: ubuntu:rolling
   variables:
     MPI_CI: 1
     CLANG_TIDY_OUTPUT: clang-tidy-output.log
+    FILES_CHANGED: git-diff-name-only.log
   before_script:
     - apt update
     - DEBIAN_FRONTEND=noninteractive apt install -y
       clang cmake
-      clang-tidy clang-tidy-14
+      clang-tidy clang-tidy-16
       python3 python3-pip
       libboost-all-dev libeigen3-dev libgtk-3-dev libopenmpi-dev libsqlite3-dev
       gsl-bin libgsl-dev libgsl27
       git ssh
-    - pip3 install cppyy
+    - pip3 install --break-system-packages cppyy
     - ./ns3 configure -d debug
       --enable-clang-tidy
       --enable-examples --enable-tests --enable-asserts
       --enable-mpi --enable-python-bindings
-  artifacts:
-    paths:
-      - $CLANG_TIDY_OUTPUT
-    when: on_failure
-  timeout: 3h
-
-clang-tidy:
-  extends: .base-clang-tidy
-  rules:
-    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
-      when: never
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      when: on_success
-    - changes:
-        paths:
-          - .clang-tidy
-        compare_to: master
-      when: on_success
   script:
-    - run-clang-tidy-14 -p cmake-cache/ -quiet
-      1> $CLANG_TIDY_OUTPUT
-      2> /dev/null
-    - (! egrep -A 3 "error:|warning:|note:" $CLANG_TIDY_OUTPUT)
-    - echo "No clang-tidy errors found"
+    - if (git remote | grep -qw upstream) ; then
+      git remote remove upstream ;
+      fi
+    - 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
 
-clang-tidy-diff:
-  extends: .base-clang-tidy
-  rules:
-    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
-      when: never
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      when: never
-    - changes:
-        paths:
-          - .clang-tidy
-        compare_to: master
-      when: never
-    - when: on_success
-  script:
-    - git remote add upstream https://gitlab.com/nsnam/ns-3-dev.git
-    - git fetch upstream master
+    # Run clang-tidy-diff in the opposite cases of the if-else below.
     # File paths generated by git diff are relative to the working tree. Therefore, iregex should only contain paths relative to the working tree.
-    - git diff -U0 upstream/master |
-      clang-tidy-diff-14.py -path cmake-cache/ -p1 -quiet -use-color
+    - 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)"
       1> $CLANG_TIDY_OUTPUT
-      2> /dev/null
-    - if egrep -A 3 "file not found \[clang-diagnostic-error\]" $CLANG_TIDY_OUTPUT ; then
-      echo "Clang-tidy could not find the above files. Running a full clang-tidy check." ;
-      run-clang-tidy-14 -p cmake-cache/ -quiet
+      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
       1> $CLANG_TIDY_OUTPUT
       2> /dev/null ;
       fi
+
+    # Check job results
     - (! egrep -A 3 "error:|warning:|note:" $CLANG_TIDY_OUTPUT)
     - echo "No clang-tidy errors found"
+  dependencies: []
+  artifacts:
+    paths:
+      - $CLANG_TIDY_OUTPUT
+    when: on_failure
+  interruptible: true
+  timeout: 3h
 
 # Emacs line
 emacs-line:
-  stage: code-linting
+  stage: .pre
   image: ubuntu:latest
   script:
-    - (! grep -rn --include="*.h" --include="*.cc" "c-file-style:\"gnu\"" ) ||
-      (echo "Found Emacs lines on the above C++ files" && exit 1)
-    - echo "No Emacs lines found on C++ files"
+    - if ( egrep -rn --include="*.h" --include="*.cc" --include="*.c" --include="*.py" --include="*.rst" "c-file-style:|py-indent-offset:" ) ; then
+      echo "Found Emacs lines on the above C/C++, Python and RST files" ;
+      exit 1 ;
+      else
+      echo "No Emacs lines found on C/C++, Python and RST files" ;
+      exit 0 ;
+      fi
   timeout: 1h
 
 # Spell checking
 spell-check:
-  stage: code-linting
+  stage: .pre
   image: python:latest
   before_script:
     - pip install codespell
   script:
-    - codespell ./
+    # Get commit messages
+    - if (git remote | grep -qw upstream) ; then
+      git remote remove upstream ;
+      fi
+    - git remote add -t $CI_DEFAULT_BRANCH --no-tags -f upstream https://gitlab.com/nsnam/ns-3-dev.git
+
+    - git log --pretty=%B HEAD...upstream/$CI_DEFAULT_BRANCH ^upstream/$CI_DEFAULT_BRANCH > git_messages.txt
+    # Check source code and commit messages
+    - codespell -f -C0 ./
   timeout: 1h
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-cppyy.yml ns-3.39/utils/tests/gitlab-ci-cppyy.yml
--- ns-3.38/utils/tests/gitlab-ci-cppyy.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-cppyy.yml	2023-07-05 14:23:48.141428500 -0700
@@ -1,8 +1,11 @@
 cppyy-22.04:
+  stage: test
   image: ubuntu:22.04 # python 3.10
-  only:
-    variables:
-      - $RELEASE == "weekly"
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+  needs: ["weekly-slow-jobs"]
+  dependencies: []
   tags:
     - nsnam
     - linux
@@ -13,7 +16,7 @@
       python3 python3-pip
       libboost-dev libgsl-dev libgtk-3-dev
       git wget
-    - pip install cppyy matplotlib numpy
+    - pip install cppyy==2.4.2 matplotlib numpy
   script:
     - ./ns3 configure -G Ninja --enable-python-bindings
     - ./ns3 build
@@ -23,7 +26,6 @@
     - ./ns3 run wifi-ap.py
     - ./ns3 run simple-routing-ping6.py
     - ./ns3 run realtime-udp-echo.py
-    - ./ns3 run bianchi11ax.py
     - ./ns3 run sample-simulator.py
     - ./ns3 run "sample-rng-plot.py --not-blocking"
     - ./ns3 run csma-bridge.py
@@ -34,10 +36,10 @@
   timeout: 9h
 
 cppyy-20.04:
+  stage: test
   image: ubuntu:20.04 # python 3.8
-  only:
-    variables:
-      - $RELEASE == "manual"
+  rules:
+    - if: $RELEASE == "manual"
   tags:
     - nsnam
     - linux
@@ -48,7 +50,7 @@
       python3 python3-pip
       libboost-dev libgsl-dev libgtk-3-dev
       git wget
-    - pip install cppyy
+    - pip install cppyy==2.4.2
   script:
     - ./ns3 configure -G Ninja --enable-python-bindings
     - ./ns3 build
@@ -59,10 +61,10 @@
   timeout: 9h
 
 cppyy-18.04:
+  stage: test
   image: ubuntu:18.04 # python 3.6
-  only:
-    variables:
-      - $RELEASE == "manual"
+  rules:
+    - if: $RELEASE == "manual"
   tags:
     - nsnam
     - linux
@@ -86,3 +88,89 @@
     - ./ns3 run third.py
     - ./ns3 run ./utils/python-unit-tests.py
   timeout: 9h
+
+.manylinux-pip-wheel:
+  stage: test
+  image: quay.io/pypa/manylinux_2_28_x86_64
+  rules:
+    - if: $RELEASE == "manual"
+  script:
+    # Untar libraries (just to make CMake happy, but we are not going to actually link them)
+    # https://github.com/scikit-build/scikit-build/pull/47
+    - tar -xvf /opt/_internal/static-libs-for-embedding-only.tar.xz -C /opt/_internal
+    # Install minimal toolchain
+    - yum install -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel
+    # Create Python venv
+    - $PYTHON -m venv ./venv
+    - . ./venv/bin/activate
+    # Upgrade the pip version to reuse the pre-build cppyy
+    - $PYTHON -m pip install pip --upgrade
+    - $PYTHON -m pip install setuptools setuptools_scm wheel --upgrade
+    - $PYTHON -m pip install wheel auditwheel cmake-build-extension cppyy==2.4.2
+    # Configure and build wheel
+    - $PYTHON setup.py bdist_wheel build_ext
+    - export EXCLUDE_INTERNAL_LIBRARIES=`$PYTHON ./build-support/pip-wheel/auditwheel-exclude-list.py`
+    # Bundle in shared libraries that were not explicitly packaged or depended upon
+    - $PYTHON -m auditwheel repair ./dist/*whl -L /lib64 $EXCLUDE_INTERNAL_LIBRARIES
+    # Clean the build directory
+    - $PYTHON ./ns3 clean
+    # Clean up the environment
+    - deactivate
+    - rm -R ./venv
+    # Delete toolchain to check if required headers/libraries were really packaged
+    - yum remove -y libxml2-devel gsl-devel sqlite-devel gtk3-devel boost-devel
+    # Install wheel
+    - $PYTHON -m pip install ./wheelhouse/*whl
+    - $PYTHON -m pip install matplotlib numpy
+    # Test the bindings
+    - $PYTHON ./utils/python-unit-tests.py
+    - $PYTHON ./examples/realtime/realtime-udp-echo.py
+    - $PYTHON ./examples/routing/simple-routing-ping6.py
+    - $PYTHON ./examples/tutorial/first.py
+    - $PYTHON ./examples/tutorial/second.py
+    - $PYTHON ./examples/tutorial/third.py
+    - $PYTHON ./examples/wireless/wifi-ap.py
+    - $PYTHON ./examples/wireless/mixed-wired-wireless.py
+    - $PYTHON ./src/bridge/examples/csma-bridge.py
+    - $PYTHON ./src/brite/examples/brite-generic-example.py
+    - $PYTHON ./src/core/examples/sample-simulator.py
+    - $PYTHON ./src/core/examples/sample-rng-plot.py --not-blocking
+    - $PYTHON ./src/click/examples/nsclick-simple-lan.py
+    - $PYTHON ./src/flow-monitor/examples/wifi-olsr-flowmon.py
+    - $PYTHON ./src/flow-monitor/examples/flowmon-parse-results.py output.xml
+    - $PYTHON ./src/openflow/examples/openflow-switch.py
+  timeout: 3h
+  artifacts:
+    paths:
+      - wheelhouse/*.whl
+    when: on_success
+
+manylinux-pip-wheel-py3.6:
+  extends: .manylinux-pip-wheel
+  variables:
+    PYTHON: "python3.6"
+
+manylinux-pip-wheel-py3.7:
+  extends: .manylinux-pip-wheel
+  variables:
+    PYTHON: "python3.7"
+
+manylinux-pip-wheel-py3.8:
+  extends: .manylinux-pip-wheel
+  variables:
+    PYTHON: "python3.8"
+
+manylinux-pip-wheel-py3.9:
+  extends: .manylinux-pip-wheel
+  variables:
+    PYTHON: "python3.9"
+
+manylinux-pip-wheel-py3.10:
+  extends: .manylinux-pip-wheel
+  variables:
+    PYTHON: "python3.10"
+
+manylinux-pip-wheel-py3.11:
+  extends: .manylinux-pip-wheel
+  variables:
+    PYTHON: "python3.11"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-daily.yml ns-3.39/utils/tests/gitlab-ci-daily.yml
--- ns-3.38/utils/tests/gitlab-ci-daily.yml	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/utils/tests/gitlab-ci-daily.yml	2023-07-05 14:23:48.141428500 -0700
@@ -0,0 +1,218 @@
+# NS3 CI script for testing
+
+# Defines the steps to run the tests
+# Inherit with "extends: .base-test" and remember to set
+# the following variables: COMPILER (g++, clang++, ...) and
+# MODE (debug, default, optimized)
+
+.base-test:
+  image: archlinux
+  before_script:
+    # add the core-debug repo to pacman.conf
+    - printf "\n%s\n%s\n" "[core-debug]" "Include = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
+    - pacman-key --init
+    - pacman -Syu --noconfirm
+      base-devel cmake ninja ccache valgrind
+      python
+      boost gsl gtk3
+      glibc-debug
+  script:
+    - mkdir -p $CCACHE_BASEDIR_VALUE
+    - export CCACHE_BASEDIR=${PWD}
+    - export CCACHE_DIR=${PWD}/$CCACHE_BASEDIR_VALUE
+    # missing the flag --disable-python
+    - ./ns3 configure -d $MODE -GNinja --enable-examples --enable-tests --disable-gtk
+    - if [[ "$CI_JOB_STAGE" == "build" ]];
+      then ./ns3 build;
+      fi
+    - if [[ "$CI_JOB_STAGE" == "test" ]] || [[ "$FORCE_TESTS" == "Force" ]];
+      then ./test.py -n $VALGRIND_FLAG $FULLNESS;
+      fi
+  cache:
+    key: "ccache-$CI_JOB_NAME"
+    paths:
+      - $CCACHE_BASEDIR_VALUE/
+  variables:
+    COMPILER: g++
+    CCACHE_BASEDIR_VALUE: ns-3-ccache-storage
+    VALGRIND_FLAG: ""
+    FULLNESS: ""
+    # workaround for Valgrind on Archlinux https://bbs.archlinux.org/viewtopic.php?pid=2036171#p2036171
+    DEBUGINFOD_URLS: "https://debuginfod.archlinux.org"
+
+daily-jobs:
+  rules:
+    - if: $RELEASE == "daily"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      when: manual
+      allow_failure: true
+  stage: pre-build
+  script:
+    - echo "Starting daily jobs"
+
+weekly-slow-jobs:
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      when: manual
+      allow_failure: true
+  stage: pre-build
+  script:
+    - echo "Starting weekly jobs"
+
+# Run the test.py script with files compiled in debug mode
+daily-build-test-debug:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "daily"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  needs: ["daily-jobs"]
+  dependencies: []
+  stage: build
+  variables:
+    MODE: debug
+  tags:
+    - nsnam
+    - linux
+
+# Run the test.py script with files compiled in default mode
+daily-build-default:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "daily"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: build
+  needs: ["daily-jobs"]
+  variables:
+    MODE: default
+
+daily-test-default:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "daily"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: test
+  needs: ["daily-build-default"]
+  dependencies:
+    - daily-build-default
+  variables:
+    MODE: default
+
+# Run the test.py script with files compiled in optimized mode
+daily-build-optimized:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "daily"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: build
+  needs: ["daily-jobs"]
+  variables:
+    MODE: optimized
+
+daily-test-optimized:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "daily"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: test
+  needs: ["daily-build-optimized"]
+  dependencies:
+    - daily-build-optimized
+  variables:
+    MODE: optimized
+
+### Valgrind tests
+# Run the test.py script with files compiled in optimized mode + valgrind (daily)
+daily-build-test-optimized-valgrind:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "daily"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: build
+  needs: ["daily-jobs"]
+  dependencies: []
+  variables:
+    MODE: optimized
+    VALGRIND_FLAG: -g
+    FORCE_TESTS: Force
+  tags:
+    - nsnam
+    - linux
+
+# Run the test.py script with files compiled in debug mode
+weekly-build-test-debug-valgrind:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: build
+  needs: ["weekly-slow-jobs"]
+  dependencies: []
+  variables:
+    MODE: debug
+    VALGRIND_FLAG: -g
+    FORCE_TESTS: Force
+  tags:
+    - nsnam
+    - linux
+
+# Run the test.py script with files compiled in default mode
+weekly-build-test-default-valgrind:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: build
+  needs: ["weekly-slow-jobs"]
+  dependencies: []
+  variables:
+    MODE: default
+    VALGRIND_FLAG: -g
+    FORCE_TESTS: Force
+  tags:
+    - nsnam
+    - linux
+
+# Run the test.py script with files compiled in optimized mode
+weekly-build-test-optimized-valgrind:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: build
+  needs: ["weekly-slow-jobs"]
+  dependencies: []
+  variables:
+    MODE: optimized
+    VALGRIND_FLAG: -g
+    FORCE_TESTS: Force
+  tags:
+    - nsnam
+    - linux
+
+# Do a check for the TAKES_FOREVER jobs, only in optimized mode
+weekly-build-test-takes-forever-optimized:
+  extends: .base-test
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
+  stage: build
+  needs: ["weekly-slow-jobs"]
+  dependencies: []
+  variables:
+    MODE: optimized
+    FULLNESS: "-f TAKES_FOREVER"
+    FORCE_TESTS: Force
+  tags:
+    - nsnam
+    - linux
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-doc.yml ns-3.39/utils/tests/gitlab-ci-doc.yml
--- ns-3.38/utils/tests/gitlab-ci-doc.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-doc.yml	2023-07-05 14:23:48.141428500 -0700
@@ -7,16 +7,21 @@
     - pacman-key --init
     - pacman -Syu --noconfirm
       base-devel ninja cmake python ccache gsl gtk3 boost openmpi openssh
-      graphviz imagemagick texlive-core texlive-bin texlive-latexextra python-sphinx
-      doxygen
+      graphviz imagemagick texlive-basic texlive-bin texlive-binextra texlive-latexextra
+      texlive-fontutils texlive-fontsrecommended python-sphinx
+    #  doxygen
     # dia is archived
     - pacman --noconfirm -U https://archive.archlinux.org/packages/d/dia/dia-0.97.3-9-x86_64.pkg.tar.zst
+    # temporary revert to Doxygen 1.9.6
+    - pacman --noconfirm -U https://archive.archlinux.org/packages/d/doxygen/doxygen-1.9.6-1-x86_64.pkg.tar.zst
     # no policy is a good policy.
     - mv /etc/ImageMagick-7/policy.xml /etc/ImageMagick-7/policy.xml.bak
     # - sed -i "s/EPS,PDF,//g" /etc/ImageMagick-7/policy.xml
     # - sed -i "s/none/read\ |\ write/g" /etc/ImageMagick-7/policy.xml
   image: archlinux
   timeout: 2h
+  interruptible: true
+  dependencies: []
   # artifacts:
   #   paths:
   #     - public
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-fedora.yml ns-3.39/utils/tests/gitlab-ci-fedora.yml
--- ns-3.38/utils/tests/gitlab-ci-fedora.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-fedora.yml	2023-07-05 14:23:48.141428500 -0700
@@ -9,10 +9,10 @@
 # Weekly jobs: Fedora
 .weekly-build-fedora:
   extends: .base-build
-  only:
-    variables:
-      - $RELEASE == "weekly"
-      - $FEDORA == "True"
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
   before_script:
     - dnf update --assumeyes
     - dnf install --assumeyes
@@ -33,59 +33,150 @@
   variables:
     COMPILER: g++
 
-# Fedora 34
-# weekly-build-fedora-34-debug:
+weekly-fedora:
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      when: manual
+      allow_failure: true
+  stage: pre-build
+  script:
+    - echo "Starting Fedora jobs"
+
+# Fedora 36
+# weekly-build-fedora-36-debug:
 #   extends: .weekly-build-fedora
-#   image: fedora:34
+#   needs: ["weekly-fedora"]
+#   image: fedora:36
+#   stage: build
 #   variables:
 #     MODE: debug
 
-# weekly-build-fedora-34-default:
+# weekly-build-fedora-36-default:
+#   extends: .weekly-build-fedora
+#   needs: ["weekly-fedora"]
+#   image: fedora:36
+#   stage: build
+#   variables:
+#     MODE: default
+
+# weekly-build-fedora-36-optimized:
+#   extends: .weekly-build-fedora
+#   needs: ["weekly-fedora"]
+#   image: fedora:36
+#   stage: build
+#   variables:
+#     MODE: optimized
+
+# Fedora 36 test
+# weekly-test-fedora-36-default:
 #   extends: .weekly-build-fedora
-#   image: fedora:34
+#   image: fedora:36
+#   stage: test
+#   needs: ["weekly-build-fedora-36-default"]
+#   dependencies:
+#     - weekly-build-fedora-36-default
 #   variables:
 #     MODE: default
 
-# weekly-build-fedora-34-optimized:
+# weekly-test-fedora-36-optimized:
 #   extends: .weekly-build-fedora
-#   image: fedora:34
+#   image: fedora:36
+#   stage: test
+#   needs: ["weekly-build-fedora-36-optimized"]
+#   dependencies:
+#     - weekly-build-fedora-36-optimized
 #   variables:
 #     MODE: optimized
 
-# Fedora 35
-weekly-build-fedora-35-debug:
+# Fedora 37
+weekly-build-fedora-37-debug:
   extends: .weekly-build-fedora
-  image: fedora:35
+  needs: ["weekly-fedora"]
+  image: fedora:37
+  stage: build
   variables:
     MODE: debug
 
-weekly-build-fedora-35-default:
+weekly-build-fedora-37-default:
   extends: .weekly-build-fedora
-  image: fedora:35
+  needs: ["weekly-fedora"]
+  image: fedora:37
+  stage: build
   variables:
     MODE: default
 
-weekly-build-fedora-35-optimized:
+weekly-build-fedora-37-optimized:
   extends: .weekly-build-fedora
-  image: fedora:35
+  needs: ["weekly-fedora"]
+  image: fedora:37
+  stage: build
   variables:
     MODE: optimized
 
-# Fedora 36
-weekly-build-fedora-36-debug:
+# Fedora 37 test
+weekly-test-fedora-37-default:
   extends: .weekly-build-fedora
-  image: fedora:36
+  image: fedora:37
+  stage: test
+  needs: ["weekly-build-fedora-37-default"]
+  dependencies:
+    - weekly-build-fedora-37-default
+  variables:
+    MODE: default
+
+weekly-test-fedora-37-optimized:
+  extends: .weekly-build-fedora
+  image: fedora:37
+  stage: test
+  needs: ["weekly-build-fedora-37-optimized"]
+  dependencies:
+    - weekly-build-fedora-37-optimized
+  variables:
+    MODE: optimized
+
+# Fedora 38
+weekly-build-fedora-38-debug:
+  extends: .weekly-build-fedora
+  needs: ["weekly-fedora"]
+  image: fedora:38
+  stage: build
   variables:
     MODE: debug
 
-weekly-build-fedora-36-default:
+weekly-build-fedora-38-default:
+  extends: .weekly-build-fedora
+  needs: ["weekly-fedora"]
+  image: fedora:38
+  stage: build
+  variables:
+    MODE: default
+
+weekly-build-fedora-38-optimized:
+  extends: .weekly-build-fedora
+  needs: ["weekly-fedora"]
+  image: fedora:38
+  stage: build
+  variables:
+    MODE: optimized
+
+# Fedora 38 test
+weekly-test-fedora-38-default:
   extends: .weekly-build-fedora
-  image: fedora:36
+  image: fedora:38
+  stage: test
+  needs: ["weekly-build-fedora-38-default"]
+  dependencies:
+    - weekly-build-fedora-38-default
   variables:
     MODE: default
 
-weekly-build-fedora-36-optimized:
+weekly-test-fedora-38-optimized:
   extends: .weekly-build-fedora
-  image: fedora:36
+  image: fedora:38
+  stage: test
+  needs: ["weekly-build-fedora-38-optimized"]
+  dependencies:
+    - weekly-build-fedora-38-optimized
   variables:
     MODE: optimized
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-gcc.yml ns-3.39/utils/tests/gitlab-ci-gcc.yml
--- ns-3.38/utils/tests/gitlab-ci-gcc.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-gcc.yml	2023-07-05 14:23:48.141428500 -0700
@@ -7,12 +7,12 @@
 # We support from 8 to the latest version. Check if everything builds fine
 # under debug, default, and optimized, on Linux.
 
-.weekly-build-gcc-base:
+.weekly-gcc-base:
   extends: .base-build
-  only:
-    variables:
-      - $RELEASE == "weekly"
-      - $UBUNTU == "True"
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
   before_script:
     - apt update
     - apt upgrade -y
@@ -32,101 +32,286 @@
   variables:
     ENABLE_MPI: --enable-mpi
 
+weekly-gcc:
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      when: manual
+      allow_failure: true
+  stage: pre-build
+  script:
+    - echo "Starting GCC jobs"
+
+# GCC 8
+weekly-build-gcc-8-debug:
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
+  image: ubuntu:20.04
+  stage: build
+  variables:
+    MODE: debug
+    COMPILER: g++-8
+    LIBGSL: libgsl23
+
+weekly-build-gcc-8-default:
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
+  image: ubuntu:20.04
+  stage: build
+  variables:
+    MODE: default
+    COMPILER: g++-8
+    LIBGSL: libgsl23
+
+weekly-build-gcc-8-optimized:
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
+  image: ubuntu:20.04
+  stage: build
+  variables:
+    MODE: optimized
+    COMPILER: g++-8
+    LIBGSL: libgsl23
+
+weekly-test-gcc-8-default:
+  extends: .weekly-gcc-base
+  image: ubuntu:20.04
+  stage: test
+  needs: ["weekly-build-gcc-8-default"]
+  dependencies:
+    - weekly-build-gcc-8-default
+  variables:
+    MODE: default
+    COMPILER: g++-8
+    LIBGSL: libgsl23
+
+weekly-test-gcc-8-optimized:
+  extends: .weekly-gcc-base
+  image: ubuntu:20.04
+  stage: test
+  needs: ["weekly-build-gcc-8-optimized"]
+  dependencies:
+    - weekly-build-gcc-8-optimized
+  variables:
+    MODE: optimized
+    COMPILER: g++-8
+    LIBGSL: libgsl23
+
 # GCC 9
 weekly-build-gcc-9-debug:
-  extends: .weekly-build-gcc-base
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
   image: ubuntu:20.04
+  stage: build
   variables:
     MODE: debug
     COMPILER: g++-9
     LIBGSL: libgsl23
 
 weekly-build-gcc-9-default:
-  extends: .weekly-build-gcc-base
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
   image: ubuntu:20.04
+  stage: build
   variables:
     MODE: default
     COMPILER: g++-9
     LIBGSL: libgsl23
 
 weekly-build-gcc-9-optimized:
-  extends: .weekly-build-gcc-base
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
   image: ubuntu:20.04
+  stage: build
   variables:
     MODE: optimized
     COMPILER: g++-9
     LIBGSL: libgsl23
 
-# GCC 10
-weekly-build-gcc-10-debug:
-  extends: .weekly-build-gcc-base
-  image: ubuntu:20.04
-  variables:
-    MODE: debug
-    COMPILER: g++-10
-    LIBGSL: libgsl23
-
-weekly-build-gcc-10-default:
-  extends: .weekly-build-gcc-base
+weekly-test-gcc-9-default:
+  extends: .weekly-gcc-base
   image: ubuntu:20.04
+  stage: test
+  needs: ["weekly-build-gcc-9-default"]
+  dependencies:
+    - weekly-build-gcc-9-default
   variables:
     MODE: default
-    COMPILER: g++-10
+    COMPILER: g++-9
     LIBGSL: libgsl23
 
-weekly-build-gcc-10-optimized:
-  extends: .weekly-build-gcc-base
+weekly-test-gcc-9-optimized:
+  extends: .weekly-gcc-base
   image: ubuntu:20.04
+  stage: test
+  needs: ["weekly-build-gcc-9-optimized"]
+  dependencies:
+    - weekly-build-gcc-9-optimized
   variables:
     MODE: optimized
-    COMPILER: g++-10
+    COMPILER: g++-9
     LIBGSL: libgsl23
 
+# GCC 10
+# weekly-build-gcc-10-debug:
+#   extends: .weekly-gcc-base
+#   needs: ["weekly-gcc"]
+#   image: ubuntu:20.04
+#   stage: build
+#   variables:
+#     MODE: debug
+#     COMPILER: g++-10
+#     LIBGSL: libgsl23
+
+# weekly-build-gcc-10-default:
+#   extends: .weekly-gcc-base
+#   needs: ["weekly-gcc"]
+#   image: ubuntu:20.04
+#   stage: build
+#   variables:
+#     MODE: default
+#     COMPILER: g++-10
+#     LIBGSL: libgsl23
+
+# weekly-build-gcc-10-optimized:
+#   extends: .weekly-gcc-base
+#   needs: ["weekly-gcc"]
+#   image: ubuntu:20.04
+#   stage: build
+#   variables:
+#     MODE: optimized
+#     COMPILER: g++-10
+#     LIBGSL: libgsl23
+
+# weekly-test-gcc-10-default:
+#   extends: .weekly-gcc-base
+#   image: ubuntu:20.04
+#   stage: test
+#   needs: ["weekly-build-gcc-10-default"]
+#   dependencies:
+#     - weekly-build-gcc-10-default
+#   variables:
+#     MODE: default
+#     COMPILER: g++-10
+#     LIBGSL: libgsl23
+
+# weekly-test-gcc-10-optimized:
+#   extends: .weekly-gcc-base
+#   image: ubuntu:20.04
+#   stage: test
+#   needs: ["weekly-build-gcc-10-optimized"]
+#   dependencies:
+#     - weekly-build-gcc-10-optimized
+#   variables:
+#     MODE: optimized
+#     COMPILER: g++-10
+#     LIBGSL: libgsl23
+
 # GCC 11
-weekly-build-gcc-11-debug:
-  extends: .weekly-build-gcc-base
+# weekly-build-gcc-11-debug:
+#   extends: .weekly-gcc-base
+#   needs: ["weekly-gcc"]
+#   image: ubuntu:22.04
+#   stage: build
+#   variables:
+#     MODE: debug
+#     COMPILER: g++-11
+#     LIBGSL: libgsl27
+
+# weekly-build-gcc-11-default:
+#   extends: .weekly-gcc-base
+#   needs: ["weekly-gcc"]
+#   image: ubuntu:22.04
+#   stage: build
+#   variables:
+#     MODE: default
+#     COMPILER: g++-11
+#     LIBGSL: libgsl27
+
+# weekly-build-gcc-11-optimized:
+#   extends: .weekly-gcc-base
+#   needs: ["weekly-gcc"]
+#   image: ubuntu:22.04
+#   stage: build
+#   variables:
+#     MODE: optimized
+#     COMPILER: g++-11
+#     LIBGSL: libgsl27
+
+# weekly-test-gcc-11-default:
+#   extends: .weekly-gcc-base
+#   image: ubuntu:22.04
+#   stage: test
+#   needs: ["weekly-build-gcc-11-default"]
+#   dependencies:
+#     - weekly-build-gcc-11-default
+#   variables:
+#     MODE: default
+#     COMPILER: g++-11
+#     LIBGSL: libgsl27
+
+# weekly-test-gcc-11-optimized:
+#   extends: .weekly-gcc-base
+#   image: ubuntu:22.04
+#   stage: test
+#   needs: ["weekly-build-gcc-11-optimized"]
+#   dependencies:
+#     - weekly-build-gcc-11-optimized
+#   variables:
+#     MODE: optimized
+#     COMPILER: g++-11
+#     LIBGSL: libgsl27
+
+# GCC 12
+weekly-build-gcc-12-debug:
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
   image: ubuntu:22.04
+  stage: build
   variables:
     MODE: debug
-    COMPILER: g++-11
+    COMPILER: g++-12
     LIBGSL: libgsl27
 
-weekly-build-gcc-11-default:
-  extends: .weekly-build-gcc-base
+weekly-build-gcc-12-default:
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
   image: ubuntu:22.04
+  stage: build
   variables:
     MODE: default
-    COMPILER: g++-11
+    COMPILER: g++-12
     LIBGSL: libgsl27
 
-weekly-build-gcc-11-optimized:
-  extends: .weekly-build-gcc-base
+weekly-build-gcc-12-optimized:
+  extends: .weekly-gcc-base
+  needs: ["weekly-gcc"]
   image: ubuntu:22.04
+  stage: build
   variables:
     MODE: optimized
-    COMPILER: g++-11
-    LIBGSL: libgsl27
-
-# GCC 12
-weekly-build-gcc-12-debug:
-  extends: .weekly-build-gcc-base
-  image: ubuntu:22.04
-  variables:
-    MODE: debug
     COMPILER: g++-12
     LIBGSL: libgsl27
 
-weekly-build-gcc-12-default:
-  extends: .weekly-build-gcc-base
+weekly-test-gcc-12-default:
+  extends: .weekly-gcc-base
   image: ubuntu:22.04
+  stage: test
+  needs: ["weekly-build-gcc-12-default"]
+  dependencies:
+    - weekly-build-gcc-12-default
   variables:
     MODE: default
     COMPILER: g++-12
     LIBGSL: libgsl27
 
-weekly-build-gcc-12-optimized:
-  extends: .weekly-build-gcc-base
+weekly-test-gcc-12-optimized:
+  extends: .weekly-gcc-base
   image: ubuntu:22.04
+  stage: test
+  needs: ["weekly-build-gcc-12-optimized"]
+  dependencies:
+    - weekly-build-gcc-12-optimized
   variables:
     MODE: optimized
     COMPILER: g++-12
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-per-commit.yml ns-3.39/utils/tests/gitlab-ci-per-commit.yml
--- ns-3.38/utils/tests/gitlab-ci-per-commit.yml	1969-12-31 16:00:00.000000000 -0800
+++ ns-3.39/utils/tests/gitlab-ci-per-commit.yml	2023-07-05 14:23:48.141428500 -0700
@@ -0,0 +1,77 @@
+# NS3 CI script for per-commit jobs
+
+# Defines the per-commit jobs. They are executed for any branch
+.base-per-commit-compile:
+  extends: .base-build
+  except:
+    variables:
+      - $CPPYY == "True"
+      - $RELEASE == "weekly"
+      - $RELEASE == "daily"
+  interruptible: true
+  image: archlinux
+  before_script:
+    - pacman-key --init
+    - pacman -Syu --noconfirm
+      base-devel gcc clang cmake ninja ccache
+      python
+      boost gsl gtk3 openmpi
+      openssh
+  variables:
+    ENABLE_MPI: --enable-mpi
+
+per-commit-clang-debug:
+  extends: .base-per-commit-compile
+  stage: build
+  variables:
+    MODE: debug
+    COMPILER: clang++
+
+per-commit-gcc-debug:
+  extends: .base-per-commit-compile
+  stage: build
+  variables:
+    MODE: debug
+    COMPILER: g++
+
+per-commit-gcc-default:
+  extends: .base-per-commit-compile
+  stage: build
+  variables:
+    MODE: default
+    COMPILER: g++
+
+per-commit-gcc-optimized:
+  extends: .base-per-commit-compile
+  stage: build
+  variables:
+    MODE: optimized
+    COMPILER: g++
+
+per-commit-gcc-default-test:
+  extends: .base-per-commit-compile
+  stage: test
+  needs: ["per-commit-gcc-default"]
+  dependencies:
+    - per-commit-gcc-default
+  variables:
+    MODE: default
+    COMPILER: g++
+
+per-commit-gcc-optimized-test:
+  extends: .base-per-commit-compile
+  stage: test
+  needs: ["per-commit-gcc-optimized"]
+  dependencies:
+    - per-commit-gcc-optimized
+  variables:
+    MODE: optimized
+    COMPILER: g++
+
+per-commit-disabled-precompiled-headers:
+  extends: .base-per-commit-compile
+  stage: build
+  variables:
+    MODE: debug
+    COMPILER: g++
+    EXTRA_OPTIONS: --disable-precompiled-headers
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-test.yml ns-3.39/utils/tests/gitlab-ci-test.yml
--- ns-3.38/utils/tests/gitlab-ci-test.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-test.yml	1969-12-31 16:00:00.000000000 -0800
@@ -1,135 +0,0 @@
-# NS3 CI script for testing
-
-# Defines the steps to run the tests
-# Inherit with "extends: .base-test" and remember to set
-# the following variables: COMPILER (g++, clang++, ...) and
-# MODE (debug, default, optimized)
-
-.base-test:
-  stage: test
-  image: archlinux
-  before_script:
-    # add the core-debug repo to pacman.conf
-    - printf "\n%s\n%s\n" "[core-debug]" "Include = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
-    - pacman-key --init
-    - pacman -Syu --noconfirm
-      base-devel cmake ninja ccache valgrind
-      python
-      boost gsl gtk3
-      glibc-debug
-  script:
-    - mkdir -p $CCACHE_BASEDIR_VALUE
-    - export CCACHE_BASEDIR=${PWD}
-    - export CCACHE_DIR=${PWD}/$CCACHE_BASEDIR_VALUE
-    # missing the flag --disable-python
-    - ./ns3 configure -d $MODE -GNinja --enable-examples --enable-tests --disable-gtk
-    - ./ns3 build
-    - ./test.py -n $VALGRIND_FLAG $FULLNESS
-  cache:
-    key: "ccache-$CI_JOB_NAME"
-    paths:
-      - $CCACHE_BASEDIR_VALUE/
-    when: "always"
-  variables:
-    COMPILER: g++
-    CCACHE_BASEDIR_VALUE: ns-3-ccache-storage
-    VALGRIND_FLAG: ""
-    FULLNESS: ""
-    # workaround for Valgrind on Archlinux https://bbs.archlinux.org/viewtopic.php?pid=2036171#p2036171
-    DEBUGINFOD_URLS: "https://debuginfod.archlinux.org"
-
-# Run the test.py script with files compiled in debug mode
-daily-test-debug:
-  extends: .base-test
-  variables:
-    MODE: debug
-  only:
-    variables:
-      - $RELEASE == "daily"
-  tags:
-    - nsnam
-    - linux
-
-# Run the test.py script with files compiled in default mode
-daily-test-default:
-  extends: .base-test
-  variables:
-    MODE: default
-  only:
-    variables:
-      - $RELEASE == "daily"
-
-# Run the test.py script with files compiled in optimized mode
-daily-test-optimized:
-  extends: .base-test
-  variables:
-    MODE: optimized
-  only:
-    variables:
-      - $RELEASE == "daily"
-
-### Valgrind tests
-# Run the test.py script with files compiled in optimized mode + valgrind (daily)
-daily-test-optimized-valgrind:
-  extends: .base-test
-  variables:
-    MODE: optimized
-    VALGRIND_FLAG: -g
-  only:
-    variables:
-      - $RELEASE == "daily"
-  tags:
-    - nsnam
-    - linux
-
-# Run the test.py script with files compiled in debug mode
-weekly-test-debug-valgrind:
-  extends: .base-test
-  variables:
-    MODE: debug
-    VALGRIND_FLAG: -g
-  only:
-    variables:
-      - $RELEASE == "weekly"
-  tags:
-    - nsnam
-    - linux
-
-# Run the test.py script with files compiled in default mode
-weekly-test-default-valgrind:
-  extends: .base-test
-  variables:
-    MODE: default
-    VALGRIND_FLAG: -g
-  only:
-    variables:
-      - $RELEASE == "weekly"
-  tags:
-    - nsnam
-    - linux
-
-# Run the test.py script with files compiled in optimized mode
-weekly-test-optimized-valgrind:
-  extends: .base-test
-  variables:
-    MODE: optimized
-    VALGRIND_FLAG: -g
-  only:
-    variables:
-      - $RELEASE == "weekly"
-  tags:
-    - nsnam
-    - linux
-
-# Do a check for the TAKES_FOREVER jobs, only in optimized mode
-weekly-test-takes-forever-optimized:
-  extends: .base-test
-  variables:
-    MODE: optimized
-    FULLNESS: "-f TAKES_FOREVER"
-  only:
-    variables:
-      - $RELEASE == "weekly"
-  tags:
-    - nsnam
-    - linux
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci-ubuntu.yml ns-3.39/utils/tests/gitlab-ci-ubuntu.yml
--- ns-3.38/utils/tests/gitlab-ci-ubuntu.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci-ubuntu.yml	2023-07-05 14:23:48.141428500 -0700
@@ -7,17 +7,21 @@
 # Please take a look at .gitlab-ci.yml to check the dependencies of Ubuntu jobs.
 
 # Ubuntu base
-.weekly-build-ubuntu:
+.weekly-base-ubuntu:
   extends: .base-build
-  only:
-    variables:
-      - $RELEASE == "weekly"
-      - $UBUNTU == "True"
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      allow_failure: true
   before_script:
+    #Â The following ppa is needed only for Ubuntu 18.04 to install g++9
+    - apt update
+    - DEBIAN_FRONTEND=noninteractive apt install -y software-properties-common
+    - add-apt-repository ppa:ubuntu-toolchain-r/test -y
     - apt update
     - apt upgrade -y
     - DEBIAN_FRONTEND=noninteractive apt install -y
-      g++ cmake ninja-build ccache
+      $COMPILER cmake ninja-build ccache
       python3 python3-dev
       gsl-bin libgsl-dev $LIBGSL
       libboost-all-dev
@@ -30,194 +34,220 @@
       qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
       ssh
 
-  variables:
-    COMPILER: g++
+weekly-ubuntu:
+  rules:
+    - if: $RELEASE == "weekly"
+    - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
+      when: manual
+      allow_failure: true
+  stage: pre-build
+  script:
+    - echo "Starting Ubuntu jobs"
 
 # Ubuntu 18.04 (Until April 2023)
 weekly-build-ubuntu-18.04-debug:
-  extends: .weekly-build-ubuntu
+  extends: .weekly-base-ubuntu
+  needs: ["weekly-ubuntu"]
   image: ubuntu:18.04
-  before_script:
-    - apt update
-    - apt upgrade -y
-    - apt install -y software-properties-common
-    - add-apt-repository ppa:ubuntu-toolchain-r/test -y
-    - apt update
-    - DEBIAN_FRONTEND=noninteractive apt install -y
-      g++-9 cmake ninja-build ccache
-      python3 python3-dev
-      gsl-bin libgsl-dev $LIBGSL
-      libboost-all-dev
-      libgtk-3-dev
-      libfl-dev
-      libxml2 libxml2-dev
-      libopenmpi-dev openmpi-bin openmpi-common openmpi-doc
-      libsqlite3-dev sqlite3
-      qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      ssh
-    - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 700 --slave /usr/bin/g++ g++ /usr/bin/g++-7
-    - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 800 --slave /usr/bin/g++ g++ /usr/bin/g++-9
+  stage: build
   variables:
     MODE: debug
     LIBGSL: libgsl23
+    COMPILER: g++-9
 
 weekly-build-ubuntu-18.04-default:
-  extends: .weekly-build-ubuntu
+  extends: .weekly-base-ubuntu
+  needs: ["weekly-ubuntu"]
   image: ubuntu:18.04
-  before_script:
-    - apt update
-    - apt install -y software-properties-common
-    - add-apt-repository ppa:ubuntu-toolchain-r/test -y
-    - apt update
-    - DEBIAN_FRONTEND=noninteractive apt install -y
-      g++-9 cmake ninja-build ccache
-      python3 python3-dev
-      gsl-bin libgsl-dev $LIBGSL
-      libboost-all-dev
-      libgtk-3-dev
-      libfl-dev
-      libxml2 libxml2-dev
-      libopenmpi-dev openmpi-bin openmpi-common openmpi-doc
-      libsqlite3-dev sqlite3
-      qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      ssh
-    - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 700 --slave /usr/bin/g++ g++ /usr/bin/g++-7
-    - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 800 --slave /usr/bin/g++ g++ /usr/bin/g++-9
+  stage: build
   variables:
     MODE: default
     LIBGSL: libgsl23
+    COMPILER: g++-9
 
 weekly-build-ubuntu-18.04-optimized:
-  extends: .weekly-build-ubuntu
+  extends: .weekly-base-ubuntu
+  needs: ["weekly-ubuntu"]
   image: ubuntu:18.04
-  before_script:
-    - apt update
-    - apt install -y software-properties-common
-    - add-apt-repository ppa:ubuntu-toolchain-r/test -y
-    - apt update
-    - DEBIAN_FRONTEND=noninteractive apt install -y
-      g++-9 cmake ninja-build ccache
-      python3 python3-dev
-      gsl-bin libgsl-dev $LIBGSL
-      libboost-all-dev
-      libgtk-3-dev
-      libfl-dev
-      libxml2 libxml2-dev
-      libopenmpi-dev openmpi-bin openmpi-common openmpi-doc
-      libsqlite3-dev sqlite3
-      qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools
-      ssh
-    - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 700 --slave /usr/bin/g++ g++ /usr/bin/g++-7
-    - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 800 --slave /usr/bin/g++ g++ /usr/bin/g++-9
+  stage: build
   variables:
     MODE: optimized
     LIBGSL: libgsl23
+    COMPILER: g++-9
+
+weekly-test-ubuntu-18.04-default:
+  extends: .weekly-base-ubuntu
+  image: ubuntu:18.04
+  stage: test
+  needs: ["weekly-build-ubuntu-18.04-default"]
+  dependencies:
+    - weekly-build-ubuntu-18.04-default
+  variables:
+    MODE: default
+    LIBGSL: libgsl23
+    COMPILER: g++-9
+
+weekly-test-ubuntu-18.04-optimized:
+  extends: .weekly-base-ubuntu
+  image: ubuntu:18.04
+  stage: test
+  needs: ["weekly-build-ubuntu-18.04-optimized"]
+  dependencies:
+    - weekly-build-ubuntu-18.04-optimized
+  variables:
+    MODE: optimized
+    LIBGSL: libgsl23
+    COMPILER: g++-9
 
 # # Ubuntu 20.04 (Until April 2025)
 # weekly-build-ubuntu-20.04-debug:
-#   extends: .weekly-build-ubuntu
+#   extends: .weekly-base-ubuntu
 #   image: ubuntu:20.04
 #   variables:
 #     MODE: debug
 #     LIBGSL: libgsl23
 
 # weekly-build-ubuntu-20.04-default:
-#   extends: .weekly-build-ubuntu
+#   extends: .weekly-base-ubuntu
 #   image: ubuntu:20.04
 #   variables:
 #     MODE: default
 #     LIBGSL: libgsl23
 
 # weekly-build-ubuntu-20.04-optimized:
-#   extends: .weekly-build-ubuntu
+#   extends: .weekly-base-ubuntu
 #   image: ubuntu:20.04
 #   variables:
 #     MODE: optimized
 #     LIBGSL: libgsl23
 
-# # Ubuntu 21.04 (Until January 2022)
-# weekly-build-ubuntu-21.04-debug:
-#   extends: .weekly-build-ubuntu
-#   image: ubuntu:21.04
+# Ubuntu 22.04 (Until January 2032) -- commented out because we are using Ubuntu 22.04 for gcc jobs
+# weekly-build-ubuntu-22.04-debug:
+#   extends: .weekly-base-ubuntu
+#   needs: ["weekly-ubuntu"]
+#   image: ubuntu:22.04
+#   stage: build
 #   variables:
 #     MODE: debug
-#     LIBGSL: libgsl25
+#     LIBGSL: libgsl27
+#     COMPILER: g++
+
+# weekly-build-ubuntu-22.04-default:
+#   extends: .weekly-base-ubuntu
+#   needs: ["weekly-ubuntu"]
+#   image: ubuntu:22.04
+#   stage: build
+#   variables:
+#     MODE: default
+#     LIBGSL: libgsl27
+#     COMPILER: g++
+
+# weekly-build-ubuntu-22.04-optimized:
+#   extends: .weekly-base-ubuntu
+#   needs: ["weekly-ubuntu"]
+#   image: ubuntu:22.04
+#   stage: build
+#   variables:
+#     MODE: optimized
+#     LIBGSL: libgsl27
+#     COMPILER: g++
 
-# weekly-build-ubuntu-21.04-default:
-#   extends: .weekly-build-ubuntu
-#   image: ubuntu:21.04
+# weekly-test-ubuntu-22.04-default:
+#   extends: .weekly-base-ubuntu
+#   image: ubuntu:22.04
+#   stage: test
+#   needs: ["weekly-build-ubuntu-22.04-default"]
+#   dependencies:
+#     - weekly-build-ubuntu-22.04-default
 #   variables:
 #     MODE: default
-#     LIBGSL: libgsl25
+#     LIBGSL: libgsl27
+#     COMPILER: g++
 
-# weekly-build-ubuntu-21.04-optimized:
-#   extends: .weekly-build-ubuntu
-#   image: ubuntu:21.04
+# weekly-test-ubuntu-22.04-optimized:
+#   extends: .weekly-base-ubuntu
+#   image: ubuntu:22.04
+#   stage: test
+#   needs: ["weekly-build-ubuntu-22.04-optimized"]
+#   dependencies:
+#     - weekly-build-ubuntu-22.04-optimized
 #   variables:
 #     MODE: optimized
-#     LIBGSL: libgsl25
+#     LIBGSL: libgsl27
+#     COMPILER: g++
 
-# Ubuntu 22.04 (Until January 2032)
-weekly-build-ubuntu-22.04-debug:
-  extends: .weekly-build-ubuntu
-  image: ubuntu:22.04
+# Ubuntu Latest
+weekly-build-ubuntu-latest-debug:
+  extends: .weekly-base-ubuntu
+  needs: ["weekly-ubuntu"]
+  image: ubuntu:latest
+  stage: build
   variables:
     MODE: debug
     LIBGSL: libgsl27
+    COMPILER: g++
 
-weekly-build-ubuntu-22.04-default:
-  extends: .weekly-build-ubuntu
-  image: ubuntu:22.04
+weekly-build-ubuntu-latest-default:
+  extends: .weekly-base-ubuntu
+  needs: ["weekly-ubuntu"]
+  image: ubuntu:latest
+  stage: build
   variables:
     MODE: default
     LIBGSL: libgsl27
+    COMPILER: g++
 
-weekly-build-ubuntu-22.04-optimized:
-  extends: .weekly-build-ubuntu
-  image: ubuntu:22.04
-  variables:
-    MODE: optimized
-    LIBGSL: libgsl27
-
-# Ubuntu Latest
-weekly-build-ubuntu-latest-debug:
-  extends: .weekly-build-ubuntu
+weekly-build-ubuntu-latest-optimized:
+  extends: .weekly-base-ubuntu
+  needs: ["weekly-ubuntu"]
   image: ubuntu:latest
+  stage: build
   variables:
-    MODE: debug
+    MODE: optimized
     LIBGSL: libgsl27
+    COMPILER: g++
 
-weekly-build-ubuntu-latest-default:
-  extends: .weekly-build-ubuntu
+weekly-test-ubuntu-latest-default:
+  extends: .weekly-base-ubuntu
   image: ubuntu:latest
+  stage: test
+  needs: ["weekly-build-ubuntu-latest-default"]
+  dependencies:
+    - weekly-build-ubuntu-latest-default
   variables:
     MODE: default
     LIBGSL: libgsl27
+    COMPILER: g++
 
-weekly-build-ubuntu-latest-optimized:
-  extends: .weekly-build-ubuntu
+weekly-test-ubuntu-latest-optimized:
+  extends: .weekly-base-ubuntu
   image: ubuntu:latest
+  stage: test
+  needs: ["weekly-build-ubuntu-latest-optimized"]
+  dependencies:
+    - weekly-build-ubuntu-latest-optimized
   variables:
     MODE: optimized
     LIBGSL: libgsl27
+    COMPILER: g++
 
 # Ubuntu Rolling
 # weekly-build-ubuntu-rolling-debug:
-#   extends: .weekly-build-ubuntu
+#   extends: .weekly-base-ubuntu
 #   image: ubuntu:rolling
 #   variables:
 #     MODE: debug
 #     LIBGSL: libgsl27
 
 # weekly-build-ubuntu-rolling-default:
-#   extends: .weekly-build-ubuntu
+#   extends: .weekly-base-ubuntu
 #   image: ubuntu:rolling
 #   variables:
 #     MODE: default
 #     LIBGSL: libgsl27
 
 # weekly-build-ubuntu-rolling-optimized:
-#   extends: .weekly-build-ubuntu
+#   extends: .weekly-base-ubuntu
 #   image: ubuntu:rolling
 #   variables:
 #     MODE: optimized
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/gitlab-ci.yml ns-3.39/utils/tests/gitlab-ci.yml
--- ns-3.38/utils/tests/gitlab-ci.yml	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/gitlab-ci.yml	2023-07-05 14:23:48.141428500 -0700
@@ -18,11 +18,19 @@
 # configuration, still in definition. It will be in the documentation.
 
 stages:
+  - pre-build
   - build
   - test
   - code-linting
   - documentation
 
+workflow:
+  rules:
+    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
+    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
+      when: never
+    - if: $CI_COMMIT_BRANCH
+
 ###################### BUILD STAGE #############################################
 
 # Defines the steps to run the tests
@@ -30,18 +38,28 @@
 # the following variables: COMPILER (g++, clang++, ...) and
 # MODE (debug, default, optimized)
 .base-build:
-  stage: build
   script:
     - mkdir -p $CCACHE_BASEDIR_VALUE
     - 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_MPI
+      $EXTRA_OPTIONS
     - ccache -z
-    - ./ns3 build
-    - if [ "$MODE" != "debug" ] && [ "`./utils/ccache-miss-rate.py`" != "0" ]; then ./test.py -n; fi
+    - if [[ "$CI_JOB_STAGE" == "build" ]];
+      then ./ns3 build;
+      fi
+    - if [[ "$CI_JOB_STAGE" == "build" ]] && [[ "`./utils/ccache-miss-rate.py`" != "0" ]];
+      then `touch build/tests-$CI_COMMIT_REF_SLUG.txt`;
+      fi
+    - if [[ "$CI_JOB_STAGE" == "test" ]] && [[ "$MODE" != "debug" ]] && [[ -f build/tests-$CI_COMMIT_REF_SLUG.txt ]];
+      then ./test.py -n;
+      if [[ $? == 0 ]];
+      then `rm build/tests-$CI_COMMIT_REF_SLUG.txt` || true;
+      fi;
+      fi
     - ccache -s
-    - ./ns3 clean
+    # - ./ns3 clean
   cache:
     # Use separate key for each (debug/default/optimized) jobs because
     # they run in parallel and will otherwise overwrite each other
@@ -57,58 +75,21 @@
   timeout: 12h
   variables:
     CCACHE_BASEDIR_VALUE: ns-3-ccache-storage
-
-# Defines the per-commit jobs. They are executed for any branch
-.base-per-commit-compile:
-  extends: .base-build
-  except:
-    variables:
-      - $CPPYY == "True"
-      - $RELEASE == "weekly"
-      - $RELEASE == "daily"
-  image: archlinux
-  before_script:
-    - pacman-key --init
-    - pacman -Syu --noconfirm
-      base-devel gcc clang cmake ninja ccache
-      python
-      boost gsl gtk3 openmpi
-      openssh
-  variables:
-    ENABLE_MPI: --enable-mpi
-
-per-commit-clang-debug:
-  extends: .base-per-commit-compile
-  variables:
-    MODE: debug
-    COMPILER: clang++
-
-per-commit-gcc-debug:
-  extends: .base-per-commit-compile
-  variables:
-    MODE: debug
-    COMPILER: g++
-
-per-commit-gcc-default:
-  extends: .base-per-commit-compile
-  variables:
-    MODE: default
-    COMPILER: g++
-
-per-commit-gcc-optimized:
-  extends: .base-per-commit-compile
-  variables:
-    MODE: optimized
-    COMPILER: g++
+  artifacts:
+    expire_in: 2 day
+    paths:
+      - build/
+      - .lock-*
 
 # Weekly jobs for other distribution and compilers
 include:
+  - "utils/tests/gitlab-ci-per-commit.yml"
   - "utils/tests/gitlab-ci-alpine.yml"
   - "utils/tests/gitlab-ci-ubuntu.yml"
   - "utils/tests/gitlab-ci-fedora.yml"
   - "utils/tests/gitlab-ci-gcc.yml"
   - "utils/tests/gitlab-ci-clang.yml"
-  - "utils/tests/gitlab-ci-test.yml"
+  - "utils/tests/gitlab-ci-daily.yml"
   - "utils/tests/gitlab-ci-code-linting.yml"
   - "utils/tests/gitlab-ci-doc.yml"
   - "utils/tests/gitlab-ci-cppyy.yml"
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/TestBase.py ns-3.39/utils/tests/TestBase.py
--- ns-3.38/utils/tests/TestBase.py	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/TestBase.py	2023-07-05 14:23:48.137428500 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 #
 # Copyright (c) 2014 Siddharth Santurkar
 #
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/test-ns3.py ns-3.39/utils/tests/test-ns3.py
--- ns-3.38/utils/tests/test-ns3.py	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/test-ns3.py	2023-07-05 14:23:48.141428500 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 #
 # Copyright (c) 2021 Universidade de BrasÃ­lia
 #
@@ -970,7 +969,7 @@
             self.assertLess(len(get_enabled_modules()), len(self.ns3_modules))
             self.assertIn("ns3-lte", enabled_modules)
             self.assertTrue(get_test_enabled())
-            self.assertGreaterEqual(len(get_programs_list()), len(self.ns3_executables))
+            self.assertLessEqual(len(get_programs_list()), len(self.ns3_executables))
 
             # Replace the ns3rc file with the wifi module, enabling examples and disabling tests
             with open(ns3rc_script, "w") as f:
@@ -1210,7 +1209,8 @@
         test_files = ["scratch/main.cc",
                       "scratch/empty.cc",
                       "scratch/subdir1/main.cc",
-                      "scratch/subdir2/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
@@ -1253,6 +1253,25 @@
             else:
                 self.assertEqual(return_code, 1)
 
+        # Now test them in CMake 3.10
+        run_ns3("clean")
+        with DockerContainerManager(self, "ubuntu:18.04") as container:
+            container.execute("apt-get update")
+            container.execute("apt-get install -y python3 cmake g++-8 ninja-build")
+            try:
+                container.execute(
+                    "./ns3 configure --enable-modules=core,network,internet -- -DCMAKE_CXX_COMPILER=/usr/bin/g++-8")
+            except DockerException as e:
+                self.fail()
+            for path in test_files:
+                path = path.replace(".cc", "")
+                try:
+                    container.execute(f"./ns3 run {path}")
+                except DockerException as e:
+                    if "main" in path:
+                        self.fail()
+        run_ns3("clean")
+
         # Delete the test files and reconfigure to clean them up
         for path in test_files + backup_files:
             source_absolute_path = os.path.join(ns3_path, path)
@@ -1267,7 +1286,7 @@
                                    )[0]
 
             os.remove(os.path.join(executable_absolute_path, executable_name))
-            if path not in ["scratch/main.cc", "scratch/empty.cc"]:
+            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}\"")
@@ -1368,10 +1387,10 @@
                 self.assertIn("Invalid library name: %s" % invalid_or_nonexistent_library, stderr)
             elif invalid_or_nonexistent_library in ["gsd", "libfi", "calibre"]:
                 self.assertEqual(return_code, 2)  # should fail due to missing library
-                # GCC's LD says cannot find
-                # LLVM's LLD says unable to find
                 if "lld" in stdout + 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:
                     self.assertIn("cannot find -l%s" % invalid_or_nonexistent_library, stderr)
             else:
@@ -1839,6 +1858,21 @@
             except DockerException as e:
                 self.assertTrue(False, "Precompiled headers should have been enabled")
 
+    def test_24_CheckTestSettings(self):
+        """!
+        Check for regressions in test object build.
+        @return None
+        """
+        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')
+        self.assertEqual(return_code, 0)
+        self.assertTrue(os.path.exists(test_module_cache))
+
 
 class NS3BuildBaseTestCase(NS3BaseTestCase):
     """!
@@ -2303,7 +2337,7 @@
 
         # First enable examples and static build
         return_code, stdout, stderr = run_ns3(
-            "configure -G \"{generator}\" --enable-python-bindings")
+            "configure -G \"{generator}\" --enable-examples --enable-python-bindings")
 
         # If configuration passes, we are half way done
         self.assertEqual(return_code, 0)
@@ -2320,6 +2354,58 @@
         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):
+        """!
+        Test if we had regressions with brite, click and openflow modules
+        that depend on homonymous libraries
+        @return None
+        """
+        if shutil.which("git") is None:
+            self.skipTest("Missing git")
+
+        # First enable automatic components fetching
+        return_code, stdout, stderr = run_ns3("configure -- -DNS3_FETCH_OPTIONAL_COMPONENTS=ON")
+        self.assertEqual(return_code, 0)
+
+        # Build the optional components to check if their dependencies were fetched
+        # and there were no build regressions
+        return_code, stdout, stderr = run_ns3("build brite click openflow")
+        self.assertEqual(return_code, 0)
+
+    def test_14_LinkContribModuleToSrcModule(self):
+        """!
+        Test if we can link contrib modules to src modules
+        @return None
+        """
+        if shutil.which("git") is None:
+            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")
+        # Remove pre-existing directories
+        if os.path.exists(destination_contrib):
+            shutil.rmtree(destination_contrib)
+        if os.path.exists(destination_src):
+            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)
+
+        # Then configure
+        return_code, stdout, stderr = run_ns3("configure --enable-examples")
+        self.assertEqual(return_code, 0)
+
+        # Build the src module that depend on a contrib module
+        return_code, stdout, stderr = run_ns3("run source-example")
+        self.assertEqual(return_code, 0)
+
+        # Remove module copies
+        shutil.rmtree(destination_contrib)
+        shutil.rmtree(destination_src)
+
 
 class NS3ExpectedUseTestCase(NS3BaseTestCase):
     """!
@@ -2810,10 +2896,15 @@
                 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)
+
+                    # skip everything that isn't a file
+                    if not os.path.isfile(filepath):
+                        continue
+
                     # skip svg files
                     if file.endswith(".svg"):
                         continue
-                    filepath = os.path.join(root, file)
 
                     try:
                         with open(filepath, "r") as f:
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/utils/tests/test-test.py ns-3.39/utils/tests/test-test.py
--- ns-3.38/utils/tests/test-test.py	2023-03-16 20:54:23.884869300 -0700
+++ ns-3.39/utils/tests/test-test.py	2023-07-05 14:23:48.141428500 -0700
@@ -1,5 +1,4 @@
 #! /usr/bin/env python3
-## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
 #
 # Copyright (c) 2014 Siddharth Santurkar
 #
diff -Naur -x '*.dia' -x '*.pcap' -x '*.png' ns-3.38/VERSION ns-3.39/VERSION
--- ns-3.38/VERSION	2023-03-16 20:54:51.628957000 -0700
+++ ns-3.39/VERSION	2023-07-05 14:24:01.481474000 -0700
@@ -1 +1 @@
-3.38
+3.39
